Remove non-billable fragments from statistics
This commit is contained in:
@@ -218,7 +218,7 @@ export const helpSections: HelpSection[] = [
|
||||
{
|
||||
id: "statistiken",
|
||||
title: "Statistiken",
|
||||
description: "Statistiken vergleichen abgerechnete Session-Zeit, nicht abrechenbare Arbeit, offene Bewertungen und Teamspace-Werte.",
|
||||
description: "Statistiken vergleichen abgerechnete Session-Zeit, offene Bewertungen und Teamspace-Werte.",
|
||||
quickItems: [
|
||||
"Abgerechnet und Teamspace haben getrennte Farben.",
|
||||
"Positive oder gleiche Differenz ist grün.",
|
||||
@@ -226,13 +226,12 @@ export const helpSections: HelpSection[] = [
|
||||
],
|
||||
details: [
|
||||
"Die Statistikseite betrachtet immer einen Monat.",
|
||||
"Abgerechnet zeigt nur Sessions mit der Bewertung Abgerechnet. Nicht abrechenbare und offene Zeiten sind dort nicht enthalten.",
|
||||
"Abgerechnet zeigt nur Sessions mit der Bewertung Abgerechnet. Offene Zeiten sind dort nicht enthalten.",
|
||||
"Alle Statistik- und Vergleichswerte nutzen nur abgerechnete Zeiten.",
|
||||
"Teamspace zeigt, wie viel Zeit für dieselben abrechenbaren Ticket-Tage im CRM eingetragen wurde.",
|
||||
"Eine positive Differenz bedeutet: In Teamspace steht mehr Zeit als abgerechnet wurde.",
|
||||
"Eine negative Differenz bedeutet: In Teamspace steht weniger Zeit als abgerechnet wurde.",
|
||||
"Nicht abrechenbar und Offen werden jeweils als eigene Statuswerte ausgewiesen.",
|
||||
"Sie werden nicht in Relationen, Graphen, Teamspace-Differenzen, Hotspots, Manuell/Fix-Werte oder Durchschnittswerte eingerechnet.",
|
||||
"Offene Sessions werden nicht in Relationen, Graphen, Teamspace-Differenzen, Hotspots, Manuell/Fix-Werte oder Durchschnittswerte eingerechnet.",
|
||||
"Der Monatsverlauf zeigt abgerechnete Session-Zeit als Balken und Teamspace als Linie.",
|
||||
"Organisationen, Typen und Ticket-Hotspots helfen dabei, Auffälligkeiten im Monat schnell zu finden."
|
||||
]
|
||||
@@ -248,9 +247,9 @@ export const helpSections: HelpSection[] = [
|
||||
],
|
||||
details: [
|
||||
"Abgerechnet umfasst nur Sessions des angemeldeten Benutzers, die aktiv als Abgerechnet markiert wurden.",
|
||||
"Nicht abrechenbare Sessions und offene Sessions werden aus diesem Wert herausgehalten.",
|
||||
"Offene Sessions werden aus diesem Wert herausgehalten.",
|
||||
"Auch automatisch erzeugte Sessions aus fixen Abrechnungen zählen hier mit, sobald sie als Abgerechnet markiert wurden.",
|
||||
"Es werden keine offenen oder nicht abrechenbaren Zeiten in diesen Wert eingerechnet.",
|
||||
"Es werden keine offenen Zeiten in diesen Wert eingerechnet.",
|
||||
"Dieser Wert ist die Ausgangsbasis für Teamspace-Differenzen: Teamspace minus abgerechnete Session-Zeit ergibt die Differenz."
|
||||
]
|
||||
},
|
||||
@@ -268,39 +267,22 @@ export const helpSections: HelpSection[] = [
|
||||
"Die Statistik summiert alle Teamspace-Werte des Monats für den angemeldeten Benutzer.",
|
||||
"Eine positive Differenz bedeutet, dass in Teamspace mehr Zeit steht als abgerechnet wurde.",
|
||||
"Eine negative Differenz bedeutet, dass in Teamspace weniger Zeit steht als abgerechnet wurde.",
|
||||
"Nicht abrechenbare und offene Sessions fließen nicht in diese Differenz ein.",
|
||||
"Offene Sessions fließen nicht in diese Differenz ein.",
|
||||
"Fehlende Teamspace-Werte können im Monatsabschluss geprüft und bei Bedarf zur Kenntnis genommen werden."
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "statistik-bewertet",
|
||||
title: "Statistik: Nicht abrechenbar",
|
||||
description: "Diese Kennzahl zeigt Arbeit, die bewusst nicht abgerechnet wird.",
|
||||
quickItems: [
|
||||
"Nur Sessions mit Bewertung Nicht abrechenbar.",
|
||||
"Wird nur separat ausgewiesen.",
|
||||
"Nicht abrechenbare Sessions blockieren den Monatsabschluss nicht."
|
||||
],
|
||||
details: [
|
||||
"Nicht abrechenbar ist getrennt von Abgerechnet, damit interne, Kulanz- oder nicht fakturierbare Arbeit sichtbar bleibt.",
|
||||
"Diese Sessions werden nicht mit Teamspace verglichen.",
|
||||
"Die Statistik zeigt die Anzahl der nicht abrechenbaren Sessions, aber keinen Stundenwert.",
|
||||
"Es werden daraus keine Anteile, Differenzen oder Vergleichswerte berechnet.",
|
||||
"Wenn eine Bewertung zurückgenommen wird, wandert die Session wieder in Offen."
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "statistik-offen",
|
||||
title: "Statistik: Offen",
|
||||
description: "Diese Kennzahl zeigt Sessions, die noch keine Bewertung haben.",
|
||||
quickItems: [
|
||||
"Offen bedeutet: weder Abgerechnet noch Nicht abrechenbar.",
|
||||
"Offen bedeutet: noch keine Bewertung.",
|
||||
"Offene Sessions blockieren den Monatsabschluss.",
|
||||
"Die Klärung erfolgt in der Ticketdetailansicht."
|
||||
],
|
||||
details: [
|
||||
"Offen umfasst alle Sessions des Monats ohne Bewertung.",
|
||||
"Diese Sessions müssen vor dem Monatsabschluss entweder als Abgerechnet oder Nicht abrechenbar markiert werden.",
|
||||
"Diese Sessions müssen vor dem Monatsabschluss bewertet werden.",
|
||||
"Die offene Zeit zeigt, wie viele Minuten noch bewertet werden müssen.",
|
||||
"Der Monatsabschluss listet offene Bewertungen gruppiert nach Ticket, damit du direkt in die passende Ticketdetailansicht springen kannst.",
|
||||
"Wird eine Bewertung zurückgenommen, steigt die offene Anzahl wieder."
|
||||
@@ -319,7 +301,7 @@ export const helpSections: HelpSection[] = [
|
||||
"Der Monatsverlauf zeigt jeden Tag des Monats. Tage ohne Sessions bleiben leer, damit die Monatsstruktur sichtbar bleibt.",
|
||||
"Die blauen Balken stehen für abgerechnete Session-Zeit.",
|
||||
"Die fuchsiafarbene Linie steht für Teamspace-Zeit, sofern für den Tag Werte gepflegt wurden.",
|
||||
"Der Bewertungsstand weist Abgerechnet, Nicht abrechenbar und Offen nur als getrennte Werte aus.",
|
||||
"Der Bewertungsstand weist Abgerechnet und Offen getrennt aus.",
|
||||
"Der Teamspace-Abgleich zeigt die Gesamtdifferenz zwischen CRM-Werten und abgerechneter Session-Zeit.",
|
||||
"Manuell und Fix trennen selbst erfasste abrechenbare Sessions von automatisch erzeugten abrechenbaren Sessions aus fixen Abrechnungen."
|
||||
]
|
||||
@@ -338,7 +320,6 @@ export const helpSections: HelpSection[] = [
|
||||
"Sessions zeigt die Anzahl der einzelnen Arbeitseinträge.",
|
||||
"Zeiten vergleicht abgerechnete Session-Zeit mit Teamspace-Zeit.",
|
||||
"Die Differenz wird als Teamspace minus abgerechnete Zeit berechnet.",
|
||||
"Nicht abrechenbare Sessions werden separat ausgewiesen und nicht in die Differenz gerechnet.",
|
||||
"Der Anteil am Monat zeigt, wie groß die abgerechnete Zeit dieser Organisation im Verhältnis zur größten Organisation im Monat ist.",
|
||||
"Bei vielen Organisationen wird die Liste paginiert, damit die Seite kompakt bleibt."
|
||||
]
|
||||
@@ -357,7 +338,7 @@ export const helpSections: HelpSection[] = [
|
||||
"Support und Consulting werden getrennt summiert.",
|
||||
"Die Zeitwerte zeigen abgerechnete Sessions und Teamspace für den jeweiligen Typ.",
|
||||
"Die Differenz folgt derselben Logik wie überall: Teamspace minus abgerechnete Session-Zeit.",
|
||||
"Die Zeile Abr., Nicht und Offen zeigt, wie viele Sessions dieses Typs bereits bewertet oder noch offen sind."
|
||||
"Die Zeile Abr. und Offen zeigt, wie viele Sessions dieses Typs bereits abgerechnet oder noch offen sind."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -372,7 +353,7 @@ export const helpSections: HelpSection[] = [
|
||||
details: [
|
||||
"Ticket-Hotspots helfen, die größten Aufwandstreiber im Monat schnell zu erkennen.",
|
||||
"Die Liste zeigt die wichtigsten Tickets nach abgerechneter Zeit.",
|
||||
"Pro Ticket siehst du Organisation, Sessionanzahl, aktive abrechenbare Tage, abgerechnete Zeit, nicht abrechenbare Sessions, Teamspace-Zeit und Differenz.",
|
||||
"Pro Ticket siehst du Organisation, Sessionanzahl, aktive abrechenbare Tage, abgerechnete Zeit, Teamspace-Zeit und Differenz.",
|
||||
"Offene Tickets werden mit einem Warnstatus angezeigt, damit Bewertungen nachgezogen werden können.",
|
||||
"Über Öffnen gelangst du direkt in die Ticketdetailansicht des Monats."
|
||||
]
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
CircleAlert,
|
||||
Clock3,
|
||||
ExternalLink,
|
||||
ListChecks,
|
||||
RotateCcw,
|
||||
Ticket,
|
||||
TrendingUp,
|
||||
@@ -36,7 +35,6 @@ const trackedFillClass = "fill-sky-500/70 dark:fill-sky-400/65";
|
||||
const teamspaceStrokeClass = "text-fuchsia-600 dark:text-fuchsia-300";
|
||||
const trackedDotClass = "bg-sky-500 dark:bg-sky-400";
|
||||
const teamspaceDotClass = "bg-fuchsia-500 dark:bg-fuchsia-400";
|
||||
const nonBillableTextClass = "text-indigo-700 dark:text-indigo-300";
|
||||
const openTextClass = "text-amber-700 dark:text-amber-300";
|
||||
const organizationPageSize = 8;
|
||||
|
||||
@@ -125,10 +123,6 @@ function ColorLegend() {
|
||||
<span className="size-2 rounded-full bg-red-500 dark:bg-red-400" />
|
||||
<span className="text-red-700 dark:text-red-300">Differenz minus</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<span className="size-2 rounded-full bg-indigo-500 dark:bg-indigo-400" />
|
||||
<span className={nonBillableTextClass}>nicht abrechenbar</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<span className="size-2 rounded-full bg-amber-500 dark:bg-amber-400" />
|
||||
<span className={openTextClass}>offen</span>
|
||||
@@ -197,7 +191,6 @@ function buildMonthSeries(month: string, buckets: StatisticsDailyBucket[]) {
|
||||
billedMinutes: bucket?.billed_minutes ?? 0,
|
||||
openMinutes: bucket?.open_minutes ?? 0,
|
||||
billedSessions: bucket?.billed_sessions ?? 0,
|
||||
nonBillableSessions: bucket?.non_billable_sessions ?? 0,
|
||||
openSessions: bucket?.open_sessions ?? 0,
|
||||
crmBilledMinutes: bucket?.crm_billed_minutes ?? 0
|
||||
};
|
||||
@@ -344,7 +337,6 @@ function TicketHotspot({ ticket, month, onNavigate }: { ticket: StatisticsTicket
|
||||
<div className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-xs">
|
||||
<span className={trackedTextClass}>Abgerechnet {formatMinutes(ticket.total_minutes)}</span>
|
||||
<span className={teamspaceTextClass}>Teamspace {formatMinutes(ticket.crm_billed_minutes)}</span>
|
||||
<span className={nonBillableTextClass}>Nicht {ticket.non_billable_sessions} Session(s)</span>
|
||||
<span className="text-muted-foreground">{ticket.billed_sessions} abgerechnet</span>
|
||||
<span>{ticket.active_days} Tag(e)</span>
|
||||
<span className={deltaTextClass(ticket.crm_delta_minutes)}>{formatTeamspaceDelta(ticket.crm_delta_minutes)}</span>
|
||||
@@ -421,7 +413,6 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
const totals = stats?.totals;
|
||||
const billedSessions = totals?.billedSessions ?? 0;
|
||||
const billedMinutes = totals?.billedMinutes ?? 0;
|
||||
const nonBillableSessions = totals?.nonBillableSessions ?? 0;
|
||||
const openSessions = totals?.openSessions ?? 0;
|
||||
const openMinutes = totals?.openMinutes ?? 0;
|
||||
const crmCoverage = percentage(totals?.crmBilledMinutes ?? 0, totals?.minutes ?? 0);
|
||||
@@ -469,7 +460,7 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||
<MetricCard
|
||||
label="Abgerechnet"
|
||||
value={formatMinutes(totals?.minutes ?? 0)}
|
||||
@@ -488,19 +479,6 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
helpAnchor="statistik-teamspace"
|
||||
helpLabel="Hilfe zur Kennzahl Teamspace"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Nicht abrechenbar"
|
||||
value={`${nonBillableSessions} Session(s)`}
|
||||
detail={
|
||||
<div className="space-y-0.5">
|
||||
<div>wird nicht kalkuliert</div>
|
||||
</div>
|
||||
}
|
||||
icon={ListChecks}
|
||||
valueClassName={nonBillableTextClass}
|
||||
helpAnchor="statistik-bewertet"
|
||||
helpLabel="Hilfe zur Kennzahl Nicht abrechenbar"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Offen"
|
||||
value={formatMinutes(openMinutes)}
|
||||
@@ -541,9 +519,8 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
<span className="text-muted-foreground">Bewertungsstand</span>
|
||||
<span className="font-medium">{billedSessions} abrechenbar</span>
|
||||
</div>
|
||||
<div className="grid gap-2 text-xs sm:grid-cols-3 xl:grid-cols-1 2xl:grid-cols-3">
|
||||
<div className="grid gap-2 text-xs sm:grid-cols-2 xl:grid-cols-1 2xl:grid-cols-2">
|
||||
<div className={trackedTextClass}>Abgerechnet: {billedSessions} Session(s), {formatMinutes(billedMinutes)}</div>
|
||||
<div className={nonBillableTextClass}>Nicht: {nonBillableSessions} Session(s)</div>
|
||||
<div className={openTextClass}>Offen: {openSessions} Session(s), {formatMinutes(openMinutes)}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -587,7 +564,6 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
<p className="flex flex-wrap gap-x-3 gap-y-1 text-xs">
|
||||
<span className={trackedTextClass}>Abgerechnet {formatMinutes(topOrganization.total_minutes)}</span>
|
||||
<span className={teamspaceTextClass}>Teamspace {formatMinutes(topOrganization.crm_billed_minutes)}</span>
|
||||
<span className={nonBillableTextClass}>Nicht {topOrganization.non_billable_sessions} Session(s)</span>
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -647,7 +623,6 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
<div className="flex flex-wrap gap-x-3 gap-y-1 text-xs">
|
||||
<span className={trackedTextClass}>Abgerechnet {formatMinutes(organization.total_minutes)}</span>
|
||||
<span className={teamspaceTextClass}>Teamspace {formatMinutes(organization.crm_billed_minutes)}</span>
|
||||
<span className={nonBillableTextClass}>Nicht {organization.non_billable_sessions} Session(s)</span>
|
||||
<span className="text-muted-foreground">{percentage(organization.total_minutes, totals?.minutes ?? 0)}% vom Monat</span>
|
||||
<span className={deltaTextClass(organization.crm_delta_minutes)}>{formatTeamspaceDelta(organization.crm_delta_minutes)}</span>
|
||||
</div>
|
||||
@@ -718,9 +693,8 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
<span className={trackedTextClass}>Abgerechnet {formatMinutes(group.total_minutes)}</span>
|
||||
<span className={teamspaceTextClass}>Teamspace {formatMinutes(group.crm_billed_minutes)}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2 text-xs text-muted-foreground">
|
||||
<div className="grid grid-cols-2 gap-2 text-xs text-muted-foreground">
|
||||
<span>Abr. {group.billed_sessions} / {formatMinutes(group.billed_minutes)}</span>
|
||||
<span>Nicht {group.non_billable_sessions}</span>
|
||||
<span>Offen {group.open_sessions} / {formatMinutes(group.open_minutes)}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -758,7 +732,7 @@ export function StatisticsPage({ onNavigate }: StatisticsPageProps) {
|
||||
<Ticket className="size-8 text-muted-foreground" />
|
||||
<div>
|
||||
<p className="font-medium">Keine abgerechneten Sessions in diesem Monat</p>
|
||||
<p className="text-sm text-muted-foreground">Offene oder nicht abrechenbare Sessions werden separat gezeigt und fließen nicht in Statistiken oder Vergleiche ein.</p>
|
||||
<p className="text-sm text-muted-foreground">Offene Sessions werden separat gezeigt und fließen nicht in Statistiken oder Vergleiche ein.</p>
|
||||
</div>
|
||||
<Button variant="secondary" size="sm" onClick={() => setMonth(currentMonth())}>
|
||||
<RotateCcw className="size-4" />
|
||||
|
||||
Reference in New Issue
Block a user