ui: overhaul files, kanban, calendar, settings, chat modules
- FileBrowser: modernize breadcrumbs, toolbar, list/grid items, empty states - KanbanColumn: remove fixed height, border-based styling, compact header - KanbanCard: cleaner border styling, smaller tags, compact footer - Calendar: compact nav bar, border grid, today circle indicator, day view empty state - DocumentViewer: remove bg-night rounded-[32px], border-b header pattern - Settings tags: inline border/rounded-xl cards, icon action buttons - Chat: create +layout.svelte with PageHeader, overhaul sidebar and main area - Chat i18n: add nav_chat, chat_title, chat_subtitle keys (en + et) svelte-check: 0 errors, vitest: 112/112 passed
This commit is contained in:
36
src/routes/[orgSlug]/chat/+layout.svelte
Normal file
36
src/routes/[orgSlug]/chat/+layout.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
import { navigating } from "$app/stores";
|
||||
import { PageHeader, ContentSkeleton } from "$lib/components/ui";
|
||||
import * as m from "$lib/paraglide/messages";
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
org: { id: string; name: string; slug: string };
|
||||
};
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
let { data, children }: Props = $props();
|
||||
|
||||
const isNavigatingHere = $derived(
|
||||
$navigating?.to?.url.pathname?.includes(`/${data.org.slug}/chat`),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full overflow-hidden">
|
||||
<PageHeader
|
||||
title={m.chat_title()}
|
||||
subtitle={m.chat_subtitle()}
|
||||
icon="chat"
|
||||
iconColor="text-primary"
|
||||
/>
|
||||
|
||||
{#if isNavigatingHere && !$navigating?.from?.url.pathname?.includes(`/${data.org.slug}/chat`)}
|
||||
<ContentSkeleton variant="default" />
|
||||
{:else}
|
||||
<div class="flex-1 overflow-hidden">
|
||||
{@render children()}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -392,9 +392,9 @@
|
||||
<!-- Matrix Login Modal -->
|
||||
{#if showMatrixLogin}
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<div class="bg-night rounded-[32px] p-8 w-full max-w-md">
|
||||
<h2 class="font-heading text-h3 text-white mb-2">Connect to Chat</h2>
|
||||
<p class="text-light/50 text-body mb-6">
|
||||
<div class="bg-dark/30 border border-light/5 rounded-xl p-8 w-full max-w-md">
|
||||
<h2 class="font-heading text-body text-white mb-1">Connect to Chat</h2>
|
||||
<p class="text-body-sm text-light/50 mb-6">
|
||||
Enter your Matrix credentials to enable messaging.
|
||||
</p>
|
||||
|
||||
@@ -410,12 +410,12 @@
|
||||
placeholder="@user:matrix.org"
|
||||
/>
|
||||
<div>
|
||||
<label class="block text-body-sm font-body text-light mb-1">Password</label>
|
||||
<label class="block text-body-sm font-body text-light/60 mb-1">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={matrixPassword}
|
||||
placeholder="Password"
|
||||
class="w-full bg-dark border border-light/10 rounded-2xl px-4 py-3 text-white font-body text-body placeholder:text-light/30 focus:outline-none focus:border-primary"
|
||||
class="w-full bg-dark border border-light/10 rounded-xl px-3 py-2 text-white font-body text-body-sm placeholder:text-light/30 focus:outline-none focus:border-primary"
|
||||
onkeydown={(e) => {
|
||||
if (e.key === "Enter") handleMatrixLogin();
|
||||
}}
|
||||
@@ -438,9 +438,9 @@
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<div class="text-center">
|
||||
<div
|
||||
class="animate-spin w-12 h-12 border-4 border-primary border-t-transparent rounded-full mx-auto mb-4"
|
||||
class="animate-spin w-10 h-10 border-3 border-primary border-t-transparent rounded-full mx-auto mb-4"
|
||||
></div>
|
||||
<p class="text-light/50">
|
||||
<p class="text-body-sm text-light/40">
|
||||
{#if isInitializing}
|
||||
Connecting to Matrix...
|
||||
{:else if $syncState === "CATCHUP"}
|
||||
@@ -460,56 +460,50 @@
|
||||
{:else if matrixClient}
|
||||
<MatrixProvider client={matrixClient}>
|
||||
{#snippet children()}
|
||||
<div class="h-full flex gap-2 min-h-0">
|
||||
<div class="h-full flex min-h-0">
|
||||
<!-- Chat Sidebar -->
|
||||
<aside class="w-56 bg-night rounded-[32px] flex flex-col overflow-hidden shrink-0">
|
||||
<header class="px-3 py-5">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="material-symbols-rounded text-light" style="font-size: 20px;">chat</span>
|
||||
<span class="flex-1 font-heading text-light text-base">Messages</span>
|
||||
<button
|
||||
class="text-light hover:text-primary transition-colors"
|
||||
onclick={() => (showStartDMModal = true)}
|
||||
title="New message"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 20px;">add</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<aside class="w-56 border-r border-light/5 flex flex-col overflow-hidden shrink-0">
|
||||
<div class="flex items-center gap-2 px-3 py-2.5 border-b border-light/5">
|
||||
<span class="flex-1 font-heading text-body-sm text-white">Messages</span>
|
||||
<button
|
||||
class="p-1 text-light/40 hover:text-white hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={() => (showStartDMModal = true)}
|
||||
title="New message"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px;">add</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Room search -->
|
||||
<div class="px-3 pb-2">
|
||||
<div class="px-2 py-2">
|
||||
<div class="relative">
|
||||
<span
|
||||
class="material-symbols-rounded absolute left-3 top-1/2 -translate-y-1/2 text-light/40"
|
||||
class="material-symbols-rounded absolute left-2.5 top-1/2 -translate-y-1/2 text-light/30"
|
||||
style="font-size: 16px;"
|
||||
>search</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={roomSearchQuery}
|
||||
placeholder="Search rooms..."
|
||||
class="w-full pl-9 pr-3 py-2 bg-dark text-light text-sm rounded-lg border border-light/10 placeholder:text-light/30 focus:outline-none focus:border-primary"
|
||||
placeholder="Search..."
|
||||
class="w-full pl-8 pr-3 py-1.5 bg-dark/50 text-white text-[12px] rounded-lg border border-light/5 placeholder:text-light/30 focus:outline-none focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Room list (sectioned) -->
|
||||
<nav class="flex-1 overflow-y-auto px-2 pb-2">
|
||||
<nav class="flex-1 overflow-y-auto px-1.5 pb-2">
|
||||
{#if allRooms.length === 0}
|
||||
<p class="text-light/40 text-sm text-center py-8">
|
||||
<p class="text-light/30 text-[12px] text-center py-8">
|
||||
{roomSearchQuery ? "No matching rooms" : "No rooms yet"}
|
||||
</p>
|
||||
{:else}
|
||||
<!-- Org / Space Rooms -->
|
||||
{#if filteredOrgRooms.length > 0}
|
||||
<div class="mb-2">
|
||||
<div class="mb-1.5">
|
||||
<div class="flex items-center justify-between px-2 py-1">
|
||||
<span class="text-xs font-semibold text-light/40 uppercase tracking-wider">
|
||||
<span class="material-symbols-rounded align-middle" style="font-size: 14px;">workspaces</span>
|
||||
Organization
|
||||
</span>
|
||||
<span class="text-[10px] font-body text-light/30 uppercase tracking-wider">Organization</span>
|
||||
<button
|
||||
class="w-5 h-5 flex items-center justify-center text-light/40 hover:text-light hover:bg-light/10 rounded transition-colors"
|
||||
class="p-0.5 text-light/30 hover:text-white hover:bg-dark/50 rounded transition-colors"
|
||||
onclick={() => (showCreateRoomModal = true)}
|
||||
title="Create room"
|
||||
>
|
||||
@@ -520,16 +514,16 @@
|
||||
{#each filteredOrgRooms as room (room.roomId)}
|
||||
<li>
|
||||
<button
|
||||
class="w-full flex items-center gap-2 pl-1 pr-2 py-1 rounded-[50px] transition-colors text-left
|
||||
{$selectedRoomId === room.roomId ? 'bg-primary/20' : 'hover:bg-light/5'}"
|
||||
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-lg transition-colors text-left
|
||||
{$selectedRoomId === room.roomId ? 'bg-primary/10 text-white' : 'text-light/60 hover:bg-dark/50 hover:text-white'}"
|
||||
onclick={() => handleRoomSelect(room.roomId)}
|
||||
>
|
||||
<Avatar src={room.avatarUrl} name={room.name} size="xs" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<span class="font-bold text-sm text-light truncate block">{room.name}</span>
|
||||
<span class="text-[12px] font-body truncate block">{room.name}</span>
|
||||
</div>
|
||||
{#if room.unreadCount > 0}
|
||||
<span class="bg-primary text-white text-xs px-1.5 py-0.5 rounded-full min-w-[18px] text-center">
|
||||
<span class="bg-primary text-background text-[10px] px-1.5 py-0.5 rounded-full min-w-[16px] text-center font-bold">
|
||||
{room.unreadCount > 99 ? "99+" : room.unreadCount}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -542,14 +536,11 @@
|
||||
|
||||
<!-- Direct Messages -->
|
||||
{#if filteredDmRooms.length > 0}
|
||||
<div class="mb-2">
|
||||
<div class="mb-1.5">
|
||||
<div class="flex items-center justify-between px-2 py-1">
|
||||
<span class="text-xs font-semibold text-light/40 uppercase tracking-wider">
|
||||
<span class="material-symbols-rounded align-middle" style="font-size: 14px;">chat_bubble</span>
|
||||
Direct Messages
|
||||
</span>
|
||||
<span class="text-[10px] font-body text-light/30 uppercase tracking-wider">Direct Messages</span>
|
||||
<button
|
||||
class="w-5 h-5 flex items-center justify-center text-light/40 hover:text-light hover:bg-light/10 rounded transition-colors"
|
||||
class="p-0.5 text-light/30 hover:text-white hover:bg-dark/50 rounded transition-colors"
|
||||
onclick={() => (showStartDMModal = true)}
|
||||
title="New DM"
|
||||
>
|
||||
@@ -560,16 +551,16 @@
|
||||
{#each filteredDmRooms as room (room.roomId)}
|
||||
<li>
|
||||
<button
|
||||
class="w-full flex items-center gap-2 pl-1 pr-2 py-1 rounded-[50px] transition-colors text-left
|
||||
{$selectedRoomId === room.roomId ? 'bg-primary/20' : 'hover:bg-light/5'}"
|
||||
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-lg transition-colors text-left
|
||||
{$selectedRoomId === room.roomId ? 'bg-primary/10 text-white' : 'text-light/60 hover:bg-dark/50 hover:text-white'}"
|
||||
onclick={() => handleRoomSelect(room.roomId)}
|
||||
>
|
||||
<Avatar src={room.avatarUrl} name={room.name} size="xs" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<span class="font-bold text-sm text-light truncate block">{room.name}</span>
|
||||
<span class="text-[12px] font-body truncate block">{room.name}</span>
|
||||
</div>
|
||||
{#if room.unreadCount > 0}
|
||||
<span class="bg-primary text-white text-xs px-1.5 py-0.5 rounded-full min-w-[18px] text-center">
|
||||
<span class="bg-primary text-background text-[10px] px-1.5 py-0.5 rounded-full min-w-[16px] text-center font-bold">
|
||||
{room.unreadCount > 99 ? "99+" : room.unreadCount}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -582,14 +573,11 @@
|
||||
|
||||
<!-- Other Rooms (not in a space, not DMs) -->
|
||||
{#if filteredOtherRooms.length > 0}
|
||||
<div class="mb-2">
|
||||
<div class="mb-1.5">
|
||||
<div class="flex items-center justify-between px-2 py-1">
|
||||
<span class="text-xs font-semibold text-light/40 uppercase tracking-wider">
|
||||
<span class="material-symbols-rounded align-middle" style="font-size: 14px;">tag</span>
|
||||
Rooms
|
||||
</span>
|
||||
<span class="text-[10px] font-body text-light/30 uppercase tracking-wider">Rooms</span>
|
||||
<button
|
||||
class="w-5 h-5 flex items-center justify-center text-light/40 hover:text-light hover:bg-light/10 rounded transition-colors"
|
||||
class="p-0.5 text-light/30 hover:text-white hover:bg-dark/50 rounded transition-colors"
|
||||
onclick={() => (showCreateRoomModal = true)}
|
||||
title="Create room"
|
||||
>
|
||||
@@ -600,16 +588,16 @@
|
||||
{#each filteredOtherRooms as room (room.roomId)}
|
||||
<li>
|
||||
<button
|
||||
class="w-full flex items-center gap-2 pl-1 pr-2 py-1 rounded-[50px] transition-colors text-left
|
||||
{$selectedRoomId === room.roomId ? 'bg-primary/20' : 'hover:bg-light/5'}"
|
||||
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-lg transition-colors text-left
|
||||
{$selectedRoomId === room.roomId ? 'bg-primary/10 text-white' : 'text-light/60 hover:bg-dark/50 hover:text-white'}"
|
||||
onclick={() => handleRoomSelect(room.roomId)}
|
||||
>
|
||||
<Avatar src={room.avatarUrl} name={room.name} size="xs" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<span class="font-bold text-sm text-light truncate block">{room.name}</span>
|
||||
<span class="text-[12px] font-body truncate block">{room.name}</span>
|
||||
</div>
|
||||
{#if room.unreadCount > 0}
|
||||
<span class="bg-primary text-white text-xs px-1.5 py-0.5 rounded-full min-w-[18px] text-center">
|
||||
<span class="bg-primary text-background text-[10px] px-1.5 py-0.5 rounded-full min-w-[16px] text-center font-bold">
|
||||
{room.unreadCount > 99 ? "99+" : room.unreadCount}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -623,76 +611,76 @@
|
||||
</nav>
|
||||
|
||||
<!-- User footer -->
|
||||
<footer class="p-3 border-t border-light/10">
|
||||
<div class="px-2 py-2 border-t border-light/5">
|
||||
<div class="flex items-center gap-2">
|
||||
<Avatar name={$auth.userId || "User"} size="xs" status="online" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-xs font-medium text-light truncate">{$auth.userId}</p>
|
||||
<p class="text-[11px] text-light/50 truncate">{$auth.userId}</p>
|
||||
</div>
|
||||
<button
|
||||
class="text-light/50 hover:text-light p-1 rounded-lg hover:bg-light/10 transition-colors"
|
||||
class="p-1 text-light/30 hover:text-white hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={handleLogout}
|
||||
title="Disconnect chat"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px;">logout</span>
|
||||
<span class="material-symbols-rounded" style="font-size: 16px;">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Chat Area -->
|
||||
<main class="flex-1 flex flex-col min-h-0 overflow-hidden bg-night rounded-[32px]">
|
||||
<main class="flex-1 flex flex-col min-h-0 overflow-hidden">
|
||||
{#if $selectedRoomId}
|
||||
<div class="flex-1 flex flex-col min-h-0 overflow-hidden">
|
||||
<!-- Room Header -->
|
||||
<header class="h-14 px-5 flex items-center border-b border-light/10">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-light/5 shrink-0">
|
||||
{#each $roomSummaries.filter((r) => r.roomId === $selectedRoomId) as room}
|
||||
<div class="flex items-center gap-3 w-full">
|
||||
<div class="flex items-center gap-2.5 w-full">
|
||||
<Avatar src={room.avatarUrl} name={room.name} size="sm" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<h2 class="font-heading text-h5 text-light truncate">{room.name}</h2>
|
||||
<p class="text-xs text-light/50">
|
||||
<h2 class="font-heading text-body-sm text-white truncate">{room.name}</h2>
|
||||
<p class="text-[11px] text-light/40">
|
||||
{room.memberCount} members{room.isEncrypted ? " · Encrypted" : ""}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="p-2 text-light/50 hover:text-light hover:bg-light/10 rounded-lg transition-colors"
|
||||
class="p-1.5 text-light/40 hover:text-white hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={() => (showMessageSearch = !showMessageSearch)}
|
||||
title="Search messages"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 20px;">search</span>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px;">search</span>
|
||||
</button>
|
||||
<button
|
||||
class="p-2 text-light/50 hover:text-light hover:bg-light/10 rounded-lg transition-colors"
|
||||
class="p-1.5 text-light/40 hover:text-white hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={() => (showRoomInfo = !showRoomInfo)}
|
||||
title="Room info"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 20px;">info</span>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px;">info</span>
|
||||
</button>
|
||||
<button
|
||||
class="p-2 text-light/50 hover:text-light hover:bg-light/10 rounded-lg transition-colors"
|
||||
class="p-1.5 text-light/40 hover:text-white hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={() => (showMemberList = !showMemberList)}
|
||||
title="Members"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 20px;">group</span>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px;">group</span>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- Message search panel -->
|
||||
{#if showMessageSearch}
|
||||
<div class="border-b border-light/10 p-3 bg-dark/50">
|
||||
<div class="border-b border-light/5 px-4 py-2.5">
|
||||
<div class="relative">
|
||||
<span class="material-symbols-rounded absolute left-3 top-1/2 -translate-y-1/2 text-light/40" style="font-size: 16px;">search</span>
|
||||
<span class="material-symbols-rounded absolute left-2.5 top-1/2 -translate-y-1/2 text-light/30" style="font-size: 16px;">search</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={messageSearchQuery}
|
||||
placeholder="Search messages in this room..."
|
||||
class="w-full pl-9 pr-8 py-2 bg-night text-light text-sm rounded-lg border border-light/10 placeholder:text-light/30 focus:outline-none focus:border-primary"
|
||||
placeholder="Search messages..."
|
||||
class="w-full pl-8 pr-8 py-1.5 bg-dark/50 text-white text-[12px] rounded-lg border border-light/5 placeholder:text-light/30 focus:outline-none focus:border-primary"
|
||||
/>
|
||||
<button
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 text-light/40 hover:text-light"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 text-light/30 hover:text-white"
|
||||
onclick={() => { showMessageSearch = false; messageSearchQuery = ""; }}
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 16px;">close</span>
|
||||
@@ -700,22 +688,22 @@
|
||||
</div>
|
||||
{#if messageSearchQuery && messageSearchResults.length > 0}
|
||||
<div class="mt-2 max-h-48 overflow-y-auto">
|
||||
<p class="text-xs text-light/40 mb-2">
|
||||
<p class="text-[11px] text-light/30 mb-1.5">
|
||||
{messageSearchResults.length} result{messageSearchResults.length !== 1 ? "s" : ""}
|
||||
</p>
|
||||
{#each messageSearchResults.slice(0, 20) as result}
|
||||
<button
|
||||
class="w-full text-left px-3 py-2 hover:bg-light/5 rounded transition-colors"
|
||||
class="w-full text-left px-3 py-1.5 hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={() => { showMessageSearch = false; messageSearchQuery = ""; }}
|
||||
>
|
||||
<p class="text-xs text-primary">{result.senderName}</p>
|
||||
<p class="text-sm text-light truncate">{result.content}</p>
|
||||
<p class="text-xs text-light/30">{new Date(result.timestamp).toLocaleString()}</p>
|
||||
<p class="text-[11px] text-primary">{result.senderName}</p>
|
||||
<p class="text-body-sm text-white truncate">{result.content}</p>
|
||||
<p class="text-[10px] text-light/30">{new Date(result.timestamp).toLocaleString()}</p>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if messageSearchQuery}
|
||||
<p class="text-sm text-light/40 mt-2">No results found</p>
|
||||
<p class="text-body-sm text-light/30 mt-2">No results found</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -729,11 +717,11 @@
|
||||
role="region"
|
||||
>
|
||||
{#if isDraggingFile}
|
||||
<div class="absolute inset-0 z-50 bg-primary/20 border-2 border-dashed border-primary rounded-lg flex items-center justify-center backdrop-blur-sm">
|
||||
<div class="absolute inset-0 z-50 bg-primary/10 border-2 border-dashed border-primary rounded-xl flex items-center justify-center backdrop-blur-sm">
|
||||
<div class="text-center">
|
||||
<span class="material-symbols-rounded text-primary mb-4 block" style="font-size: 64px;">upload_file</span>
|
||||
<p class="text-xl font-semibold text-primary">Drop to upload</p>
|
||||
<p class="text-sm text-light/60 mt-1">Release to send file</p>
|
||||
<span class="material-symbols-rounded text-primary mb-3 block" style="font-size: 48px; font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 48;">upload_file</span>
|
||||
<p class="text-body-sm font-heading text-primary">Drop to upload</p>
|
||||
<p class="text-[12px] text-light/40 mt-0.5">Release to send file</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -764,7 +752,7 @@
|
||||
<!-- Side panels -->
|
||||
{#if showRoomInfo}
|
||||
{#each $roomSummaries.filter((r) => r.roomId === $selectedRoomId) as currentRoom}
|
||||
<aside class="w-72 border-l border-light/10 bg-dark/30">
|
||||
<aside class="w-72 border-l border-light/5">
|
||||
<RoomInfoPanel
|
||||
room={currentRoom}
|
||||
members={currentMembers}
|
||||
@@ -773,7 +761,7 @@
|
||||
</aside>
|
||||
{/each}
|
||||
{:else if showMemberList}
|
||||
<aside class="w-64 border-l border-light/10 bg-dark/30">
|
||||
<aside class="w-64 border-l border-light/5">
|
||||
<MemberList members={currentMembers} />
|
||||
</aside>
|
||||
{/if}
|
||||
@@ -782,10 +770,10 @@
|
||||
{:else}
|
||||
<!-- No room selected -->
|
||||
<div class="flex-1 flex items-center justify-center">
|
||||
<div class="text-center text-light/40">
|
||||
<span class="material-symbols-rounded mb-4 block" style="font-size: 64px; font-variation-settings: 'FILL' 0, 'wght' 300;">chat</span>
|
||||
<h2 class="font-heading text-h4 text-light/50 mb-2">Select a room</h2>
|
||||
<p class="text-body text-light/30">Choose a conversation to start chatting</p>
|
||||
<div class="text-center text-light/30">
|
||||
<span class="material-symbols-rounded mb-3 block" style="font-size: 48px; font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 48;">chat</span>
|
||||
<p class="text-body-sm text-light/40 mb-1">Select a room</p>
|
||||
<p class="text-[12px] text-light/20">Choose a conversation to start chatting</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -322,74 +322,73 @@
|
||||
|
||||
<!-- Tags Tab -->
|
||||
{#if activeTab === "tags"}
|
||||
<div class="space-y-6 max-w-2xl">
|
||||
<div class="space-y-4 max-w-2xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-light">
|
||||
<h2 class="text-body font-heading text-white">
|
||||
{m.settings_tags_title()}
|
||||
</h2>
|
||||
<p class="text-sm text-light/50">
|
||||
<p class="text-body-sm text-light/50 mt-0.5">
|
||||
{m.settings_tags_desc()}
|
||||
</p>
|
||||
</div>
|
||||
<Button onclick={() => openTagModal()} icon="add">
|
||||
<Button size="sm" onclick={() => openTagModal()} icon="add">
|
||||
{m.settings_tags_create()}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if orgTags.length === 0 && tagsLoaded}
|
||||
<Card>
|
||||
<div class="p-8 text-center">
|
||||
<span
|
||||
class="material-symbols-rounded text-light/20 mb-4 block"
|
||||
style="font-size: 48px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 48;"
|
||||
>label</span
|
||||
>
|
||||
<p class="text-light/50">{m.settings_tags_empty()}</p>
|
||||
</div>
|
||||
</Card>
|
||||
<div class="bg-dark/30 border border-light/5 rounded-xl p-8 text-center">
|
||||
<span
|
||||
class="material-symbols-rounded text-light/20 mb-3 block"
|
||||
style="font-size: 48px; font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 48;"
|
||||
>label</span
|
||||
>
|
||||
<p class="text-body-sm text-light/40">{m.settings_tags_empty()}</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid gap-3">
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each orgTags as tag}
|
||||
<Card>
|
||||
<div class="flex items-center justify-between p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg flex items-center justify-center"
|
||||
style="background-color: {tag.color ||
|
||||
'#00A3E0'}"
|
||||
<div class="flex items-center justify-between px-4 py-3 bg-dark/30 border border-light/5 rounded-xl hover:border-light/10 transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-7 h-7 rounded-lg flex items-center justify-center shrink-0"
|
||||
style="background-color: {tag.color || '#00A3E0'}"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-rounded text-night"
|
||||
style="font-size: 16px; font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 16;"
|
||||
>label</span
|
||||
>
|
||||
<span
|
||||
class="material-symbols-rounded text-night"
|
||||
style="font-size: 18px; font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 18;"
|
||||
>label</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-light font-medium">
|
||||
{tag.name}
|
||||
</p>
|
||||
<p class="text-xs text-light/40">
|
||||
{tag.color || "#00A3E0"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
onclick={() => openTagModal(tag)}
|
||||
>Edit</Button
|
||||
>
|
||||
<Button
|
||||
variant="danger"
|
||||
size="sm"
|
||||
onclick={() => deleteOrgTag(tag)}
|
||||
>Delete</Button
|
||||
>
|
||||
<div>
|
||||
<p class="text-body-sm text-white font-medium">
|
||||
{tag.name}
|
||||
</p>
|
||||
<p class="text-[11px] text-light/30">
|
||||
{tag.color || "#00A3E0"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
class="p-1.5 text-light/40 hover:text-white hover:bg-dark/50 rounded-lg transition-colors"
|
||||
onclick={() => openTagModal(tag)}
|
||||
title="Edit"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 18;">edit</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1.5 text-light/40 hover:text-error hover:bg-error/10 rounded-lg transition-colors"
|
||||
onclick={() => deleteOrgTag(tag)}
|
||||
title="Delete"
|
||||
>
|
||||
<span class="material-symbols-rounded" style="font-size: 18px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 18;">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user