From 1a1b4972afbd2ff1ac9d687da5fb652e76ada71a Mon Sep 17 00:00:00 2001 From: senke Date: Fri, 6 Feb 2026 21:59:42 +0100 Subject: [PATCH] refactor(web): split SettingsView into settings-view module Co-authored-by: Cursor --- .../components/views/SettingsView.stories.tsx | 77 ++++---- .../web/src/components/views/SettingsView.tsx | 168 +----------------- .../views/settings-view/SettingsView.tsx | 29 +++ .../settings-view/SettingsViewContent.tsx | 54 ++++++ .../settings-view/SettingsViewHeader.tsx | 12 ++ .../settings-view/SettingsViewSkeleton.tsx | 25 +++ .../views/settings-view/SettingsViewTabs.tsx | 36 ++++ .../components/views/settings-view/index.ts | 4 + .../components/views/settings-view/types.ts | 23 +++ .../views/settings-view/useSettingsView.tsx | 43 +++++ 10 files changed, 272 insertions(+), 199 deletions(-) create mode 100644 apps/web/src/components/views/settings-view/SettingsView.tsx create mode 100644 apps/web/src/components/views/settings-view/SettingsViewContent.tsx create mode 100644 apps/web/src/components/views/settings-view/SettingsViewHeader.tsx create mode 100644 apps/web/src/components/views/settings-view/SettingsViewSkeleton.tsx create mode 100644 apps/web/src/components/views/settings-view/SettingsViewTabs.tsx create mode 100644 apps/web/src/components/views/settings-view/index.ts create mode 100644 apps/web/src/components/views/settings-view/types.ts create mode 100644 apps/web/src/components/views/settings-view/useSettingsView.tsx diff --git a/apps/web/src/components/views/SettingsView.stories.tsx b/apps/web/src/components/views/SettingsView.stories.tsx index 16a97f29d..88d4ce841 100644 --- a/apps/web/src/components/views/SettingsView.stories.tsx +++ b/apps/web/src/components/views/SettingsView.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { SettingsView } from './SettingsView'; +import { SettingsView, SettingsViewSkeleton } from './settings-view'; /** * SettingsView - Vue principale des paramètres @@ -8,42 +8,55 @@ import { SettingsView } from './SettingsView'; * sections de paramètres (profil, compte, apparence, sécurité, etc.) */ const meta: Meta = { - title: 'Components/Features/Views/SettingsView', - component: SettingsView, - parameters: { - layout: 'fullscreen', - docs: { - description: { - component: 'Vue avec navigation par onglets vers toutes les sections de paramètres.', - }, - }, + title: 'Components/Features/Views/SettingsView', + component: SettingsView, + parameters: { + layout: 'fullscreen', + docs: { + description: { + component: + 'Vue avec navigation par onglets vers toutes les sections de paramètres.', + }, }, - tags: ['autodocs'], - argTypes: { - initialTab: { - control: 'select', - options: ['profile', 'account', 'appearance', 'accessibility', 'security', 'integrations', 'cloud', 'backups', 'data', 'audio', 'notifications'], - description: 'Onglet à afficher par défaut', - }, + }, + tags: ['autodocs'], + argTypes: { + initialTab: { + control: 'select', + options: [ + 'profile', + 'account', + 'appearance', + 'accessibility', + 'security', + 'integrations', + 'cloud', + 'backups', + 'data', + 'audio', + 'notifications', + ], + description: 'Onglet à afficher par défaut', }, - decorators: [ - (Story) => ( -
- -
- ), - ], + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], }; export default meta; type Story = StoryObj; -/** - * État par défaut avec onglet Profile. - */ -export const Default: Story = { - name: 'Par défaut (Profile)', - args: { - initialTab: 'profile', - }, +export const Loading: Story = { + name: 'Loading', + render: () => , +}; + +export const Default: Story = { + name: 'Par défaut (Profile)', + args: { initialTab: 'profile' }, }; diff --git a/apps/web/src/components/views/SettingsView.tsx b/apps/web/src/components/views/SettingsView.tsx index 3a9f1ab45..e08904eff 100644 --- a/apps/web/src/components/views/SettingsView.tsx +++ b/apps/web/src/components/views/SettingsView.tsx @@ -1,167 +1 @@ -import React, { useState, useEffect } from 'react'; -import { Card } from '../ui/card'; -import { Tabs, TabsList, TabsTrigger } from '../ui/tabs'; - -import { useToast } from '../../components/feedback/ToastProvider'; -import { - User, - Bell, - Palette, - Shield, - Volume2, - UserCog, - Cloud, - Database, - Accessibility, - Plug, - HardDrive, -} from 'lucide-react'; -import { SecuritySettings } from '../settings/security/SecuritySettings'; -import { EditProfile } from '../settings/profile/EditProfile'; -import { AccountSettings } from '../settings/account/AccountSettings'; -import { CloudIntegrationView } from '../settings/cloud/CloudIntegrationView'; -import { BackupsView } from '../settings/backups/BackupsView'; -import { AppearanceSettingsView } from '../settings/appearance/AppearanceSettingsView'; -import { AccessibilitySettingsView } from '../settings/accessibility/AccessibilitySettingsView'; -import { IntegrationsView } from '../settings/integrations/IntegrationsView'; -import { DataExportView } from '../settings/data/DataExportView'; - -interface SettingsViewProps { - initialTab?: string; -} - -export const SettingsView: React.FC = ({ - initialTab = 'profile', -}) => { - const { addToast: _addToast } = useToast(); - const [activeTab, setActiveTab] = useState(initialTab); - - // Sync active tab if initialTab changes - useEffect(() => { - if (initialTab) setActiveTab(initialTab); - }, [initialTab]); - - const settingsTabs = [ - { id: 'profile', label: 'Profile', icon: }, - { id: 'account', label: 'Account', icon: }, - { - id: 'appearance', - label: 'Appearance', - icon: , - }, - { - id: 'accessibility', - label: 'Accessibility', - icon: , - }, - { id: 'security', label: 'Security', icon: }, - { - id: 'integrations', - label: 'Integrations', - icon: , - }, - { id: 'cloud', label: 'Cloud & Sync', icon: }, - { - id: 'backups', - label: 'Backups', - icon: , - }, - { - id: 'data', - label: 'Privacy & Data', - icon: , - }, - { id: 'audio', label: 'Audio', icon: }, - { - id: 'notifications', - label: 'Notifications', - icon: , - }, - ]; - - return ( -
- {/* Header */} -
-

- SETTINGS -

-

- Configure your studio, account, and preferences. -

-
- - {/* Top Navigation using new Tabs component */} -
- - - {settingsTabs.map((tab) => ( - - - {tab.icon} - {tab.label} - - - ))} - - -
- - {/* Content Area */} -
- {/* PROFILE TAB (Phase 3) */} - {activeTab === 'profile' && } - - {/* ACCOUNT TAB (Phase 4) */} - {activeTab === 'account' && } - - {/* APPEARANCE TAB (Phase 21) */} - {activeTab === 'appearance' && } - - {/* ACCESSIBILITY TAB (Phase 21) */} - {activeTab === 'accessibility' && } - - {/* SECURITY TAB (Phase 2) */} - {activeTab === 'security' && } - - {/* INTEGRATIONS TAB (Phase 23) */} - {activeTab === 'integrations' && } - - {/* CLOUD TAB (Phase 18) */} - {activeTab === 'cloud' && } - - {/* BACKUPS TAB (Phase 18) */} - {activeTab === 'backups' && } - - {/* DATA EXPORT TAB (Phase 23) */} - {activeTab === 'data' && } - - {/* Placeholder for Audio & Notifications */} - {['audio', 'notifications'].includes(activeTab) && ( - -
-
- {activeTab === 'audio' ? ( - - ) : ( - - )} -
-

- {activeTab} Settings -

-

- Advanced configurations for {activeTab} will be available in the - next system update (v2.1). -

-
-
- )} -
-
- ); -}; +export { SettingsView } from './settings-view'; diff --git a/apps/web/src/components/views/settings-view/SettingsView.tsx b/apps/web/src/components/views/settings-view/SettingsView.tsx new file mode 100644 index 000000000..59badb783 --- /dev/null +++ b/apps/web/src/components/views/settings-view/SettingsView.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { useSettingsView } from './useSettingsView'; +import { SettingsViewHeader } from './SettingsViewHeader'; +import { SettingsViewTabs } from './SettingsViewTabs'; +import { SettingsViewContent } from './SettingsViewContent'; +import type { SettingsViewProps } from './types'; +import type { SettingsTabId } from './types'; + +export function SettingsView({ initialTab = 'profile' }: SettingsViewProps = {}) { + const { activeTab, setActiveTab, tabs } = useSettingsView( + (initialTab as SettingsTabId) ?? 'profile', + ); + + return ( +
+ + + setActiveTab(v as SettingsTabId)} + tabs={tabs} + /> + +
+ +
+
+ ); +} diff --git a/apps/web/src/components/views/settings-view/SettingsViewContent.tsx b/apps/web/src/components/views/settings-view/SettingsViewContent.tsx new file mode 100644 index 000000000..24b69453d --- /dev/null +++ b/apps/web/src/components/views/settings-view/SettingsViewContent.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { Card } from '@/components/ui/card'; +import { Volume2, Bell } from 'lucide-react'; +import { SecuritySettings } from '@/components/settings/security/SecuritySettings'; +import { EditProfile } from '@/components/settings/profile/EditProfile'; +import { AccountSettings } from '@/components/settings/account/AccountSettings'; +import { CloudIntegrationView } from '@/components/settings/cloud/CloudIntegrationView'; +import { BackupsView } from '@/components/settings/backups/BackupsView'; +import { AppearanceSettingsView } from '@/components/settings/appearance/AppearanceSettingsView'; +import { AccessibilitySettingsView } from '@/components/settings/accessibility/AccessibilitySettingsView'; +import { IntegrationsView } from '@/components/settings/integrations/IntegrationsView'; +import { DataExportView } from '@/components/settings/data/DataExportView'; +import type { SettingsTabId } from './types'; + +interface SettingsViewContentProps { + activeTab: SettingsTabId; +} + +export function SettingsViewContent({ activeTab }: SettingsViewContentProps) { + if (activeTab === 'profile') return ; + if (activeTab === 'account') return ; + if (activeTab === 'appearance') return ; + if (activeTab === 'accessibility') return ; + if (activeTab === 'security') return ; + if (activeTab === 'integrations') return ; + if (activeTab === 'cloud') return ; + if (activeTab === 'backups') return ; + if (activeTab === 'data') return ; + + if (activeTab === 'audio' || activeTab === 'notifications') { + return ( + +
+
+ {activeTab === 'audio' ? ( + + ) : ( + + )} +
+

+ {activeTab} Settings +

+

+ Advanced configurations for {activeTab} will be available in the next + system update (v2.1). +

+
+
+ ); + } + + return null; +} diff --git a/apps/web/src/components/views/settings-view/SettingsViewHeader.tsx b/apps/web/src/components/views/settings-view/SettingsViewHeader.tsx new file mode 100644 index 000000000..f69db1187 --- /dev/null +++ b/apps/web/src/components/views/settings-view/SettingsViewHeader.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export function SettingsViewHeader() { + return ( +
+

SETTINGS

+

+ Configure your studio, account, and preferences. +

+
+ ); +} diff --git a/apps/web/src/components/views/settings-view/SettingsViewSkeleton.tsx b/apps/web/src/components/views/settings-view/SettingsViewSkeleton.tsx new file mode 100644 index 000000000..0c38ccbdb --- /dev/null +++ b/apps/web/src/components/views/settings-view/SettingsViewSkeleton.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Skeleton } from '@/components/ui/skeleton'; + +export function SettingsViewSkeleton() { + return ( +
+
+ + +
+ +
+
+ {[1, 2, 3, 4, 5, 6].map((i) => ( + + ))} +
+
+ +
+ +
+
+ ); +} diff --git a/apps/web/src/components/views/settings-view/SettingsViewTabs.tsx b/apps/web/src/components/views/settings-view/SettingsViewTabs.tsx new file mode 100644 index 000000000..5f61a7efd --- /dev/null +++ b/apps/web/src/components/views/settings-view/SettingsViewTabs.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import type { SettingsTabId, SettingsTabConfig } from './types'; + +interface SettingsViewTabsProps { + activeTab: SettingsTabId; + onTabChange: (value: string) => void; + tabs: SettingsTabConfig[]; +} + +export function SettingsViewTabs({ + activeTab, + onTabChange, + tabs, +}: SettingsViewTabsProps) { + return ( +
+ + + {tabs.map((tab) => ( + + + {tab.icon} + {tab.label} + + + ))} + + +
+ ); +} diff --git a/apps/web/src/components/views/settings-view/index.ts b/apps/web/src/components/views/settings-view/index.ts new file mode 100644 index 000000000..2f70b25a8 --- /dev/null +++ b/apps/web/src/components/views/settings-view/index.ts @@ -0,0 +1,4 @@ +export type { SettingsViewProps, SettingsTabId, SettingsTabConfig } from './types'; +export { SettingsView } from './SettingsView'; +export { SettingsViewSkeleton } from './SettingsViewSkeleton'; +export { useSettingsView } from './useSettingsView'; diff --git a/apps/web/src/components/views/settings-view/types.ts b/apps/web/src/components/views/settings-view/types.ts new file mode 100644 index 000000000..5129fdf4c --- /dev/null +++ b/apps/web/src/components/views/settings-view/types.ts @@ -0,0 +1,23 @@ +export type SettingsTabId = + | 'profile' + | 'account' + | 'appearance' + | 'accessibility' + | 'security' + | 'integrations' + | 'cloud' + | 'backups' + | 'data' + | 'audio' + | 'notifications'; + +export interface SettingsViewProps { + initialTab?: SettingsTabId; +} + +export interface SettingsTabConfig { + id: SettingsTabId; + label: string; + icon: React.ReactNode; +} + diff --git a/apps/web/src/components/views/settings-view/useSettingsView.tsx b/apps/web/src/components/views/settings-view/useSettingsView.tsx new file mode 100644 index 000000000..e58be581e --- /dev/null +++ b/apps/web/src/components/views/settings-view/useSettingsView.tsx @@ -0,0 +1,43 @@ +import { useState, useEffect } from 'react'; +import { + User, + Bell, + Palette, + Shield, + Volume2, + UserCog, + Cloud, + Database, + Accessibility, + Plug, + HardDrive, +} from 'lucide-react'; +import type { SettingsTabId, SettingsTabConfig } from './types'; + +const SETTINGS_TABS: SettingsTabConfig[] = [ + { id: 'profile', label: 'Profile', icon: }, + { id: 'account', label: 'Account', icon: }, + { id: 'appearance', label: 'Appearance', icon: }, + { id: 'accessibility', label: 'Accessibility', icon: }, + { id: 'security', label: 'Security', icon: }, + { id: 'integrations', label: 'Integrations', icon: }, + { id: 'cloud', label: 'Cloud & Sync', icon: }, + { id: 'backups', label: 'Backups', icon: }, + { id: 'data', label: 'Privacy & Data', icon: }, + { id: 'audio', label: 'Audio', icon: }, + { id: 'notifications', label: 'Notifications', icon: }, +]; + +export function useSettingsView(initialTab: SettingsTabId = 'profile') { + const [activeTab, setActiveTab] = useState(initialTab); + + useEffect(() => { + if (initialTab) setActiveTab(initialTab); + }, [initialTab]); + + return { + activeTab, + setActiveTab, + tabs: SETTINGS_TABS, + }; +}