1.1.0-rc1
This commit is contained in:
1 parent
649dd6f028
commit
45ff46b7eb
26 files changed
+4752
-1108
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Clock3, Download, ExternalLink, RotateCcw, SlidersHorizontal, Ticket, UserCheck, Users } from "lucide-react";
|
||||
import { AlertCircle, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Clock3, Download, ExternalLink, RotateCcw, SlidersHorizontal, Ticket, UserCheck, Users } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
@@ -168,6 +168,41 @@ function TicketStatus({ ticket }: { ticket: TicketSummary }) {
|
||||
return <Badge variant="success">bewertet</Badge>;
|
||||
}
|
||||
|
||||
function formatSignedMinutes(minutes: number) {
|
||||
if (minutes === 0) {
|
||||
return formatMinutes(0);
|
||||
}
|
||||
|
||||
return `${minutes > 0 ? "+" : "-"}${formatMinutes(Math.abs(minutes))}`;
|
||||
}
|
||||
|
||||
function teamspaceMonthDeltaMinutes(ticket: TicketSummary) {
|
||||
const actuals = ticket.teamspace_month_actuals ?? [];
|
||||
|
||||
if (actuals.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const tsMinutes = ticket.teamspace_month_billable_minutes ?? actuals.reduce((sum, actual) => sum + actual.teamspace_billable_minutes, 0);
|
||||
return ticket.teamspace_month_delta_minutes ?? tsMinutes - ticket.crm_billed_minutes;
|
||||
}
|
||||
|
||||
function TeamspaceDeviationIcon({ ticket }: { ticket: TicketSummary }) {
|
||||
const deltaMinutes = teamspaceMonthDeltaMinutes(ticket);
|
||||
|
||||
if (deltaMinutes === 0) {
|
||||
return <span className="inline-block size-4" aria-hidden="true" />;
|
||||
}
|
||||
|
||||
const title = deltaMinutes > 0 ? `${formatMinutes(deltaMinutes)} fehlt in TT` : `${formatMinutes(Math.abs(deltaMinutes))} fehlt in TS`;
|
||||
|
||||
return (
|
||||
<span title={`${formatSignedMinutes(deltaMinutes)} · ${title}`} aria-label={`Teamspace-Abweichung: ${title}`}>
|
||||
<AlertCircle className="mx-auto size-4 text-amber-500" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ticketStatusKey(ticket: TicketSummary): "open" | "evaluated" {
|
||||
return (ticket.ticket_open_count ?? ticket.open_count) > 0 ? "open" : "evaluated";
|
||||
}
|
||||
@@ -690,6 +725,9 @@ export function AnalysisPage({ currentUser, onNavigate }: AnalysisPageProps) {
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Ticket</TableHead>
|
||||
<TableHead className="w-10 text-center">
|
||||
<span className="sr-only">Teamspace-Abweichung</span>
|
||||
</TableHead>
|
||||
<TableHead>Organisation</TableHead>
|
||||
<TableHead>Art</TableHead>
|
||||
<TableHead>Sessions</TableHead>
|
||||
@@ -730,6 +768,9 @@ export function AnalysisPage({ currentUser, onNavigate }: AnalysisPageProps) {
|
||||
</div>
|
||||
{ticket.title ? <p className="max-w-[220px] truncate text-xs text-muted-foreground">{ticket.title}</p> : null}
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<TeamspaceDeviationIcon ticket={ticket} />
|
||||
</TableCell>
|
||||
<TableCell>{ticket.customer_name ?? "-"}</TableCell>
|
||||
<TableCell>{workTypeLabel(ticket.work_type)}</TableCell>
|
||||
<TableCell>{ticket.session_count}</TableCell>
|
||||
@@ -785,7 +826,10 @@ export function AnalysisPage({ currentUser, onNavigate }: AnalysisPageProps) {
|
||||
{ticket.title ? <p className="truncate text-xs text-muted-foreground">{ticket.title}</p> : null}
|
||||
<p className="text-sm text-muted-foreground">{ticket.customer_name ?? "Keine Organisation"} · {workTypeLabel(ticket.work_type)} · {ticket.session_count} Session(s)</p>
|
||||
</div>
|
||||
<TicketStatus ticket={ticket} />
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<TeamspaceDeviationIcon ticket={ticket} />
|
||||
<TicketStatus ticket={ticket} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
<div className="rounded-md bg-muted/50 p-2">
|
||||
|
||||
Reference in new issue
Block a user