codex ticket rework
This commit is contained in:
@@ -182,6 +182,29 @@ const hasTicketMetadata =
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="assignee_user_id">
|
||||
Assignee
|
||||
</Label>
|
||||
<select
|
||||
id="assignee_user_id"
|
||||
name="assignee_user_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 value="">Unassigned</option>
|
||||
<option
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:value="user.id"
|
||||
>
|
||||
{{ user.name }}
|
||||
</option>
|
||||
</select>
|
||||
<InputError
|
||||
:message="errors.assignee_user_id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid gap-2"
|
||||
v-if="project.show_story_points"
|
||||
|
||||
@@ -66,6 +66,12 @@ const linkedRepositoryConnections = computed<RepositoryConnectionModel[]>(() =>
|
||||
const canCreateBranches = computed(
|
||||
() => props.canEditTicket && linkedRepositoryConnections.value.length > 0,
|
||||
);
|
||||
const canEditAuthor = computed(
|
||||
() => props.canEditTicket && props.canManageProject,
|
||||
);
|
||||
const canViewAuthor = computed(
|
||||
() => props.project.show_author || props.canManageProject,
|
||||
);
|
||||
|
||||
const defaultBranchRepositoryConnectionId = computed(() =>
|
||||
String(
|
||||
@@ -97,6 +103,8 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
|
||||
const form = useForm({
|
||||
acceptance_criteria: props.ticket.acceptance_criteria ?? '',
|
||||
assignee_user_id: props.ticket.assignee_user_id ?? '',
|
||||
author_user_id: props.ticket.author_user_id ?? '',
|
||||
blocked_comment: props.ticket.blocked_comment ?? '',
|
||||
description: props.ticket.description ?? '',
|
||||
field_values: props.ticketFieldDefinitions.map((ticketFieldDefinition) => {
|
||||
@@ -461,6 +469,58 @@ const formatDateTime = (value: string): string => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2 md:grid-cols-2 md:gap-6">
|
||||
<div class="grid gap-2">
|
||||
<Label for="assignee_user_id">
|
||||
Assignee
|
||||
</Label>
|
||||
<select
|
||||
id="assignee_user_id"
|
||||
v-model="form.assignee_user_id"
|
||||
:disabled="
|
||||
!canEditTicket || form.processing
|
||||
"
|
||||
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] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
<option value="">Unassigned</option>
|
||||
<option
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:value="user.id"
|
||||
>
|
||||
{{ user.name }}
|
||||
</option>
|
||||
</select>
|
||||
<InputError
|
||||
:message="form.errors.assignee_user_id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="canViewAuthor" class="grid gap-2">
|
||||
<Label for="author_user_id">Author</Label>
|
||||
<select
|
||||
id="author_user_id"
|
||||
v-model="form.author_user_id"
|
||||
:disabled="
|
||||
!canEditAuthor || form.processing
|
||||
"
|
||||
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] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
<option value="">No author</option>
|
||||
<option
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:value="user.id"
|
||||
>
|
||||
{{ user.name }}
|
||||
</option>
|
||||
</select>
|
||||
<InputError
|
||||
:message="form.errors.author_user_id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid gap-2"
|
||||
v-if="project.show_story_points"
|
||||
|
||||
@@ -44,6 +44,9 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
href: metadataSettings(props.project.id),
|
||||
},
|
||||
];
|
||||
|
||||
const replacementTicketTypes = (ticketTypeId: number) =>
|
||||
props.ticketTypes.filter((ticketType) => ticketType.id !== ticketTypeId);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -133,6 +136,18 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
|
||||
<InputError :message="errors.show_story_points" />
|
||||
|
||||
<Label for="enable-author">
|
||||
<Checkbox
|
||||
id="enable-author"
|
||||
name="show_author"
|
||||
:default-value="project.show_author"
|
||||
value="1"
|
||||
/>
|
||||
Show author on ticket details
|
||||
</Label>
|
||||
|
||||
<InputError :message="errors.show_author" />
|
||||
|
||||
<Button type="submit" :disabled="processing">
|
||||
Save
|
||||
</Button>
|
||||
@@ -171,7 +186,7 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
name="is_parent"
|
||||
value="1"
|
||||
/>
|
||||
Ticket type is can be a parent
|
||||
Ticket type can act as a parent
|
||||
</Label>
|
||||
<InputError :message="errors.is_parent" />
|
||||
</div>
|
||||
@@ -196,6 +211,20 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
v-slot="{ errors, processing }"
|
||||
>
|
||||
<div class="grid gap-2">
|
||||
<div
|
||||
v-if="ticketType.is_parent"
|
||||
class="flex flex-wrap items-center gap-2"
|
||||
>
|
||||
<Badge variant="secondary">
|
||||
Parent type
|
||||
</Badge>
|
||||
<span
|
||||
class="text-muted-foreground text-xs"
|
||||
>
|
||||
This flag is fixed after creation.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Label :for="`type-name-${ticketType.id}`">
|
||||
Name
|
||||
</Label>
|
||||
@@ -206,21 +235,6 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
required
|
||||
/>
|
||||
<InputError :message="errors.name" />
|
||||
|
||||
<Label
|
||||
:for="`type-is-parent-${ticketType.id}}`"
|
||||
>
|
||||
<Checkbox
|
||||
:id="`type-is-parent-${ticketType.id}}`"
|
||||
name="is_parent"
|
||||
:default-value="
|
||||
ticketType.is_parent
|
||||
"
|
||||
value="1"
|
||||
/>
|
||||
Ticket type is can be a parent
|
||||
</Label>
|
||||
<InputError :message="errors.is_parent" />
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
@@ -260,16 +274,71 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
[project.id, ticketType.id],
|
||||
)
|
||||
"
|
||||
class="mt-3 flex justify-end"
|
||||
v-slot="{ processing: deleting }"
|
||||
class="mt-3 grid gap-3"
|
||||
v-slot="{ errors, processing: deleting }"
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="destructive"
|
||||
:disabled="deleting"
|
||||
<div
|
||||
v-if="
|
||||
(ticketType.tickets_count ?? 0) > 0 &&
|
||||
ticketTypes.length > 1
|
||||
"
|
||||
class="grid gap-2"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Label
|
||||
:for="`replacement-type-${ticketType.id}`"
|
||||
>
|
||||
Replacement type for
|
||||
{{ ticketType.tickets_count }} assigned
|
||||
ticket{{
|
||||
ticketType.tickets_count === 1
|
||||
? ''
|
||||
: 's'
|
||||
}}
|
||||
</Label>
|
||||
<select
|
||||
:id="`replacement-type-${ticketType.id}`"
|
||||
name="replacement_ticket_type_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]"
|
||||
required
|
||||
>
|
||||
<option value="">Choose type</option>
|
||||
<option
|
||||
v-for="replacementTicketType in replacementTicketTypes(
|
||||
ticketType.id,
|
||||
)"
|
||||
:key="replacementTicketType.id"
|
||||
:value="replacementTicketType.id"
|
||||
>
|
||||
{{ replacementTicketType.name }}
|
||||
</option>
|
||||
</select>
|
||||
<InputError
|
||||
:message="
|
||||
errors.replacement_ticket_type_id
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between gap-3"
|
||||
>
|
||||
<p
|
||||
v-if="
|
||||
(ticketType.tickets_count ?? 0) > 0
|
||||
"
|
||||
class="text-muted-foreground text-sm"
|
||||
>
|
||||
Assigned tickets will be moved before
|
||||
this type is deleted.
|
||||
</p>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="destructive"
|
||||
:disabled="deleting"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -2,6 +2,7 @@ export type ProjectModel = {
|
||||
id: number;
|
||||
name: string;
|
||||
key: string;
|
||||
show_author: boolean;
|
||||
show_story_points: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
|
||||
@@ -2,10 +2,15 @@ import type { TicketFieldValueModel } from '@/types/models/ticket-field-value-mo
|
||||
import type { TicketPriorityModel } from '@/types/models/ticket-priority-model';
|
||||
import type { TicketTypeModel } from '@/types/models/ticket-type-model';
|
||||
import type { TicketWorklogEntryModel } from '@/types/models/ticket-worklog-entry-model';
|
||||
import type { UserModel } from '@/types/models/user-model';
|
||||
|
||||
export type TicketModel = {
|
||||
id: number;
|
||||
acceptance_criteria: string | null;
|
||||
assignee: UserModel | null;
|
||||
assignee_user_id: number | null;
|
||||
author: UserModel | null;
|
||||
author_user_id: number | null;
|
||||
board_column_id: number | null;
|
||||
blocked_comment: string | null;
|
||||
description: string | null;
|
||||
|
||||
@@ -4,6 +4,7 @@ export type TicketTypeModel = {
|
||||
name: string;
|
||||
position: number;
|
||||
project_id: number;
|
||||
tickets_count?: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ export type TicketFieldTypeOption = {
|
||||
export type ProjectTicketMetadataSettingsPageProps = {
|
||||
fieldTypeOptions: TicketFieldTypeOption[];
|
||||
project: ProjectModel;
|
||||
error?: string | null;
|
||||
error?: boolean;
|
||||
status?: string | null;
|
||||
ticketFieldDefinitions: TicketFieldDefinitionModel[];
|
||||
ticketLinkTypes: TicketLinkTypeModel[];
|
||||
@@ -107,10 +107,12 @@ export type ProjectBacklogPageProps = {
|
||||
ticketPriorities: TicketPriorityModel[];
|
||||
tickets: TicketModel[];
|
||||
ticketTypes: TicketTypeModel[];
|
||||
users: UserModel[];
|
||||
};
|
||||
|
||||
export type ProjectTicketPageProps = {
|
||||
canEditTicket: boolean;
|
||||
canManageProject: boolean;
|
||||
childTicketTree: TicketTreeNodeModel[];
|
||||
linkedTickets: TicketLinkModel[];
|
||||
parentTicketOptions: TicketReferenceModel[];
|
||||
|
||||
Reference in New Issue
Block a user