Files
crm-system/app/webui/dashboard.html
T

146 lines
6.8 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CRM Dashboard</title>
<!-- Tailwind CSS via CDN (Dev) -->
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="/css/app.css" />
<!-- Alpine.js -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>
<!-- App stores + dashboard component -->
<script type="module" src="/js/store.js"></script>
<script type="module" src="/js/components/notifications.js"></script>
<script type="module" src="/components/app-shell.js"></script>
<script type="module" src="/components/dashboard-kpis.js"></script>
</head>
<body class="min-h-screen bg-slate-50">
<!-- === TOAST CONTAINER === -->
<div x-data="toastContainer()" class="crm-toast-container" x-cloak>
<template x-for="n in items" :key="n.id">
<div class="crm-toast" :class="'toast-' + n.type"
@click="dismiss(n.id)" x-text="n.message"></div>
</template>
</div>
<!-- === LAYOUT SHELL === -->
<!-- Auth-Gate + appShell: store.js's init() redirects to /index.html when no JWT. -->
<div x-data="{ init: () => Alpine.store('auth').init() }" x-init="init()" class="flex min-h-screen">
<div x-data="appShell('dashboard')" x-cloak class="flex min-h-screen flex-1">
<!-- === SIDEBAR === -->
<aside class="crm-sidebar">
<div class="px-6 py-4 text-white text-lg font-bold border-b border-slate-700">
<a href="/app.html" class="!text-white !border-0">CRM</a>
</div>
<nav class="mt-2">
<a href="/app.html" :class="{ active: active === 'dashboard' }">Dashboard</a>
<a href="/accounts.html" :class="{ active: active === 'accounts' }">Accounts</a>
<a href="/contacts.html" :class="{ active: active === 'contacts' }">Contacts</a>
<a href="/pipeline.html" :class="{ active: active === 'pipeline' }">Pipeline</a>
<a href="/activities.html" :class="{ active: active === 'activities' }">Activities</a>
<a href="/settings-profile.html":class="{ active: active === 'settings-profile' }">Profil</a>
<a href="/settings-users.html" :class="{ active: active === 'settings-users' }"
x-show="$store.auth.isAdmin">Users (Admin)</a>
<a href="/settings-org.html" :class="{ active: active === 'settings-org' }"
x-show="$store.auth.isAdmin">Org (Admin)</a>
</nav>
</aside>
<!-- === MAIN AREA === -->
<div class="flex-1 flex flex-col">
<!-- Top navigation -->
<header class="bg-white border-b border-slate-200 px-6 py-3 flex items-center justify-between">
<h1 class="text-xl font-semibold text-slate-800" x-text="title"></h1>
<div class="flex items-center gap-3 text-sm">
<span class="text-slate-600">
Angemeldet als
<strong class="text-slate-900"
x-text="$store.auth.user ? $store.auth.user.name : '…'"></strong>
</span>
<button class="btn-secondary" @click="logout()">Logout</button>
</div>
</header>
<!-- === DASHBOARD CONTENT (default landing) === -->
<main class="flex-1 p-6" x-data="dashboardKpis()" x-init="init()">
<!-- Loading -->
<div x-show="loading" class="crm-card text-center py-8">
<div class="crm-spinner mx-auto"></div>
<p class="text-slate-500 mt-2 text-sm">Lade Dashboard …</p>
</div>
<!-- Error -->
<div x-show="error" class="crm-card text-red-700 bg-red-50">
<span x-text="error"></span>
</div>
<div x-show="!loading && !error">
<!-- KPI Grid -->
<h2 class="text-lg font-semibold mb-3">Kennzahlen</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="crm-card">
<p class="text-xs text-slate-500">Open Deals</p>
<p class="text-2xl font-bold text-slate-900" x-text="kpis.open_deals ?? ''"></p>
</div>
<div class="crm-card">
<p class="text-xs text-slate-500">Pipeline Value</p>
<p class="text-2xl font-bold text-slate-900"
x-text="formatCurrency(kpis.pipeline_value)"></p>
</div>
<div class="crm-card">
<p class="text-xs text-slate-500">Won this Month</p>
<p class="text-2xl font-bold text-slate-900" x-text="kpis.won_this_month ?? ''"></p>
</div>
<div class="crm-card">
<p class="text-xs text-slate-500">Conversion Rate</p>
<p class="text-2xl font-bold text-slate-900"
x-text="formatPercent(kpis.conversion_rate)"></p>
</div>
</div>
<!-- Quick actions -->
<h2 class="text-lg font-semibold mb-3">Schnellaktionen</h2>
<div class="flex gap-2 mb-6">
<a href="/accounts.html" class="btn-primary">+ New Account</a>
<a href="/contacts.html" class="btn-primary">+ New Contact</a>
<a href="/pipeline.html" class="btn-primary">+ New Deal</a>
</div>
<!-- Activity feed -->
<h2 class="text-lg font-semibold mb-3">Letzte Aktivitäten</h2>
<div class="crm-card">
<template x-for="a in feed" :key="a.id">
<div class="py-2 border-b border-slate-100 last:border-0">
<div class="flex justify-between items-center">
<p class="text-sm">
<span class="crm-badge" :class="badgeColor(a.type)" x-text="a.type"></span>
<span class="ml-2 font-medium text-slate-800" x-text="a.subject"></span>
</p>
<span class="text-xs text-slate-400" x-text="formatRelative(a.created_at)"></span>
</div>
<p class="text-xs text-slate-500 mt-1" x-text="a.body || ''"></p>
</div>
</template>
<p x-show="feed.length === 0" class="text-sm text-slate-500 text-center py-4">
Keine Aktivitäten.
</p>
</div>
</div>
</main>
</div>
</div>
</div>
</body>
</html>