Conversation
|
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
1 Skipped Deployment
|
📝 Walkthrough## Walkthrough
This update introduces a new `WorkspaceNavbar` component to unify and streamline navigation across workspace settings pages. Multiple settings pages—including General, Billing, Team, and Root Keys—are refactored to use this new navigation bar, replacing older navigation components and adjusting page layouts for consistency. New utility components, such as `CopyWorkspaceId` and `RootKeyNav`, are added to enhance usability. Several UI components are updated for styling consistency, and navigation breadcrumbs are improved. The update also standardizes input and button imports across various settings forms and makes minor layout and class name adjustments throughout the dashboard settings UI.
## Changes
| File(s) | Change Summary |
|----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `apps/dashboard/app/(app)/settings/workspace-navbar.tsx`<br>`apps/dashboard/app/(app)/settings/general/copy-workspace-id.tsx`<br>`apps/dashboard/app/(app)/settings/root-keys/root-key-nav.tsx` | Added new components: `WorkspaceNavbar` for unified workspace settings navigation, `CopyWorkspaceId` for copying the workspace ID, and `RootKeyNav` for root key section navigation. |
| `apps/dashboard/app/(app)/settings/general/page.tsx`<br>`apps/dashboard/app/(app)/settings/billing/page.tsx`<br>`apps/dashboard/app/(app)/settings/team/page.tsx`<br>`apps/dashboard/app/(app)/settings/root-keys/page.tsx` | Refactored to use `WorkspaceNavbar`, replacing previous navigation components. Adjusted layout and removed old navigation/submenu components. |
| `apps/dashboard/app/(app)/settings/billing/client.tsx`<br>`apps/dashboard/app/(app)/settings/billing/components/shell.tsx`<br>`apps/dashboard/app/(app)/settings/billing/components/usage.tsx` | Updated layout to include `WorkspaceNavbar`, passed `workspace` prop to `Shell`, reordered and standardized class names, and adjusted component structure for consistency. |
| `apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx` | Refactored form schema and UI: renamed `name` to `workspaceName`, updated form control and button logic, replaced `Card` with `SettingCard`, and improved validation handling. |
| `apps/dashboard/components/navigation/sidebar/workspace-navigations.tsx` | Added "General" as a submenu item under "Settings" in the workspace sidebar navigation. |
| `apps/dashboard/app/(app)/settings/root-keys/navigation.tsx` | Removed the old navigation component for root keys settings page. |
| `apps/dashboard/app/(app)/settings/root-keys/new/navigation.tsx`<br>`apps/dashboard/app/(app)/settings/root-keys/new/page.tsx` | Added new breadcrumb navigation for the "New Root Key" page and updated import paths accordingly. |
| `apps/dashboard/app/(app)/settings/root-keys/[keyId]/navigation.tsx` | Added "Settings" as the first breadcrumb link for key details navigation. |
| `apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/settings-client.tsx` | Adjusted layout and styling of setting cards and buttons, including adding a bottom border and refining width classes. |
| `apps/dashboard/app/(app)/apis/[apiId]/settings/components/copy-api-id.tsx` | Adjusted input width on large screens from 320px to 315px and consolidated imports. |
| `apps/dashboard/app/(app)/apis/[apiId]/settings/components/default-bytes.tsx`<br>`.../default-prefix.tsx`<br>`.../delete-api.tsx`<br>`.../delete-protection.tsx`<br>`.../update-api-name.tsx`<br>`.../update-ip-whitelist.tsx` | Standardized import sources for `Input`/`Textarea` components to use `@unkey/ui`, updated button and input class names for layout consistency, and adjusted logic for form validation and button states in the IP whitelist component. |
| `apps/dashboard/app/(app)/settings/team/client.tsx` | Simplified the empty state layout by removing unnecessary wrappers and applying margin directly, changing centering approach from flexbox to absolute positioning. |
| `apps/dashboard/lib/trpc/routers/workspace/changeName.ts` | Enhanced input validation for workspace name by adding a maximum length of 50 characters alongside the existing minimum length. |
| `apps/dashboard/app/(app)/apis/[apiId]/settings/components/settings-client.tsx` | Removed `Separator` imports and elements, adjusted container div classes for simplified layout without separators, and updated padding and spacing. |
| `apps/dashboard/app/(app)/apis/[apiId]/settings/page.tsx` | Removed `PageContent` wrapper around `SettingsClient` component, rendering it directly instead. |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant WorkspaceNavbar
participant SettingsPage (General/Billing/Team/Root Keys)
participant CopyWorkspaceId (if applicable)
participant RootKeyNav (if applicable)
User->>SettingsPage: Navigates to a settings page
SettingsPage->>WorkspaceNavbar: Renders WorkspaceNavbar with workspace info and active page
WorkspaceNavbar-->>User: Displays breadcrumbs and actions
alt General Settings
SettingsPage->>CopyWorkspaceId: Renders CopyWorkspaceId with workspaceId
User->>CopyWorkspaceId: Clicks copy button
CopyWorkspaceId-->>User: Copies ID to clipboard and shows toast
end
alt Root Keys Section
SettingsPage->>RootKeyNav: Renders RootKeyNav with active page
RootKeyNav-->>User: Displays root key navigation and actions
endPossibly related PRs
Suggested labels
Suggested reviewers
|
|
Thank you for following the naming conventions for pull request titles! 🙏 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/dashboard/app/(app)/settings/general/copy-workspace-id.tsx (1)
32-37: Consider adding error handling for clipboard operationsWhile the current implementation works in modern browsers, clipboard operations can sometimes fail due to permissions or in certain environments.
<button type="button" onClick={() => { - navigator.clipboard.writeText(workspaceId); - toast.success("Copied to clipboard", { - description: workspaceId, - }); + navigator.clipboard.writeText(workspaceId) + .then(() => { + toast.success("Copied to clipboard", { + description: workspaceId, + }); + }) + .catch(() => { + toast.error("Failed to copy to clipboard"); + }); }} >apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx (1)
90-94: Remove unused onBlur handlerThe onBlur event handler doesn't perform any meaningful action - it returns early if the value is empty but doesn't define any action for non-empty values.
- onBlur={(e) => { - if (e.target.value === "") { - return; - } - }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
apps/dashboard/app/(app)/settings/billing/client.tsx(9 hunks)apps/dashboard/app/(app)/settings/billing/components/shell.tsx(1 hunks)apps/dashboard/app/(app)/settings/billing/page.tsx(4 hunks)apps/dashboard/app/(app)/settings/general/copy-workspace-id.tsx(1 hunks)apps/dashboard/app/(app)/settings/general/page.tsx(2 hunks)apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx(4 hunks)apps/dashboard/app/(app)/settings/root-keys/page.tsx(2 hunks)apps/dashboard/app/(app)/settings/team/page.tsx(1 hunks)apps/dashboard/app/(app)/settings/workspace-navbar.tsx(1 hunks)apps/dashboard/components/settings-card.tsx(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
apps/dashboard/app/(app)/settings/root-keys/page.tsx (3)
apps/dashboard/app/(app)/settings/workspace-navbar.tsx (1)
WorkspaceNavbar(32-79)apps/dashboard/components/page-content.tsx (1)
PageContent(3-5)apps/dashboard/components/dashboard/page-header.tsx (1)
PageHeader(18-50)
apps/dashboard/app/(app)/settings/general/copy-workspace-id.tsx (2)
apps/dashboard/components/settings-card.tsx (1)
SettingCard(12-49)internal/icons/src/icons/clone.tsx (1)
Clone(15-50)
apps/dashboard/app/(app)/settings/billing/client.tsx (1)
apps/dashboard/app/(app)/settings/billing/components/shell.tsx (1)
Shell(7-26)
apps/dashboard/app/(app)/settings/team/page.tsx (2)
apps/dashboard/app/(app)/settings/workspace-navbar.tsx (1)
WorkspaceNavbar(32-79)apps/dashboard/app/(app)/settings/team/client.tsx (1)
TeamPageClient(22-98)
apps/dashboard/app/(app)/settings/billing/components/shell.tsx (2)
apps/dashboard/app/(app)/settings/workspace-navbar.tsx (1)
WorkspaceNavbar(32-79)apps/dashboard/components/page-content.tsx (1)
PageContent(3-5)
apps/dashboard/app/(app)/settings/general/page.tsx (4)
apps/dashboard/app/(app)/settings/workspace-navbar.tsx (1)
WorkspaceNavbar(32-79)apps/dashboard/components/page-content.tsx (1)
PageContent(3-5)apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx (1)
UpdateWorkspaceName(28-118)apps/dashboard/app/(app)/settings/general/copy-workspace-id.tsx (1)
CopyWorkspaceId(8-45)
⏰ Context from checks skipped due to timeout of 90000ms (19)
- GitHub Check: Test Go API Local / Test (Shard 7/8)
- GitHub Check: Test Go API Local / Test (Shard 4/8)
- GitHub Check: Test Packages / Test ./apps/dashboard
- GitHub Check: Test Packages / Test ./packages/cache
- GitHub Check: Test Packages / Test ./packages/nextjs
- GitHub Check: Test Packages / Test ./packages/hono
- GitHub Check: Test Packages / Test ./internal/clickhouse
- GitHub Check: Test Packages / Test ./packages/api
- GitHub Check: Test Packages / Test ./internal/hash
- GitHub Check: Test Packages / Test ./internal/billing
- GitHub Check: Test Packages / Test ./internal/keys
- GitHub Check: Test Packages / Test ./internal/resend
- GitHub Check: Test Packages / Test ./internal/id
- GitHub Check: Test Packages / Test ./internal/encryption
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Agent Local / test_agent_local
- GitHub Check: Build / Build
- GitHub Check: autofix
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (33)
apps/dashboard/components/settings-card.tsx (2)
4-4: Improved component flexibility!Enhancing the
titleprop type to accept React nodes in addition to strings provides more flexibility. This change allows for complex content in titles, such as the combination of text and icons or other interactive elements.
43-43: LGTM - Class order changeReordering the CSS classes from "text-accent-12 font-medium" to "font-medium text-accent-12" maintains the same visual appearance since Tailwind classes don't have order-dependent behavior. This change likely aligns with a team-wide style standard.
apps/dashboard/app/(app)/settings/general/copy-workspace-id.tsx (1)
8-45: Well-structured component implementationThe new
CopyWorkspaceIdcomponent effectively encapsulates the workspace ID display and copy functionality. It appropriately leverages the enhancedSettingCardcomponent, making good use of React nodes for both title and description to achieve the desired layout.apps/dashboard/app/(app)/settings/root-keys/page.tsx (2)
35-38: Improved navigation consistencyReplacing the previous navigation component with
WorkspaceNavbarenhances consistency across the application's settings pages. The component properly receives the workspace context and active page information.
45-45: LGTM - Class reorderingThe reordering of CSS classes from "mb-20 grid grid-cols-1 gap-8 w-full" to "grid w-full grid-cols-1 gap-8 mb-20" maintains the same visual appearance since Tailwind classes don't have order-dependent behavior.
apps/dashboard/app/(app)/settings/billing/client.tsx (3)
86-86: Improved component context with workspace propPassing the workspace prop to the Shell component aligns with the application-wide refactoring to provide components with workspace context. This change enables more cohesive navigation and workspace-specific functionality.
117-124: LGTM - Consistent class reorderingThe class reorderings throughout this section (and elsewhere in the file) are consistent with the style changes seen in other files. These changes don't affect functionality but help maintain a consistent style convention across the codebase.
236-237: LGTM - Link styling adjustmentsThe reordering of "text-info-11 underline" to "underline text-info-11" is consistent with the style standards being applied elsewhere in the code.
apps/dashboard/app/(app)/settings/billing/page.tsx (4)
11-11: LGTM: Importing the new WorkspaceNavbar componentThe import statement correctly references the new WorkspaceNavbar component from the parent directory.
35-44: UI improvement for the error stateGood enhancement to the error state UI. Now when Stripe is not configured, users still see the consistent workspace navigation rather than just an error message, providing better context and navigation options.
77-77: Appropriate prop update for Shell componentThe Shell component now correctly receives the workspace information it needs for the new navigation structure.
118-118: Minor style adjustmentSimple class order adjustment that maintains the same styling functionality.
apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx (5)
3-4: LGTM: Updated imports for the UI redesignThe imports have been appropriately updated to include the new SettingCard and Form-related components needed for the UI redesign.
18-18: Improved field namingRenaming from
nametoworkspaceNameenhances clarity about what the field represents while maintaining the same validation requirements.
38-38: Consistent field renaming in default valuesThe default values have been correctly updated to use
workspaceNameto maintain consistency with the schema update.
54-54: More explicit submission object structureThe submission function now uses a more explicit object structure, clearly specifying which properties are being sent to the API.
59-114: Improved UI with SettingCard componentThe UI redesign with SettingCard provides better context and usability. The improved button validation logic (disabling when appropriate) enhances user experience.
apps/dashboard/app/(app)/settings/team/page.tsx (5)
4-4: LGTM: Importing the WorkspaceNavbar componentThe import statement correctly references the WorkspaceNavbar component, consistent with other files in this PR.
11-11: Improved variable namingRenaming from
wstoworkspaceenhances code readability by using a more descriptive variable name.
16-16: Consistent variable usageThe team property access has been updated to use the renamed
workspacevariable.
18-34: Better error handling with conditional renderingGood improvement to handle the case when a workspace isn't found, providing a clear fallback message to the user instead of potentially crashing.
20-23: Consistent navigation pattern with WorkspaceNavbarThe navigation has been updated to use the new WorkspaceNavbar component, matching the new navigation pattern implemented across settings pages.
apps/dashboard/app/(app)/settings/workspace-navbar.tsx (4)
1-8: LGTM: Well-structured component importsThe imports are organized logically, separating client directive, component imports, and UI elements.
9-30: Good navigation data structureThe settings navigation data is well-organized in a clear, maintainable array structure that makes it easy to add, remove or modify navigation items in the future.
32-44: Clear component interface with TypeScript propsThe WorkspaceNavbar component has a well-defined interface with TypeScript types that clearly communicate what data the component expects.
45-78: Well-implemented navigation with advanced featuresThe component implementation effectively combines several UI elements:
- Breadcrumb navigation for context
- QuickNavPopover for efficient navigation between settings
- Workspace ID display with copy functionality
This provides a consistent, user-friendly navigation experience across settings pages.
apps/dashboard/app/(app)/settings/billing/components/shell.tsx (3)
5-13: Good implementation of the new WorkspaceNavbar patternThe code now uses the standardized WorkspaceNavbar component, which creates a consistent navigation experience across workspace settings pages. The implementation correctly passes both the workspace object and the appropriate activePage props.
7-10: Well-structured prop typingGood job on properly typing the workspace prop with explicit shape (
{ id: string; name: string }). This ensures type safety and makes the component contract clearer for other developers.
15-18: Minor styling adjustments maintain layout consistencyThe subtle adjustments to padding and spacing in the layout container help maintain visual consistency while accommodating the new navigation component.
apps/dashboard/app/(app)/settings/general/page.tsx (4)
2-7: Good modularization of importsThe import changes reflect the improved component organization, with separate components for specific functionality (Separator, WorkspaceNavbar, CopyWorkspaceId) instead of generic UI components.
28-34: Clean implementation of consistent workspace navigationThe WorkspaceNavbar implementation aligns with the pattern used in other settings pages, creating a unified navigation experience. The section header with proper styling helps establish visual hierarchy.
37-38: Good use of semantic components for visual separationUsing the Separator component before the CopyWorkspaceId creates clear visual grouping, which improves the UX by distinguishing between different functional areas of the settings page.
30-31: Responsive layout considerationsThe container uses
lg:w-[760px]for larger screens while remaining flexible on smaller screens. This is a good practice for maintaining a responsive design.
|
should I take this over :thinkies: |
|
@MichaelUnkey are we close to merging wdyt? |
|
Yeah just fixed some imports. Should be good if it builds this time. |
|
I can double submit. Because briefly button becomes submittable. Screen.Recording.2025-04-28.at.17.57.04.mov |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx (3)
54-57: Improve validation error messagesThe current error message combines two different validation cases (unchanged name or empty name). Consider providing distinct error messages for each case for better user experience.
async function onSubmit(values: z.infer<typeof formSchema>) { - if (name === values.workspaceName || !values.workspaceName) { - return toast.error("Please provide a different name before saving."); - } + if (!values.workspaceName) { + return toast.error("Workspace name cannot be empty."); + } + + if (name === values.workspaceName) { + return toast.error("Please provide a different name before saving."); + } await updateName.mutateAsync({ workspaceId: workspace.id, name: values.workspaceName }); }
97-101: Remove empty onBlur handlerThis onBlur handler doesn't perform any action as it only contains a return statement inside a condition. Consider removing it if it's not needed or implement proper validation feedback.
{...field} autoComplete="off" - onBlur={(e) => { - if (e.target.value === "") { - return; - } - }}
111-115: Consider displaying validation feedback to usersThe button is correctly disabled when the form is invalid, but users may not understand why. Consider adding validation error messages near the input field to explain requirements (like minimum 3 characters).
<FormField control={form.control} name="workspaceName" render={({ field }) => ( <FormItem> <FormControl> <Input type="text" id="workspaceName" disabled={form?.formState?.isSubmitting || form.formState.isLoading} className="w-[20rem] lg:w-[16rem]" {...field} autoComplete="off" /> </FormControl> + {form.formState.errors.workspaceName && ( + <div className="text-sm text-red-500 mt-1"> + {form.formState.errors.workspaceName.message} + </div> + )} </FormItem> )} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
apps/dashboard/app/(app)/apis/[apiId]/settings/components/copy-api-id.tsx(2 hunks)apps/dashboard/app/(app)/apis/[apiId]/settings/components/default-bytes.tsx(2 hunks)apps/dashboard/app/(app)/apis/[apiId]/settings/components/default-prefix.tsx(2 hunks)apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-api.tsx(1 hunks)apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx(2 hunks)apps/dashboard/app/(app)/apis/[apiId]/settings/components/update-api-name.tsx(2 hunks)apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/settings-client.tsx(5 hunks)apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx(2 hunks)apps/dashboard/app/(app)/settings/root-keys/page.tsx(2 hunks)apps/dashboard/app/(app)/settings/team/client.tsx(1 hunks)apps/dashboard/app/(app)/settings/team/page.tsx(1 hunks)apps/dashboard/lib/trpc/routers/workspace/changeName.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- apps/dashboard/app/(app)/apis/[apiId]/settings/components/copy-api-id.tsx
- apps/dashboard/app/(app)/apis/[apiId]/settings/components/default-bytes.tsx
- apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/settings-client.tsx
- apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-api.tsx
- apps/dashboard/app/(app)/apis/[apiId]/settings/components/update-api-name.tsx
- apps/dashboard/app/(app)/apis/[apiId]/settings/components/default-prefix.tsx
- apps/dashboard/app/(app)/settings/root-keys/page.tsx
- apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx
- apps/dashboard/app/(app)/settings/team/page.tsx
- apps/dashboard/app/(app)/settings/team/client.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Test Packages / Test ./internal/clickhouse
🔇 Additional comments (1)
apps/dashboard/lib/trpc/routers/workspace/changeName.ts (1)
12-15: Good addition of maximum length validationAdding a maximum length constraint of 50 characters is a good improvement that prevents overly long workspace names. The error message is clear and informative.
apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/general/update-workspace-name.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
apps/dashboard/app/(app)/settings/root-keys/new/navigation.tsx (4)
37-43: Simplify the unnecessary flattening operation.The
flatMapis being used but not actually flattening any nested arrays, making it unnecessarily complex. A simplemapwould be more appropriate here.- items={settingsNavbar.flatMap((setting) => [ - { - id: setting.href, - label: setting.text, - href: `/settings/${setting.href}`, - }, - ])} + items={settingsNavbar.map((setting) => ({ + id: setting.href, + label: setting.text, + href: `/settings/${setting.href}`, + }))}
47-47: Use dynamic value instead of hardcoded string.The "Root Keys" text is hardcoded here, but it could be sourced from the navigation items for better maintainability. This ensures consistency if the display text ever changes.
- {"Root Keys"} + {settingsNavbar.find(item => item.id === "root-keys")?.text || "Root Keys"}
30-56: Enhance accessibility for the navigation component.The navigation component looks well-structured, but consider adding ARIA attributes to improve accessibility for screen readers, especially for the popover interaction.
Enhance the QuickNavPopover trigger with appropriate ARIA labels:
<div className="flex items-center gap-1 p-1 rounded-lg hover:bg-gray-3" + aria-label="Navigate to settings sections" + role="button" + aria-haspopup="menu" >
7-28: Consider using path constants for navigation links.The navigation structure is well-defined, but consider extracting these paths to constants that can be reused across the application. This would ensure consistency if routes change.
Consider creating a separate paths constant file that can be imported wherever navigation paths are needed.
// Example: constants/paths.ts export const SETTINGS_PATHS = { GENERAL: '/settings/general', TEAM: '/settings/team', ROOT_KEYS: '/settings/root-keys', BILLING: '/settings/billing', }; // Then use them like: // href: SETTINGS_PATHS.GENERAL.split('/').pop(),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/dashboard/app/(app)/settings/root-keys/new/navigation.tsx(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: Test Packages / Test ./internal/clickhouse
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/dashboard/app/(app)/settings/root-keys/new/navigation.tsx (1)
1-56: Overall implementation of the workspace navigation is well-structured.The Navigation component effectively implements breadcrumb navigation with a quick navigation popover, which enhances the user experience by providing easy access to different settings sections. The component composition is clean and the code is well-organized.
ogzhanolguncu
left a comment
There was a problem hiding this comment.
- Every settings page look identical.
- Root key creation still works
- Workspace settings works
- Api settings works




What does this PR do?
Fixes # (issue)
If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists
Type of change
How should this be tested?
Navigation functions like it should
Looks match the Figma on other settings pages
Checklist
Required
pnpm buildpnpm fmtconsole.logsgit pull origin mainAppreciated
Summary by CodeRabbit
New Features
Refactor
Style
Chores