From aa1779170adc5e983441c43003a97df10e5605c9 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 9 Jul 2026 23:39:10 +0700 Subject: [PATCH] refactor: enhance settings and auth components for improved navigation and user feedback Updated the Settings and Auth components to include better navigation handling by adding search parameters for tab selection. Enhanced the Access component to utilize badges for status indicators, improving visual clarity. Refactored the Schedule component to streamline job status display with badges, ensuring a more cohesive user experience. Additionally, integrated client directives in UI components for better performance. --- .cursor/debug-ce85d7.log | 8 + .../analytics/analytics-kpi-row.tsx | 69 +-- apps/web/src/components/badge-tabs.tsx | 6 +- .../components/account-settings.tsx | 116 +++++ .../settings-7/components/billing-tab.tsx | 180 ++++++++ .../blocks/settings-7/components/data.tsx | 187 ++++++++ .../components/notifications-tab.tsx | 223 ++++++++++ .../components/profile-avatar-upload.tsx | 103 +++++ .../settings-7/components/profile-tab.tsx | 217 +++++++++ .../settings-7/components/security-tab.tsx | 257 +++++++++++ .../settings-7/components/setting-row.tsx | 62 +++ .../settings-7/components/settings-card.tsx | 59 +++ .../components/settings-field-group.tsx | 37 ++ .../blocks/settings-7/components/utils.ts | 22 + .../src/components/blocks/settings-7/page.tsx | 9 + .../dashboard/dashboard-kpi-grid.tsx | 74 +--- apps/web/src/components/examples/c-tabs-2.tsx | 129 ++++++ apps/web/src/components/kpi-stat-grid.tsx | 126 ++++++ apps/web/src/components/layout/app-shell.tsx | 2 +- .../components/modules/module-kpi-cards.tsx | 92 ++-- .../components/network/network-peers-card.tsx | 6 +- .../operations/operations-jobs-card.tsx | 6 +- apps/web/src/components/patterns/index.ts | 1 + apps/web/src/components/reui/alert.tsx | 92 ++++ .../schedule/schedule-jobs-card.tsx | 6 +- apps/web/src/components/section-cards.tsx | 129 ++---- .../settings/appearance-settings-tab.tsx | 68 +++ .../settings/connection-settings-tab.tsx | 115 +++++ .../settings/sections-settings-tab.tsx | 90 ++++ .../settings/session-settings-tab.tsx | 114 +++++ .../settings/settings-page-shell.tsx | 45 ++ .../settings/settings-tabs-data.tsx | 49 +++ apps/web/src/components/skeletons.tsx | 40 +- apps/web/src/hooks/use-file-upload.ts | 415 ++++++++++++++++++ apps/web/src/lib/ui-surface.ts | 25 +- apps/web/src/routes/_auth.tsx | 2 +- apps/web/src/routes/_auth/access.tsx | 25 +- apps/web/src/routes/_auth/directories.tsx | 19 +- apps/web/src/routes/_auth/schedule.tsx | 55 ++- apps/web/src/routes/_auth/settings.tsx | 224 ++-------- apps/web/tsconfig.tsbuildinfo | 2 +- packages/ui/src/components/avatar.tsx | 2 + packages/ui/src/components/field.tsx | 2 + packages/ui/src/components/input-group.tsx | 2 - packages/ui/src/components/select.tsx | 2 - packages/ui/src/components/separator.tsx | 2 + packages/ui/src/components/switch.tsx | 2 + packages/ui/src/components/tabs.tsx | 12 +- 48 files changed, 3019 insertions(+), 511 deletions(-) create mode 100644 .cursor/debug-ce85d7.log create mode 100644 apps/web/src/components/blocks/settings-7/components/account-settings.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/billing-tab.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/data.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/notifications-tab.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/profile-avatar-upload.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/profile-tab.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/security-tab.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/setting-row.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/settings-card.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/settings-field-group.tsx create mode 100644 apps/web/src/components/blocks/settings-7/components/utils.ts create mode 100644 apps/web/src/components/blocks/settings-7/page.tsx create mode 100644 apps/web/src/components/examples/c-tabs-2.tsx create mode 100644 apps/web/src/components/kpi-stat-grid.tsx create mode 100644 apps/web/src/components/reui/alert.tsx create mode 100644 apps/web/src/components/settings/appearance-settings-tab.tsx create mode 100644 apps/web/src/components/settings/connection-settings-tab.tsx create mode 100644 apps/web/src/components/settings/sections-settings-tab.tsx create mode 100644 apps/web/src/components/settings/session-settings-tab.tsx create mode 100644 apps/web/src/components/settings/settings-page-shell.tsx create mode 100644 apps/web/src/components/settings/settings-tabs-data.tsx create mode 100644 apps/web/src/hooks/use-file-upload.ts diff --git a/.cursor/debug-ce85d7.log b/.cursor/debug-ce85d7.log new file mode 100644 index 0000000..95bd0a2 --- /dev/null +++ b/.cursor/debug-ce85d7.log @@ -0,0 +1,8 @@ +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":0,"jobs":0,"loading":true,"modulesError":false,"jobsError":false},"timestamp":1783613834659} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":4,"jobs":0,"loading":false,"modulesError":false,"jobsError":false},"timestamp":1783613834740} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":0,"jobs":0,"loading":true,"modulesError":false,"jobsError":false},"timestamp":1783613854903} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":4,"jobs":0,"loading":false,"modulesError":false,"jobsError":false},"timestamp":1783613854967} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":0,"jobs":0,"loading":true,"modulesError":false,"jobsError":false},"timestamp":1783613897698} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":4,"jobs":0,"loading":false,"modulesError":false,"jobsError":false},"timestamp":1783613897769} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":4,"jobs":0,"loading":false,"modulesError":false,"jobsError":false},"timestamp":1783614164222} +{"sessionId":"ce85d7","runId":"pre-fix","hypothesisId":"C","location":"schedule.tsx:mount","message":"schedule page data loaded","data":{"modules":4,"jobs":0,"loading":false,"modulesError":false,"jobsError":false},"timestamp":1783614181455} diff --git a/apps/web/src/components/analytics/analytics-kpi-row.tsx b/apps/web/src/components/analytics/analytics-kpi-row.tsx index 0a0d936..4a44722 100644 --- a/apps/web/src/components/analytics/analytics-kpi-row.tsx +++ b/apps/web/src/components/analytics/analytics-kpi-row.tsx @@ -1,5 +1,5 @@ -import { Minus, TrendingDown, TrendingUp } from 'lucide-react' - +import { KpiStatGrid, type KpiStatItem } from '@/components/kpi-stat-grid' +import { Badge } from '@/components/reui/badge' import { cn } from '@evobgp/ui/lib/utils' export type AnalyticsKpiItem = { @@ -12,39 +12,42 @@ export type AnalyticsKpiItem = { } } -const TONE_CLASS = { - success: 'text-success', - warning: 'text-warning', - destructive: 'text-destructive', - muted: 'text-muted-foreground', -} as const - -function DeltaIcon({ direction }: { direction: AnalyticsKpiItem['delta'] extends infer D ? D extends { direction: infer Dir } ? Dir : never : never }) { - if (direction === 'up') return - if (direction === 'down') return - return +function deltaBadgeVariant( + tone?: 'success' | 'warning' | 'destructive' | 'muted', +): 'success-light' | 'warning-light' | 'destructive-light' | 'outline' { + if (tone === 'success') return 'success-light' + if (tone === 'warning') return 'warning-light' + if (tone === 'destructive') return 'destructive-light' + return 'outline' } -export function AnalyticsKpiRow({ items, className }: { items: AnalyticsKpiItem[]; className?: string }) { +function toKpiItem(item: AnalyticsKpiItem): KpiStatItem { + return { + id: item.label, + value: item.value, + label: item.label, + footer: item.delta ? ( + + {item.delta.label} + + ) : undefined, + } +} + +/** Compact KPI row inside analytics panels (stats-12 embedded tiles). */ +export function AnalyticsKpiRow({ + items, + className, +}: { + items: AnalyticsKpiItem[] + className?: string +}) { return ( -
- {items.map((item) => ( -
-

{item.label}

-

{item.value}

- {item.delta ? ( -

- - {item.delta.label} -

- ) : null} -
- ))} -
+ ) } diff --git a/apps/web/src/components/badge-tabs.tsx b/apps/web/src/components/badge-tabs.tsx index 208ef0e..4a6f673 100644 --- a/apps/web/src/components/badge-tabs.tsx +++ b/apps/web/src/components/badge-tabs.tsx @@ -40,14 +40,12 @@ export function BadgeTabs({ value={value} defaultValue={defaultValue} onValueChange={onValueChange} + orientation="horizontal" className={cn('w-full', className)} > {items.map((item) => ( diff --git a/apps/web/src/components/blocks/settings-7/components/account-settings.tsx b/apps/web/src/components/blocks/settings-7/components/account-settings.tsx new file mode 100644 index 0000000..83c2039 --- /dev/null +++ b/apps/web/src/components/blocks/settings-7/components/account-settings.tsx @@ -0,0 +1,116 @@ +"use client" + +import { useState, type ComponentType } from "react" + +import { useIsMobile } from "@/hooks/use-mobile" +import { cn } from "@evobgp/ui/lib/utils" +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@evobgp/ui/components/tabs" + +import { BillingTab } from "./billing-tab" +import { SETTINGS_TAB_ITEMS } from "./data" +import { NotificationsTab } from "./notifications-tab" +import { ProfileTab } from "./profile-tab" +import { SecurityTab } from "./security-tab" + +const TAB_COMPONENTS: Record = { + profile: ProfileTab, + security: SecurityTab, + notifications: NotificationsTab, + billing: BillingTab, +} + +// ── Settings Navigation ── + +function SettingsNavigation({ + isMobile, + activeValue, +}: { + isMobile: boolean + activeValue: string +}) { + return ( +
+ {isMobile ? ( +
+ + {SETTINGS_TAB_ITEMS.map((tab) => ( + + {tab.icon} + {tab.label} + + ))} + +
+ ) : ( + + {SETTINGS_TAB_ITEMS.map((tab) => ( + + {tab.icon} + {tab.label} + + ))} + + )} +
+ ) +} + +export function AccountSettings() { + const isMobile = useIsMobile() + const [activeTab, setActiveTab] = useState("profile") + + return ( +
+ {/* Header */} +
+

+ Account Settings +

+

+ Update your profile, access, notifications, and billing preferences. +

+
+ + {/* Tabs */} + + + +
+ {SETTINGS_TAB_ITEMS.map((tab) => { + const TabComponent = TAB_COMPONENTS[tab.value] + + return ( + + + + ) + })} +
+
+
+ ) +} \ No newline at end of file diff --git a/apps/web/src/components/blocks/settings-7/components/billing-tab.tsx b/apps/web/src/components/blocks/settings-7/components/billing-tab.tsx new file mode 100644 index 0000000..604dfa8 --- /dev/null +++ b/apps/web/src/components/blocks/settings-7/components/billing-tab.tsx @@ -0,0 +1,180 @@ +import { useState } from "react" +import { Badge } from "@/components/reui/badge" + +import { Button } from "@evobgp/ui/components/button" +import { + Field, + FieldDescription, + FieldGroup, + FieldLabel, + FieldLegend, + FieldSet, +} from "@evobgp/ui/components/field" +import { Input } from "@evobgp/ui/components/input" +import { + InputGroup, + InputGroupAddon, + InputGroupInput, + InputGroupText, +} from "@evobgp/ui/components/input-group" +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@evobgp/ui/components/select" + +import { BILLING_PLANS, COUNTRIES } from "./data" +import { SettingRow } from "./setting-row" +import { SettingsCard } from "./settings-card" +import { SettingsFieldGroup } from "./settings-field-group" +import { createSelectValueHandler, getOptionLabel } from "./utils" + +export function BillingTab() { + const [country, setCountry] = useState("us") + const handleCountryChange = createSelectValueHandler(setCountry) + + return ( +
+ {/* Card */} + + + {BILLING_PLANS.map((plan, index) => ( + + Current + + ) : null + } + description={ + <> + + {plan.price} + {" "} + / {plan.period} · {plan.features.join(", ")} + + } + last={index === BILLING_PLANS.length - 1} + > + + + ))} + + + + Update billing} + > + + + + + + +
+ Invoice profile + + Company identity used for invoices and tax documents. + + + + + + Company name + + + + + + + Country + + + + +
+
+ + + + + VAT + + + + + + +
+ + Visa ending in 4242 + + +
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/apps/web/src/components/blocks/settings-7/components/data.tsx b/apps/web/src/components/blocks/settings-7/components/data.tsx new file mode 100644 index 0000000..f2ef237 --- /dev/null +++ b/apps/web/src/components/blocks/settings-7/components/data.tsx @@ -0,0 +1,187 @@ +"use client" + +import { type ReactNode } from "react" +import { UserIcon, ShieldIcon, BellIcon, CreditCardIcon, MonitorIcon, SmartphoneIcon } from "lucide-react" + +// ── Types ── + +export type SelectOption = { + value: string + label: string +} + +export type BillingPlan = { + id: string + name: string + price: string + period: string + current: boolean + features: string[] +} + +export type Session = { + id: string + device: string + browser: string + location: string + lastActive: string + current: boolean + icon: ReactNode +} + +export type SettingsTabItem = { + value: string + label: string + icon: ReactNode +} + +// ── Data ── + +export const SETTINGS_TAB_ITEMS: SettingsTabItem[] = [ + { + value: "profile", + label: "My Profile", + icon: ( +