feat: UI overhaul - component library + route layouts with instant headers

- Created 11 reusable UI components: PageHeader, SectionCard, StatCard, StatusBadge, TabBar, MemberList, ActivityFeed, EventCard, ContentSkeleton, QuickLinkGrid, ModuleCard
- Created route-specific +layout.svelte for documents, calendar, kanban, events, settings, account
- Each layout renders PageHeader instantly from parent data, shows ContentSkeleton during navigation
- Removed full-page PageSkeleton from parent layout
- Refactored all pages to use new components instead of inline markup
- Overview page: uses StatCard, SectionCard, EventCard, ActivityFeed, MemberList, QuickLinkGrid
- Events list: uses EventCard, Button components
- Event detail: uses ModuleCard, SectionCard
- Settings/Account/Calendar/Kanban: headers in layouts, toolbars in pages
- Added i18n keys for overview page (EN + ET)
- 0 errors, 112 tests pass
This commit is contained in:
AlacrisDevs
2026-02-07 10:44:53 +02:00
parent fe6ec6e0af
commit 2913912cb8
30 changed files with 1240 additions and 604 deletions

View File

@@ -20,7 +20,7 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
}
// Now fetch membership, members, activity, and user profile in parallel (all depend on org.id)
const [membershipResult, membersResult, activityResult, profileResult, docCountResult, folderCountResult, kanbanCountResult] = await Promise.all([
const [membershipResult, membersResult, activityResult, profileResult, docCountResult, folderCountResult, kanbanCountResult, eventCountResult] = await Promise.all([
locals.supabase
.from('org_members')
.select('role, role_id')
@@ -68,7 +68,11 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
.from('documents')
.select('id', { count: 'exact', head: true })
.eq('org_id', org.id)
.eq('type', 'kanban')
.eq('type', 'kanban'),
locals.supabase
.from('events')
.select('id', { count: 'exact', head: true })
.eq('org_id', org.id)
]);
const { data: membership } = membershipResult;
@@ -81,6 +85,7 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
documentCount: docCountResult.count ?? 0,
folderCount: folderCountResult.count ?? 0,
kanbanCount: kanbanCountResult.count ?? 0,
eventCount: eventCountResult.count ?? 0,
};
if (!membership) {
@@ -121,6 +126,15 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
profiles: (m.user_id ? memberProfilesMap[m.user_id] : null) ?? null
}));
// Fetch upcoming events for the overview
const { data: upcomingEvents } = await locals.supabase
.from('events')
.select('id, name, slug, status, start_date, end_date, color, venue_name')
.eq('org_id', org.id)
.in('status', ['planning', 'active'])
.order('start_date', { ascending: true, nullsFirst: false })
.limit(5);
return {
org,
userRole: membership.role,
@@ -128,6 +142,7 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
members,
recentActivity: recentActivity ?? [],
stats,
upcomingEvents: upcomingEvents ?? [],
user,
profile: profile ?? { id: user.id, email: user.email ?? '', full_name: null, avatar_url: null }
};