import React, { useState, useEffect, useRef, Fragment, useMemo } from 'react'; import { useBlocker, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from '../hooks/useTranslation'; import { AlertCircle, UserPlus, Trash2, Shield } from 'lucide-react'; import { Dialog, Transition } from '@headlessui/react'; import { toast } from 'sonner'; import { updateWorkspace, addWorkspaceMembership, removeWorkspaceMembership, updateWorkspaceMembership, fetchWorkspaceMemberships, getWorkspace } from '../api/workspaces'; import { searchUserByExactMobile, type SearchedUser } from '../api/users'; import { useAppContext } from '../context/AppContext'; import { Button } from '../components/ui/button'; import { InfiniteScroll } from '../components/infiniteScroll'; import { Select } from '../components/ui/Select'; import { Input } from '../components/ui/input'; import { TextAreaInput } from '../components/ui/TextAreaInput'; const toEnglishDigits = (str: string) => { return str.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d).toString()) .replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d).toString()); }; const LIMIT = 10; export default function EditWorkspace() { const navigate = useNavigate(); const { id } = useParams<{ id: string }>(); const { t, lang } = useTranslation(); const isFa = lang === 'fa'; const toPersianNum = (num: string | number | undefined | null) => { if (num === null || num === undefined) return num; if (!isFa) return num; return num.toString().replace(/\d/g, d => '۰۱۲۳۴۵۶۷۸۹'[d as any]); }; const { user } = useAppContext(); const currentUserId = user?.id || ''; // Workspace Info States const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [myRole, setMyRole] = useState<'owner' | 'admin' | 'member' | 'guest'>('member'); const [workspaceOwnerId, setWorkspaceOwnerId] = useState(''); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); // Members States const [members, setMembers] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [searchResult, setSearchResult] = useState(null); const [searchError, setSearchError] = useState(false); const [isSearching, setIsSearching] = useState(false); const [newMemberRole, setNewMemberRole] = useState<'owner' | 'admin' | 'member' | 'guest'>('member'); // Pagination States const [offset, setOffset] = useState(0); const [hasMore, setHasMore] = useState(true); const [isLoadingMembers, setIsLoadingMembers] = useState(false); // Modal State const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [memberIdToDelete, setMemberIdToDelete] = useState(null); const searchTimeoutRef = useRef(); const [initialData, setInitialData] = useState({ name: '', description: '', }); const hasUnsavedChanges = useMemo(() => { if (isLoading) return false; const isNameChanged = name.trim() !== (initialData.name || '').trim(); const isDescChanged = description.trim() !== (initialData.description || '').trim(); return isNameChanged || isDescChanged; }, [name, description, initialData, isLoading]); useEffect(() => { const handleBeforeUnload = (e: BeforeUnloadEvent) => { if (hasUnsavedChanges && !isSaving) { e.preventDefault(); e.returnValue = ''; } }; window.addEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, [hasUnsavedChanges, isSaving]); useBlocker(({ currentLocation, nextLocation }) => { if (hasUnsavedChanges && !isSaving && currentLocation.pathname !== nextLocation.pathname) { return !window.confirm(t.confirmLeave || "تغییرات ذخیره نشده‌ای دارید. آیا مطمئن هستید که می‌خواهید خارج شوید؟"); } return false; }); useBlocker(({ currentLocation, nextLocation }) => { if (hasUnsavedChanges && !isSaving && currentLocation.pathname !== nextLocation.pathname) { return !window.confirm(t.confirmLeave || "You have unsaved changes. Are you sure you want to leave?"); } return false; }); useEffect(() => { if (id) loadData(); }, [id]); const loadData = async () => { try { setIsLoading(true); const workspaceData = await getWorkspace(id!); setName(workspaceData.name); setDescription(workspaceData.description || ''); setMyRole(workspaceData.my_role || 'member'); setWorkspaceOwnerId(workspaceData.owner || ''); const membersData = await fetchWorkspaceMemberships({ workspace: id!, limit: LIMIT, offset: 0 }); const results = membersData.results || (Array.isArray(membersData) ? membersData : []); setMembers(results); setOffset(LIMIT); setHasMore(!!membersData.next); setInitialData({ name: workspaceData.name, description: workspaceData.description || '', }); } catch (error) { toast.error(t.workspace?.toast?.errorLoad || "Failed to load workspace data."); navigate('/workspaces'); } finally { setIsLoading(false); } }; const loadMoreMembers = async () => { if (isLoadingMembers || !hasMore || !id) return; try { setIsLoadingMembers(true); const membersData = await fetchWorkspaceMemberships({ workspace: id, limit: `${LIMIT}`, offset: `${offset}` }); const results = membersData.results || []; setMembers((prev) => [...prev, ...results]); setOffset((prev) => prev + LIMIT); setHasMore(!!membersData.next); } catch (error) { console.error("Failed to load more members", error); } finally { setIsLoadingMembers(false); } }; useEffect(() => { if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current); const cleanQuery = toEnglishDigits(searchQuery.trim()); setSearchError(false); if (cleanQuery.length >= 10) { searchTimeoutRef.current = setTimeout(async () => { setIsSearching(true); try { const user = await searchUserByExactMobile(cleanQuery); if (user && user.id) { setSearchResult(user); setSearchError(false); } else { setSearchResult(null); setSearchError(true); } } catch (error) { setSearchResult(null); setSearchError(true); } finally { setIsSearching(false); } }, 500); } else { setSearchResult(null); } return () => { if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current); }; }, [searchQuery]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name.trim() || !id) return; try { setIsSaving(true); await updateWorkspace(id, { name, description }); toast.success(t.workspace?.toast?.successUpdate || "Workspace updated successfully."); window.dispatchEvent(new CustomEvent('workspace_edited', { detail: { id, name, description } })); navigate('/workspaces'); } catch (error) { toast.error(t.workspace?.toast?.errorUpdate || "Failed to update workspace."); } finally { setIsSaving(false); } }; const handleAddMember = async () => { if (!searchResult || !id) return; try { const newMembership = await addWorkspaceMembership({ workspace: id, user: searchResult.id, role: newMemberRole }); setMembers([newMembership, ...members]); toast.success(t.workspace?.toast?.successAdd || "Member added successfully."); setSearchQuery(''); setSearchResult(null); setNewMemberRole('member'); } catch (error) { toast.error(t.workspace?.toast?.errorAdd || "Failed to add member."); } }; const openDeleteModal = (membershipId: string) => { setMemberIdToDelete(membershipId); setIsDeleteDialogOpen(true); }; const handleDeleteMember = async () => { if (!memberIdToDelete) return; try { await removeWorkspaceMembership(memberIdToDelete); setMembers(members.filter(m => m.id !== memberIdToDelete)); toast.success(t.workspace?.toast?.successRemove || "Member removed successfully."); setIsDeleteDialogOpen(false); } catch (error) { toast.error(t.workspace?.toast?.errorRemove || "Failed to remove member."); } }; const handleChangeRole = async (membershipId: string, newRole: string) => { try { await updateWorkspaceMembership(membershipId, { role: newRole }); setMembers(members.map(m => m.id === membershipId ? { ...m, role: newRole } : m)); toast.success(t.workspace?.toast?.successRole || "Role updated successfully."); } catch (error) { toast.error(t.workspace?.toast?.errorRole || "Failed to update role."); } }; const canManageMembers = myRole === 'owner' || myRole === 'admin'; const isFirstOwner = currentUserId === workspaceOwnerId; if (isLoading) return
{t.workspace?.loading || "Loading..."}
; return (

{t.workspace?.editTitle || "Edit Workspace"}

{/* --- ستون سمت چپ: فرم ویرایش --- */}
setName(e.target.value)} className="w-full px-4 py-2 border border-slate-300 dark:border-slate-700 rounded-lg bg-transparent text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500" required />
setDescription(e.target.value)} placeholder={t.workspace?.descriptionPlaceholder || "Optional description..."} className="w-full px-4 py-2 border border-slate-300 dark:border-slate-700 rounded-lg bg-transparent text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 h-32 resize-none" />
{/* --- ستون سمت راست: لیست اعضا --- */}
{/* بخش جستجو و هدر (ثابت در بالا) */}

