Update
This commit is contained in:
1 parent
0c14a3acbd
commit
2105a0c5f7
11 files changed
+442
-86
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { ArrowLeft, CheckCircle2, ChevronDown, ChevronUp, ClipboardList, Copy, EyeOff, FolderKanban, Pencil, Play, Plus, RotateCcw, Save, Star, Trash2 } from "lucide-react";
|
||||
import { ArrowLeft, CheckCircle2, ChevronDown, ChevronUp, ClipboardList, Copy, EyeOff, FolderKanban, Pencil, Play, Plus, RotateCcw, Save, Send, Star, Trash2 } from "lucide-react";
|
||||
import { FormEvent, useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Alert } from "@/components/ui/alert";
|
||||
@@ -30,10 +30,12 @@ import {
|
||||
updateProject,
|
||||
updateProjectStatus
|
||||
} from "../api";
|
||||
import type { CreateSessionResult } from "../api";
|
||||
import { currentDay, currentMonth, formatDate, formatDateTime, formatMinutes, formatTimeRange } from "../format";
|
||||
import { ticketHasRecurringBilling } from "../recurring-billings";
|
||||
import type { AuthUser, BillingStatus, PeriodType, ProjectSummary, ProjectTeamspaceEntry, SessionEntry, TicketDetailPeriodType, TicketPeriod, WorkType } from "../types";
|
||||
import { ZammadPriorityBadge, ZammadStateBadge } from "../zammad-meta";
|
||||
import { zammadArticleTypeFromValue, zammadArticleTypeLabel, zammadArticleTypeOptions, zammadArticleTypeValue, type ZammadArticleTypeId } from "../zammad-article-types";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type TicketDetailPageProps = {
|
||||
@@ -63,6 +65,7 @@ type TicketSessionFormState = {
|
||||
activity: string;
|
||||
billingStatus: BillingStatus;
|
||||
createZammadArticle: boolean;
|
||||
zammadArticleTypeId: ZammadArticleTypeId;
|
||||
workType: WorkType;
|
||||
day: string;
|
||||
startTime: string;
|
||||
@@ -96,6 +99,7 @@ function emptyTicketSessionForm(): TicketSessionFormState {
|
||||
activity: "",
|
||||
billingStatus: null,
|
||||
createZammadArticle: false,
|
||||
zammadArticleTypeId: 6,
|
||||
workType: "support",
|
||||
day: currentDay(),
|
||||
startTime: "09:00",
|
||||
@@ -103,6 +107,19 @@ function emptyTicketSessionForm(): TicketSessionFormState {
|
||||
};
|
||||
}
|
||||
|
||||
function notifyZammadArticleResult(result: CreateSessionResult) {
|
||||
if (result.zammadArticle?.status === "sent") {
|
||||
toast.success("Zammad-Notiz übertragen");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.zammadArticle?.status === "queued") {
|
||||
toast.warning("Zammad-Notiz geparkt", {
|
||||
description: result.zammadArticle.error ?? "Die Session wurde gespeichert. Die Notiz kann im Profil später erneut gesendet werden."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function projectEntryId() {
|
||||
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
||||
return crypto.randomUUID();
|
||||
@@ -599,16 +616,21 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
|
||||
useEffect(() => {
|
||||
setTicketSession((current) => {
|
||||
if (current.activity || current.billingStatus || current.createZammadArticle === currentUser.zammad_session_note_default_enabled) {
|
||||
if (
|
||||
current.activity ||
|
||||
current.billingStatus ||
|
||||
(current.createZammadArticle === currentUser.zammad_session_note_default_enabled && current.zammadArticleTypeId === currentUser.zammad_article_type_id)
|
||||
) {
|
||||
return current;
|
||||
}
|
||||
|
||||
return {
|
||||
...current,
|
||||
createZammadArticle: currentUser.zammad_session_note_default_enabled
|
||||
createZammadArticle: currentUser.zammad_session_note_default_enabled,
|
||||
zammadArticleTypeId: currentUser.zammad_article_type_id
|
||||
};
|
||||
});
|
||||
}, [currentUser.zammad_session_note_default_enabled]);
|
||||
}, [currentUser.zammad_article_type_id, currentUser.zammad_session_note_default_enabled]);
|
||||
|
||||
useEffect(() => {
|
||||
setProjectDescriptionExpanded(false);
|
||||
@@ -1031,7 +1053,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
setSavingTicketSession(true);
|
||||
|
||||
try {
|
||||
await createSession({
|
||||
const result = await createSession({
|
||||
ticketNumber: ticket.ticket_number,
|
||||
organizationId: effectiveOrganizationId,
|
||||
activity: ticketSession.activity,
|
||||
@@ -1041,11 +1063,13 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
durationSeconds,
|
||||
projectTeamspaceEntryId: effectiveWorkType === "consulting" && requiresProjectTeamspaceEntry ? ticketSession.projectTeamspaceEntryId : null,
|
||||
billingStatus: ticketSession.billingStatus,
|
||||
createZammadArticle: ticketSession.createZammadArticle
|
||||
createZammadArticle: ticketSession.createZammadArticle,
|
||||
zammadArticleTypeId: ticketSession.zammadArticleTypeId
|
||||
});
|
||||
toast.success("Session nachgetragen", {
|
||||
description: "Der Eintrag wurde diesem Ticket zugeordnet. Betroffene Abschlüsse wurden wieder geöffnet."
|
||||
});
|
||||
notifyZammadArticleResult(result);
|
||||
setTicketSession({
|
||||
...emptyTicketSessionForm(),
|
||||
organizationId: ticket.organization_id ?? "",
|
||||
@@ -1054,7 +1078,8 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
workType: ticket.work_type ?? "support",
|
||||
day: ticketSession.day,
|
||||
billingStatus: null,
|
||||
createZammadArticle: currentUser.zammad_session_note_default_enabled
|
||||
createZammadArticle: currentUser.zammad_session_note_default_enabled,
|
||||
zammadArticleTypeId: currentUser.zammad_article_type_id
|
||||
});
|
||||
await load();
|
||||
} catch (error) {
|
||||
@@ -1842,7 +1867,16 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
<p className="whitespace-pre-wrap break-words text-sm font-medium">{session.activity}</p>
|
||||
<div className="mt-2 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground">
|
||||
<span>{session.organization_name ?? session.customer_name}</span>
|
||||
<span>{session.work_type === "support" ? "Support" : "Consulting"}</span>
|
||||
{session.zammad_article_status ? (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
{session.zammad_article_status === "sent" ? (
|
||||
<Send className="size-3.5 text-emerald-500" aria-hidden="true" />
|
||||
) : null}
|
||||
<span>{zammadArticleTypeLabel(session.zammad_article_type_id)}</span>
|
||||
</span>
|
||||
) : (
|
||||
<span>{session.work_type === "support" ? "Support" : "Consulting"}</span>
|
||||
)}
|
||||
<span>Erfasst {formatDateTime(session.created_at)}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2009,6 +2043,25 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
<span className="block text-xs text-muted-foreground">Überträgt die Tätigkeit inklusive time_unit nach Zammad oder parkt sie bei Fehlern.</span>
|
||||
</span>
|
||||
</label>
|
||||
{ticketSession.createZammadArticle ? (
|
||||
<div className="space-y-2 lg:col-span-5">
|
||||
<label className="text-sm font-medium" htmlFor="ticket-session-zammad-article-type">
|
||||
Zammad-Typ
|
||||
</label>
|
||||
<select
|
||||
id="ticket-session-zammad-article-type"
|
||||
className="h-10 w-full rounded-md border border-input bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
value={zammadArticleTypeValue(ticketSession.zammadArticleTypeId)}
|
||||
onChange={(event) => setTicketSession({ ...ticketSession, zammadArticleTypeId: zammadArticleTypeFromValue(event.currentTarget.value) })}
|
||||
>
|
||||
{zammadArticleTypeOptions.map((option) => (
|
||||
<option key={option.value || "empty"} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
) : null}
|
||||
<Button type="submit" disabled={savingTicketSession}>
|
||||
<Save className="size-4" />
|
||||
{savingTicketSession ? "Speichert..." : "Nachtragen"}
|
||||
|
||||
Reference in new issue
Block a user