Update
This commit is contained in:
1 parent
b7f7d7e793
commit
c01d747352
14 files changed
+987
-294
No files matched your search
@@ -20,6 +20,7 @@ import {
|
||||
type ZammadSyncResult
|
||||
} from "../api";
|
||||
import type { AuthUser } from "../types";
|
||||
import { zammadPriorityLabel, zammadStateLabel } from "../zammad-meta";
|
||||
|
||||
type ZammadSyncPageProps = {
|
||||
currentUser: AuthUser;
|
||||
@@ -32,10 +33,11 @@ const defaultFields: ZammadSyncExistingFields = {
|
||||
organization: true,
|
||||
type: true,
|
||||
projectName: true,
|
||||
budget: true
|
||||
budget: true,
|
||||
overrideBudget: false
|
||||
};
|
||||
|
||||
const fieldOptions: Array<{ key: keyof ZammadSyncExistingFields; label: string; description: string }> = [
|
||||
const fieldOptions: Array<{ key: Exclude<keyof ZammadSyncExistingFields, "overrideBudget">; label: string; description: string }> = [
|
||||
{ key: "title", label: "Titel", description: "Ticket-Titel aus Zammad übernehmen." },
|
||||
{ key: "organization", label: "Organisation", description: "Lokale Organisation anhand der Zammad-ID setzen." },
|
||||
{ key: "type", label: "Typ", description: "Support/Consulting übernehmen, Projekte anlegen oder entfernen." },
|
||||
@@ -164,16 +166,8 @@ function formatZammadGroup(value: unknown) {
|
||||
function formatZammadPriority(value: unknown) {
|
||||
const priorityId = numericValue(value);
|
||||
|
||||
if (priorityId === 1) {
|
||||
return "1 (Niedrig)";
|
||||
}
|
||||
|
||||
if (priorityId === 2) {
|
||||
return "2 (Normal)";
|
||||
}
|
||||
|
||||
if (priorityId === 3) {
|
||||
return "3 (Hoch)";
|
||||
if (priorityId !== null) {
|
||||
return `${priorityId} (${zammadPriorityLabel(priorityId)})`;
|
||||
}
|
||||
|
||||
return formatValue(value);
|
||||
@@ -182,20 +176,8 @@ function formatZammadPriority(value: unknown) {
|
||||
function formatZammadState(value: unknown) {
|
||||
const stateId = numericValue(value);
|
||||
|
||||
if (stateId === 2) {
|
||||
return "2 (offen)";
|
||||
}
|
||||
|
||||
if (stateId === 3) {
|
||||
return "3 (warten auf Erinnerung)";
|
||||
}
|
||||
|
||||
if (stateId === 4) {
|
||||
return "4 (geschlossen)";
|
||||
}
|
||||
|
||||
if (stateId === 7) {
|
||||
return "7 (warten auf schließen)";
|
||||
if (stateId !== null) {
|
||||
return `${stateId} (${zammadStateLabel(stateId)})`;
|
||||
}
|
||||
|
||||
return formatValue(value);
|
||||
@@ -222,6 +204,10 @@ function formatChangeValue(field: string, value: unknown) {
|
||||
return formatMinutes(value);
|
||||
}
|
||||
|
||||
if (field === "Teamspace-Projekte") {
|
||||
return formatTeamspaceProjectList(value) || "-";
|
||||
}
|
||||
|
||||
if (field === "Typ") {
|
||||
return formatWorkType(value);
|
||||
}
|
||||
@@ -242,7 +228,7 @@ function formatChangeValue(field: string, value: unknown) {
|
||||
}
|
||||
|
||||
function visibleTicketTrackerChanges(item: ZammadSyncItem) {
|
||||
return item.changes.filter((change) => !change.field.startsWith("Zammad-"));
|
||||
return item.changes.filter((change) => !change.field.startsWith("Zammad-") || change.field === "Zammad-Status" || change.field === "Zammad-Priorität");
|
||||
}
|
||||
|
||||
function changeAllowedByFields(item: ZammadSyncItem, change: ZammadSyncChange, fields: ZammadSyncExistingFields) {
|
||||
@@ -270,6 +256,10 @@ function changeAllowedByFields(item: ZammadSyncItem, change: ZammadSyncChange, f
|
||||
return fields.projectName && consultingProjectCanBeTouched;
|
||||
}
|
||||
|
||||
if (change.field === "Teamspace-Projekte") {
|
||||
return fields.projectName && consultingProjectCanBeTouched;
|
||||
}
|
||||
|
||||
if (change.field === "Teamspace-Budget") {
|
||||
return fields.budget && consultingProjectCanBeTouched;
|
||||
}
|
||||
@@ -290,6 +280,23 @@ function cachedObject(source: Record<string, unknown> | null | undefined, key: s
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
|
||||
}
|
||||
|
||||
function cachedStringList(source: Record<string, unknown> | null | undefined, key: string) {
|
||||
const value = cachedValue(source, key);
|
||||
return Array.isArray(value) ? value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean) : [];
|
||||
}
|
||||
|
||||
function formatTeamspaceProjectList(value: unknown) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean).join(" + ");
|
||||
}
|
||||
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function splitTeamspaceProjects(value: unknown) {
|
||||
return typeof value === "string" ? value.split("+").map((entry) => entry.trim()).filter(Boolean) : [];
|
||||
}
|
||||
|
||||
function comparablePreviewValue(value: unknown) {
|
||||
if (typeof value === "string" && value.trim().length === 0) {
|
||||
return null;
|
||||
@@ -317,10 +324,14 @@ function comparablePreviewIdValue(value: unknown) {
|
||||
}
|
||||
|
||||
function comparablePreviewValueForField(field: string, value: unknown) {
|
||||
if (field === "Organisation") {
|
||||
if (field === "Organisation" || field === "Zammad-Priorität" || field === "Zammad-Status") {
|
||||
return comparablePreviewIdValue(value);
|
||||
}
|
||||
|
||||
if (field === "Teamspace-Projekte" && Array.isArray(value)) {
|
||||
return value.map((entry) => comparablePreviewValue(entry));
|
||||
}
|
||||
|
||||
return comparablePreviewValue(value);
|
||||
}
|
||||
|
||||
@@ -349,8 +360,16 @@ function changesFromExistingPreviewCache(item: ZammadSyncItem, fields: ZammadSyn
|
||||
const localWorkType = cachedValue(local, "workType");
|
||||
const zammadWorkType = cachedValue(zammad, "workType");
|
||||
const zammadStateId = numericValue(cachedValue(zammad, "stateId"));
|
||||
const localTeamspaceProjects = cachedStringList(project, "teamspaceProjects");
|
||||
const zammadTeamspaceProjects = cachedStringList(zammad, "teamspaceProjects");
|
||||
const localTeamspaceProjectList = localTeamspaceProjects.length ? localTeamspaceProjects : [cachedValue(project, "teamspaceProject")].filter((entry): entry is string => typeof entry === "string");
|
||||
const zammadTeamspaceProjectList = zammadTeamspaceProjects.length ? zammadTeamspaceProjects : splitTeamspaceProjects(cachedValue(zammad, "teamspaceProject"));
|
||||
const canSyncBudget = fields.budget && (cachedValue(project, "defaultBudgetSyncDisabled") !== true || fields.overrideBudget);
|
||||
const changes: ZammadSyncChange[] = [];
|
||||
|
||||
addCachedChange(changes, "Zammad-Priorität", cachedValue(local, "zammadPriorityId"), cachedValue(zammad, "priorityId"));
|
||||
addCachedChange(changes, "Zammad-Status", cachedValue(local, "zammadStateId"), cachedValue(zammad, "stateId"));
|
||||
|
||||
if (zammadWorkType === "support" && zammadStateId === 4) {
|
||||
return changes;
|
||||
}
|
||||
@@ -379,11 +398,11 @@ function changesFromExistingPreviewCache(item: ZammadSyncItem, fields: ZammadSyn
|
||||
}
|
||||
|
||||
if (fields.projectName) {
|
||||
addCachedChange(changes, "Teamspace-Projekt", cachedValue(project, "teamspaceProject"), cachedValue(zammad, "teamspaceProject"));
|
||||
addCachedChange(changes, "Teamspace-Projekte", localTeamspaceProjectList, zammadTeamspaceProjectList);
|
||||
}
|
||||
|
||||
if (fields.budget) {
|
||||
addCachedChange(changes, "Teamspace-Budget", cachedValue(project, "hourBudgetMinutes"), cachedValue(zammad, "teamspaceBudgetMinutes"));
|
||||
if (canSyncBudget) {
|
||||
addCachedChange(changes, "Teamspace-Budget", cachedValue(project, "defaultHourBudgetMinutes") ?? cachedValue(project, "hourBudgetMinutes"), cachedValue(zammad, "teamspaceBudgetMinutes"));
|
||||
}
|
||||
|
||||
if (zammadStateId === 4 && cachedValue(project, "closedAt") === null && cachedValue(project, "closeReviewRequired") !== true) {
|
||||
@@ -516,7 +535,7 @@ function zammadDetails(item: ZammadSyncItem) {
|
||||
`Owner ${formatValue(zammad.ownerId)}`,
|
||||
`Org ${formatValue(zammad.organizationId)}`,
|
||||
`Titel ${formatValue(zammad.title)}`,
|
||||
`Projekt ${formatValue(zammad.teamspaceProject)}`,
|
||||
`Projekt ${formatValue(formatTeamspaceProjectList(zammad.teamspaceProjects) || zammad.teamspaceProject)}`,
|
||||
`Budget ${formatMinutes(zammad.teamspaceBudgetMinutes)}`
|
||||
];
|
||||
}
|
||||
@@ -681,7 +700,7 @@ export function ZammadSyncPage({ currentUser }: ZammadSyncPageProps) {
|
||||
|
||||
function setField(key: keyof ZammadSyncExistingFields, checked: boolean) {
|
||||
setZammadSyncPageState((current) => ({
|
||||
fields: { ...current.fields, [key]: checked }
|
||||
fields: { ...current.fields, [key]: checked, ...(key === "budget" && !checked ? { overrideBudget: false } : {}) }
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -894,6 +913,15 @@ export function ZammadSyncPage({ currentUser }: ZammadSyncPageProps) {
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
{fields.budget ? (
|
||||
<label className={cn("flex min-w-0 gap-3 rounded-md border p-3", fields.overrideBudget ? "bg-muted/30" : "bg-background")}>
|
||||
<Checkbox checked={fields.overrideBudget} onCheckedChange={(checked) => setField("overrideBudget", checked === true)} />
|
||||
<span className="min-w-0">
|
||||
<span className="block text-sm font-medium">Manuelle Budget-Änderungen überschreiben</span>
|
||||
<span className="block text-xs text-muted-foreground">Ausgeschaltet: geschützte Budgets bleiben in TicketTracker unverändert.</span>
|
||||
</span>
|
||||
</label>
|
||||
) : null}
|
||||
<div className="flex flex-wrap justify-end gap-2">
|
||||
{syncingExisting ? (
|
||||
<Button type="button" variant="outline" onClick={resetRunningState}>
|
||||
|
||||
Reference in new issue
Block a user