From 38438b4700183b9a831ec17ba944d438345259aa Mon Sep 17 00:00:00 2001 From: ericasch6 Date: Mon, 14 Sep 2026 14:53:08 +0200 Subject: [PATCH] feat(KAN-34): rework blocker and ticket history --- .../tickets/detail/TicketDetailBlocker.vue | 204 ++++++++++++++++++ .../tickets/detail/TicketDetailContent.vue | 59 ++--- .../tickets/detail/TicketDetailHeader.vue | 1 + .../tickets/detail/TicketDetailHistory.vue | 89 ++++++-- resources/js/locales/en.ts | 20 ++ resources/js/pages/projects/Ticket.vue | 9 +- resources/js/types/ticket-detail.ts | 4 + tests/Fixtures/ticket-detail-browser.mjs | 96 +++++++++ tests/Fixtures/ticket-detail.mjs | 27 ++- 9 files changed, 447 insertions(+), 62 deletions(-) create mode 100644 resources/js/components/tickets/detail/TicketDetailBlocker.vue diff --git a/resources/js/components/tickets/detail/TicketDetailBlocker.vue b/resources/js/components/tickets/detail/TicketDetailBlocker.vue new file mode 100644 index 0000000..a3ce18a --- /dev/null +++ b/resources/js/components/tickets/detail/TicketDetailBlocker.vue @@ -0,0 +1,204 @@ + + + diff --git a/resources/js/components/tickets/detail/TicketDetailContent.vue b/resources/js/components/tickets/detail/TicketDetailContent.vue index b488033..187d798 100644 --- a/resources/js/components/tickets/detail/TicketDetailContent.vue +++ b/resources/js/components/tickets/detail/TicketDetailContent.vue @@ -3,7 +3,15 @@ import { useLocalization } from '@/composables/useLocalization'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { useForm, useRemember } from '@inertiajs/vue3'; -import { computed, ref, toRef, unref, useTemplateRef, nextTick } from 'vue'; +import { + computed, + ref, + toRef, + unref, + useTemplateRef, + nextTick, + watch, +} from 'vue'; import ProjectTicketController from '@/actions/App/Http/Controllers/ProjectTicketController'; import InputError from '@/components/InputError.vue'; import AcceptanceCriteriaChecklist from '@/components/tickets/AcceptanceCriteriaChecklist.vue'; @@ -11,7 +19,6 @@ import FormFeedback from '@/components/FormFeedback.vue'; import { Button } from '@/components/ui/button'; import TicketFormField from '@/components/tickets/TicketFormField.vue'; import TicketFormFields from '@/components/tickets/TicketFormFields.vue'; -import { Label } from '@/components/ui/label'; import { useFormProtection } from '@/composables/useFormProtection'; import { normalizeAcceptanceCriteria, @@ -81,6 +88,16 @@ const savedFormData = () => ({ const form = useForm(draftKey, savedFormData()); +watch( + () => [props.ticket.is_blocked, props.ticket.blocked_comment] as const, + ([isBlocked, blockedComment]) => { + form.is_blocked = isBlocked; + form.blocked_comment = blockedComment ?? ''; + form.defaults('is_blocked', isBlocked); + form.defaults('blocked_comment', blockedComment ?? ''); + }, +); + const hasUnsavedAcceptanceCriteriaDraft = computed( () => JSON.stringify(acceptanceCriteriaItems.value) !== @@ -364,44 +381,6 @@ const submit = (): void => { /> -
- - {{ t('ticketForm.blocker') }} - -
- - -
- - -
-
+
  • -
    - - {{ worklogEntry.entry_type }} - - - {{ - worklogEntry.actor?.name ?? - t('ticketDetail.system') - }} +
    + +
    -

    - {{ worklogEntry.summary }} -

    -

    - {{ formatDateTime(worklogEntry.created_at) }} -

    -
    -
  • +
    +
    +
    + + {{ + worklogEntry.actor?.name ?? + t('ticketDetail.system') + }} + + + {{ + t( + entryTypeLabels[ + worklogEntry.entry_type + ], + ) + }} + +
    + +
    +

    + {{ worklogEntry.summary }} +

    +
    + + diff --git a/resources/js/locales/en.ts b/resources/js/locales/en.ts index 3a11ba4..b9134de 100644 --- a/resources/js/locales/en.ts +++ b/resources/js/locales/en.ts @@ -456,9 +456,24 @@ export const en = { 'ticketDetail.checklistPlaceholder': '[ ] User can sign in\n [x] Validation is shown for invalid credentials', 'ticketDetail.blocked': 'Mark ticket as blocked', + 'ticketDetail.blocker': 'Blocker', + 'ticketDetail.blockerDescription': + 'Record whether progress is blocked and why. Changes are added to the ticket worklog.', + 'ticketDetail.blockerActive': 'Blocked', + 'ticketDetail.blockerInactive': 'Not blocked', + 'ticketDetail.blockerActiveDescription': + 'A blocker reason is required while this ticket is blocked.', + 'ticketDetail.blockerInactiveDescription': + 'This ticket currently has no active blocker.', 'ticketDetail.blockerComment': 'Blocker comment', + 'ticketDetail.blockerCommentHelp': + 'Explain the concrete dependency or decision preventing progress.', 'ticketDetail.blockerPlaceholder': 'Describe what currently blocks progress on this ticket.', + 'ticketDetail.saveBlocker': 'Save blocker', + 'ticketDetail.unblockTicket': 'Unblock ticket', + 'ticketDetail.resetBlocker': 'Discard blocker changes', + 'ticketDetail.savingBlocker': 'Saving blocker…', 'ticketDetail.customFields': 'Custom fields', 'ticketDetail.customFieldsDescription': 'Project-configured fields can be tracked directly on the ticket.', @@ -533,6 +548,11 @@ export const en = { 'Every tracked ticket change is recorded with actor and timestamp.', 'ticketDetail.noHistory': 'No worklog entries exist for this ticket yet.', 'ticketDetail.system': 'System', + 'ticketDetail.historyType.created': 'Ticket created', + 'ticketDetail.historyType.updated': 'Ticket updated', + 'ticketDetail.historyType.statusChanged': 'Status changed', + 'ticketDetail.historyType.linkUpdated': 'Link added', + 'ticketDetail.historyType.linkRemoved': 'Link removed', 'ticketDetail.sourceBranch': 'Source branch: {name}', 'ticketForm.create': 'Create ticket', 'ticketForm.pageTitle': 'Create {project} ticket', diff --git a/resources/js/pages/projects/Ticket.vue b/resources/js/pages/projects/Ticket.vue index 3d72127..276b4d4 100644 --- a/resources/js/pages/projects/Ticket.vue +++ b/resources/js/pages/projects/Ticket.vue @@ -1,6 +1,7 @@