From 2a28d88849bc88de38c9cc6374dffea21ab478a7 Mon Sep 17 00:00:00 2001 From: AlacrisDevs Date: Sat, 28 Feb 2026 17:06:03 +0200 Subject: [PATCH] UI redesign vol1 --- messages/en.json | 22 +- messages/et.json | 22 +- .../components/documents/FileBrowser.svelte | 148 +++--- .../components/kanban/CardDetailModal.svelte | 2 +- src/lib/components/kanban/KanbanBoard.svelte | 6 +- src/lib/components/kanban/KanbanCard.svelte | 28 +- .../settings/SettingsActivityLog.svelte | 2 +- .../settings/SettingsGeneral.svelte | 2 +- .../settings/SettingsIntegrations.svelte | 7 +- src/lib/components/ui/Breadcrumb.svelte | 63 +++ src/lib/components/ui/Button.svelte | 69 +-- src/lib/components/ui/ContentHeader.svelte | 65 ++- src/lib/components/ui/Input.svelte | 43 +- src/lib/components/ui/KanbanColumn.svelte | 19 +- src/lib/components/ui/ListCard.svelte | 55 +++ src/lib/components/ui/PageHeader.svelte | 10 +- src/lib/components/ui/SectionCard.svelte | 6 +- src/lib/components/ui/Select.svelte | 6 +- src/lib/components/ui/StatCard.svelte | 26 +- src/lib/components/ui/index.ts | 2 + src/routes/+error.svelte | 12 +- src/routes/+page.server.ts | 13 + src/routes/+page.svelte | 415 +++++++++-------- src/routes/[orgSlug]/+layout.svelte | 218 +++------ src/routes/[orgSlug]/+page.svelte | 24 +- src/routes/[orgSlug]/account/+page.svelte | 365 ++++++++------- src/routes/[orgSlug]/calendar/+page.svelte | 103 +++-- src/routes/[orgSlug]/documents/+page.svelte | 17 +- .../documents/file/[id]/+page.svelte | 203 +++++++- .../documents/folder/[id]/+page.svelte | 10 +- src/routes/[orgSlug]/events/+page.svelte | 60 ++- src/routes/[orgSlug]/kanban/+page.svelte | 8 +- src/routes/invite/[token]/+page.svelte | 6 +- src/routes/layout.css | 43 +- src/routes/login/+page.svelte | 110 +++-- src/routes/settings/+page.server.ts | 29 ++ src/routes/settings/+page.svelte | 437 ++++++++++++++++++ 37 files changed, 1722 insertions(+), 954 deletions(-) create mode 100644 src/lib/components/ui/Breadcrumb.svelte create mode 100644 src/lib/components/ui/ListCard.svelte create mode 100644 src/routes/settings/+page.server.ts create mode 100644 src/routes/settings/+page.svelte diff --git a/messages/en.json b/messages/en.json index 72f71c1..ad52b55 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1,7 +1,9 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", "app_name": "Root", + "nav_home": "Home", "nav_files": "Files", + "nav_storage": "Storage", "nav_calendar": "Calendar", "nav_settings": "Settings", "nav_kanban": "Kanban", @@ -43,6 +45,7 @@ "org_selector_slug_placeholder": "my-team", "org_overview": "Organization Overview", "files_title": "Files", + "files_subtitle": "Organization files are stored here.", "files_breadcrumb_home": "Home", "files_create_title": "Create New", "files_type_document": "Document", @@ -82,6 +85,7 @@ "kanban_go_to_files": "Go to Files", "kanban_select_board": "Select a board above", "calendar_title": "Calendar", + "calendar_subtitle": "Keep track of organization events here.", "calendar_subscribe": "Subscribe to Calendar", "calendar_refresh": "Refresh Events", "calendar_settings": "Calendar Settings", @@ -1035,5 +1039,21 @@ "settings_transfer_ownership": "Transfer ownership", "settings_transfer_confirm": "Transfer ownership to {name}? You will be demoted to admin. This action is immediate.", "toast_error_transfer_ownership": "Failed to transfer ownership", - "toast_success_transfer_ownership": "Ownership transferred to {name}" + "toast_success_transfer_ownership": "Ownership transferred to {name}", + "home_nav_organizations": "Organizations", + "home_title": "Your Organizations", + "home_subtitle": "Select an Organization to get started.", + "home_empty_title": "No organizations yet", + "home_empty_desc": "Create your first organization to start collaborating", + "home_pending_invitations": "Pending Invitations", + "home_invite_click_accept": "Click to accept", + "home_invite_joining": "Joining...", + "home_create_org_title": "Create Organization", + "home_create_org_name_label": "Organization Name", + "home_create_org_name_placeholder": "e.g. Acme Inc", + "home_create_org_url_preview": "URL: /{slug}", + "account_settings_title": "Your Settings", + "account_settings_subtitle": "Manage your settings here.", + "user_settings_title": "User Settings", + "user_settings_subtitle": "Manage your personal account settings." } \ No newline at end of file diff --git a/messages/et.json b/messages/et.json index 90509fc..6994503 100644 --- a/messages/et.json +++ b/messages/et.json @@ -1,7 +1,9 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", "app_name": "Root", + "nav_home": "Avaleht", "nav_files": "Failid", + "nav_storage": "Hoidla", "nav_calendar": "Kalender", "nav_settings": "Seaded", "nav_kanban": "Kanban", @@ -42,6 +44,7 @@ "org_selector_slug_placeholder": "minu-meeskond", "org_overview": "Organisatsiooni ülevaade", "files_title": "Failid", + "files_subtitle": "Organisatsiooni failid on siin.", "files_breadcrumb_home": "Avaleht", "files_create_title": "Loo uus", "files_type_document": "Dokument", @@ -81,6 +84,7 @@ "kanban_go_to_files": "Mine Failidesse", "kanban_select_board": "Vali tahvel ülalt", "calendar_title": "Kalender", + "calendar_subtitle": "Jälgi organisatsiooni sündmusi siin.", "calendar_subscribe": "Telli kalender", "calendar_refresh": "Värskenda sündmusi", "calendar_settings": "Kalendri seaded", @@ -1035,5 +1039,21 @@ "settings_transfer_ownership": "Kanna omanikõigused üle", "settings_transfer_confirm": "Kanna omanikõigused üle kasutajale {name}? Sind alandatakse adminiks. See toiming on kohene.", "toast_error_transfer_ownership": "Omanikõiguste ülekandmine ebaõnnestus", - "toast_success_transfer_ownership": "Omanikõigused kanti üle kasutajale {name}" + "toast_success_transfer_ownership": "Omanikõigused kanti üle kasutajale {name}", + "home_nav_organizations": "Organisatsioonid", + "home_title": "Sinu organisatsioonid", + "home_subtitle": "Vali organisatsioon alustamiseks.", + "home_empty_title": "Organisatsioone pole veel", + "home_empty_desc": "Loo oma esimene organisatsioon koostöö alustamiseks", + "home_pending_invitations": "Ootel kutsed", + "home_invite_click_accept": "Kliki nõustumiseks", + "home_invite_joining": "Liitun...", + "home_create_org_title": "Loo organisatsioon", + "home_create_org_name_label": "Organisatsiooni nimi", + "home_create_org_name_placeholder": "nt. Acme OÜ", + "home_create_org_url_preview": "URL: /{slug}", + "account_settings_title": "Sinu seaded", + "account_settings_subtitle": "Halda oma seadeid siin.", + "user_settings_title": "Kasutaja seaded", + "user_settings_subtitle": "Halda oma isiklikke konto seadeid." } \ No newline at end of file diff --git a/src/lib/components/documents/FileBrowser.svelte b/src/lib/components/documents/FileBrowser.svelte index 5e3b070..9246aad 100644 --- a/src/lib/components/documents/FileBrowser.svelte +++ b/src/lib/components/documents/FileBrowser.svelte @@ -141,10 +141,7 @@ } function getDocColor(doc: Document): string { - if (doc.type === "folder") return "text-amber-400"; - if (doc.type === "kanban") return "text-purple-400"; - if (doc.type === "file") return "text-pink-400"; - return "text-light/50"; + return "text-white"; } function handleItemClick(doc: Document) { @@ -638,29 +635,27 @@ {/if} - +
-
@@ -819,28 +810,32 @@ {:else}
{#if currentFolderItems.length === 0}
folder_open -

{m.files_empty()}

+

+ {m.files_empty()} +

{:else} {#each currentFolderItems as item} {/each} {/if} @@ -961,7 +949,7 @@ : m.files_doc_placeholder()} />
- {#if avatarUrl} (showConnectModal = false)} + >Cancel + {/if} +
diff --git a/src/lib/components/ui/Button.svelte b/src/lib/components/ui/Button.svelte index 21b7a85..4b437d9 100644 --- a/src/lib/components/ui/Button.svelte +++ b/src/lib/components/ui/Button.svelte @@ -2,7 +2,13 @@ import type { Snippet } from "svelte"; interface Props { - variant?: "primary" | "secondary" | "tertiary" | "danger" | "success"; + variant?: + | "primary" + | "secondary" + | "outline" + | "danger" + | "success" + | "ghost"; size?: "sm" | "md" | "lg"; disabled?: boolean; loading?: boolean; @@ -28,36 +34,26 @@ }: Props = $props(); const baseClasses = - "inline-flex items-center justify-center gap-2 font-heading rounded-[32px] overflow-clip transition-all cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"; + "inline-flex items-center justify-center gap-2 font-bold font-body rounded-[32px] overflow-clip transition-all cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"; const variantClasses = { primary: - "btn-primary bg-primary text-night hover:btn-primary-hover active:btn-primary-active", - secondary: - "bg-transparent text-primary border-solid border-primary hover:bg-primary/10 active:bg-primary/20", - tertiary: - "bg-primary/10 text-primary hover:bg-primary/20 active:bg-primary/30", - danger: "btn-primary bg-error text-white hover:btn-primary-hover active:btn-primary-active", + "bg-primary text-night hover:brightness-110 active:brightness-90", + secondary: "bg-night text-white hover:bg-surface active:bg-dark", + outline: + "bg-transparent text-primary border-2 border-primary hover:bg-primary/10 active:bg-primary/20", + danger: "bg-error text-white hover:brightness-110 active:brightness-90", success: - "btn-primary bg-success text-night hover:btn-primary-hover active:btn-primary-active", + "bg-success text-night hover:brightness-110 active:brightness-90", + ghost: "bg-transparent text-white hover:bg-white/5 active:bg-white/10", }; const sizeClasses = { - sm: "min-w-[36px] p-[10px] text-btn-sm", - md: "min-w-[48px] p-[12px] text-btn-md", - lg: "min-w-[56px] p-[16px] text-btn-lg", + sm: "min-w-[36px] px-[12px] py-[8px] text-btn-sm", + md: "min-w-[48px] px-[14px] py-[10px] text-btn-md", + lg: "min-w-[56px] px-[16px] py-[12px] text-btn-lg", }; - const borderClasses = { - sm: "border-2", - md: "border-3", - lg: "border-4", - }; - - const secondaryBorder = $derived( - variant === "secondary" ? borderClasses[size] : "", - ); - const iconSize = $derived(size === "sm" ? 16 : size === "lg" ? 20 : 18); @@ -65,7 +61,7 @@ {type} class="{baseClasses} {variantClasses[variant]} {sizeClasses[ size - ]} {secondaryBorder} {className ?? ''}" + ]} {className ?? ''}" class:w-full={fullWidth} disabled={disabled || loading} {onclick} @@ -89,30 +85,3 @@ {@render children()} {/if} - - diff --git a/src/lib/components/ui/ContentHeader.svelte b/src/lib/components/ui/ContentHeader.svelte index a333869..c1b17dd 100644 --- a/src/lib/components/ui/ContentHeader.svelte +++ b/src/lib/components/ui/ContentHeader.svelte @@ -4,39 +4,54 @@ interface Props { title: string; + subtitle?: string; actionLabel?: string; + actionIcon?: string; onAction?: () => void; onMore?: () => void; children?: Snippet; } - let { title, actionLabel, onAction, onMore, children }: Props = $props(); + let { + title, + subtitle, + actionLabel, + actionIcon, + onAction, + onMore, + children, + }: Props = $props(); -
-
-

{title}

+
+
+

{title}

+ {#if subtitle} +

{subtitle}

+ {/if}
- {#if children} - {@render children()} - {/if} - {#if actionLabel && onAction} - - {/if} - {#if onMore} - + {/if} + {#if onMore} + - {/if} + + more_horiz + + + {/if} +
diff --git a/src/lib/components/ui/Input.svelte b/src/lib/components/ui/Input.svelte index d0615e4..4bc22de 100644 --- a/src/lib/components/ui/Input.svelte +++ b/src/lib/components/ui/Input.svelte @@ -14,6 +14,7 @@ value?: string; placeholder?: string; label?: string; + description?: string; error?: string; hint?: string; disabled?: boolean; @@ -33,6 +34,7 @@ value = $bindable(""), placeholder = "", label, + description, error, hint, disabled = false, @@ -55,16 +57,22 @@ const inputType = $derived(isPassword && showPassword ? "text" : type); -
+
{#if label} - +
+ + {#if description} +

{description}

+ {/if} +
{/if}
@@ -97,23 +105,20 @@ class=" w-full {isCompact ? 'px-3 py-2 bg-dark border border-light/10 rounded-xl text-body-sm' - : 'p-3 bg-background rounded-[32px] font-medium font-input text-body'} - text-white placeholder:text-light/30 - focus:outline-none {isCompact - ? 'focus:border-primary' - : 'focus:ring-2 focus:ring-primary'} + : 'px-3 py-2 bg-night rounded-[20px] font-medium font-input text-body'} + text-white placeholder:text-white/50 + focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-30 disabled:cursor-not-allowed transition-colors {className ?? ''} " - class:ring-1={error && !isCompact} - class:ring-error={error && !isCompact} - class:border-error={error && isCompact} + class:ring-1={error} + class:ring-error={error} class:pr-12={isPassword} /> {#if isPassword}
diff --git a/src/lib/components/ui/ListCard.svelte b/src/lib/components/ui/ListCard.svelte new file mode 100644 index 0000000..26ed8ed --- /dev/null +++ b/src/lib/components/ui/ListCard.svelte @@ -0,0 +1,55 @@ + + +{#if href} + + + {iconMap[type]} + +

{label}

+
+{:else} + +{/if} diff --git a/src/lib/components/ui/PageHeader.svelte b/src/lib/components/ui/PageHeader.svelte index 034b157..e1884b7 100644 --- a/src/lib/components/ui/PageHeader.svelte +++ b/src/lib/components/ui/PageHeader.svelte @@ -20,9 +20,7 @@ }: Props = $props(); -
+
{#if icon} {icon} {/if} -
-

{title}

+
+

{title}

{#if subtitle} -

{subtitle}

+

{subtitle}

{/if}
diff --git a/src/lib/components/ui/SectionCard.svelte b/src/lib/components/ui/SectionCard.svelte index bdb4e27..84fcdf1 100644 --- a/src/lib/components/ui/SectionCard.svelte +++ b/src/lib/components/ui/SectionCard.svelte @@ -24,13 +24,11 @@ }; -
+
{#if title || titleRight}
{#if title} -

{title}

+

{title}

{/if} {#if titleRight} {@render titleRight()} diff --git a/src/lib/components/ui/Select.svelte b/src/lib/components/ui/Select.svelte index b2aef60..8d355e3 100644 --- a/src/lib/components/ui/Select.svelte +++ b/src/lib/components/ui/Select.svelte @@ -66,11 +66,9 @@ {onchange} class="w-full {isCompact ? 'px-3 py-2 bg-dark border border-light/10 rounded-xl text-body-sm' - : 'p-3 bg-background rounded-[32px] font-medium font-input text-body'} + : 'px-3 py-2 bg-night rounded-[20px] font-medium font-input text-body'} text-white - focus:outline-none {isCompact - ? 'focus:border-primary' - : 'focus:ring-2 focus:ring-primary'} + focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-30 disabled:cursor-not-allowed transition-colors appearance-none cursor-pointer {className ?? ''}" class:ring-1={error && !isCompact} diff --git a/src/lib/components/ui/StatCard.svelte b/src/lib/components/ui/StatCard.svelte index 54f06ea..7af1543 100644 --- a/src/lib/components/ui/StatCard.svelte +++ b/src/lib/components/ui/StatCard.svelte @@ -21,38 +21,36 @@ {#if href}
{icon}
-
-

{value}

-

{label}

+
+

{value}

+

{label}

{:else} -
+
{icon}
-
-

{value}

-

{label}

+
+

{value}

+

{label}

{/if} diff --git a/src/lib/components/ui/index.ts b/src/lib/components/ui/index.ts index ce53b36..f46e098 100644 --- a/src/lib/components/ui/index.ts +++ b/src/lib/components/ui/index.ts @@ -43,3 +43,5 @@ export { default as Twemoji } from './Twemoji.svelte'; export { default as EmojiPicker } from './EmojiPicker.svelte'; export { default as VirtualList } from './VirtualList.svelte'; export { default as PersonContactModal } from './PersonContactModal.svelte'; +export { default as Breadcrumb } from './Breadcrumb.svelte'; +export { default as ListCard } from './ListCard.svelte'; diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index 615df79..5db41cb 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -36,7 +36,9 @@ ${dump}

{$page.status}

- {$page.status === 404 ? "Page not found" : "Something went wrong"} + {$page.status === 404 + ? "Page not found" + : "Something went wrong"}

{$page.error?.message || "An unexpected error occurred."} @@ -54,10 +56,10 @@ ${dump}

- - {#if showLogs} -
{logDump || "No recent logs."}
+
{logDump ||
+						"No recent logs."}
{/if}
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 1aa2fc2..ac5b832 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -41,8 +41,21 @@ export const load: PageServerLoad = async ({ locals }) => { org: (inv as Record).organizations as { id: string; name: string; slug: string } | null, })).filter((inv) => inv.org !== null); + // Fetch user profile for sidebar + const { data: profile } = await locals.supabase + .from('profiles') + .select('full_name, avatar_url') + .eq('id', user.id) + .single(); + return { organizations, pendingInvites, + user: { + id: user.id, + email: user.email, + fullName: profile?.full_name ?? user.user_metadata?.full_name ?? null, + avatarUrl: profile?.avatar_url ?? null, + }, }; }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e4e90ba..bc79dcc 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,7 +1,9 @@ - Files - {data.org.name} | root + {m.nav_storage()} - {data.org.name} | root -
+
+ fileBrowserRef?.handleAdd()} + /> {data.document.name} - {data.org.name} | root -
+
{#if data.isKanban} - + -
-

- {data.document.name} -

+
+
+

+ {data.document.name} +

+

+ {data.document.type} +

+
-
+
+ + +
+ + +
@@ -718,31 +777,137 @@
progress_activity -

Loading board...

+

+ Loading board... +

{/if}
{:else} - - + +
+
+

+ {data.document.name} +

+

+ {data.document.type} +

+
+ +
+ + +
+ + +
+ + +
+ +
{/if} {#if isSaving} -
Saving...
+
Saving...
{/if}
@@ -803,7 +968,7 @@ />
+
+ + + +
+ +
+
+

+ {m.user_settings_title()} +

+

+ {m.user_settings_subtitle()} +

+
+
+ + +
+ +
+
+

+ {m.account_profile()} +

+
+ +
+ +
+ +
+ + + {#if avatarUrl} + + {/if} +
+
+ + + + + + + + +
+ +
+ +
+
+ + +
+
+

+ {m.account_contact_info()} +

+
+ +
+ + + + + ({ + value: s, + label: s, + }))} + /> +
+ +
+ +
+
+ + +
+
+

+ {m.account_appearance()} +

+
+ +
+