Simplify acceptance criteria checklist model
linter / quality (pull_request) Successful in 1m16s
tests / ci (8.4) (pull_request) Successful in 1m30s

This commit is contained in:
2026-05-19 22:44:28 +00:00
parent 7805bd057e
commit 689db9e41b
16 changed files with 140 additions and 363 deletions
+8 -25
View File
@@ -7,24 +7,15 @@ const MAX_INDENT_LEVEL = 8;
export type AcceptanceCriteriaEditorState = {
checklist: string;
summary: string;
};
export const emptyAcceptanceCriteria = (): AcceptanceCriteriaModel => ({
summary: null,
items: [],
});
export const emptyAcceptanceCriteria = (): AcceptanceCriteriaModel => [];
export const normalizeAcceptanceCriteria = (
value: AcceptanceCriteriaModel | null | undefined,
): AcceptanceCriteriaModel => {
if (value === null || value === undefined) {
return emptyAcceptanceCriteria();
}
const summary = value.summary?.trim() ?? null;
const items = Array.isArray(value.items)
? value.items
const items = Array.isArray(value)
? value
.map((item) => ({
checked: item.checked === true,
indent: clampIndent(item.indent),
@@ -33,10 +24,7 @@ export const normalizeAcceptanceCriteria = (
.filter((item) => item.text !== '')
: [];
return {
summary: summary === '' ? null : summary,
items,
};
return items;
};
export const parseChecklistLines = (
@@ -69,15 +57,9 @@ export const formatChecklistLines = (
};
export const buildAcceptanceCriteria = (
summaryInput: string,
checklistInput: string,
): AcceptanceCriteriaModel => {
const summary = summaryInput.trim();
return normalizeAcceptanceCriteria({
summary: summary === '' ? null : summary,
items: parseChecklistLines(checklistInput),
});
return normalizeAcceptanceCriteria(parseChecklistLines(checklistInput));
};
export const acceptanceCriteriaToEditorState = (
@@ -86,8 +68,7 @@ export const acceptanceCriteriaToEditorState = (
const normalized = normalizeAcceptanceCriteria(value);
return {
checklist: formatChecklistLines(normalized.items),
summary: normalized.summary ?? '',
checklist: formatChecklistLines(normalized),
};
};
@@ -123,6 +104,8 @@ const parseChecklistLine = (
if (bulletMatch?.groups !== undefined) {
content = bulletMatch.groups.text.trim();
} else {
return null;
}
}
+3 -26
View File
@@ -111,14 +111,10 @@ const breadcrumbs: BreadcrumbItem[] = [
const acceptanceCriteriaEditorState = acceptanceCriteriaToEditorState(
props.ticket.acceptance_criteria,
);
const acceptanceCriteriaSummary = ref(acceptanceCriteriaEditorState.summary);
const acceptanceCriteriaChecklist = ref(acceptanceCriteriaEditorState.checklist);
const form = useForm({
acceptance_criteria: buildAcceptanceCriteria(
acceptanceCriteriaSummary.value,
acceptanceCriteriaChecklist.value,
),
acceptance_criteria: buildAcceptanceCriteria(acceptanceCriteriaChecklist.value),
assignee_user_id: props.ticket.assignee_user_id ?? '',
author_user_id: props.ticket.author_user_id ?? '',
blocked_comment: props.ticket.blocked_comment ?? '',
@@ -151,10 +147,8 @@ const acceptanceCriteriaPreviewItems = computed(() =>
const acceptanceCriteriaError = computed(() => {
return (
form.errors.acceptance_criteria ??
form.errors['acceptance_criteria.summary'] ??
form.errors['acceptance_criteria.items'] ??
Object.entries(form.errors).find(([key]) =>
key.startsWith('acceptance_criteria.items.'),
key.startsWith('acceptance_criteria.'),
)?.[1] ??
''
);
@@ -348,10 +342,7 @@ const submit = (): void => {
return;
}
form.acceptance_criteria = buildAcceptanceCriteria(
acceptanceCriteriaSummary.value,
acceptanceCriteriaChecklist.value,
);
form.acceptance_criteria = buildAcceptanceCriteria(acceptanceCriteriaChecklist.value);
form.submit(
ProjectTicketController.update({
@@ -625,20 +616,6 @@ const formatDateTime = (value: string): string => {
</div>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="acceptance_criteria_summary">
Acceptance criteria summary
</Label>
<Input
id="acceptance_criteria_summary"
v-model="acceptanceCriteriaSummary"
:disabled="
!canEditTicket || form.processing
"
placeholder="What should be true when this ticket is done?"
/>
</div>
<div class="grid gap-2">
<Label for="acceptance_criteria_checklist">
Acceptance criteria checklist
+3 -24
View File
@@ -61,14 +61,10 @@ const hasTicketMetadata =
props.ticketPriorities.length > 0 &&
props.ticketStatusOptions.length > 0;
const acceptanceCriteriaSummary = ref('');
const acceptanceCriteriaChecklist = ref('');
const form = useForm({
acceptance_criteria: buildAcceptanceCriteria(
acceptanceCriteriaSummary.value,
acceptanceCriteriaChecklist.value,
),
acceptance_criteria: buildAcceptanceCriteria(acceptanceCriteriaChecklist.value),
assignee_user_id: '',
description: '',
story_points: '',
@@ -87,20 +83,15 @@ const acceptanceCriteriaPreviewItems = computed(() =>
const acceptanceCriteriaError = computed(() => {
return (
form.errors.acceptance_criteria ??
form.errors['acceptance_criteria.summary'] ??
form.errors['acceptance_criteria.items'] ??
Object.entries(form.errors).find(([key]) =>
key.startsWith('acceptance_criteria.items.'),
key.startsWith('acceptance_criteria.'),
)?.[1] ??
''
);
});
const submit = (): void => {
form.acceptance_criteria = buildAcceptanceCriteria(
acceptanceCriteriaSummary.value,
acceptanceCriteriaChecklist.value,
);
form.acceptance_criteria = buildAcceptanceCriteria(acceptanceCriteriaChecklist.value);
form.submit(ProjectTicketController.store({ project: props.project.id }), {
preserveScroll: true,
@@ -197,18 +188,6 @@ const ticketStatusLabel = (ticketStatus: TicketStatusModel): string => {
</div>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="acceptance_criteria_summary">
Acceptance criteria summary
</Label>
<Input
id="acceptance_criteria_summary"
v-model="acceptanceCriteriaSummary"
:disabled="form.processing"
placeholder="What should be true when this ticket is done?"
/>
</div>
<div class="grid gap-2">
<Label for="acceptance_criteria_checklist">
Acceptance criteria checklist
@@ -4,7 +4,4 @@ export type AcceptanceCriteriaItemModel = {
text: string;
};
export type AcceptanceCriteriaModel = {
summary: string | null;
items: AcceptanceCriteriaItemModel[];
};
export type AcceptanceCriteriaModel = AcceptanceCriteriaItemModel[];
@@ -9,7 +9,6 @@ import type { UserModel } from '@/types/models/user-model';
export type TicketModel = {
id: number;
acceptance_criteria: AcceptanceCriteriaModel;
acceptance_criteria_text: string | null;
assignee: UserModel | null;
assignee_user_id: number | null;
author: UserModel | null;