v1.0.11-rc1
This commit is contained in:
1 parent
d5952d82bb
commit
e1456ed4ca
8 files changed
+1315
-501
No files matched your search
@@ -1664,7 +1664,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
|
||||
const remaining = projectRemaining(project, viewSettings.standardTeamspaceOnly);
|
||||
const usedPercent = projectConsumptionPercent(project, viewSettings.standardTeamspaceOnly);
|
||||
const showBudgetedTeamspaceEntries = !viewSettings.standardTeamspaceOnly && budgetedTeamspaceEntries.length > 1;
|
||||
const canEditProject = project.can_edit !== false;
|
||||
const canEditProject = project.can_edit === true;
|
||||
|
||||
return (
|
||||
<div key={project.id} className="border-b last:border-b-0">
|
||||
@@ -1797,7 +1797,7 @@ export function ProjectsPage({ onNavigate, onStartTimer }: ProjectsPageProps) {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 shrink-0 px-2 text-xs"
|
||||
disabled={savingProjectStatusId === project.id || project.can_edit === false}
|
||||
disabled={savingProjectStatusId === project.id || !canEditProject}
|
||||
onClick={() => void acknowledgeProjectReviewCheck(project)}
|
||||
>
|
||||
<CheckCircle2 className="mr-1.5 h-3.5 w-3.5" />
|
||||
|
||||
@@ -912,8 +912,17 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
const projectBudgetMinutes = project?.hour_budget_minutes ?? null;
|
||||
const projectConsumptionUsagePercent = project ? projectConsumptionPercent(projectConsumptionMinutes, projectBudgetMinutes) : null;
|
||||
const projectRemainingMinutes = projectBudgetMinutes && projectBudgetMinutes > 0 ? projectBudgetMinutes - projectConsumptionMinutes : null;
|
||||
const canEditProject = project?.can_edit !== false;
|
||||
const canEditTicketData = !project || canEditProject;
|
||||
const canEditProject = project?.can_edit === true;
|
||||
const ticketManagedByZammad = Boolean(ticket?.zammad_ticket_id);
|
||||
const canEditTicketData = ticket?.can_edit_ticket_data === true;
|
||||
const zammadOwnerLabel = ticket?.zammad_owner_display_name
|
||||
|| ticket?.zammad_owner_username
|
||||
|| (ticket?.zammad_owner_id ? `Zammad-Owner ${ticket.zammad_owner_id}` : null);
|
||||
const projectOwnerLabel = project?.owner_display_name
|
||||
|| project?.owner_username
|
||||
|| (project?.zammad_owner_id ? `Zammad-Owner ${project.zammad_owner_id}` : null)
|
||||
|| zammadOwnerLabel;
|
||||
const currentZammadUserLabel = currentUser.zammad_user_id ? `Zammad-User ${currentUser.zammad_user_id}` : "dein Zammad-User";
|
||||
const isProject = Boolean(project);
|
||||
const isGlobalTicket = periodType === "all";
|
||||
const isPeriodClosed = Boolean(data?.closed);
|
||||
@@ -1045,6 +1054,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
const periodTeamspaceMinutes = (data?.dayBillings ?? []).reduce((sum, billing) => sum + Number(billing.billed_minutes ?? 0), 0);
|
||||
const ticketDataDirty = Boolean(
|
||||
ticket &&
|
||||
canEditTicketData &&
|
||||
(editTicketNumber !== ticket.ticket_number ||
|
||||
editTicketTitle !== (ticket.title ?? "") ||
|
||||
editOrganizationId !== (ticket.organization_id ?? "") ||
|
||||
@@ -1284,8 +1294,10 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
}
|
||||
|
||||
if (!canEditTicketData) {
|
||||
toast.error("Projekt ist geteilt", {
|
||||
description: "Nur der Zammad-Owner kann Ticket- und Projektdaten ändern."
|
||||
toast.error(ticketManagedByZammad ? "Ticketdaten kommen aus Zammad" : "Ticket gehört einem anderen Owner", {
|
||||
description: ticketManagedByZammad
|
||||
? "Nummer, Art, Titel und Organisation werden durch den Zammad-Sync verwaltet."
|
||||
: "Nur der Owner kann Ticketdaten ändern."
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -1334,7 +1346,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.can_edit === false) {
|
||||
if (!canEditProject) {
|
||||
toast.error("Projekt ist geteilt", {
|
||||
description: "Nur der Zammad-Owner kann die Projektdaten bearbeiten."
|
||||
});
|
||||
@@ -1352,7 +1364,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.can_edit === false) {
|
||||
if (!canEditProject) {
|
||||
toast.error("Projekt ist geteilt", {
|
||||
description: "Nur der Zammad-Owner kann die Projektdaten bearbeiten."
|
||||
});
|
||||
@@ -1407,7 +1419,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.can_edit === false) {
|
||||
if (!canEditProject) {
|
||||
toast.error("Projekt ist geteilt", {
|
||||
description: "Nur der Zammad-Owner kann den Projektstatus ändern."
|
||||
});
|
||||
@@ -1433,7 +1445,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.can_edit === false) {
|
||||
if (!canEditProject) {
|
||||
toast.error("Projekt ist geteilt", {
|
||||
description: "Nur der Zammad-Owner kann die Prüfung bestätigen."
|
||||
});
|
||||
@@ -1811,6 +1823,26 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{ticket?.is_foreign_zammad_owner ? (
|
||||
<Alert className="py-3 text-sm">
|
||||
<AlertCircle className="size-4" />
|
||||
<AlertTitle>Anderer Zammad-Owner</AlertTitle>
|
||||
<AlertDescription>
|
||||
Dieses Ticket gehört in Zammad {zammadOwnerLabel ? `${zammadOwnerLabel}` : "einem anderen Owner"}; du bist {currentZammadUserLabel}. Du kannst deine Sessions sehen, Projekt- und Ticketdaten sind für dich geschützt.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{ticketManagedByZammad ? (
|
||||
<Alert className="py-3 text-sm">
|
||||
<AlertCircle className="size-4" />
|
||||
<AlertTitle>Ticketdaten werden von Zammad verwaltet</AlertTitle>
|
||||
<AlertDescription>
|
||||
Nummer, Art, Titel und Organisation wurden durch den Zammad-Sync übernommen und können in TicketTracker nicht manuell geändert werden.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<div className="grid gap-4">
|
||||
<Card>
|
||||
<CardHeader className="p-4 pb-2">
|
||||
@@ -1820,7 +1852,9 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
<CardDescription>
|
||||
{canEditTicketData
|
||||
? "Änderungen an Organisation und Art werden auf alle vorhandenen Sessions dieses Tickets übernommen."
|
||||
: "Dieses Projekt ist geteilt. Nur der Zammad-Owner kann Ticket- und Projektdaten ändern."}
|
||||
: ticketManagedByZammad
|
||||
? "Nummer, Art, Titel und Organisation kommen aus Zammad und sind lokal gesperrt."
|
||||
: "Nur der Owner kann Ticketdaten ändern."}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<HelpLink anchor="ticketdaten" label="Hilfe zu Ticketdaten" />
|
||||
@@ -1891,8 +1925,8 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
{(project.unassigned_teamspace_session_count ?? 0) > 0 ? <Badge variant="warning">{project.unassigned_teamspace_session_count} ohne TS</Badge> : null}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{!canEditProject && project.owner_display_name
|
||||
? `Eigentümer: ${project.owner_display_name}`
|
||||
{!canEditProject && projectOwnerLabel
|
||||
? `Eigentümer: ${projectOwnerLabel}`
|
||||
: showBudgetedProjectEntries
|
||||
? `${budgetedProjectEntries.length} budgetierte Teamspace-Einträge`
|
||||
: budgetedProjectEntries[0] ? teamspaceEntryDisplayLabel(budgetedProjectEntries[0]) : project.teamspace_project || "Kein Teamspace-Projekt hinterlegt"}
|
||||
@@ -1944,7 +1978,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onS
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="shrink-0"
|
||||
disabled={savingProjectStatus || project.can_edit === false}
|
||||
disabled={savingProjectStatus || !canEditProject}
|
||||
onClick={() => void acknowledgeProjectReviewCheck()}
|
||||
>
|
||||
<CheckCircle2 className="mr-2 h-4 w-4" />
|
||||
|
||||
@@ -659,6 +659,25 @@ function addCachedChange(changes: ZammadSyncChange[], field: string, from: unkno
|
||||
}
|
||||
}
|
||||
|
||||
const preservedExistingPreviewChangeFields = new Set([
|
||||
"Fremdes Ticket",
|
||||
"Fremder Zammad-Owner",
|
||||
"Owner",
|
||||
"Ticket-Zuordnung",
|
||||
"Hinweis",
|
||||
"Ticketnummer",
|
||||
"TicketTracker-Zusammenführung"
|
||||
]);
|
||||
|
||||
function preserveExistingPreviewChanges(item: ZammadSyncItem, changes: ZammadSyncChange[], fields: ZammadSyncExistingFields) {
|
||||
const existingFields = new Set(changes.map((change) => change.field));
|
||||
|
||||
return [
|
||||
...item.changes.filter((change) => preservedExistingPreviewChangeFields.has(change.field) && !existingFields.has(change.field) && changeAllowedByFields(item, change, fields)),
|
||||
...changes
|
||||
];
|
||||
}
|
||||
|
||||
function changesFromExistingPreviewCache(item: ZammadSyncItem, fields: ZammadSyncExistingFields) {
|
||||
if (!item.zammad) {
|
||||
return item.changes.filter((change) => changeAllowedByFields(item, change, fields));
|
||||
@@ -756,7 +775,7 @@ function changesFromExistingPreviewCache(item: ZammadSyncItem, fields: ZammadSyn
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
return preserveExistingPreviewChanges(item, changes, fields);
|
||||
}
|
||||
|
||||
function rebuildZammadSyncResultFromItems(mode: ZammadSyncResult["mode"], items: ZammadSyncItem[], checked = items.length): ZammadSyncResult {
|
||||
|
||||
Reference in new issue
Block a user