79ecf134-0188-4613-b691-50e43114247e
This commit is contained in:
1 parent
9b5477020c
commit
8fa25577c0
10 files changed
+117
-1
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 144 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 144 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 135 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "AOS — Agency Operating System",
|
||||
"short_name": "AOS",
|
||||
"description": "Selbstgehostete Plattform für Software-Agenturen: CRM, Leads, Projekte, Lizenzen, Finanzen — mit EU/DE-Compliance.",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#0a0a0a",
|
||||
"theme_color": "#0a0a0a",
|
||||
"orientation": "any",
|
||||
"categories": ["business", "productivity", "finance"],
|
||||
"lang": "de",
|
||||
"dir": "ltr",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Leads prüfen",
|
||||
"url": "/?view=leads",
|
||||
"description": "Leads Finder Review-Queue"
|
||||
},
|
||||
{
|
||||
"name": "Zeit erfassen",
|
||||
"url": "/?view=projects",
|
||||
"description": "Projekt-Zeiterfassung"
|
||||
},
|
||||
{
|
||||
"name": "Freigaben",
|
||||
"url": "/?view=dashboard",
|
||||
"description": "Offene Meilenstein-Freigaben"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -29,8 +29,15 @@ export const metadata: Metadata = {
|
||||
"GoBD",
|
||||
],
|
||||
authors: [{ name: "Nordlicht Digital GmbH" }],
|
||||
manifest: "/manifest.json",
|
||||
appleWebApp: {
|
||||
capable: true,
|
||||
title: "AOS",
|
||||
statusBarStyle: "black",
|
||||
},
|
||||
icons: {
|
||||
icon: "https://z-cdn.chatglm.cn/z-ai/static/logo.svg",
|
||||
apple: "https://z-cdn.chatglm.cn/z-ai/static/logo.svg",
|
||||
},
|
||||
openGraph: {
|
||||
title: "AOS — Agency Operating System",
|
||||
|
||||
@@ -28,6 +28,7 @@ import { GlobalSettingsView } from './views/global-settings'
|
||||
import { SetupWizardView } from './views/setup-wizard'
|
||||
import { PortalShell } from './portal-shell'
|
||||
import { OnboardingOverlay } from './onboarding-overlay'
|
||||
import { MobileBottomNav } from './mobile-bottom-nav'
|
||||
|
||||
export function AppShell() {
|
||||
const { view, sidebarOpen, setSidebarOpen, portalMode } = useAos()
|
||||
@@ -55,7 +56,7 @@ export function AppShell() {
|
||||
{/* Main */}
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<TopBar />
|
||||
<main className="flex-1">
|
||||
<main className="flex-1 pb-16 md:pb-0">
|
||||
{view === 'dashboard' && <DashboardView />}
|
||||
{view === 'calendar' && <CalendarView />}
|
||||
{view === 'reporting' && <ReportingView />}
|
||||
@@ -82,6 +83,7 @@ export function AppShell() {
|
||||
<CommandMenu />
|
||||
<CompanyDrawer />
|
||||
<OnboardingOverlay />
|
||||
<MobileBottomNav />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
'use client'
|
||||
|
||||
import { useAos, type ViewId } from '@/store/aos'
|
||||
import { LayoutDashboard, Radar, FolderKanban, Bell, Menu } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useFetch } from '@/hooks/use-fetch'
|
||||
|
||||
// Mobile bottom navigation bar (PRD §9: "Mobile: usable PWA for time tracking,
|
||||
// approvals and quick CRM lookups"). Shows on mobile only (md:hidden).
|
||||
// Provides 5 quick-access destinations optimized for touch.
|
||||
export function MobileBottomNav() {
|
||||
const { view, setView, setSidebarOpen, setCommandOpen } = useAos()
|
||||
const { data } = useFetch<{ stats: { total: number; urgent: number } }>('/api/aos/notifications')
|
||||
|
||||
const urgent = data?.stats.urgent ?? 0
|
||||
const total = data?.stats.total ?? 0
|
||||
|
||||
const items: { id: ViewId | 'search' | 'menu'; label: string; icon: typeof LayoutDashboard; badge?: number }[] = [
|
||||
{ id: 'dashboard', label: 'Home', icon: LayoutDashboard },
|
||||
{ id: 'leads', label: 'Leads', icon: Radar },
|
||||
{ id: 'search', label: 'Suche', icon: Menu },
|
||||
{ id: 'projects', label: 'Zeit', icon: FolderKanban },
|
||||
{ id: 'notifications', label: 'Alerts', icon: Bell, badge: urgent > 0 ? urgent : total > 0 ? total : undefined },
|
||||
]
|
||||
|
||||
const handleClick = (id: typeof items[number]['id']) => {
|
||||
if (id === 'search') setCommandOpen(true)
|
||||
else if (id === 'notifications') setView('calendar')
|
||||
else if (id === 'menu') setSidebarOpen(true)
|
||||
else setView(id as ViewId)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-40 flex h-16 items-stretch border-t border-border bg-background/95 backdrop-blur-md md:hidden">
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = view === (item.id === 'notifications' ? 'calendar' : item.id === 'search' || item.id === 'menu' ? '' : item.id)
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => handleClick(item.id)}
|
||||
className={cn(
|
||||
'relative flex flex-1 flex-col items-center justify-center gap-0.5 text-[10px] font-medium transition',
|
||||
active ? 'text-foreground' : 'text-muted-foreground'
|
||||
)}
|
||||
aria-label={item.label}
|
||||
>
|
||||
<div className="relative">
|
||||
<Icon className={cn('size-5', active && 'text-foreground')} />
|
||||
{item.badge !== undefined && item.badge > 0 && (
|
||||
<span className="absolute -right-2 -top-1.5 flex size-4 items-center justify-center rounded-full bg-foreground text-[8px] font-bold text-background">
|
||||
{item.badge > 9 ? '9+' : item.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span>{item.label}</span>
|
||||
{active && <span className="absolute bottom-0 left-1/2 h-0.5 w-8 -translate-x-1/2 rounded-full bg-foreground" />}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user