Update
This commit is contained in:
1 parent
c66b5a9aa9
commit
3ebcea0b3d
1 file changed
+99
-97
@@ -1242,108 +1242,110 @@ export function TicketDetailPage({ periodType, period, ticketId, onNavigate, onO
|
|||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{isGlobalTicket ? (
|
<div className={cn("grid gap-4", isGlobalTicket && "xxl:grid-cols-2")}>
|
||||||
|
{isGlobalTicket ? (
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="p-4">
|
||||||
|
<CardTitle>Filter</CardTitle>
|
||||||
|
<CardDescription>Grenzt die Sessionliste dieses Tickets ein. Die Ticketdaten selbst bleiben global.</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="grid gap-3 px-4 pb-4 md:grid-cols-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium" htmlFor="ticket-month-filter">Monat</label>
|
||||||
|
<select
|
||||||
|
id="ticket-month-filter"
|
||||||
|
className="h-8 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={globalMonthFilter}
|
||||||
|
onChange={(event) => setGlobalMonthFilter(event.currentTarget.value)}
|
||||||
|
>
|
||||||
|
<option value="all">Alle Monate</option>
|
||||||
|
{globalMonthFilter !== "all" && !selectedMonthHasSessions ? <option value={globalMonthFilter}>{globalMonthFilter} (0)</option> : null}
|
||||||
|
{availableMonths.map((month) => (
|
||||||
|
<option key={month.month} value={month.month}>
|
||||||
|
{month.month} ({month.session_count})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium" htmlFor="ticket-status-filter">Bewertung</label>
|
||||||
|
<select
|
||||||
|
id="ticket-status-filter"
|
||||||
|
className="h-8 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={globalStatusFilter}
|
||||||
|
onChange={(event) => setGlobalStatusFilter(event.currentTarget.value as typeof globalStatusFilter)}
|
||||||
|
>
|
||||||
|
<option value="all">Alle Bewertungen</option>
|
||||||
|
<option value="open">Offen</option>
|
||||||
|
<option value="billed">Abgerechnet</option>
|
||||||
|
<option value="non_billable">Nicht abrechenbar</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="p-4">
|
<CardHeader className="p-4">
|
||||||
<CardTitle>Filter</CardTitle>
|
<div className="flex items-start justify-between gap-3">
|
||||||
<CardDescription>Grenzt die Sessionliste dieses Tickets ein. Die Ticketdaten selbst bleiben global.</CardDescription>
|
<div>
|
||||||
|
<CardTitle>Ticketdaten</CardTitle>
|
||||||
|
<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."}
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<HelpLink anchor="ticketdaten" label="Hilfe zu Ticketdaten" />
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-3 px-4 pb-4 md:grid-cols-3">
|
<CardContent className="px-4 pb-4">
|
||||||
<div className="space-y-2">
|
<form className="grid gap-3 md:grid-cols-2" onSubmit={saveTicketData}>
|
||||||
<label className="text-sm font-medium" htmlFor="ticket-month-filter">Monat</label>
|
<div className="space-y-2">
|
||||||
<select
|
<label className="text-sm font-medium" htmlFor="edit-ticket-number">Ticketnummer</label>
|
||||||
id="ticket-month-filter"
|
<Input id="edit-ticket-number" value={editTicketNumber} disabled={!canEditTicketData} onChange={(event) => setEditTicketNumber(event.currentTarget.value)} required />
|
||||||
className="h-8 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"
|
</div>
|
||||||
value={globalMonthFilter}
|
<div className="space-y-2">
|
||||||
onChange={(event) => setGlobalMonthFilter(event.currentTarget.value)}
|
<label className="text-sm font-medium" htmlFor="edit-ticket-work-type">Art</label>
|
||||||
>
|
<select
|
||||||
<option value="all">Alle Monate</option>
|
id="edit-ticket-work-type"
|
||||||
{globalMonthFilter !== "all" && !selectedMonthHasSessions ? <option value={globalMonthFilter}>{globalMonthFilter} (0)</option> : null}
|
className="h-8 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"
|
||||||
{availableMonths.map((month) => (
|
value={editWorkType}
|
||||||
<option key={month.month} value={month.month}>
|
disabled={!canEditTicketData}
|
||||||
{month.month} ({month.session_count})
|
onChange={(event) => setEditWorkType(event.currentTarget.value as WorkType)}
|
||||||
</option>
|
>
|
||||||
))}
|
<option value="support">Support</option>
|
||||||
</select>
|
<option value="consulting">Consulting</option>
|
||||||
</div>
|
</select>
|
||||||
<div className="space-y-2">
|
</div>
|
||||||
<label className="text-sm font-medium" htmlFor="ticket-status-filter">Bewertung</label>
|
<div className="space-y-2">
|
||||||
<select
|
<label className="text-sm font-medium" htmlFor="edit-ticket-title">Titel</label>
|
||||||
id="ticket-status-filter"
|
<Input id="edit-ticket-title" value={editTicketTitle} disabled={!canEditTicketData} onChange={(event) => setEditTicketTitle(event.currentTarget.value)} placeholder="optional" />
|
||||||
className="h-8 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"
|
</div>
|
||||||
value={globalStatusFilter}
|
<div className="space-y-2">
|
||||||
onChange={(event) => setGlobalStatusFilter(event.currentTarget.value as typeof globalStatusFilter)}
|
<label className="text-sm font-medium" htmlFor="edit-ticket-organization">Organisation</label>
|
||||||
>
|
{ticket?.customer_name && !ticket.organization_id ? (
|
||||||
<option value="all">Alle Bewertungen</option>
|
<p className="text-xs text-muted-foreground">Bisheriger Freitext: {ticket.customer_name}</p>
|
||||||
<option value="open">Offen</option>
|
) : null}
|
||||||
<option value="billed">Abgerechnet</option>
|
<OrganizationSelect
|
||||||
<option value="non_billable">Nicht abrechenbar</option>
|
value={editOrganizationId}
|
||||||
</select>
|
selectedName={editOrganizationName}
|
||||||
</div>
|
disabled={!canEditTicketData}
|
||||||
|
onChange={(organization) => {
|
||||||
|
setEditOrganizationId(organization.id);
|
||||||
|
setEditOrganizationName(organization.name);
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button type="submit" className="md:col-span-2 md:justify-self-end" disabled={!canEditTicketData || savingTicket}>
|
||||||
|
<Save className="size-4" />
|
||||||
|
{savingTicket ? "Speichert..." : "Speichern"}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : null}
|
</div>
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="p-4">
|
|
||||||
<div className="flex items-start justify-between gap-3">
|
|
||||||
<div>
|
|
||||||
<CardTitle>Ticketdaten</CardTitle>
|
|
||||||
<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."}
|
|
||||||
</CardDescription>
|
|
||||||
</div>
|
|
||||||
<HelpLink anchor="ticketdaten" label="Hilfe zu Ticketdaten" />
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="px-4 pb-4">
|
|
||||||
<form className="grid gap-3 lg:grid-cols-[180px_minmax(220px,1fr)_minmax(220px,1fr)_160px_auto] lg:items-end" onSubmit={saveTicketData}>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium" htmlFor="edit-ticket-number">Ticketnummer</label>
|
|
||||||
<Input id="edit-ticket-number" value={editTicketNumber} disabled={!canEditTicketData} onChange={(event) => setEditTicketNumber(event.currentTarget.value)} required />
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium" htmlFor="edit-ticket-title">Titel</label>
|
|
||||||
<Input id="edit-ticket-title" value={editTicketTitle} disabled={!canEditTicketData} onChange={(event) => setEditTicketTitle(event.currentTarget.value)} placeholder="optional" />
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium" htmlFor="edit-ticket-organization">Organisation</label>
|
|
||||||
{ticket?.customer_name && !ticket.organization_id ? (
|
|
||||||
<p className="text-xs text-muted-foreground">Bisheriger Freitext: {ticket.customer_name}</p>
|
|
||||||
) : null}
|
|
||||||
<OrganizationSelect
|
|
||||||
value={editOrganizationId}
|
|
||||||
selectedName={editOrganizationName}
|
|
||||||
disabled={!canEditTicketData}
|
|
||||||
onChange={(organization) => {
|
|
||||||
setEditOrganizationId(organization.id);
|
|
||||||
setEditOrganizationName(organization.name);
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium" htmlFor="edit-ticket-work-type">Art</label>
|
|
||||||
<select
|
|
||||||
id="edit-ticket-work-type"
|
|
||||||
className="h-8 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={editWorkType}
|
|
||||||
disabled={!canEditTicketData}
|
|
||||||
onChange={(event) => setEditWorkType(event.currentTarget.value as WorkType)}
|
|
||||||
>
|
|
||||||
<option value="support">Support</option>
|
|
||||||
<option value="consulting">Consulting</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<Button type="submit" disabled={!canEditTicketData || savingTicket}>
|
|
||||||
<Save className="size-4" />
|
|
||||||
{savingTicket ? "Speichert..." : "Speichern"}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div className={cn("grid gap-4", project && "xl:grid-cols-2")}>
|
<div className={cn("grid gap-4", project && "xl:grid-cols-2")}>
|
||||||
{project ? (
|
{project ? (
|
||||||
|
|||||||
Reference in new issue
Block a user