diff --git a/src/context/WorkspaceContext.tsx b/src/context/WorkspaceContext.tsx index 165b79c..898cdb3 100644 --- a/src/context/WorkspaceContext.tsx +++ b/src/context/WorkspaceContext.tsx @@ -3,6 +3,7 @@ import { fetchWorkspaces, createWorkspace, type Workspace } from "../api/workspa import { useTranslation } from "../hooks/useTranslation" import { toast } from "sonner" import { Button } from "../components/ui/button" +import { Input } from "../components/ui/input" interface WorkspaceContextType { workspaces: Workspace[] @@ -100,7 +101,7 @@ export const WorkspaceProvider = ({ children }: { children: ReactNode }) => { - setNewWorkspaceName(e.target.value)} diff --git a/src/pages/WorkspaceCreate.tsx b/src/pages/WorkspaceCreate.tsx index 914b94f..145db64 100644 --- a/src/pages/WorkspaceCreate.tsx +++ b/src/pages/WorkspaceCreate.tsx @@ -1,13 +1,16 @@ import React, { useState, useEffect, useRef, Fragment } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useBlocker, useNavigate } from 'react-router-dom'; import { useTranslation } from '../hooks/useTranslation'; -import { AlertCircle, UserPlus, Trash2, Shield } from 'lucide-react'; +import { AlertCircle, UserPlus, Trash2, Shield, Loader2 } 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()) @@ -52,6 +55,25 @@ export default function WorkspaceCreate() { const [memberIdToDelete, setMemberIdToDelete] = useState(null); const searchTimeoutRef = useRef(); + const hasUnsavedChanges = name.trim() !== '' || description.trim() !== '' || members.length > 0; + + 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); @@ -101,7 +123,7 @@ export default function WorkspaceCreate() { description, members: members.map(m => ({ user_id: m.user.id, role: m.role })) }; - const newWorkspace = await createWorkspace({ name, description }); + const newWorkspace = await createWorkspace(payload); window.dispatchEvent(new CustomEvent('workspace_created', { detail: newWorkspace @@ -152,181 +174,210 @@ export default function WorkspaceCreate() { setMembers(members.map(m => m.localId === localId ? { ...m, role: newRole as any } : m)); }; - // Creator has full rights to manage members before creating const canManageMembers = true; const isFirstOwner = true; return ( -
-

+
+

{t.workspace?.createTitle || "Create 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 - /> +
+
+ +
+ + 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" + /> +
+
+ + +
+
-
- -