Auto-fill manual sessions from existing tickets
This commit is contained in:
1 parent
018c06dc3e
commit
2792051a16
2 files changed
+87
-6
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { FormEvent, useMemo, useState } from "react";
|
||||
import { FormEvent, useEffect, useMemo, useState } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { Pause, Play, Plus, RotateCcw, Square, Upload } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
@@ -73,12 +73,74 @@ export function TimerPage({ timers, setTimers, selectedTimerId, setSelectedTimer
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [manualSession, setManualSession] = useState<ManualSessionFormState>(() => emptyManualSession());
|
||||
const [savingManualSession, setSavingManualSession] = useState(false);
|
||||
const [manualLookupStatus, setManualLookupStatus] = useState<"idle" | "loading" | "found" | "not-found">("idle");
|
||||
const [manualLookupTicket, setManualLookupTicket] = useState<TicketMeta | null>(null);
|
||||
|
||||
const selectedTimer = timers.find((timer) => timer.id === selectedTimerId) ?? timers[0] ?? null;
|
||||
const elapsedSeconds = useMemo(() => Math.floor(activeElapsedMs(selectedTimer, tick) / 1000), [selectedTimer, tick]);
|
||||
const isValidTicket = ticketPattern.test(ticketNumber);
|
||||
const isManualTicketValid = ticketPattern.test(manualSession.ticketNumber.trim());
|
||||
const manualTicketDefaultsLocked = Boolean(manualLookupTicket?.organization_id && manualLookupTicket.work_type);
|
||||
const runningTimer = timers.find((timer) => timer.phase === "running") ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
const normalizedTicketNumber = manualSession.ticketNumber.trim();
|
||||
|
||||
if (!normalizedTicketNumber) {
|
||||
setManualLookupStatus("idle");
|
||||
setManualLookupTicket(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ticketPattern.test(normalizedTicketNumber)) {
|
||||
setManualLookupStatus("idle");
|
||||
setManualLookupTicket(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setManualLookupStatus("loading");
|
||||
setManualLookupTicket(null);
|
||||
|
||||
const timeout = window.setTimeout(async () => {
|
||||
try {
|
||||
const result = await lookupTicket(normalizedTicketNumber);
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setManualLookupTicket(result.ticket);
|
||||
setManualLookupStatus(result.ticket ? "found" : "not-found");
|
||||
|
||||
if (result.ticket) {
|
||||
setManualSession((current) => {
|
||||
if (current.ticketNumber.trim() !== normalizedTicketNumber) {
|
||||
return current;
|
||||
}
|
||||
|
||||
return {
|
||||
...current,
|
||||
organizationId: result.ticket?.organization_id ?? "",
|
||||
organizationName: result.ticket?.organization_name ?? result.ticket?.customer_name ?? null,
|
||||
workType: result.ticket?.work_type ?? current.workType
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) {
|
||||
setManualLookupStatus("idle");
|
||||
setManualLookupTicket(null);
|
||||
}
|
||||
}
|
||||
}, 250);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
window.clearTimeout(timeout);
|
||||
};
|
||||
}, [manualSession.ticketNumber]);
|
||||
|
||||
async function addTimer() {
|
||||
if (!isValidTicket) {
|
||||
toast.error("Ticketnummer prüfen", {
|
||||
@@ -231,7 +293,7 @@ export function TimerPage({ timers, setTimers, selectedTimerId, setSelectedTimer
|
||||
async function submitManualSession(event: FormEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!ticketPattern.test(manualSession.ticketNumber)) {
|
||||
if (!isManualTicketValid) {
|
||||
toast.error("Ticketnummer prüfen", {
|
||||
description: "Das Format muss Ticket#XXXXXX sein."
|
||||
});
|
||||
@@ -262,7 +324,7 @@ export function TimerPage({ timers, setTimers, selectedTimerId, setSelectedTimer
|
||||
|
||||
try {
|
||||
await createSession({
|
||||
ticketNumber: manualSession.ticketNumber,
|
||||
ticketNumber: manualSession.ticketNumber.trim(),
|
||||
organizationId: manualSession.organizationId,
|
||||
activity: manualSession.activity,
|
||||
workType: manualSession.workType,
|
||||
@@ -403,15 +465,33 @@ export function TimerPage({ timers, setTimers, selectedTimerId, setSelectedTimer
|
||||
id="manual-ticket-number"
|
||||
placeholder="Ticket#123456"
|
||||
value={manualSession.ticketNumber}
|
||||
onChange={(event) => setManualSession({ ...manualSession, ticketNumber: event.currentTarget.value })}
|
||||
onChange={(event) =>
|
||||
setManualSession({
|
||||
...manualSession,
|
||||
ticketNumber: event.currentTarget.value,
|
||||
organizationId: "",
|
||||
organizationName: null,
|
||||
workType: "support"
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
{manualSession.ticketNumber.length > 0 && !isManualTicketValid ? <p className="text-xs text-destructive">Format: Ticket#XXXXXX</p> : null}
|
||||
{manualLookupStatus === "loading" ? <p className="text-xs text-muted-foreground">Prüfe vorhandenes Ticket...</p> : null}
|
||||
{manualLookupStatus === "found" && manualTicketDefaultsLocked ? (
|
||||
<p className="text-xs text-muted-foreground">Ticket gefunden. Organisation und Art wurden übernommen.</p>
|
||||
) : null}
|
||||
{manualLookupStatus === "found" && !manualTicketDefaultsLocked ? (
|
||||
<p className="text-xs text-muted-foreground">Ticket gefunden, aber Stammdaten sind unvollständig. Bitte ergänzen.</p>
|
||||
) : null}
|
||||
{manualLookupStatus === "not-found" ? <p className="text-xs text-muted-foreground">Neues Ticket. Organisation und Art bitte wählen.</p> : null}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Organisation</label>
|
||||
<OrganizationSelect
|
||||
value={manualSession.organizationId}
|
||||
selectedName={manualSession.organizationName}
|
||||
disabled={manualTicketDefaultsLocked}
|
||||
onChange={(organization) =>
|
||||
setManualSession({
|
||||
...manualSession,
|
||||
@@ -428,6 +508,7 @@ export function TimerPage({ timers, setTimers, selectedTimerId, setSelectedTimer
|
||||
id="manual-work-type"
|
||||
className="h-9 w-full rounded-lg border border-input bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/50"
|
||||
value={manualSession.workType}
|
||||
disabled={manualTicketDefaultsLocked}
|
||||
onChange={(event) => setManualSession({ ...manualSession, workType: event.currentTarget.value as WorkType })}
|
||||
>
|
||||
<option value="support">Support</option>
|
||||
|
||||
Reference in new issue
Block a user