{ t.workspace?.members || "Members" }

{canManageMembers && (
setSearchQuery(toEnglishDigits(e.target.value))} className="w-full px-4 py-2 border border-slate-300 dark:border-slate-700 rounded-lg bg-transparent text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500" dir="auto" /> {isSearching &&

{t.workspace?.searching || "Searching..."}

} {searchError && !isSearching && (
{t.workspace?.userNotFound || "No user found with this exact number."}
)} {searchResult && !isSearching && (
{searchResult.profile_picture ? ( {searchResult.first_name} ) : (
{searchResult.first_name?.[0] || "U"}
)}

{searchResult.first_name} {searchResult.last_name}

{toPersianNum(searchResult.mobile)}

handleChangeRole(m.id, val)} options={[ ...(isFirstOwner ? [{ value: "owner", label: t.workspace?.roles?.owner || "Owner" }] : []), { value: "admin", label: t.workspace?.roles?.admin || "Admin" }, { value: "member", label: t.workspace?.roles?.member || "Member" }, { value: "guest", label: t.workspace?.roles?.guest || "Guest" }, ]} buttonClassName="w-[110px] px-3 py-1.5 text-sm" /> ) : ( {m.role === 'owner' && } {m.role ? t.workspace?.roles?.[m.role] || m.role : "-"} )} {canManageMembers && !isThisMemberTheFirstOwner && (isFirstOwner || m.role !== 'owner') && ( )}
); })} {members.length === 0 && !isLoadingMembers && (

{t.workspace?.noMembers || "No members found."}

)}
setIsDeleteDialogOpen(false)}>
{t.workspace?.confirmDeleteTitle || "Remove Member"}

{t.workspace?.confirmDeleteMessage || "Are you sure you want to remove this member from the workspace?"}

); }