From d40fdad6e98a486680865b0184c61beb9b7ea63f Mon Sep 17 00:00:00 2001 From: hailin Date: Tue, 30 Jun 2026 18:49:41 +0800 Subject: [PATCH] feat: add ticket workflow pages --- frontend/src/app/router.tsx | 21 +++-- frontend/src/components/app/AppEmptyState.tsx | 13 +++ frontend/src/components/app/AppField.tsx | 15 +++ frontend/src/components/ui/input.tsx | 15 +++ frontend/src/components/ui/select.tsx | 15 +++ frontend/src/components/ui/textarea.tsx | 15 +++ .../src/features/tickets/TicketComments.tsx | 39 ++++++++ .../src/features/tickets/TicketCreatePage.tsx | 88 ++++++++++++++++++ .../src/features/tickets/TicketDetailPage.tsx | 93 +++++++++++++++++++ .../src/features/tickets/TicketListPage.tsx | 68 ++++++++++++++ .../features/tickets/TicketStatusTimeline.tsx | 16 ++++ frontend/src/features/tickets/api.ts | 36 +++++++ frontend/src/features/tickets/types.ts | 41 ++++++++ 13 files changed, 467 insertions(+), 8 deletions(-) create mode 100644 frontend/src/components/app/AppEmptyState.tsx create mode 100644 frontend/src/components/app/AppField.tsx create mode 100644 frontend/src/components/ui/input.tsx create mode 100644 frontend/src/components/ui/select.tsx create mode 100644 frontend/src/components/ui/textarea.tsx create mode 100644 frontend/src/features/tickets/TicketComments.tsx create mode 100644 frontend/src/features/tickets/TicketCreatePage.tsx create mode 100644 frontend/src/features/tickets/TicketDetailPage.tsx create mode 100644 frontend/src/features/tickets/TicketListPage.tsx create mode 100644 frontend/src/features/tickets/TicketStatusTimeline.tsx create mode 100644 frontend/src/features/tickets/api.ts create mode 100644 frontend/src/features/tickets/types.ts diff --git a/frontend/src/app/router.tsx b/frontend/src/app/router.tsx index eb33387..ccfc86a 100644 --- a/frontend/src/app/router.tsx +++ b/frontend/src/app/router.tsx @@ -1,6 +1,9 @@ import * as React from 'react' import { DesignSystemPreview } from '@/features/design-system/DesignSystemPreview' +import { TicketCreatePage } from '@/features/tickets/TicketCreatePage' +import { TicketDetailPage } from '@/features/tickets/TicketDetailPage' +import { TicketListPage } from '@/features/tickets/TicketListPage' function getRoute() { return window.location.hash.replace(/^#/, '') || '/tickets' @@ -19,12 +22,14 @@ export function Router() { return } - return ( -
-
-

Route

-

{route}

-
-
- ) + if (route === '/tickets/new') { + return + } + + const ticketMatch = route.match(/^\/tickets\/(\d+)$/) + if (ticketMatch) { + return + } + + return } diff --git a/frontend/src/components/app/AppEmptyState.tsx b/frontend/src/components/app/AppEmptyState.tsx new file mode 100644 index 0000000..c7933ce --- /dev/null +++ b/frontend/src/components/app/AppEmptyState.tsx @@ -0,0 +1,13 @@ +type AppEmptyStateProps = { + title: string + description?: string +} + +export function AppEmptyState({ title, description }: AppEmptyStateProps) { + return ( +
+

{title}

+ {description ?

{description}

: null} +
+ ) +} diff --git a/frontend/src/components/app/AppField.tsx b/frontend/src/components/app/AppField.tsx new file mode 100644 index 0000000..2a160cc --- /dev/null +++ b/frontend/src/components/app/AppField.tsx @@ -0,0 +1,15 @@ +import type * as React from 'react' + +type AppFieldProps = { + label: string + children: React.ReactNode +} + +export function AppField({ label, children }: AppFieldProps) { + return ( + + ) +} diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx new file mode 100644 index 0000000..e326526 --- /dev/null +++ b/frontend/src/components/ui/input.tsx @@ -0,0 +1,15 @@ +import * as React from 'react' + +import { cn } from '@/lib/cn' + +export function Input({ className, ...props }: React.InputHTMLAttributes) { + return ( + + ) +} diff --git a/frontend/src/components/ui/select.tsx b/frontend/src/components/ui/select.tsx new file mode 100644 index 0000000..02230ab --- /dev/null +++ b/frontend/src/components/ui/select.tsx @@ -0,0 +1,15 @@ +import * as React from 'react' + +import { cn } from '@/lib/cn' + +export function Select({ className, ...props }: React.SelectHTMLAttributes) { + return ( +