feat: shared setting components - shell, danger/warning zones#5456
feat: shared setting components - shell, danger/warning zones#5456ogzhanolguncu merged 9 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds new settings UI primitives (SettingsShell, SettingsZone, SettingsDangerZone, SettingsZoneRow) and refactors many dashboard, project, ratelimit, and billing settings pages to use them; removes many explicit SettingCard border props and consolidates delete/cancel flows into zone/row patterns and dialog-managed actions. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
web/internal/ui/src/components/settings-card.tsx (1)
250-251: Avoid implicitdangerfallback whenSettingsZoneRowis outside a provider.Defaulting
SettingsZoneContextto"danger"silently renders destructive styling ifSettingsZoneRowis used withoutSettingsZone. Prefer a nullable context and explicit guard.♻️ Proposed type-safe guard
-type SettingsZoneContext = React.createContext<SettingsZoneVariant>("danger"); +const SettingsZoneContext = React.createContext<SettingsZoneVariant | null>(null); function SettingsZoneRow({ title, description, action, }: { title: React.ReactNode; description: React.ReactNode; action: SettingsZoneAction; }) { const zoneVariant = React.useContext(SettingsZoneContext); + if (!zoneVariant) { + throw new Error("SettingsZoneRow must be used within SettingsZone"); + } const btnProps = zoneButtonProps[zoneVariant];As per coding guidelines, "Make illegal states unrepresentable by modeling domain with discriminated unions and parsing inputs at boundaries into typed structures."
Also applies to: 321-323
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/internal/ui/src/components/settings-card.tsx` around lines 250 - 251, SettingsZoneContext currently defaults to "danger", causing silent destructive styling when SettingsZoneRow is rendered outside a provider; change the context type to SettingsZoneVariant | null (make SettingsZoneContext default null) and update consumers (e.g., SettingsZoneRow) to explicitly guard for a null value and throw or render a safe fallback if no provider (refer to SettingsZoneContext, SettingsZoneVariant, SettingsZoneRow, and SettingsZone), ensuring the illegal state is made unrepresentable by failing fast instead of implicitly using "danger".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/apis/[apiId]/settings/components/delete-protection.tsx:
- Line 98: The button color is inverted: it currently sets
color={api.deleteProtection ? "warning" : "danger"}, making the "Enable" action
red; update the color logic so enabling protection (when api.deleteProtection is
falsy) uses a positive color (e.g., "success") and disabling uses "warning".
Locate the JSX in the DeleteProtection component (the button that reads the
api.deleteProtection prop) and change the ternary to map falsy -> "success" and
truthy -> "warning" so enabling is green and disabling remains a cautionary
color.
In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/settings/billing/client.tsx:
- Around line 99-103: The subscription.status is being force-cast to
Stripe.Subscription.Status; fix this by updating the zod schema
(subscriptionSchema used by getBillingInfo) so status is validated as the
concrete Stripe subscription status values (e.g., replace z.string() with a
z.enum or union of the exact Stripe.Subscription.Status literals),
regenerate/infer the types from that schema so the returned type from
getBillingInfo reflects the exact union, and then remove the `as
Stripe.Subscription.Status` cast where SubscriptionStatus is rendered.
In `@web/internal/ui/src/components/settings-card.tsx`:
- Around line 349-353: The SettingsShell's main element currently hardcodes a
large fixed width via the "w-225" utility (in the className passed to the main
element in settings-card.tsx), which causes horizontal overflow on narrow
viewports; replace the fixed width with responsive sizing such as using "w-full"
combined with a max-width utility (e.g., "max-w-[...]" or Tailwind tokens like
"max-w-3xl/4xl" and responsive breakpoints like "sm:", "md:") so the main
container uses full available width on small screens but caps at a sensible max
on larger screens; update the className passed to the main element (and any
usage in the SettingsShell component) to "w-full" plus the chosen responsive
max-width utilities and remove "w-225".
---
Nitpick comments:
In `@web/internal/ui/src/components/settings-card.tsx`:
- Around line 250-251: SettingsZoneContext currently defaults to "danger",
causing silent destructive styling when SettingsZoneRow is rendered outside a
provider; change the context type to SettingsZoneVariant | null (make
SettingsZoneContext default null) and update consumers (e.g., SettingsZoneRow)
to explicitly guard for a null value and throw or render a safe fallback if no
provider (refer to SettingsZoneContext, SettingsZoneVariant, SettingsZoneRow,
and SettingsZone), ensuring the illegal state is made unrepresentable by failing
fast instead of implicitly using "danger".
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 2f05dd5e-c3c3-4e85-b463-e7a6bd1be513
📒 Files selected for processing (22)
web/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/copy-key-space-id.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/default-bytes.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/default-prefix.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/delete-api.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/delete-protection.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/settings-client.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/skeleton.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/update-api-name.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/update-ip-whitelist.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/openapi-diff/page.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/delete-project.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/page.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/ratelimits/[namespaceId]/settings/components/settings-client.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/ratelimits/[namespaceId]/settings/components/skeleton.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/client.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/cancel-alert.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/cancel-plan.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/current-plan-card.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/shell.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/usage.tsxweb/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/page.tsxweb/internal/ui/src/components/settings-card.tsx
💤 Files with no reviewable changes (6)
- web/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/update-api-name.tsx
- web/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/default-prefix.tsx
- web/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/copy-key-space-id.tsx
- web/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/shell.tsx
- web/apps/dashboard/app/(app)/[workspaceSlug]/settings/billing/components/usage.tsx
- web/apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/settings/components/default-bytes.tsx
What does this PR do?
This PR, uses same type of Container(Shell) component to wrap settings pages. Also moves every danger/warning zone to a shared component.
Affected pages:
Testing
Every page above should look good and consistent with each other. Also work without an issue