Update
This commit is contained in:
1 parent
8a4cf3fc9b
commit
d33b2c487d
1 file changed
+25
-5
@@ -305,6 +305,18 @@ function projectConsumptionPercent(consumptionMinutes: number, budgetMinutes?: n
|
|||||||
return Math.round((consumptionMinutes / budgetMinutes) * 100);
|
return Math.round((consumptionMinutes / budgetMinutes) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function projectRemainingLabel(remainingMinutes: number | null) {
|
||||||
|
if (remainingMinutes === null) {
|
||||||
|
return "Rest offen";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMinutes < 0) {
|
||||||
|
return `Überzogen ${formatMinutes(Math.abs(remainingMinutes))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Rest ${formatMinutes(remainingMinutes)}`;
|
||||||
|
}
|
||||||
|
|
||||||
function isPartialHoursInput(value: string) {
|
function isPartialHoursInput(value: string) {
|
||||||
return /^\d*(?:[,.]\d*)?$/.test(value.trim());
|
return /^\d*(?:[,.]\d*)?$/.test(value.trim());
|
||||||
}
|
}
|
||||||
@@ -562,6 +574,7 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onO
|
|||||||
const projectConsumptionMinutes = project?.consumption_minutes ?? 0;
|
const projectConsumptionMinutes = project?.consumption_minutes ?? 0;
|
||||||
const projectBudgetMinutes = project?.hour_budget_minutes ?? null;
|
const projectBudgetMinutes = project?.hour_budget_minutes ?? null;
|
||||||
const projectConsumptionUsagePercent = project ? projectConsumptionPercent(projectConsumptionMinutes, projectBudgetMinutes) : null;
|
const projectConsumptionUsagePercent = project ? projectConsumptionPercent(projectConsumptionMinutes, projectBudgetMinutes) : null;
|
||||||
|
const projectRemainingMinutes = projectBudgetMinutes && projectBudgetMinutes > 0 ? projectBudgetMinutes - projectConsumptionMinutes : null;
|
||||||
const canEditProject = project?.can_edit !== false;
|
const canEditProject = project?.can_edit !== false;
|
||||||
const canEditTicketData = !project || canEditProject;
|
const canEditTicketData = !project || canEditProject;
|
||||||
const isProject = Boolean(project);
|
const isProject = Boolean(project);
|
||||||
@@ -1396,16 +1409,24 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onO
|
|||||||
<Badge variant="secondary">Zeitraum {project.starts_on || "offen"} - {project.ends_on || "offen"}</Badge>
|
<Badge variant="secondary">Zeitraum {project.starts_on || "offen"} - {project.ends_on || "offen"}</Badge>
|
||||||
<Badge variant="secondary">Kontingent {projectBudgetMinutes ? formatMinutes(projectBudgetMinutes) : "offen"}</Badge>
|
<Badge variant="secondary">Kontingent {projectBudgetMinutes ? formatMinutes(projectBudgetMinutes) : "offen"}</Badge>
|
||||||
<Badge variant="secondary">Verbrauch {formatMinutes(projectConsumptionMinutes)}</Badge>
|
<Badge variant="secondary">Verbrauch {formatMinutes(projectConsumptionMinutes)}</Badge>
|
||||||
|
<Badge variant={projectRemainingMinutes !== null && projectRemainingMinutes < 0 ? "destructive" : "secondary"}>{projectRemainingLabel(projectRemainingMinutes)}</Badge>
|
||||||
{showBudgetedProjectEntries ? <Badge variant="outline">{budgetedProjectEntries.length} Budgets</Badge> : null}
|
{showBudgetedProjectEntries ? <Badge variant="outline">{budgetedProjectEntries.length} Budgets</Badge> : null}
|
||||||
</div>
|
</div>
|
||||||
{showBudgetedProjectEntries ? (
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-1 flex items-center justify-between gap-2 text-xs text-muted-foreground">
|
<div className="mb-1 flex items-center justify-between gap-2 text-xs text-muted-foreground">
|
||||||
<span>Gesamtverbrauch</span>
|
<span>Budgetverbrauch</span>
|
||||||
<span>{projectConsumptionUsagePercent !== null ? `${projectConsumptionUsagePercent}%` : "ohne Gesamtbudget"}</span>
|
<span className="shrink-0 text-right">
|
||||||
|
{projectBudgetMinutes && projectBudgetMinutes > 0
|
||||||
|
? `${formatMinutes(projectConsumptionMinutes)} / ${formatMinutes(projectBudgetMinutes)} · ${projectRemainingLabel(projectRemainingMinutes)}`
|
||||||
|
: `${formatMinutes(projectConsumptionMinutes)} / offen`}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-1.5 overflow-hidden rounded-full bg-muted">
|
<div
|
||||||
|
className="h-2 overflow-hidden rounded-full bg-muted"
|
||||||
|
title={projectConsumptionUsagePercent !== null ? `${projectConsumptionUsagePercent}% Verbrauch` : "ohne Gesamtbudget"}
|
||||||
|
aria-label={projectConsumptionUsagePercent !== null ? `${projectConsumptionUsagePercent}% Verbrauch` : "ohne Gesamtbudget"}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={cn("h-full rounded-full", projectConsumptionUsagePercent === null ? "bg-muted-foreground/20" : projectConsumptionBarClass(projectConsumptionUsagePercent))}
|
className={cn("h-full rounded-full", projectConsumptionUsagePercent === null ? "bg-muted-foreground/20" : projectConsumptionBarClass(projectConsumptionUsagePercent))}
|
||||||
style={{ width: `${projectConsumptionUsagePercent === null ? 0 : Math.min(100, projectConsumptionUsagePercent)}%` }}
|
style={{ width: `${projectConsumptionUsagePercent === null ? 0 : Math.min(100, projectConsumptionUsagePercent)}%` }}
|
||||||
@@ -1441,7 +1462,6 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onO
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="mb-1 flex items-center gap-2 font-medium">
|
<div className="mb-1 flex items-center gap-2 font-medium">
|
||||||
|
|||||||
Reference in new issue
Block a user