feat: migrate auth from supabase to better auth (#596)
* feat: add README for @cio/db package and remove existingAccounts.json * load session, load profile and organization, add create profile hook * chore: cache session request The trick was passing all the cookies to the backend. Previously I was only passing the better-auth.session_token and not better-auth.session_data. Read about the difference here https://www.better-auth.com/docs/concepts/cookies#custom-cookies * feat: login, logout, onboarding, verify email, reset password. * feat: complete change email and email verification * feat: standardise how we make rpc calls and create org endpoints * fix ui errors * fix: handle potential undefined customerId and standardize avatar URL property * refactor: standardize custom domain handling and update telegram chat ID references * chore: add tailwind prefix to @cio/ui this fixed the media query issues because @cio/ui classes were overriding this dashboard tailwind build * chore: clean up org sidebar * chore: persist mode state * feat: implement order breadcrumbs * chore: upgrade to better auth 1.4 * chore: address common auth flow bugs and setup app for others to setup locally.
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
import type { AccountOrg } from '$lib/features/app/types';
|
||||
import CogIcon from '@lucide/svelte/icons/cog';
|
||||
import type { Component } from 'svelte';
|
||||
import LayoutDashboardIcon from '@lucide/svelte/icons/layout-dashboard';
|
||||
import LibraryBigIcon from '@lucide/svelte/icons/library-big';
|
||||
import MessageSquareMoreIcon from '@lucide/svelte/icons/message-square-more';
|
||||
import Settings2Icon from '@lucide/svelte/icons/settings-2';
|
||||
import UsersIcon from '@lucide/svelte/icons/users';
|
||||
import { isActive } from '$lib/utils/functions/app';
|
||||
|
||||
export interface NavItem {
|
||||
title: string; // translation key
|
||||
url: string;
|
||||
path: string; // Actual path (e.g., '/settings') for breadcrumb generation
|
||||
icon?: Component;
|
||||
isActive?: boolean;
|
||||
isExpanded?: boolean;
|
||||
items?: NavItem[]; // for nested items like settings
|
||||
// Metadata for breadcrumb generation
|
||||
useHashUrl?: boolean; // Use '#' as URL (for collapsible items like settings)
|
||||
nestedRoutes?: NestedRouteConfig[]; // Static nested routes (like community/ask, settings/customize-lms)
|
||||
supportsDynamicSegment?: boolean; // Supports dynamic segments (like [slug])
|
||||
}
|
||||
|
||||
export interface NavItemConfig {
|
||||
titleKey: string;
|
||||
path: string;
|
||||
icon?: Component;
|
||||
requiresAdmin?: boolean;
|
||||
items?: NavItemConfig[];
|
||||
useHashUrl?: boolean; // Use '#' as URL (for collapsible items like settings)
|
||||
nestedRoutes?: NestedRouteConfig[]; // Static nested routes
|
||||
supportsDynamicSegment?: boolean; // Supports dynamic segments (like [slug])
|
||||
}
|
||||
|
||||
export interface NestedRouteConfig {
|
||||
path: string; // Relative to parent (e.g., 'ask', 'customize-lms')
|
||||
titleKey: string; // Translation key or plain text
|
||||
}
|
||||
|
||||
// Base navigation configuration structure
|
||||
const baseNavConfig: NavItemConfig[] = [
|
||||
{
|
||||
titleKey: 'org_navigation.dashboard',
|
||||
path: '',
|
||||
icon: LayoutDashboardIcon
|
||||
},
|
||||
{
|
||||
titleKey: 'org_navigation.courses',
|
||||
path: '/courses',
|
||||
icon: LibraryBigIcon
|
||||
},
|
||||
{
|
||||
titleKey: 'org_navigation.community',
|
||||
path: '/community',
|
||||
icon: MessageSquareMoreIcon,
|
||||
supportsDynamicSegment: true, // Supports /community/[slug]
|
||||
nestedRoutes: [
|
||||
{
|
||||
path: 'ask',
|
||||
titleKey: 'Ask Question' // Could be translated
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
titleKey: 'org_navigation.audience',
|
||||
path: '/audience',
|
||||
icon: UsersIcon
|
||||
},
|
||||
{
|
||||
titleKey: 'org_navigation.setup',
|
||||
path: '/setup',
|
||||
icon: Settings2Icon,
|
||||
requiresAdmin: true
|
||||
},
|
||||
{
|
||||
titleKey: 'org_navigation.settings',
|
||||
path: '/settings',
|
||||
icon: CogIcon,
|
||||
useHashUrl: true, // Use '#' for collapsible parent
|
||||
items: [
|
||||
{
|
||||
titleKey: 'Profile',
|
||||
path: '/settings'
|
||||
},
|
||||
{
|
||||
titleKey: 'Organization',
|
||||
path: '/settings?tab=org'
|
||||
},
|
||||
{
|
||||
titleKey: 'Landing Page',
|
||||
path: '/settings?tab=landingpage'
|
||||
},
|
||||
{
|
||||
titleKey: 'Billing',
|
||||
path: '/settings?tab=billing'
|
||||
}
|
||||
],
|
||||
nestedRoutes: [
|
||||
{
|
||||
path: 'customize-lms',
|
||||
titleKey: 'Customize LMS'
|
||||
},
|
||||
{
|
||||
path: 'domains',
|
||||
titleKey: 'Domains'
|
||||
},
|
||||
{
|
||||
path: 'teams',
|
||||
titleKey: 'Teams'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Get navigation items based on organization context and permissions
|
||||
*/
|
||||
export function getOrgNavigationItems(
|
||||
currentOrgPath: string,
|
||||
currentOrg: AccountOrg,
|
||||
isOrgAdmin: boolean | null,
|
||||
t: (key: string) => string,
|
||||
pagePathname: string
|
||||
): NavItem[] {
|
||||
const items: NavItem[] = [];
|
||||
|
||||
for (const config of baseNavConfig) {
|
||||
// Skip admin-only items if user is not admin
|
||||
if (config.requiresAdmin && !isOrgAdmin) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const url = config.path === '' ? currentOrgPath : `${currentOrgPath}${config.path}`;
|
||||
const fullPath = config.path === '' ? `/org/${currentOrg.siteName}` : `/org/${currentOrg.siteName}${config.path}`;
|
||||
|
||||
const item: NavItem = {
|
||||
title: t(config.titleKey),
|
||||
url: config.useHashUrl ? '#' : url,
|
||||
path: config.path, // Store actual path for breadcrumb generation
|
||||
icon: config.icon,
|
||||
isActive: isActive(pagePathname, fullPath),
|
||||
isExpanded: config.items ? isActive(pagePathname, fullPath) : undefined,
|
||||
useHashUrl: config.useHashUrl,
|
||||
nestedRoutes: config.nestedRoutes,
|
||||
supportsDynamicSegment: config.supportsDynamicSegment
|
||||
};
|
||||
|
||||
// Handle nested items (like settings sub-items)
|
||||
if (config.items) {
|
||||
item.items = config.items.map((subConfig) => ({
|
||||
title: subConfig.titleKey,
|
||||
url: `${currentOrgPath}${subConfig.path}`,
|
||||
path: subConfig.path
|
||||
}));
|
||||
}
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
Reference in New Issue
Block a user