"use client"; import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; import { useCallback, useEffect, useState } from "react"; import { GoPlus } from "react-icons/go"; import { LuPencil, LuTrash2 } from "react-icons/lu"; import { CreateGroupDialog } from "@/components/create-group-dialog"; import { DeleteGroupDialog } from "@/components/delete-group-dialog"; import { EditGroupDialog } from "@/components/edit-group-dialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { showErrorToast, showSuccessToast } from "@/lib/toast-utils"; import type { GroupWithCount, ProfileGroup } from "@/types"; import { RippleButton } from "./ui/ripple"; type SyncStatus = "disabled" | "syncing" | "synced" | "error" | "waiting"; function getSyncStatusDot( group: GroupWithCount, liveStatus: SyncStatus | undefined, ): { color: string; tooltip: string; animate: boolean } { const status = liveStatus ?? (group.sync_enabled ? "synced" : "disabled"); switch (status) { case "syncing": return { color: "bg-yellow-500", tooltip: "Syncing...", animate: true }; case "synced": return { color: "bg-green-500", tooltip: group.last_sync ? `Synced ${new Date(group.last_sync * 1000).toLocaleString()}` : "Synced", animate: false, }; case "waiting": return { color: "bg-yellow-500", tooltip: "Waiting to sync", animate: false, }; case "error": return { color: "bg-red-500", tooltip: "Sync error", animate: false }; default: return { color: "bg-gray-400", tooltip: "Not synced", animate: false }; } } interface GroupManagementDialogProps { isOpen: boolean; onClose: () => void; onGroupManagementComplete: () => void; } export function GroupManagementDialog({ isOpen, onClose, onGroupManagementComplete, }: GroupManagementDialogProps) { const [groups, setGroups] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // Dialog states const [createDialogOpen, setCreateDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [selectedGroup, setSelectedGroup] = useState( null, ); const [groupSyncStatus, setGroupSyncStatus] = useState< Record >({}); const [groupInUse, setGroupInUse] = useState>({}); const [isTogglingSync, setIsTogglingSync] = useState>( {}, ); // Listen for group sync status events useEffect(() => { let unlisten: (() => void) | undefined; const setupListener = async () => { unlisten = await listen<{ id: string; status: string }>( "group-sync-status", (event) => { const { id, status } = event.payload; setGroupSyncStatus((prev) => ({ ...prev, [id]: status as SyncStatus, })); }, ); }; void setupListener(); return () => { unlisten?.(); }; }, []); const loadGroups = useCallback(async () => { setIsLoading(true); setError(null); try { const groupList = await invoke( "get_groups_with_profile_counts", ); setGroups(groupList); // Check which groups are in use by synced profiles const inUse: Record = {}; for (const group of groupList) { try { const inUseBySynced = await invoke( "is_group_in_use_by_synced_profile", { groupId: group.id }, ); inUse[group.id] = inUseBySynced; } catch (_error) { // Ignore errors } } setGroupInUse(inUse); } catch (err) { console.error("Failed to load groups:", err); setError(err instanceof Error ? err.message : "Failed to load groups"); } finally { setIsLoading(false); } }, []); const handleGroupCreated = useCallback( (_newGroup: ProfileGroup) => { void loadGroups(); onGroupManagementComplete(); }, [loadGroups, onGroupManagementComplete], ); const handleGroupUpdated = useCallback( (_updatedGroup: ProfileGroup) => { void loadGroups(); onGroupManagementComplete(); }, [loadGroups, onGroupManagementComplete], ); const handleGroupDeleted = useCallback(() => { void loadGroups(); onGroupManagementComplete(); }, [loadGroups, onGroupManagementComplete]); const handleEditGroup = useCallback((group: GroupWithCount) => { setSelectedGroup(group); setEditDialogOpen(true); }, []); const handleDeleteGroup = useCallback((group: GroupWithCount) => { setSelectedGroup(group); setDeleteDialogOpen(true); }, []); const handleToggleSync = useCallback( async (group: GroupWithCount) => { setIsTogglingSync((prev) => ({ ...prev, [group.id]: true })); try { await invoke("set_group_sync_enabled", { groupId: group.id, enabled: !group.sync_enabled, }); showSuccessToast(group.sync_enabled ? "Sync disabled" : "Sync enabled"); await loadGroups(); } catch (error) { console.error("Failed to toggle sync:", error); showErrorToast( error instanceof Error ? error.message : "Failed to update sync", ); } finally { setIsTogglingSync((prev) => ({ ...prev, [group.id]: false })); } }, [loadGroups], ); useEffect(() => { if (isOpen) { void loadGroups(); } }, [isOpen, loadGroups]); return ( <> Manage Profile Groups Create, edit, and delete profile groups. Profiles without a group will appear in the "Default" group.
{/* Create new group button */}
setCreateDialogOpen(true)} className="flex gap-2 items-center" > Create
{error && (
{error}
)} {/* Groups list */} {isLoading ? (
Loading groups...
) : groups.length === 0 ? (
No groups created yet. Create your first group using the button above.
) : (
Name Profiles Sync Actions {groups.map((group) => { const syncDot = getSyncStatusDot( group, groupSyncStatus[group.id], ); return (

{syncDot.tooltip}

{group.name}
{group.count}
handleToggleSync(group) } disabled={ isTogglingSync[group.id] || groupInUse[group.id] } />
{groupInUse[group.id] ? (

Sync cannot be disabled while this group is used by synced profiles

) : (

{group.sync_enabled ? "Disable sync" : "Enable sync"}

)}

Edit group

Delete group

); })}
)}
Close
setCreateDialogOpen(false)} onGroupCreated={handleGroupCreated} /> setEditDialogOpen(false)} group={selectedGroup} onGroupUpdated={handleGroupUpdated} /> setDeleteDialogOpen(false)} group={selectedGroup} onGroupDeleted={handleGroupDeleted} /> ); }