- Add admin dashboard with sync controls and data browser - Implement RMM, Auvik, and Addigy organization mappings - Add chunked ticket sync with progress tracking - Implement entity sync service with rate limiting - Add analytics engine and performance optimizer - Create data browser for all PSA entities - Add navigation components and UI improvements - Implement background processing and sync services - Add comprehensive documentation and migration scripts - Update configuration items with multi-system support - Enhance contact management and purchase history - Add issue type assignment and LLM analyzer - Improve error handling and logging utilities
95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { Check, ChevronsUpDown, Search } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from '@/components/ui/popover';
|
|
import { Company } from '@/lib/types/autotask';
|
|
import { useApi } from '@/lib/hooks/use-api';
|
|
|
|
interface CompanySelectorEnhancedProps {
|
|
value?: number;
|
|
onValueChange: (value: number | undefined, companyName?: string) => void;
|
|
label?: string;
|
|
}
|
|
|
|
export function CompanySelectorEnhanced({
|
|
value,
|
|
onValueChange,
|
|
label = 'Select Company'
|
|
}: CompanySelectorEnhancedProps) {
|
|
const [open, setOpen] = useState(false);
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const { data, loading, error } = useApi<{ companies: Company[] }>('/api/companies');
|
|
|
|
const companies = data?.companies || [];
|
|
const selectedCompany = companies.find(c => c.id === value);
|
|
|
|
const filteredCompanies = companies.filter(company =>
|
|
company.companyName.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
|
|
return (
|
|
<Popover open={open} onOpenChange={setOpen}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
aria-expanded={open}
|
|
className="w-full justify-between"
|
|
disabled={loading || !!error}
|
|
>
|
|
{selectedCompany ? selectedCompany.companyName : (loading ? 'Loading...' : 'Select company...')}
|
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="w-[600px] p-0" align="start">
|
|
<div className="flex items-center border-b px-3">
|
|
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
|
<Input
|
|
placeholder="Search companies..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className="border-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
|
/>
|
|
</div>
|
|
<div className="max-h-[300px] overflow-y-auto p-1">
|
|
{filteredCompanies.length === 0 ? (
|
|
<div className="py-6 text-center text-sm text-muted-foreground">
|
|
No company found.
|
|
</div>
|
|
) : (
|
|
filteredCompanies.map((company) => (
|
|
<div
|
|
key={company.id}
|
|
className={cn(
|
|
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground',
|
|
value === company.id && 'bg-accent'
|
|
)}
|
|
onClick={() => {
|
|
onValueChange(company.id, company.companyName);
|
|
setOpen(false);
|
|
setSearchTerm('');
|
|
}}
|
|
>
|
|
<Check
|
|
className={cn(
|
|
'mr-2 h-4 w-4',
|
|
value === company.id ? 'opacity-100' : 'opacity-0'
|
|
)}
|
|
/>
|
|
{company.companyName}
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
);
|
|
}
|