Refine help UX and monthly close layout

This commit is contained in:
2026-08-05 15:56:09 +02:00
parent 3b6f35e31a
commit f8574e5f39
5 changed files with 396 additions and 214 deletions
+30 -172
View File
@@ -3,163 +3,7 @@ import { ArrowUp, BookOpenCheck } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
type FaqSection = {
id: string;
title: string;
description: string;
items: string[];
};
const faqSections: FaqSection[] = [
{
id: "grundprinzip",
title: "Grundprinzip",
description: "TicketTracker ersetzt weder Ticketsystem noch CRM. Die App misst Zeiten, bewertet Sessions und bereitet den Monatsabschluss vor.",
items: [
"Session-Zeit ist die gemessene oder manuell erfasste Arbeitszeit einer Tätigkeit.",
"Teamspace-Zeit ist der Tageswert, der für ein Ticket wirklich im CRM eingetragen wurde.",
"Abgeschlossen wird nur ein Monat. Tickets werden nicht geschlossen, sondern nur über ihre Sessions bewertet."
]
},
{
id: "timer",
title: "Timer",
description: "Timer sind für laufende Arbeit an Tickets gedacht.",
items: [
"Ticketnummer im Format Ticket#XXXXXX eingeben und Timer starten.",
"Es können mehrere Timer vorbereitet sein, aber immer nur einer läuft aktiv.",
"Wird ein Timer aktiviert, werden alle anderen automatisch pausiert.",
"Beim Klick auf Beenden stoppt die Zeit sofort. Danach werden Tätigkeit und gegebenenfalls Organisation und Art gespeichert."
]
},
{
id: "session-nachtragen",
title: "Session Nachtragen",
description: "Vergessene Zeiten können nachträglich als Session angelegt werden.",
items: [
"Benutzer tragen eigene Sessions nach. Admins können Sessions auch anderen Benutzern zuweisen.",
"Pflichtangaben sind Ticket, Organisation, Art, Datum, Von, Bis und Tätigkeit.",
"Das Tätigkeitsfeld ist mehrzeilig. Zeilenumbrüche bleiben in den Auswertungen erhalten."
]
},
{
id: "ticketdaten",
title: "Ticketdaten",
description: "Ticketdaten können in der Ticketdetailansicht korrigiert werden.",
items: [
"Bearbeitbar sind Ticketnummer, Organisation und Art.",
"Änderungen an Organisation und Art werden auf vorhandene manuelle Sessions des Tickets übernommen.",
"Bei wiederkehrenden Sessions kommt die Art aus der Regel der fixen Abrechnung."
]
},
{
id: "organisationen",
title: "Organisationen",
description: "Organisationen werden aus Zammad synchronisiert und danach aus der lokalen Liste gewählt.",
items: [
"Freitext ist nicht mehr erlaubt, damit Auswertungen je Organisation sauber möglich sind.",
"Alte Freitexte müssen manuell korrigiert werden.",
"Der Zammad-Sync speichert nur interne ID, Zammad-ID, Name und Zeitstempel."
]
},
{
id: "auswertung",
title: "Auswertung",
description: "Die Auswertung zeigt Monats- oder Tageswerte und listet Tickets im gewählten Zeitraum.",
items: [
"Mit den Pfeilen neben Monat oder Datum wechselst du den Zeitraum. Die Daten aktualisieren sich automatisch.",
"Tickets können gefiltert, gruppiert und sortiert werden. Diese Einstellungen bleiben gespeichert.",
"Der Graph zeigt den Gesamtaufwand des gewählten Zeitraums."
]
},
{
id: "ticketdetail",
title: "Ticketdetailansicht",
description: "Hier werden alle Sessions eines Tickets im gewählten Monat oder Tag bearbeitet.",
items: [
"Sessions sind nach Tagen gruppiert.",
"Pro Tag wird die getrackte Zeit angezeigt.",
"Das Teamspace-Feld speichert die Stunden, die für diesen Ticket-Tag wirklich im CRM eingetragen wurden.",
"Der Teamspace-Wert ist in Monats- und Tagesansicht derselbe."
]
},
{
id: "sessions-bewerten",
title: "Sessions Bewerten",
description: "Jede Session braucht eine Bewertung für die Abrechnung.",
items: [
"Möglich sind Abgerechnet und Nicht abrechenbar.",
"Ein erneuter Klick auf die gewählte Bewertung nimmt sie zurück.",
"Bewertungen können bei abgeschlossenem Monat erst geändert werden, nachdem der Monat wieder geöffnet wurde."
]
},
{
id: "sessions-bearbeiten",
title: "Sessions Bearbeiten und Löschen",
description: "Sessions können korrigiert oder gelöscht werden.",
items: [
"Bearbeitbar sind Datum, Von, Bis, Organisation, Tätigkeit und Art.",
"Bei wiederkehrenden Sessions ist die Art gesperrt und wird über die Regel geändert.",
"Wird die letzte Session eines Tickets gelöscht, wird das leere Ticket entfernt.",
"Gelöschte wiederkehrende Sessions werden als Ausnahme gespeichert und nicht automatisch neu erzeugt."
]
},
{
id: "monatsabschluss",
title: "Monatsabschluss",
description: "Der Monatsabschluss bündelt offene Bewertungen und Teamspace-Prüfpunkte.",
items: [
"Ein Monat kann abgeschlossen werden, sobald alle Sessions des Monats bewertet sind.",
"Tage ohne Teamspace-Wert können zur Kenntnis genommen werden, wenn sie bewusst nicht ins CRM gehören.",
"Zur Kenntnis genommene Teamspace-Punkte können eingeblendet und zurückgenommen werden.",
"Einen Tagesabschluss gibt es nicht."
]
},
{
id: "fixe-abrechnungen",
title: "Fixe Abrechnungen",
description: "Fixe Abrechnungen erzeugen wiederkehrende Sessions für den eigenen Account.",
items: [
"Ticket ist optional. Ohne Ticket wird automatisch Fix#ID verwendet.",
"Muster können Wochentage oder alle X Wochen sein.",
"Gültig bis ist optional. Wird es später gesetzt, werden nicht mehr gültige erzeugte Sessions entfernt.",
"Bayerische Feiertage werden übersprungen."
]
},
{
id: "statistiken",
title: "Statistiken",
description: "Die Statistikseite vergleicht getrackte Sessions mit Teamspace-Zeiten.",
items: [
"Sessions und Teamspace haben eigene Farben.",
"Eine positive oder ausgeglichene Differenz ist grün.",
"Eine negative Differenz ist rot und bedeutet: in Teamspace ist weniger eingetragen als getrackt.",
"Organisationen, Typen und Ticket-Hotspots helfen bei der Monatskontrolle."
]
},
{
id: "profil",
title: "Profil",
description: "Jeder Benutzer kann eigene Accountdaten pflegen.",
items: [
"Benutzername und voller Name können geändert werden.",
"Passwortänderungen benötigen das aktuelle Passwort.",
"Der volle Name wird in der App angezeigt."
]
},
{
id: "admin",
title: "Adminbereich",
description: "Admins verwalten Benutzer, Sessions und Zammad-Organisationen.",
items: [
"Admins legen Benutzer an und können deren Daten bearbeiten.",
"Sessions können zwischen Benutzern umverteilt werden.",
"Admins können Sessions für andere Benutzer nachtragen.",
"Der Zammad-Zugang wird gespeichert und der Organisationssync aktualisiert die lokale Organisationsliste."
]
}
];
import { helpSections } from "../help-content";
export function FaqPage() {
return (
@@ -167,9 +11,9 @@ export function FaqPage() {
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
<div>
<h2 className="text-2xl font-semibold tracking-normal">FAQ und Anleitung</h2>
<p className="text-sm text-muted-foreground">Kurzanleitung für Timer, Auswertung, Teamspace, fixe Abrechnungen und Adminfunktionen.</p>
<p className="text-sm text-muted-foreground">Ausführliche Anleitung für Timer, Auswertung, Teamspace, fixe Abrechnungen, Monatsabschluss und Adminfunktionen.</p>
</div>
<Badge variant="outline">{faqSections.length} Abschnitt(e)</Badge>
<Badge variant="outline">{helpSections.length} Abschnitt(e)</Badge>
</div>
<Card>
@@ -178,10 +22,10 @@ export function FaqPage() {
<BookOpenCheck className="size-5 text-muted-foreground" />
<CardTitle>Schnellnavigation</CardTitle>
</div>
<CardDescription>Die Fragezeichen in der App springen direkt in diese Abschnitte.</CardDescription>
<CardDescription>Die Fragezeichen in der App öffnen eine Kurzfassung. Von dort gelangst du direkt zum passenden ausführlichen Abschnitt.</CardDescription>
</CardHeader>
<CardContent className="flex flex-wrap gap-2 px-4 pb-4">
{faqSections.map((section) => (
{helpSections.map((section) => (
<Button key={section.id} asChild variant="secondary" size="sm">
<a href={`#${section.id}`}>{section.title}</a>
</Button>
@@ -190,22 +34,36 @@ export function FaqPage() {
</Card>
<div className="grid gap-3 xl:grid-cols-2">
{faqSections.map((section) => (
{helpSections.map((section) => (
<Card key={section.id} id={section.id} className="scroll-mt-16">
<CardHeader className="p-4">
<CardTitle>{section.title}</CardTitle>
<CardDescription>{section.description}</CardDescription>
</CardHeader>
<CardContent className="px-4 pb-4">
<ul className="space-y-2 text-sm text-muted-foreground">
{section.items.map((item) => (
<li key={item} className="flex gap-2">
<span className="mt-2 size-1.5 shrink-0 rounded-full bg-primary" />
<span>{item}</span>
</li>
))}
</ul>
<div className="mt-4 flex justify-end">
<CardContent className="space-y-4 px-4 pb-4">
<div className="rounded-md border bg-muted/20 p-3">
<p className="mb-2 text-sm font-medium">Kurzfassung</p>
<ul className="space-y-2 text-sm text-muted-foreground">
{section.quickItems.map((item) => (
<li key={item} className="flex gap-2">
<span className="mt-2 size-1.5 shrink-0 rounded-full bg-primary" />
<span>{item}</span>
</li>
))}
</ul>
</div>
<div>
<p className="mb-2 text-sm font-medium">Details</p>
<ul className="space-y-2 text-sm text-muted-foreground">
{section.details.map((item) => (
<li key={item} className="flex gap-2">
<span className="mt-2 size-1.5 shrink-0 rounded-full bg-primary/70" />
<span>{item}</span>
</li>
))}
</ul>
</div>
<div className="flex justify-end">
<Button asChild variant="ghost" size="sm">
<a href="#top">
<ArrowUp className="size-4" />