import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { apiRequest } from "@/lib/queryClient"; import { User } from "@shared/schema"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Loader2, Share2, Check, Search } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; import { ScrollArea } from "@/components/ui/scroll-area"; import { useTranslation } from "react-i18next"; interface ShareTaskModalProps { taskId: string; open: boolean; onOpenChange: (open: boolean) => void; } export function ShareTaskModal({ taskId, open, onOpenChange }: ShareTaskModalProps) { const { toast } = useToast(); const { t } = useTranslation(); const [searchQuery, setSearchQuery] = useState(""); const [debouncedQuery, setDebouncedQuery] = useState(""); const [userToConfirm, setUserToConfirm] = useState<{ id: string; username: string } | null>(null); const { data: users, isLoading: searchLoading } = useQuery[]>({ queryKey: ["/api/users/search", debouncedQuery], queryFn: async () => { if (debouncedQuery.length < 2) return []; const res = await fetch(`/api/users/search?q=${encodeURIComponent(debouncedQuery)}`); if (!res.ok) throw new Error("Failed to search users"); return res.json(); }, enabled: debouncedQuery.length >= 2, }); const { data: sharedUsers, refetch: refetchShared, isLoading: sharedLoading } = useQuery[]>({ queryKey: [`/api/tasks/${taskId}/shared-users`], // Needs to be dependent on taskId enabled: open, // Only fetch when open }); const shareMutation = useMutation({ mutationFn: async (targetUserId: string) => { const res = await apiRequest("POST", `/api/tasks/${taskId}/share`, { targetUserId }); return res.json(); }, onSuccess: () => { toast({ title: t('share.successTask') }); setUserToConfirm(null); setSearchQuery(""); setDebouncedQuery(""); // Clear search refetchShared(); // Update list }, onError: () => { toast({ title: t('share.errorTask'), variant: "destructive" }); }, }); const unshareMutation = useMutation({ mutationFn: async (targetUserId: string) => { const res = await apiRequest("DELETE", `/api/tasks/${taskId}/share/${targetUserId}`); return res.json(); }, onSuccess: () => { toast({ title: t('share.removeSuccess') }); refetchShared(); }, onError: () => { toast({ title: t('share.errorTask'), variant: "destructive" }); }, }); // Confirmation State const confirmShare = () => { if (userToConfirm) { shareMutation.mutate(userToConfirm.id); } }; return ( { if (!val) { setUserToConfirm(null); // Reset on close setSearchQuery(""); setDebouncedQuery(""); } onOpenChange(val); }}> {t('share.taskTitle')} {userToConfirm ? (
{userToConfirm.username.substring(0, 2).toUpperCase()}

{userToConfirm.username}

{t('share.confirmShareDesc', { username: userToConfirm.username })}

) : (
{/* Current Shared Users */}

{t('share.sharedWith')}

{sharedLoading ? (
) : sharedUsers && sharedUsers.length > 0 ? (
{sharedUsers.map(user => (
{user.username.substring(0, 2).toUpperCase()} {user.username}
))}
) : (

Not shared with anyone yet.

)}
{/* Search to Add */}
{ setSearchQuery(e.target.value); // Simple manual debounce for now }} onKeyUp={(e) => { // Debounce could be improved, but sufficient setDebouncedQuery(searchQuery); }} />
{searchLoading && debouncedQuery.length >= 2 ? (
) : users && users.length > 0 ? (
{users.filter(u => !sharedUsers?.find(su => su.id === u.id)).map((user) => (
setUserToConfirm(user)} >
{user.username.substring(0, 2).toUpperCase()} {user.username}
))} {users.filter(u => !sharedUsers?.find(su => su.id === u.id)).length === 0 && (
No new users found to share with.
)}
) : debouncedQuery.length >= 2 ? (
{t('share.noUsers')}
) : (
{t('share.typeToSearch')}
)}
)}
); }