feat: add classification-based filtering and fix company name display

- Added classification column to companies table
- Created excluded_classifications setting (default: Tools Only)
- Updated kiosk stats and activity APIs to filter by classification
- Added comprehensive classification filtering UI with checkboxes
- Support for common classifications (Tools Only, Wulff Consulting Client, Customer)
- Allow custom classification entry
- Fixed company name display in settings (convert string IDs properly)
- Classification filtering works alongside company ID exclusions
This commit is contained in:
root 2026-02-03 09:17:07 -05:00
parent 62ec703f81
commit 1d99a8f659
4 changed files with 176 additions and 10 deletions

View file

@ -12,6 +12,7 @@ interface Company {
interface KioskSettings {
excluded_company_ids: number[];
excluded_classifications: string[];
cycle_interval: number;
refresh_interval: number;
show_rmm_alerts: boolean;
@ -21,6 +22,7 @@ export default function KioskSettingsPage() {
const router = useRouter();
const [settings, setSettings] = useState<KioskSettings>({
excluded_company_ids: [],
excluded_classifications: [],
cycle_interval: 7,
refresh_interval: 60,
show_rmm_alerts: false,
@ -99,6 +101,14 @@ export default function KioskSettingsPage() {
setting_value: settings.excluded_company_ids,
}),
}),
fetch('/api/kiosk/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
setting_key: 'excluded_classifications',
setting_value: settings.excluded_classifications,
}),
}),
fetch('/api/kiosk/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@ -135,7 +145,9 @@ export default function KioskSettingsPage() {
};
const getCompanyName = (companyId: number) => {
return companies.find(c => c.id === companyId)?.companyName || `Company ${companyId}`;
// API returns id as string, settings stores as number - convert for comparison
const idStr = companyId.toString();
return companies.find(c => c.id.toString() === idStr)?.companyName || `Company ${companyId}`;
};
if (loading) {
@ -232,6 +244,96 @@ export default function KioskSettingsPage() {
</div>
</div>
{/* Excluded Classifications Section */}
<div className="bg-gray-900 rounded-lg p-6 border border-gray-800">
<h2 className="text-xl font-semibold mb-4">Excluded Company Classifications</h2>
<p className="text-gray-400 mb-4">
Tickets from companies with these classifications will not appear in the kiosk display.
</p>
{/* Common Classifications */}
<div className="space-y-2 mb-4">
{['Tools Only', 'Wulff Consulting Client', 'Customer'].map(classification => (
<label key={classification} className="flex items-center gap-3 bg-gray-800 rounded px-4 py-3 border border-gray-700 cursor-pointer hover:bg-gray-750">
<input
type="checkbox"
checked={settings.excluded_classifications.includes(classification)}
onChange={(e) => {
if (e.target.checked) {
setSettings({
...settings,
excluded_classifications: [...settings.excluded_classifications, classification],
});
} else {
setSettings({
...settings,
excluded_classifications: settings.excluded_classifications.filter(c => c !== classification),
});
}
}}
className="w-5 h-5 rounded border-gray-600 text-blue-600 focus:ring-blue-500"
/>
<span className="flex-1">{classification}</span>
</label>
))}
</div>
{/* Custom Classification Input */}
<div className="mt-4">
<label className="block text-sm font-medium mb-2 text-gray-400">
Add custom classification:
</label>
<div className="flex gap-2">
<input
type="text"
placeholder="Enter classification name..."
className="flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-white"
onKeyPress={(e) => {
if (e.key === 'Enter') {
const value = (e.target as HTMLInputElement).value.trim();
if (value && !settings.excluded_classifications.includes(value)) {
setSettings({
...settings,
excluded_classifications: [...settings.excluded_classifications, value],
});
(e.target as HTMLInputElement).value = '';
}
}
}}
/>
</div>
<p className="text-xs text-gray-500 mt-1">Press Enter to add</p>
</div>
{/* Custom Classifications List */}
{settings.excluded_classifications.filter(c => !['Tools Only', 'Wulff Consulting Client', 'Customer'].includes(c)).length > 0 && (
<div className="mt-4 space-y-2">
<div className="text-sm font-medium text-gray-400">Custom classifications:</div>
{settings.excluded_classifications
.filter(c => !['Tools Only', 'Wulff Consulting Client', 'Customer'].includes(c))
.map(classification => (
<div
key={classification}
className="flex items-center justify-between bg-gray-800 rounded px-4 py-3 border border-gray-700"
>
<span>{classification}</span>
<Button
variant="ghost"
size="sm"
onClick={() => setSettings({
...settings,
excluded_classifications: settings.excluded_classifications.filter(c => c !== classification),
})}
className="text-red-400 hover:text-red-300 hover:bg-red-900/20"
>
<Trash2 className="w-4 h-4" />
</Button>
</div>
))}
</div>
)}
</div>
{/* Display Settings */}
<div className="bg-gray-900 rounded-lg p-6 border border-gray-800">
<h2 className="text-xl font-semibold mb-4">Display Settings</h2>