import React, { useState, useEffect, useRef, Fragment } from 'react'; import { useBlocker, useNavigate } from 'react-router-dom'; import { useTranslation } from '../hooks/useTranslation'; import { AlertCircle, UserPlus, Trash2, Shield, Loader2, UploadCloud } from 'lucide-react'; import { Dialog, Transition } from '@headlessui/react'; import { toast } from 'sonner'; import { createWorkspace } from '../api/workspaces'; import { searchUserByExactMobile, type SearchedUser } from '../api/users'; import { useAppContext } from '../context/AppContext'; import { Button } from '../components/ui/button'; import { Input } from '../components/ui/input'; import { Select } from '../components/ui/Select'; import { TextAreaInput } from '../components/ui/TextAreaInput'; const toEnglishDigits = (str: string) => { return str.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d).toString()) .replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d).toString()); }; interface LocalMember { localId: string; user: SearchedUser; role: 'owner' | 'admin' | 'member' | 'guest'; } export default function WorkspaceCreate() { const navigate = useNavigate(); 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 [thumbnailFile, setThumbnailFile] = useState(null); const [thumbnailPreview, setThumbnailPreview] = useState(null); const [isSaving, setIsSaving] = useState(false); // Members States (Local) 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'); // Modal State const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [memberIdToDelete, setMemberIdToDelete] = useState(null); const searchTimeoutRef = useRef | null>(null); const hasUnsavedChanges = name.trim() !== '' || description.trim() !== '' || members.length > 0 || !!thumbnailFile; useEffect(() => { if (!thumbnailFile) { setThumbnailPreview(null); return; } const objectUrl = URL.createObjectURL(thumbnailFile); setThumbnailPreview(objectUrl); return () => URL.revokeObjectURL(objectUrl); }, [thumbnailFile]); const handleThumbnailChange = (file: File | null) => { if (!file) { setThumbnailFile(null); return; } const allowedTypes = ["image/jpeg", "image/png", "image/webp"]; if (!allowedTypes.includes(file.type)) { toast.error(t.workspace?.thumbnailInvalidType || "Unsupported image type. Use JPG, PNG, or WebP."); return; } const maxBytes = 2 * 1024 * 1024; if (file.size > maxBytes) { toast.error(t.workspace?.thumbnailMaxSizeError || "Image size must be 2MB or less."); return; } setThumbnailFile(file); }; 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 || "You have unsaved changes. Are you sure you want to leave?"); } return 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) { // Prevent finding self if (user.id === currentUserId) { setSearchResult(null); setSearchError(true); toast.error(t.workspace?.toast?.cannotAddSelf || "You are automatically the owner."); } else { 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, currentUserId, t]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name.trim()) return; try { setIsSaving(true); const payload = { name, description, members: members.map(m => ({ user_id: m.user.id, role: m.role })), thumbnail: thumbnailFile, }; const newWorkspace = await createWorkspace(payload); window.dispatchEvent(new CustomEvent('workspace_created', { detail: newWorkspace })); toast.success(t.workspace?.toast?.successCreate || "Workspace created successfully."); navigate('/workspaces'); } catch (error) { toast.error(t.workspace?.toast?.errorCreate || "Failed to create workspace."); } finally { setIsSaving(false); } }; const handleAddMember = () => { if (!searchResult) return; if (members.some(m => m.user.id === searchResult.id)) { toast.error(t.workspace?.userAlreadyAdded || "User already added."); return; } const newMember: LocalMember = { localId: Math.random().toString(36).substr(2, 9), user: searchResult, role: newMemberRole }; setMembers([...members, newMember]); setSearchQuery(''); setSearchResult(null); setNewMemberRole('member'); }; const openDeleteModal = (localId: string) => { setMemberIdToDelete(localId); setIsDeleteDialogOpen(true); }; const handleDeleteMember = () => { if (!memberIdToDelete) return; setMembers(members.filter(m => m.localId !== memberIdToDelete)); setIsDeleteDialogOpen(false); setMemberIdToDelete(null); }; const handleChangeRole = (localId: string, newRole: string) => { setMembers(members.map(m => m.localId === localId ? { ...m, role: newRole as any } : m)); }; const canManageMembers = true; const isFirstOwner = true; return (

{t.workspace?.createTitle || "Create Workspace"}

setName(e.target.value)} placeholder={t.workspace?.namePlaceholder || "e.g. My Company"} 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" />
{thumbnailFile && ( )}

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

{canManageMembers && (
{isSearching && (
)} { const englishDigits = toEnglishDigits(e.target.value); if (/\D/.test(englishDigits)) { toast.warning(t.workspace?.onlyNumbersAllowed || "Only numbers are allowed."); } const numbersOnly = englishDigits.replace(/\D/g, ''); setSearchQuery(numbersOnly); }} className="w-full px-4 pe-10 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" />
{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.localId, 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" />
); })} {members.length === 0 && (

{t.workspace?.noMembers || "No members added yet."}

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

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

); }