diff --git a/components/settings/unified/UnifiedSettingsModal.tsx b/components/settings/unified/UnifiedSettingsModal.tsx index cf088fe..ec4f9cc 100644 --- a/components/settings/unified/UnifiedSettingsModal.tsx +++ b/components/settings/unified/UnifiedSettingsModal.tsx @@ -199,7 +199,7 @@ function TabContent({ // Workspace tabs if (context === 'workspace') { switch (activeTab) { - case 'general': return + case 'general': return case 'billing': return case 'members': return case 'notifications': return diff --git a/components/settings/unified/tabs/WorkspaceGeneralTab.tsx b/components/settings/unified/tabs/WorkspaceGeneralTab.tsx index bdbbcd4..bb1bc5c 100644 --- a/components/settings/unified/tabs/WorkspaceGeneralTab.tsx +++ b/components/settings/unified/tabs/WorkspaceGeneralTab.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef, useCallback } from 'react' import { useRouter } from 'next/navigation' import { Input, Button, toast } from '@ciphera-net/ui' import { Spinner } from '@ciphera-net/ui' @@ -9,17 +9,18 @@ import { getOrganization, updateOrganization, deleteOrganization } from '@/lib/a import { getAuthErrorMessage } from '@ciphera-net/ui' import { useUnifiedSettings } from '@/lib/unified-settings-context' -export default function WorkspaceGeneralTab() { +export default function WorkspaceGeneralTab({ onDirtyChange, onRegisterSave }: { onDirtyChange?: (dirty: boolean) => void; onRegisterSave?: (fn: () => Promise) => void }) { const { user } = useAuth() const router = useRouter() const { closeUnifiedSettings } = useUnifiedSettings() const [name, setName] = useState('') const [slug, setSlug] = useState('') const [loading, setLoading] = useState(true) - const [saving, setSaving] = useState(false) const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const [deleteText, setDeleteText] = useState('') const [deleting, setDeleting] = useState(false) + const initialRef = useRef('') + const hasInitialized = useRef(false) useEffect(() => { if (!user?.org_id) return @@ -28,23 +29,36 @@ export default function WorkspaceGeneralTab() { .then(org => { setName(org.name || '') setSlug(org.slug || '') + if (!hasInitialized.current) { + initialRef.current = JSON.stringify({ name: org.name || '', slug: org.slug || '' }) + hasInitialized.current = true + } }) .catch(() => {}) .finally(() => setLoading(false)) }, [user?.org_id]) - const handleSave = async () => { + // Track dirty state + useEffect(() => { + if (!initialRef.current) return + onDirtyChange?.(JSON.stringify({ name, slug }) !== initialRef.current) + }, [name, slug, onDirtyChange]) + + const handleSave = useCallback(async () => { if (!user?.org_id) return - setSaving(true) try { await updateOrganization(user.org_id, name, slug) + initialRef.current = JSON.stringify({ name, slug }) + onDirtyChange?.(false) toast.success('Organization updated') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to update organization') - } finally { - setSaving(false) } - } + }, [user?.org_id, name, slug, onDirtyChange]) + + useEffect(() => { + onRegisterSave?.(handleSave) + }, [handleSave, onRegisterSave]) const handleDelete = async () => { if (!user?.org_id || deleteText !== 'DELETE') return @@ -91,12 +105,6 @@ export default function WorkspaceGeneralTab() { -
- -
- {/* Danger Zone */}

Danger Zone