diff --git a/web/src/routes/[workspace]/settings/+page.svelte b/web/src/routes/[workspace]/settings/+page.svelte index ff0a3480..df6f04d9 100644 --- a/web/src/routes/[workspace]/settings/+page.svelte +++ b/web/src/routes/[workspace]/settings/+page.svelte @@ -30,6 +30,21 @@ let inviteResult = $state<{ message: string; type: 'success' | 'error' } | null>(null); let currentUserRole = $state(''); + // Tabs + let activeTab = $state('general'); + const tabs = [ + { id: 'general', label: 'General', icon: '\u2699\uFE0F' }, + { id: 'members', label: 'Members', icon: '\uD83D\uDC65' }, + { id: 'collections', label: 'Collections', icon: '\uD83D\uDCC1' }, + { id: 'danger', label: 'Danger Zone', icon: '\u26A0\uFE0F' }, + ]; + const validTabIds = tabs.map(t => t.id); + + function switchTab(tabId: string) { + activeTab = tabId; + history.replaceState(null, '', `#${tabId}`); + } + $effect(() => { if (wsSlug) load(wsSlug); }); @@ -41,6 +56,8 @@ } else { theme = document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark'; } + const hash = window.location.hash.slice(1); + if (validTabIds.includes(hash)) activeTab = hash; }); async function load(slug: string) { loading = true; @@ -178,231 +195,289 @@

Settings

-
-

Workspace

-
-
- -
- e.key === 'Enter' && saveName()} /> - - {#if nameStatus === 'saved'} - Saved - {:else if nameStatus === 'error'} - Error - {/if} -
-
-
- Slug - {wsSlug} -
- {#if createdDate} + +
+ {#each tabs as tab (tab.id)} + + {/each} +
+ + {#if activeTab === 'general'} +
+

Workspace

+
- Created - {createdDate} + +
+ e.key === 'Enter' && saveName()} /> + + {#if nameStatus === 'saved'} + Saved + {:else if nameStatus === 'error'} + Error + {/if} +
+
+
+ Slug + {wsSlug} +
+ {#if createdDate} +
+ Created + {createdDate} +
+ {/if} + - {/if} - -
-
-
-

Members

- {#if members.length === 0} -

No members yet.

- {:else} -
- {#each members as member (member.user_id)} -
-
- {member.user_name.charAt(0).toUpperCase()} -
- {member.user_name} - {member.user_email} +
+
+

Theme

+
+
+ Appearance + +
+
+
+ {:else if activeTab === 'members'} +
+ {#if members.length === 0} +

No members yet.

+ {:else} +
+ {#each members as member (member.user_id)} +
+
+ {member.user_name.charAt(0).toUpperCase()} +
+ {member.user_name} + {member.user_email} +
+
+
+ {#if isOwner} + + + {:else} + {member.role} + {/if}
-
- {#if isOwner} - -
+ {/if} + + {#if invitations.length > 0} +
+

Pending Invitations

+ {#each invitations as inv (inv.id)} +
+ {inv.email} + {inv.role} + {#if inv.join_url} + {:else} - {member.role} + {inv.code} {/if}
-
- {/each} -
- {/if} - - {#if invitations.length > 0} -
-

Pending Invitations

- {#each invitations as inv (inv.id)} -
- {inv.email} - {inv.role} - {#if inv.join_url} - - {:else} - {inv.code} - {/if} -
- {/each} -
- {/if} - - {#if isOwner} -
-

Invite Member

-
- e.key === 'Enter' && handleInvite()} - disabled={inviting} - /> - - -
- {#if inviteResult} -

- {inviteResult.message} -

- {/if} -
- {/if} -
-
-

Collections

- {#if collections.length === 0} -

No collections yet.

- {:else} -
- {#each collections as coll (coll.id)} - {@const schema = parseSchema(coll)} - - {/each} -
- {/if} - - (showCreateModal = false)} - /> - {#if editingCollection} - (editingCollection = null)} - /> - {/if} -
-
-

Theme

-
-
- Appearance - -
-
-
-
-

Danger Zone

-
- {#if !confirmDelete} -
-
- Archive this workspace -

This will hide the workspace and all its collections, items, and documents. The data is preserved but no longer accessible.

-
- -
- {:else} -
-

This will archive {wsName} and all its contents. To confirm, type the workspace slug below:

-
- {wsSlug} - e.key === 'Enter' && handleDeleteWorkspace()} - /> -
-
- - -
+ {/each}
{/if} -
-
+ + {#if isOwner} +
+

Invite Member

+
+ e.key === 'Enter' && handleInvite()} + disabled={inviting} + /> + + +
+ {#if inviteResult} +

+ {inviteResult.message} +

+ {/if} +
+ {/if} +
+ {:else if activeTab === 'collections'} +
+ {#if collections.length === 0} +

No collections yet.

+ {:else} +
+ {#each collections as coll (coll.id)} + {@const schema = parseSchema(coll)} + + {/each} +
+ {/if} + + (showCreateModal = false)} + /> + {#if editingCollection} + (editingCollection = null)} + /> + {/if} +
+ {:else if activeTab === 'danger'} +
+
+

These actions are destructive and cannot be easily reversed.

+
+
+ {#if !confirmDelete} +
+
+ Archive this workspace +

This will hide the workspace and all its collections, items, and documents. The data is preserved but no longer accessible.

+
+ +
+ {:else} +
+

This will archive {wsName} and all its contents. To confirm, type the workspace slug below:

+
+ {wsSlug} + e.key === 'Enter' && handleDeleteWorkspace()} + /> +
+
+ + +
+
+ {/if} +
+
+ {/if} {/if}