diff --git a/.trellis/tasks/05-09-billing-dashboard-replica/check.jsonl b/.trellis/tasks/05-09-billing-dashboard-replica/check.jsonl new file mode 100644 index 0000000..e7cbc34 --- /dev/null +++ b/.trellis/tasks/05-09-billing-dashboard-replica/check.jsonl @@ -0,0 +1,24 @@ +{ + "file": ".trellis/spec/frontend/index.md", + "reason": "Confirm the implementation follows the expected frontend stack." +} +{ + "file": ".trellis/spec/frontend/directory-structure.md", + "reason": "Check billing files live in the expected route-local structure." +} +{ + "file": ".trellis/spec/frontend/component-guidelines.md", + "reason": "Review component naming, style isolation, and required comments." +} +{ + "file": ".trellis/spec/frontend/state-management.md", + "reason": "Verify existing global stores are reused instead of duplicated." +} +{ + "file": ".trellis/spec/frontend/type-safety.md", + "reason": "Check local interfaces and types are documented." +} +{ + "file": ".trellis/spec/frontend/quality-guidelines.md", + "reason": "Verify async actions, loading states, and defensive rendering." +} diff --git a/.trellis/tasks/05-09-billing-dashboard-replica/implement.jsonl b/.trellis/tasks/05-09-billing-dashboard-replica/implement.jsonl new file mode 100644 index 0000000..348844f --- /dev/null +++ b/.trellis/tasks/05-09-billing-dashboard-replica/implement.jsonl @@ -0,0 +1,24 @@ +{ + "file": ".trellis/spec/frontend/index.md", + "reason": "Frontend stack and high-level conventions for Next.js, Zustand, SCSS, and TypeScript." +} +{ + "file": ".trellis/spec/frontend/directory-structure.md", + "reason": "Route-local organization and naming conventions for the billing module." +} +{ + "file": ".trellis/spec/frontend/component-guidelines.md", + "reason": "Component, style isolation, and documentation rules for new billing UI pieces." +} +{ + "file": ".trellis/spec/frontend/state-management.md", + "reason": "Rules for consuming existing Zustand stores and avoiding redundant state." +} +{ + "file": ".trellis/spec/frontend/type-safety.md", + "reason": "Type and JSDoc rules for route-local billing interfaces." +} +{ + "file": ".trellis/spec/frontend/quality-guidelines.md", + "reason": "Async handling, loading states, defensive data access, and React quality checks." +} diff --git a/.trellis/tasks/05-09-billing-dashboard-replica/prd.md b/.trellis/tasks/05-09-billing-dashboard-replica/prd.md new file mode 100644 index 0000000..c8550f5 --- /dev/null +++ b/.trellis/tasks/05-09-billing-dashboard-replica/prd.md @@ -0,0 +1,22 @@ +# Replicate Billing Dashboard Page + +## Goal + +Rebuild the existing `/web/app/dashboard/billing` page inside `/src/app/dashboard/billing` with equivalent UI and +behavior while adapting the implementation to the current `src` architecture. + +## Requirements + +- Preserve the billing page content, layout, plan cards, checkout interaction, and checkout-return cleanup behavior from + the `web` implementation. +- Do not copy the `web` structure verbatim when it conflicts with the `src` conventions. +- Reuse existing `src/api`, `src/store`, and utility wrappers whenever they cover the needed behavior. +- Keep route-local UI, hooks, styles, and types inside `src/app/dashboard/billing`. +- Keep async actions guarded by loading states and defensive error handling. + +## Acceptance Criteria + +- `/dashboard/billing` exists under `src/app`. +- The page renders without TypeScript or build errors. +- Billing-related API calls use existing request helpers and stores where applicable. +- The checkout return cleanup mirrors the current behavior from `web`. diff --git a/.trellis/tasks/05-09-billing-dashboard-replica/task.json b/.trellis/tasks/05-09-billing-dashboard-replica/task.json new file mode 100644 index 0000000..4b7379d --- /dev/null +++ b/.trellis/tasks/05-09-billing-dashboard-replica/task.json @@ -0,0 +1,26 @@ +{ + "id": "billing-dashboard-replica", + "name": "billing-dashboard-replica", + "title": "Replicate billing dashboard page", + "description": "", + "status": "in_progress", + "dev_type": null, + "scope": null, + "package": null, + "priority": "P2", + "creator": "tao", + "assignee": "tao", + "createdAt": "2026-05-09", + "completedAt": null, + "branch": null, + "base_branch": "master", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [], + "notes": "", + "meta": {} +} \ No newline at end of file diff --git a/package.json b/package.json index 27bb8da..a282220 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "qrcode.react": "^4.2.0", "react": "19.2.4", "react-dom": "19.2.4", + "sonner": "^2.0.7", "zustand": "^5.0.13" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f4a709..addb3c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: react-dom: specifier: 19.2.4 version: 19.2.4(react@19.2.4) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) zustand: specifier: ^5.0.13 version: 5.0.13(@types/react@19.2.14)(react@19.2.4) @@ -784,6 +787,12 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1425,6 +1434,11 @@ snapshots: '@img/sharp-win32-x64': 0.34.5 optional: true + sonner@2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + source-map-js@1.2.1: {} styled-jsx@5.1.6(react@19.2.4): diff --git a/src/api/stripe.ts b/src/api/stripe.ts new file mode 100644 index 0000000..fe864e0 --- /dev/null +++ b/src/api/stripe.ts @@ -0,0 +1,15 @@ +import request from "@/utils/reqeust"; + +/** + * 创建订单 + */ +export async function stripeCheckoutApi() { + return request.post("/stripe/checkout", {agreement_version: 2}) +} + +/** + * 创建支付链接 + */ +export async function stripePortalApi() { + return request.post("/stripe/portal") +} \ No newline at end of file diff --git a/src/app/dashboard/_components/header.tsx b/src/app/dashboard/_components/header.tsx index 1d738ae..a7df7f9 100644 --- a/src/app/dashboard/_components/header.tsx +++ b/src/app/dashboard/_components/header.tsx @@ -94,7 +94,7 @@ const NavTabs = () => {