Use searchable combobox for collaborator selection
This commit is contained in:
@@ -12,9 +12,11 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { Plus, Pencil, Trash2, Search, X, BookOpen } from 'lucide-react';
|
import { Plus, Pencil, Trash2, Search, X, BookOpen, ChevronsUpDown } from 'lucide-react';
|
||||||
import { RichTextEditor } from '@/components/RichTextEditor';
|
import { RichTextEditor } from '@/components/RichTextEditor';
|
||||||
import { formatIDR } from '@/lib/format';
|
import { formatIDR } from '@/lib/format';
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
@@ -91,7 +93,7 @@ export default function AdminProducts() {
|
|||||||
const [filterType, setFilterType] = useState<string>('all');
|
const [filterType, setFilterType] = useState<string>('all');
|
||||||
const [filterStatus, setFilterStatus] = useState<string>('all');
|
const [filterStatus, setFilterStatus] = useState<string>('all');
|
||||||
const [collaborators, setCollaborators] = useState<CollaboratorProfile[]>([]);
|
const [collaborators, setCollaborators] = useState<CollaboratorProfile[]>([]);
|
||||||
const [collaboratorSearch, setCollaboratorSearch] = useState('');
|
const [collaboratorPickerOpen, setCollaboratorPickerOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user && isAdmin) {
|
if (user && isAdmin) {
|
||||||
@@ -133,12 +135,6 @@ export default function AdminProducts() {
|
|||||||
|
|
||||||
// Get unique product types from actual products
|
// Get unique product types from actual products
|
||||||
const productTypes = ['all', ...Array.from(new Set(products.map(p => p.type)))];
|
const productTypes = ['all', ...Array.from(new Set(products.map(p => p.type)))];
|
||||||
const filteredCollaborators = collaborators.filter((c) => {
|
|
||||||
const q = collaboratorSearch.trim().toLowerCase();
|
|
||||||
if (!q) return true;
|
|
||||||
return (c.name || '').toLowerCase().includes(q) || (c.email || '').toLowerCase().includes(q);
|
|
||||||
});
|
|
||||||
|
|
||||||
const clearFilters = () => {
|
const clearFilters = () => {
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
setFilterType('all');
|
setFilterType('all');
|
||||||
@@ -170,14 +166,12 @@ export default function AdminProducts() {
|
|||||||
profit_share_percentage: product.profit_share_percentage ?? 50,
|
profit_share_percentage: product.profit_share_percentage ?? 50,
|
||||||
auto_grant_access: product.auto_grant_access ?? true,
|
auto_grant_access: product.auto_grant_access ?? true,
|
||||||
});
|
});
|
||||||
setCollaboratorSearch('');
|
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNew = () => {
|
const handleNew = () => {
|
||||||
setEditingProduct(null);
|
setEditingProduct(null);
|
||||||
setForm(emptyProduct);
|
setForm(emptyProduct);
|
||||||
setCollaboratorSearch('');
|
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -563,36 +557,52 @@ export default function AdminProducts() {
|
|||||||
</div>
|
</div>
|
||||||
{form.type === 'webinar' && (
|
{form.type === 'webinar' && (
|
||||||
<div className="space-y-4 border-2 border-border rounded-lg p-4">
|
<div className="space-y-4 border-2 border-border rounded-lg p-4">
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Cari Kolaborator</Label>
|
|
||||||
<Input
|
|
||||||
value={collaboratorSearch}
|
|
||||||
onChange={(e) => setCollaboratorSearch(e.target.value)}
|
|
||||||
placeholder="Cari nama atau email..."
|
|
||||||
className="border-2"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Kolaborator (opsional)</Label>
|
<Label>Kolaborator (opsional)</Label>
|
||||||
<Select
|
<Popover open={collaboratorPickerOpen} onOpenChange={setCollaboratorPickerOpen}>
|
||||||
value={form.collaborator_user_id || '__none__'}
|
<PopoverTrigger asChild>
|
||||||
onValueChange={(value) => setForm({ ...form, collaborator_user_id: value === '__none__' ? '' : value })}
|
<Button variant="outline" role="combobox" className="w-full justify-between border-2">
|
||||||
>
|
{form.collaborator_user_id
|
||||||
<SelectTrigger className="border-2">
|
? (() => {
|
||||||
<SelectValue placeholder="Pilih kolaborator" />
|
const selected = collaborators.find((c) => c.id === form.collaborator_user_id);
|
||||||
</SelectTrigger>
|
return selected ? `${selected.name || 'User'}${selected.email ? ` (${selected.email})` : ''}` : 'Pilih kolaborator';
|
||||||
<SelectContent>
|
})()
|
||||||
<SelectItem value="__none__">Tanpa kolaborator (solo)</SelectItem>
|
: 'Tanpa kolaborator (solo)'}
|
||||||
{filteredCollaborators.map((c) => (
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
<SelectItem key={c.id} value={c.id}>
|
</Button>
|
||||||
{(c.name || 'User') + (c.email ? ` (${c.email})` : '')}
|
</PopoverTrigger>
|
||||||
</SelectItem>
|
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||||
))}
|
<Command>
|
||||||
</SelectContent>
|
<CommandInput placeholder="Cari nama atau email..." />
|
||||||
</Select>
|
<CommandList>
|
||||||
{collaboratorSearch && filteredCollaborators.length === 0 && (
|
<CommandEmpty>Tidak ada kolaborator yang cocok.</CommandEmpty>
|
||||||
<p className="text-sm text-muted-foreground">Tidak ada kolaborator yang cocok.</p>
|
<CommandGroup>
|
||||||
)}
|
<CommandItem
|
||||||
|
value="Tanpa kolaborator (solo)"
|
||||||
|
onSelect={() => {
|
||||||
|
setForm({ ...form, collaborator_user_id: '' });
|
||||||
|
setCollaboratorPickerOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Tanpa kolaborator (solo)
|
||||||
|
</CommandItem>
|
||||||
|
{collaborators.map((c) => (
|
||||||
|
<CommandItem
|
||||||
|
key={c.id}
|
||||||
|
value={`${c.name || 'User'} ${c.email || ''}`}
|
||||||
|
onSelect={() => {
|
||||||
|
setForm({ ...form, collaborator_user_id: c.id });
|
||||||
|
setCollaboratorPickerOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(c.name || 'User') + (c.email ? ` (${c.email})` : '')}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!!form.collaborator_user_id && (
|
{!!form.collaborator_user_id && (
|
||||||
|
|||||||
Reference in New Issue
Block a user