Redesign ticket details with inline editing
linter / quality (pull_request) Successful in 1m23s
tests / ci (8.4) (pull_request) Successful in 1m49s

This commit is contained in:
2026-07-18 19:50:50 +02:00
parent 5f917e93eb
commit 8c70d094b8
11 changed files with 2712 additions and 1244 deletions
File diff suppressed because it is too large Load Diff
+12 -76
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { Head, Link, useForm } from '@inertiajs/vue3';
import { computed, ref } from 'vue';
import { computed } from 'vue';
import ProjectTicketController from '@/actions/App/Http/Controllers/ProjectTicketController';
import Heading from '@/components/Heading.vue';
import InputError from '@/components/InputError.vue';
import AcceptanceCriteriaEditor from '@/components/projects/AcceptanceCriteriaEditor.vue';
import ProjectTabs from '@/components/projects/ProjectTabs.vue';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@@ -17,10 +18,7 @@ import {
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import AppLayout from '@/layouts/AppLayout.vue';
import {
buildAcceptanceCriteria,
parseChecklistLines,
} from '@/lib/acceptance-criteria';
import { emptyAcceptanceCriteria } from '@/lib/acceptance-criteria';
import { index as backlogIndex } from '@/routes/projects/backlog';
import { index as boardIndex } from '@/routes/projects/board';
import { index } from '@/routes/projects/index';
@@ -63,12 +61,8 @@ const hasTicketMetadata =
props.ticketPriorities.length > 0 &&
props.ticketStatusOptions.length > 0;
const acceptanceCriteriaChecklist = ref('');
const form = useForm({
acceptance_criteria: buildAcceptanceCriteria(
acceptanceCriteriaChecklist.value,
),
acceptance_criteria: emptyAcceptanceCriteria(),
assignee_user_id: '',
description: '',
parent_ticket_id: '',
@@ -81,10 +75,6 @@ const form = useForm({
title: '',
});
const acceptanceCriteriaPreviewItems = computed(() =>
parseChecklistLines(acceptanceCriteriaChecklist.value),
);
const acceptanceCriteriaError = computed(() => {
return (
form.errors.acceptance_criteria ??
@@ -96,10 +86,6 @@ const acceptanceCriteriaError = computed(() => {
});
const submit = (): void => {
form.acceptance_criteria = buildAcceptanceCriteria(
acceptanceCriteriaChecklist.value,
);
form.submit(ProjectTicketController.store({ project: props.project.id }), {
preserveScroll: true,
});
@@ -200,64 +186,14 @@ const ticketStatusLabel = (ticketStatus: TicketStatusModel): string => {
/>
</div>
<div class="grid gap-4">
<div class="grid gap-2">
<Label
for="acceptance_criteria_checklist"
>
Acceptance criteria checklist
</Label>
<textarea
id="acceptance_criteria_checklist"
v-model="
acceptanceCriteriaChecklist
"
rows="6"
:disabled="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"
placeholder="[ ] User can sign in&#10; [x] Validation is shown for invalid credentials"
/>
<p
class="text-muted-foreground text-xs"
>
Use <code>[ ]</code> /
<code>[x]</code> for checklist
items. Indent with two spaces for
nested steps.
</p>
<InputError
:message="acceptanceCriteriaError"
/>
</div>
<div
v-if="
acceptanceCriteriaPreviewItems.length >
0
"
class="border-border rounded-xl border p-4 text-sm"
>
<p class="mb-3 font-medium">
Checklist preview
</p>
<ul class="space-y-2">
<li
v-for="(
item, index
) in acceptanceCriteriaPreviewItems"
:key="index"
:style="{
marginLeft: `${item.indent * 1.25}rem`,
}"
class="flex items-start gap-2"
>
<span class="mt-0.5">{{
item.checked ? '☑' : '☐'
}}</span>
<span>{{ item.text }}</span>
</li>
</ul>
</div>
<div class="grid gap-2">
<Label>Acceptance criteria</Label>
<AcceptanceCriteriaEditor
v-model="form.acceptance_criteria"
editable
:disabled="form.processing"
:error="acceptanceCriteriaError"
/>
</div>
</div>