31 von 45 = ca. 69 %
31 punkter der todo liste abgeabeitet
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { databases, DATABASE_ID } from '../lib/appwrite';
|
||||
import { databases, DATABASE_ID } from '@/lib/appwrite';
|
||||
import { Query } from 'appwrite';
|
||||
import Header from './Header';
|
||||
import Toast from './Toast';
|
||||
import { useToast } from '../hooks/useToast';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
||||
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
function getToday() {
|
||||
const d = new Date();
|
||||
@@ -41,7 +44,7 @@ function countInRange(assets, start, end) {
|
||||
|
||||
export default function FilialleiterDashboard() {
|
||||
const { userMeta } = useAuth();
|
||||
const { toast, showToast } = useToast();
|
||||
const { showToast } = useToast();
|
||||
const locationId = userMeta?.locationId || '';
|
||||
|
||||
const [ownAssets, setOwnAssets] = useState([]);
|
||||
@@ -104,111 +107,119 @@ export default function FilialleiterDashboard() {
|
||||
}, [colleagues, ownAssets]);
|
||||
|
||||
function trendArrow(current, previous) {
|
||||
if (current > previous) return { arrow: '▲', cls: 'trend-up' };
|
||||
if (current < previous) return { arrow: '▼', cls: 'trend-down' };
|
||||
return { arrow: '–', cls: 'trend-flat' };
|
||||
if (current > previous) return { arrow: '▲', cls: 'text-green-600' };
|
||||
if (current < previous) return { arrow: '▼', cls: 'text-red-600' };
|
||||
return { arrow: '–', cls: 'text-muted-foreground' };
|
||||
}
|
||||
|
||||
const dayTrend = trendArrow(todayCount, yesterdayCount);
|
||||
const monthTrend = trendArrow(thisMonthCount, lastMonthCount);
|
||||
|
||||
const comparisonMax = Math.max(ownTotal, avgAllFilialen, 1);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header showToast={showToast} />
|
||||
<div className="panel-page">
|
||||
<div className="panel-title-bar">
|
||||
<h1>Filialleiter Dashboard</h1>
|
||||
<p>Tägliche und monatliche Übersicht deiner Filiale</p>
|
||||
<div className="mx-auto max-w-7xl p-6">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold tracking-tight">Filialleiter Dashboard</h1>
|
||||
<p className="mt-1 text-muted-foreground">Tägliche und monatliche Übersicht deiner Filiale</p>
|
||||
</div>
|
||||
|
||||
<div className="panel-stats">
|
||||
<div className="panel-stat-card">
|
||||
<div className="panel-stat-number">{todayCount}</div>
|
||||
<div className="panel-stat-label">Heute erfasst</div>
|
||||
<div className={`panel-trend ${dayTrend.cls}`}>
|
||||
{dayTrend.arrow} Gestern: {yesterdayCount}
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-stat-card">
|
||||
<div className="panel-stat-number">{thisMonthCount}</div>
|
||||
<div className="panel-stat-label">Diesen Monat</div>
|
||||
<div className={`panel-trend ${monthTrend.cls}`}>
|
||||
{monthTrend.arrow} Letzter Monat: {lastMonthCount}
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-stat-card">
|
||||
<div className="panel-stat-number">{ownTotal}</div>
|
||||
<div className="panel-stat-label">Meine Filiale</div>
|
||||
</div>
|
||||
<div className="panel-stat-card">
|
||||
<div className="panel-stat-number">{avgAllFilialen}</div>
|
||||
<div className="panel-stat-label">⌀ Alle Filialen</div>
|
||||
</div>
|
||||
<div className="mb-8 grid grid-cols-2 gap-4 lg:grid-cols-4">
|
||||
<Card>
|
||||
<CardContent className="pt-2">
|
||||
<div className="text-3xl font-bold">{todayCount}</div>
|
||||
<p className="text-sm text-muted-foreground">Heute erfasst</p>
|
||||
<p className={`mt-1 text-xs font-medium ${dayTrend.cls}`}>
|
||||
{dayTrend.arrow} Gestern: {yesterdayCount}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="pt-2">
|
||||
<div className="text-3xl font-bold">{thisMonthCount}</div>
|
||||
<p className="text-sm text-muted-foreground">Diesen Monat</p>
|
||||
<p className={`mt-1 text-xs font-medium ${monthTrend.cls}`}>
|
||||
{monthTrend.arrow} Letzter Monat: {lastMonthCount}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="pt-2">
|
||||
<div className="text-3xl font-bold">{ownTotal}</div>
|
||||
<p className="text-sm text-muted-foreground">Meine Filiale</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="pt-2">
|
||||
<div className="text-3xl font-bold">{avgAllFilialen}</div>
|
||||
<p className="text-sm text-muted-foreground">⌀ Alle Filialen</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="panel-comparison">
|
||||
<h2>Filialvergleich</h2>
|
||||
<div className="comparison-bars">
|
||||
<div className="comparison-row">
|
||||
<span className="comparison-label">Meine Filiale</span>
|
||||
<div className="comparison-bar-bg">
|
||||
<div
|
||||
className="comparison-bar own"
|
||||
style={{ width: `${Math.min(100, avgAllFilialen > 0 ? (ownTotal / avgAllFilialen) * 50 : 50)}%` }}
|
||||
/>
|
||||
<Card className="mb-6">
|
||||
<CardHeader>
|
||||
<CardTitle>Filialvergleich</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="w-32 shrink-0 text-sm font-medium">Meine Filiale</span>
|
||||
<Progress value={Math.round((ownTotal / comparisonMax) * 100)} className="flex-1" />
|
||||
<span className="w-12 text-right text-sm font-semibold tabular-nums">{ownTotal}</span>
|
||||
</div>
|
||||
<span className="comparison-value">{ownTotal}</span>
|
||||
</div>
|
||||
<div className="comparison-row">
|
||||
<span className="comparison-label">⌀ Durchschnitt</span>
|
||||
<div className="comparison-bar-bg">
|
||||
<div className="comparison-bar avg" style={{ width: '50%' }} />
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="w-32 shrink-0 text-sm font-medium">⌀ Durchschnitt</span>
|
||||
<Progress value={Math.round((avgAllFilialen / comparisonMax) * 100)} className="flex-1" />
|
||||
<span className="w-12 text-right text-sm font-semibold tabular-nums">{avgAllFilialen}</span>
|
||||
</div>
|
||||
<span className="comparison-value">{avgAllFilialen}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="panel-card" style={{ marginTop: 24 }}>
|
||||
<h2>Mitarbeiter-Performance</h2>
|
||||
{employeeStats.length === 0 ? (
|
||||
<p className="panel-empty">Keine Mitarbeiter gefunden</p>
|
||||
) : (
|
||||
<div className="employee-table-wrap">
|
||||
<table className="employee-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mitarbeiter</th>
|
||||
<th>Zugewiesen</th>
|
||||
<th>Offen</th>
|
||||
<th>In Bearbeitung</th>
|
||||
<th>Erledigt</th>
|
||||
<th>Erledigungsrate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Mitarbeiter-Performance</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{employeeStats.length === 0 ? (
|
||||
<p className="py-4 text-center text-sm text-muted-foreground">Keine Mitarbeiter gefunden</p>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Mitarbeiter</TableHead>
|
||||
<TableHead className="text-right">Zugewiesen</TableHead>
|
||||
<TableHead className="text-right">Offen</TableHead>
|
||||
<TableHead className="text-right">In Bearbeitung</TableHead>
|
||||
<TableHead className="text-right">Erledigt</TableHead>
|
||||
<TableHead className="w-48">Erledigungsrate</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{employeeStats.map((e) => (
|
||||
<tr key={e.name}>
|
||||
<td><strong>{e.name}</strong></td>
|
||||
<td>{e.total}</td>
|
||||
<td>{e.open}</td>
|
||||
<td>{e.inProgress}</td>
|
||||
<td>{e.resolved}</td>
|
||||
<td>
|
||||
<div className="rate-bar-wrap">
|
||||
<div className="rate-bar" style={{ width: `${e.rate}%` }} />
|
||||
<span className="rate-text">{e.rate}%</span>
|
||||
<TableRow key={e.name}>
|
||||
<TableCell className="font-medium">{e.name}</TableCell>
|
||||
<TableCell className="text-right tabular-nums">{e.total}</TableCell>
|
||||
<TableCell className="text-right tabular-nums">{e.open}</TableCell>
|
||||
<TableCell className="text-right tabular-nums">{e.inProgress}</TableCell>
|
||||
<TableCell className="text-right tabular-nums">{e.resolved}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<Progress value={e.rate} className="flex-1" />
|
||||
<span className="w-10 text-right text-xs font-medium tabular-nums">{e.rate}%</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<Toast message={toast.message} color={toast.color} visible={toast.visible} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user