Implement ticket statuses
This commit is contained in:
@@ -22,6 +22,8 @@ import { index, show } from '@/routes/projects/index';
|
||||
import { show as showTicket } from '@/routes/projects/tickets';
|
||||
import { metadata as metadataSettings } from '@/routes/projects/settings';
|
||||
import type { BreadcrumbItem, ProjectBacklogPageProps } from '@/types';
|
||||
import type { TicketModel } from '@/types/models/ticket-model';
|
||||
import type { TicketStatusModel } from '@/types/models/ticket-status-model';
|
||||
|
||||
const props = defineProps<ProjectBacklogPageProps>();
|
||||
|
||||
@@ -41,7 +43,35 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
];
|
||||
|
||||
const hasTicketMetadata =
|
||||
props.ticketTypes.length > 0 && props.ticketPriorities.length > 0;
|
||||
props.ticketTypes.length > 0 &&
|
||||
props.ticketPriorities.length > 0 &&
|
||||
props.backlogTicketStatuses.length > 0;
|
||||
|
||||
const ticketStatusLabel = (ticketStatus: TicketStatusModel | null): string => {
|
||||
if (ticketStatus === null) {
|
||||
return 'Unknown status';
|
||||
}
|
||||
|
||||
if (
|
||||
ticketStatus.board_column === null ||
|
||||
ticketStatus.board_column === undefined
|
||||
) {
|
||||
return ticketStatus.name;
|
||||
}
|
||||
|
||||
if (ticketStatus.board_column.name === ticketStatus.name) {
|
||||
return ticketStatus.name;
|
||||
}
|
||||
|
||||
return `${ticketStatus.board_column.name} / ${ticketStatus.name}`;
|
||||
};
|
||||
|
||||
const allowedTargetStatuses = (ticket: TicketModel): TicketStatusModel[] => {
|
||||
return (
|
||||
ticket.ticket_status?.allowed_target_statuses ??
|
||||
props.ticketStatusOptions
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -85,7 +115,7 @@ const hasTicketMetadata =
|
||||
<CardTitle>Create ticket</CardTitle>
|
||||
<CardDescription>
|
||||
New tickets receive the next immutable project key and
|
||||
automatically enter backlog.
|
||||
enter the selected backlog status.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
@@ -182,6 +212,28 @@ const hasTicketMetadata =
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="ticket_status_id">
|
||||
Status
|
||||
</Label>
|
||||
<select
|
||||
id="ticket_status_id"
|
||||
name="ticket_status_id"
|
||||
class="border-input bg-background shadow-xs focus-visible:border-ring focus-visible:ring-ring/50 rounded-md border px-3 py-2 text-sm outline-none transition-[color,box-shadow] focus-visible:ring-[3px]"
|
||||
>
|
||||
<option
|
||||
v-for="ticketStatus in backlogTicketStatuses"
|
||||
:key="ticketStatus.id"
|
||||
:value="ticketStatus.id"
|
||||
>
|
||||
{{ ticketStatus.name }}
|
||||
</option>
|
||||
</select>
|
||||
<InputError
|
||||
:message="errors.ticket_status_id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="assignee_user_id">
|
||||
Assignee
|
||||
@@ -237,7 +289,7 @@ const hasTicketMetadata =
|
||||
<CardContent class="flex flex-col gap-4 p-6 text-sm">
|
||||
<p class="text-muted-foreground">
|
||||
Ticket creation needs at least one type and one
|
||||
priority.
|
||||
priority, plus one backlog status.
|
||||
</p>
|
||||
<div>
|
||||
<Button as-child variant="outline">
|
||||
@@ -253,8 +305,8 @@ const hasTicketMetadata =
|
||||
<CardHeader class="border-b">
|
||||
<CardTitle>Backlog tickets</CardTitle>
|
||||
<CardDescription>
|
||||
Tickets remain here until they are moved into a board
|
||||
column.
|
||||
Tickets remain here while their status belongs to
|
||||
backlog.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
@@ -277,6 +329,9 @@ const hasTicketMetadata =
|
||||
<th class="px-4 py-3 font-medium">Key</th>
|
||||
<th class="px-4 py-3 font-medium">Title</th>
|
||||
<th class="px-4 py-3 font-medium">Type</th>
|
||||
<th class="px-4 py-3 font-medium">
|
||||
Status
|
||||
</th>
|
||||
<th class="px-4 py-3 font-medium">
|
||||
Priority
|
||||
</th>
|
||||
@@ -284,7 +339,7 @@ const hasTicketMetadata =
|
||||
v-if="canEditTickets"
|
||||
class="px-4 py-3 font-medium"
|
||||
>
|
||||
Move to board
|
||||
Move to status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -330,12 +385,22 @@ const hasTicketMetadata =
|
||||
<td class="text-muted-foreground px-4 py-4">
|
||||
{{ ticket.ticket_type.name }}
|
||||
</td>
|
||||
<td class="text-muted-foreground px-4 py-4">
|
||||
{{
|
||||
ticketStatusLabel(
|
||||
ticket.ticket_status,
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="text-muted-foreground px-4 py-4">
|
||||
{{ ticket.ticket_priority.name }}
|
||||
</td>
|
||||
<td v-if="canEditTickets" class="px-4 py-4">
|
||||
<Form
|
||||
v-if="boardColumns.length > 0"
|
||||
v-if="
|
||||
allowedTargetStatuses(ticket)
|
||||
.length > 0
|
||||
"
|
||||
v-bind="
|
||||
ProjectBoardController.move.form(
|
||||
{
|
||||
@@ -353,15 +418,25 @@ const hasTicketMetadata =
|
||||
value="backlog"
|
||||
/>
|
||||
<select
|
||||
name="board_column_id"
|
||||
name="ticket_status_id"
|
||||
:value="
|
||||
ticket.ticket_status_id ??
|
||||
''
|
||||
"
|
||||
class="border-input bg-background shadow-xs focus-visible:border-ring focus-visible:ring-ring/50 min-w-44 rounded-md border px-3 py-2 text-sm outline-none transition-[color,box-shadow] focus-visible:ring-[3px]"
|
||||
>
|
||||
<option
|
||||
v-for="boardColumn in boardColumns"
|
||||
:key="boardColumn.id"
|
||||
:value="boardColumn.id"
|
||||
v-for="ticketStatus in allowedTargetStatuses(
|
||||
ticket,
|
||||
)"
|
||||
:key="ticketStatus.id"
|
||||
:value="ticketStatus.id"
|
||||
>
|
||||
{{ boardColumn.name }}
|
||||
{{
|
||||
ticketStatusLabel(
|
||||
ticketStatus,
|
||||
)
|
||||
}}
|
||||
</option>
|
||||
</select>
|
||||
<Button
|
||||
@@ -377,7 +452,7 @@ const hasTicketMetadata =
|
||||
v-else
|
||||
class="text-muted-foreground text-sm"
|
||||
>
|
||||
Add board columns first.
|
||||
No allowed status moves.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user