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>
|
||||
) : 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>
|
||||
<CardHeader className="p-4">
|
||||
<CardTitle>Filter</CardTitle>
|
||||
<CardDescription>Grenzt die Sessionliste dieses Tickets ein. Die Ticketdaten selbst bleiben global.</CardDescription>
|
||||
<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="grid gap-3 px-4 pb-4 md:grid-cols-3">
|
||||
<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 className="px-4 pb-4">
|
||||
<form className="grid gap-3 md:grid-cols-2" 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-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>
|
||||
<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>
|
||||
<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>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
<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>
|
||||
|
||||
<div className={cn("grid gap-4", project && "xl:grid-cols-2")}>
|
||||
{project ? (
|
||||
|
||||
Reference in new issue
Block a user