From 550c837c5e646b90c317818289f56ec5fc19b294 Mon Sep 17 00:00:00 2001 From: Twyco Date: Thu, 16 Apr 2026 08:59:43 +0200 Subject: [PATCH] Adopt Stitch styling for ticket pages --- .../js/components/FeatureComingSoonPanel.vue | 51 ++ .../js/components/TicketPriorityIcon.vue | 22 + resources/js/components/TicketTypeIcon.vue | 22 + resources/js/pages/projects/Ticket.vue | 544 +++++++++--- resources/js/pages/projects/TicketCreate.vue | 781 +++++++++++++----- 5 files changed, 1116 insertions(+), 304 deletions(-) create mode 100644 resources/js/components/FeatureComingSoonPanel.vue create mode 100644 resources/js/components/TicketPriorityIcon.vue create mode 100644 resources/js/components/TicketTypeIcon.vue diff --git a/resources/js/components/FeatureComingSoonPanel.vue b/resources/js/components/FeatureComingSoonPanel.vue new file mode 100644 index 0000000..3ddb553 --- /dev/null +++ b/resources/js/components/FeatureComingSoonPanel.vue @@ -0,0 +1,51 @@ + + + diff --git a/resources/js/components/TicketPriorityIcon.vue b/resources/js/components/TicketPriorityIcon.vue new file mode 100644 index 0000000..d29c5d4 --- /dev/null +++ b/resources/js/components/TicketPriorityIcon.vue @@ -0,0 +1,22 @@ + + + diff --git a/resources/js/components/TicketTypeIcon.vue b/resources/js/components/TicketTypeIcon.vue new file mode 100644 index 0000000..f5d6658 --- /dev/null +++ b/resources/js/components/TicketTypeIcon.vue @@ -0,0 +1,22 @@ + + + diff --git a/resources/js/pages/projects/Ticket.vue b/resources/js/pages/projects/Ticket.vue index f8420b5..577f395 100644 --- a/resources/js/pages/projects/Ticket.vue +++ b/resources/js/pages/projects/Ticket.vue @@ -5,9 +5,11 @@ import { computed, ref, watch } from 'vue'; import ProjectTicketBranchController from '@/actions/App/Http/Controllers/ProjectTicketBranchController'; import ProjectTicketLinkController from '@/actions/App/Http/Controllers/ProjectTicketLinkController'; import ProjectTicketController from '@/actions/App/Http/Controllers/ProjectTicketController'; -import Heading from '@/components/Heading.vue'; +import FeatureComingSoonPanel from '@/components/FeatureComingSoonPanel.vue'; import InputError from '@/components/InputError.vue'; +import TicketPriorityIcon from '@/components/TicketPriorityIcon.vue'; import TicketTreeList from '@/components/TicketTreeList.vue'; +import TicketTypeIcon from '@/components/TicketTypeIcon.vue'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { @@ -25,8 +27,17 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; +import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Textarea } from '@/components/ui/textarea'; import AppLayout from '@/layouts/AppLayout.vue'; import { edit as editGitConnections } from '@/routes/git-connections/index'; import { index as backlogIndex } from '@/routes/projects/backlog'; @@ -103,6 +114,25 @@ const breadcrumbs: BreadcrumbItem[] = [ }, ]; +const frostedCardClass = + 'rounded-[1.75rem] border-white/70 bg-white/85 shadow-[0_22px_65px_-40px_rgba(45,51,53,0.38)] backdrop-blur-sm dark:border-white/10 dark:bg-card/90 dark:shadow-black/25'; +const frostedAsideCardClass = + 'h-fit rounded-[1.75rem] border-white/70 bg-white/85 shadow-[0_22px_65px_-40px_rgba(45,51,53,0.38)] backdrop-blur-sm dark:border-white/10 dark:bg-card/90 dark:shadow-black/25'; +const airyOutlineButtonClass = + 'rounded-full border-white/70 bg-white/80 text-[#2d3335] shadow-[0_18px_45px_-34px_rgba(45,51,53,0.34)] hover:bg-white dark:border-white/10 dark:bg-card/85 dark:text-foreground dark:hover:bg-card'; +const inputClass = + 'h-12 rounded-xl border-white/70 bg-white/80 text-[#2d3335] shadow-[0_18px_45px_-34px_rgba(45,51,53,0.22)] focus-visible:border-[#9de77c] focus-visible:ring-[#abf688]/35 dark:border-white/10 dark:bg-card/90 dark:text-foreground dark:focus-visible:border-primary'; +const textareaClass = + 'min-h-32 rounded-[1.25rem] border-white/70 bg-white/80 text-[#2d3335] shadow-[0_18px_45px_-34px_rgba(45,51,53,0.22)] focus-visible:border-[#9de77c] focus-visible:ring-[#abf688]/35 dark:border-white/10 dark:bg-card/90 dark:text-foreground dark:focus-visible:border-primary'; +const nativeSelectClass = + 'h-12 w-full rounded-xl border border-white/70 bg-white/80 px-3 py-2 text-sm text-[#2d3335] shadow-[0_18px_45px_-34px_rgba(45,51,53,0.22)] outline-none transition-[color,box-shadow] focus:border-[#9de77c] focus:ring-4 focus:ring-[#abf688]/35 disabled:cursor-not-allowed disabled:opacity-60 dark:border-white/10 dark:bg-card/90 dark:text-foreground dark:focus:border-primary dark:focus:ring-primary/20'; +const selectTriggerClass = + 'h-12 w-full rounded-xl border-white/70 bg-white/80 text-left text-[#2d3335] shadow-[0_18px_45px_-34px_rgba(45,51,53,0.22)] focus:border-[#9de77c] focus:ring-[#abf688]/35 dark:border-white/10 dark:bg-card/90 dark:text-foreground dark:focus:border-primary'; +const selectContentClass = + 'rounded-[1.25rem] border-white/80 bg-white/95 shadow-[0_24px_60px_-40px_rgba(45,51,53,0.34)] backdrop-blur-sm dark:border-white/10 dark:bg-card/95'; +const fieldLabelClass = + 'text-[0.72rem] font-semibold uppercase tracking-[0.18em] text-[#5a6062]'; + const form = useForm({ acceptance_criteria: props.ticket.acceptance_criteria ?? '', assignee_user_id: props.ticket.assignee_user_id ?? '', @@ -124,12 +154,56 @@ const form = useForm({ }), is_blocked: props.ticket.is_blocked, parent_ticket_id: props.ticket.parent_ticket_id ?? '', - ticket_priority_id: props.ticket.ticket_priority_id, - ticket_type_id: props.ticket.ticket_type_id, + ticket_priority_id: String(props.ticket.ticket_priority_id), + ticket_type_id: String(props.ticket.ticket_type_id), title: props.ticket.title, story_points: props.ticket.story_points, }); +const selectedTicketType = computed( + () => + props.ticketTypes.find( + (ticketType) => String(ticketType.id) === form.ticket_type_id, + ) ?? props.ticket.ticket_type, +); + +const selectedTicketPriority = computed( + () => + props.ticketPriorities.find( + (ticketPriority) => + String(ticketPriority.id) === form.ticket_priority_id, + ) ?? props.ticket.ticket_priority, +); + +const selectedAssignee = computed( + () => + props.users.find( + (user) => String(user.id) === String(form.assignee_user_id), + ) ?? null, +); + +const selectedAuthor = computed( + () => + props.users.find( + (user) => String(user.id) === String(form.author_user_id), + ) ?? null, +); + +const repositoryBranchCount = computed(() => + visibleRepositoryBranches.value.reduce( + (count, repositoryBranch) => count + repositoryBranch.branches.length, + 0, + ), +); + +const pullRequestCount = computed(() => + visibleRepositoryPullRequests.value.reduce( + (count, repositoryPullRequest) => + count + repositoryPullRequest.pull_requests.length, + 0, + ), +); + const isCreateBranchDialogOpen = ref(false); const isCheckingBranchName = ref(false); const branchNameValidation = ref(null); @@ -271,8 +345,7 @@ watch( branchNameValidation.value = { is_valid: false, - message: - `${appName} could not validate this branch name right now.`, + message: `${appName} could not validate this branch name right now.`, }; } finally { if (!abortController.signal.aborted) { @@ -341,65 +414,139 @@ const formatDateTime = (value: string): string => { -
+
+ +
-
-
- - {{ ticket.ticket_key }} - - - {{ ticket.ticket_type.name }} - - - {{ ticket.ticket_priority.name }} - - - {{ ticket.ticket_status.name }} - - - Read only - +
+
+
+ + {{ ticket.ticket_key }} + + + + + {{ selectedTicketType.name }} + + + + + {{ selectedTicketPriority.name }} + + + + {{ ticket.ticket_status.name }} + + + + Blocked + + + + Read only + +
+ +
+

+ Ticket detail +

+

+ {{ ticket.title }} +

+

+ Review ticket details, update tracked fields, + and inspect the change timeline in the new + Stitch-inspired workspace. +

+
- -
-
- + + Open backlog + + - + - + +
-
+
{{ status }}
- + - Ticket details - + + Ticket details + + {{ canEditTicket ? 'Tracked ticket changes create worklog entries automatically.' @@ -411,61 +558,104 @@ const formatDateTime = (value: string): string => {
- +
- - - - + + + + + + + {{ ticketType.name }} + + + +
-