mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 21:46:43 +02:00
Move these apps to apps-archived/ as they are not actively developed: - inventory: Inventory management app - presi: Presentation tool - storage: Cloud storage app These can be reactivated by moving back to apps/ when needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
276 lines
7 KiB
Svelte
276 lines
7 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { page } from '$app/stores';
|
|
import { onMount } from 'svelte';
|
|
import { locale } from 'svelte-i18n';
|
|
import { PillNavigation } from '@manacore/shared-ui';
|
|
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
|
import { theme } from '$lib/stores/theme.svelte';
|
|
import { authStore } from '$lib/stores/auth.svelte';
|
|
import { userSettings } from '$lib/stores/user-settings.svelte';
|
|
import { THEME_DEFINITIONS } from '@manacore/shared-theme';
|
|
import {
|
|
isSidebarMode as sidebarModeStore,
|
|
isNavCollapsed as collapsedStore,
|
|
} from '$lib/stores/navigation';
|
|
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
|
|
import { getPillAppItems } from '@manacore/shared-branding';
|
|
import { setLocale, supportedLocales } from '$lib/i18n';
|
|
import ToastContainer from '$lib/components/ToastContainer.svelte';
|
|
import '../app.css';
|
|
|
|
// App switcher items
|
|
const appItems = getPillAppItems('storage');
|
|
|
|
let { children } = $props();
|
|
|
|
let loading = $state(true);
|
|
let isSidebarMode = $state(false);
|
|
let isCollapsed = $state(false);
|
|
|
|
// Use theme store's isDark directly
|
|
let isDark = $derived(theme.isDark);
|
|
|
|
// Theme variant dropdown items
|
|
let themeVariantItems = $derived<PillDropdownItem[]>([
|
|
...theme.variants.map((variant) => ({
|
|
id: variant,
|
|
label: THEME_DEFINITIONS[variant].label,
|
|
icon: THEME_DEFINITIONS[variant].icon,
|
|
onClick: () => theme.setVariant(variant),
|
|
active: theme.variant === variant,
|
|
})),
|
|
{
|
|
id: 'all-themes',
|
|
label: 'Alle Themes',
|
|
icon: 'palette',
|
|
onClick: () => goto('/themes'),
|
|
active: false,
|
|
},
|
|
]);
|
|
|
|
// Current theme variant label
|
|
let currentThemeVariantLabel = $derived(THEME_DEFINITIONS[theme.variant].label);
|
|
|
|
// Language selector items
|
|
let currentLocale = $derived($locale || 'de');
|
|
function handleLocaleChange(newLocale: string) {
|
|
setLocale(newLocale as any);
|
|
}
|
|
let languageItems = $derived(
|
|
getLanguageDropdownItems(supportedLocales, currentLocale, handleLocaleChange)
|
|
);
|
|
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
|
|
|
// User email for user dropdown
|
|
let userEmail = $derived(authStore.user?.email || 'Menü');
|
|
|
|
// Navigation items for Storage
|
|
const navItems: PillNavItem[] = [
|
|
{ href: '/files', label: 'Dateien', icon: 'folder' },
|
|
{ href: '/shared', label: 'Geteilt', icon: 'share' },
|
|
{ href: '/favorites', label: 'Favoriten', icon: 'heart' },
|
|
{ href: '/trash', label: 'Papierkorb', icon: 'trash' },
|
|
{ href: '/search', label: 'Suche', icon: 'search' },
|
|
{ href: '/feedback', label: 'Feedback', icon: 'chat' },
|
|
];
|
|
|
|
// Navigation shortcuts
|
|
const navRoutes = navItems.map((item) => item.href);
|
|
|
|
// Check if current path is auth page
|
|
let isAuthPage = $derived(
|
|
$page.url.pathname === '/login' ||
|
|
$page.url.pathname === '/register' ||
|
|
$page.url.pathname === '/forgot-password'
|
|
);
|
|
|
|
function handleKeydown(event: KeyboardEvent) {
|
|
const target = event.target as HTMLElement;
|
|
|
|
// Cmd/Ctrl+K to open search
|
|
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
|
|
event.preventDefault();
|
|
goto('/search');
|
|
return;
|
|
}
|
|
|
|
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
|
|
return;
|
|
}
|
|
|
|
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
|
|
const num = parseInt(event.key);
|
|
if (num >= 1 && num <= navRoutes.length) {
|
|
event.preventDefault();
|
|
const route = navRoutes[num - 1];
|
|
if (route) {
|
|
goto(route);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleModeChange(isSidebar: boolean) {
|
|
isSidebarMode = isSidebar;
|
|
sidebarModeStore.set(isSidebar);
|
|
if (typeof localStorage !== 'undefined') {
|
|
localStorage.setItem('storage-nav-sidebar', String(isSidebar));
|
|
}
|
|
}
|
|
|
|
function handleCollapsedChange(collapsed: boolean) {
|
|
isCollapsed = collapsed;
|
|
collapsedStore.set(collapsed);
|
|
if (typeof localStorage !== 'undefined') {
|
|
localStorage.setItem('storage-nav-collapsed', String(collapsed));
|
|
}
|
|
}
|
|
|
|
function handleToggleTheme() {
|
|
theme.toggleMode();
|
|
}
|
|
|
|
function handleThemeModeChange(mode: 'light' | 'dark' | 'system') {
|
|
theme.setMode(mode);
|
|
}
|
|
|
|
async function handleLogout() {
|
|
await authStore.signOut();
|
|
goto('/login');
|
|
}
|
|
|
|
onMount(async () => {
|
|
// Initialize theme
|
|
theme.initialize();
|
|
|
|
// Initialize auth
|
|
await authStore.initialize();
|
|
|
|
// Load user settings
|
|
await userSettings.load();
|
|
|
|
// Initialize sidebar mode from localStorage
|
|
const savedSidebar = localStorage.getItem('storage-nav-sidebar');
|
|
if (savedSidebar === 'true') {
|
|
isSidebarMode = true;
|
|
sidebarModeStore.set(true);
|
|
}
|
|
|
|
// Initialize collapsed state from localStorage
|
|
const savedCollapsed = localStorage.getItem('storage-nav-collapsed');
|
|
if (savedCollapsed === 'true') {
|
|
isCollapsed = true;
|
|
collapsedStore.set(true);
|
|
}
|
|
|
|
loading = false;
|
|
});
|
|
</script>
|
|
|
|
<svelte:window onkeydown={handleKeydown} />
|
|
|
|
<ToastContainer />
|
|
|
|
{#if loading}
|
|
<div class="flex min-h-screen items-center justify-center bg-background">
|
|
<div class="text-center">
|
|
<div
|
|
class="mb-4 inline-block h-12 w-12 animate-spin rounded-full border-4 border-solid border-primary border-r-transparent"
|
|
></div>
|
|
<p class="text-muted-foreground">Laden...</p>
|
|
</div>
|
|
</div>
|
|
{:else if isAuthPage}
|
|
<!-- Auth pages without navigation -->
|
|
{@render children()}
|
|
{:else}
|
|
<!-- Navigation Layout -->
|
|
<div class="layout-container">
|
|
<PillNavigation
|
|
items={navItems}
|
|
currentPath={$page.url.pathname}
|
|
appName="Storage"
|
|
homeRoute="/files"
|
|
onToggleTheme={handleToggleTheme}
|
|
{isDark}
|
|
{isSidebarMode}
|
|
onModeChange={handleModeChange}
|
|
{isCollapsed}
|
|
onCollapsedChange={handleCollapsedChange}
|
|
desktopPosition={userSettings.nav.desktopPosition}
|
|
showThemeToggle={true}
|
|
showThemeVariants={true}
|
|
{themeVariantItems}
|
|
{currentThemeVariantLabel}
|
|
themeMode={theme.mode}
|
|
onThemeModeChange={handleThemeModeChange}
|
|
showLanguageSwitcher={true}
|
|
{languageItems}
|
|
{currentLanguageLabel}
|
|
showLogout={authStore.isAuthenticated}
|
|
onLogout={handleLogout}
|
|
loginHref="/login"
|
|
primaryColor="#3b82f6"
|
|
showAppSwitcher={true}
|
|
{appItems}
|
|
{userEmail}
|
|
settingsHref="/settings"
|
|
manaHref="/mana"
|
|
profileHref="/profile"
|
|
allAppsHref="/apps"
|
|
/>
|
|
|
|
<main
|
|
class="main-content bg-background"
|
|
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
|
class:floating-mode={!isSidebarMode && !isCollapsed}
|
|
>
|
|
<div class="content-wrapper">
|
|
{@render children()}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.layout-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.main-content {
|
|
flex: 1;
|
|
transition: all 300ms ease;
|
|
}
|
|
|
|
.main-content.floating-mode {
|
|
padding-top: 100px;
|
|
}
|
|
|
|
.main-content.sidebar-mode {
|
|
padding-left: 180px;
|
|
}
|
|
|
|
.content-wrapper {
|
|
max-width: 80rem;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
padding: 2rem 1rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.content-wrapper {
|
|
padding-left: 1.5rem;
|
|
padding-right: 1.5rem;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
.content-wrapper {
|
|
padding-left: 2rem;
|
|
padding-right: 2rem;
|
|
}
|
|
}
|
|
</style>
|