chore(dx): add .cursorrules and design system audit documentation

This commit is contained in:
senke 2026-02-05 14:20:06 +01:00
parent ae9ced3ee9
commit c8b640263d
257 changed files with 1048 additions and 628 deletions

View file

@ -0,0 +1,48 @@
import React from 'react';
import { ThemeProvider } from '../src/components/theme/ThemeProvider';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { ToastProvider } from '../src/components/feedback/ToastProvider';
import { AudioProvider } from '../src/context/AudioContext';
import { AuthProvider } from '../src/context/AuthContext';
// Create a singleton query client for Storybook to share cache if needed,
// or one per story. Since decorators run per story, creating it here might
// share it. Better to create inside if we want isolation, but for performance
// reuse is okay.
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: Infinity,
// Phase 1: Silence network errors in react-query
throwOnError: false,
},
},
});
interface AppProvidersProps {
children: React.ReactNode;
isDark?: boolean;
}
export const AppProvidersForStorybook: React.FC<AppProvidersProps> = ({ children, isDark = true }) => {
return (
<ThemeProvider defaultTheme={isDark ? 'dark' : 'light'}>
<div className={isDark ? 'dark' : ''} style={{ minHeight: '100vh', padding: '1rem', background: isDark ? '#0a0a0a' : '#ffffff', color: isDark ? '#ffffff' : '#0a0a0a' }}>
<QueryClientProvider client={queryClient}>
<ToastProvider>
<AudioProvider>
<AuthProvider>
<MemoryRouter>
{children}
</MemoryRouter>
</AuthProvider>
</AudioProvider>
</ToastProvider>
</QueryClientProvider>
</div>
</ThemeProvider>
);
};

View file

@ -0,0 +1,48 @@
# Storybook Audit Report
## Summary
- **Total Stories**: 963
- **Failed Stories**: 310 (~32% failure rate)
- **Total Errors**: 1,040 (Network + Console)
## Failure Categories
### 1. Unmocked Network Requests (Critical)
**Count**: 463 Network Failures
**Diagnosis**: Stories are attempting to hit real production endpoints (`api.veza.com`) instead of using mocks.
- `ERR_NAME_NOT_RESOLVED`: `https://api.veza.com/api/v1/api/v1/logs/frontend`
- `ERR_ABORTED`: `https://api.veza.com/api/v1/auth/me`
- `ERR_ABORTED`: `https://picsum.photos/200` (External images)
**Impact**: Components depending on data stay in "Loading" state indefinitely or crash when data is missing.
### 2. Missing Context Providers
**Count**: ~15-20 identified instances (estimated from sample)
**Diagnosis**: Components accessing global state that isn't provided in the story decorator.
- `Error: useAuth must be used within AuthProvider`
- `TypeError: Cannot read properties of undefined (reading 'name')` (Likely missing user object from auth context)
### 3. Runtime Crashes
- `TypeError: Cannot read properties of undefined`: Cascading failures from missing API data.
## Remediation Plan
### Step 1: Configure Mock Service Worker (MSW)
- [ ] Initialize MSW in Storybook.
- [ ] Create `handlers.ts` to mock core endpoints:
- `/auth/me` (User profile)
- `/api/v1/logs/frontend` (Prevent logging noise)
- Domain specific endpoints for failing stories.
### Step 2: Global Decorators
- [ ] Wrap all stories in `preview.tsx` with:
- `AuthProvider` (Mocked)
- `QueryClientProvider` (React Query)
- `MemoryRouter` (already likely present but need verification)
### Step 3: Fix Specific Issues
- [ ] Fix double-path URL bug (`/api/v1/api/v1/...`)
- [ ] Mock external image services or provide local fallbacks.
## Priority
High. 30% of the component library is broken in documentation, making it unreliable for development.

View file

@ -7,7 +7,7 @@
* - Please do NOT modify this file.
*/
const PACKAGE_VERSION = '2.12.3'
const PACKAGE_VERSION = '2.12.7'
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()

View file

@ -0,0 +1,53 @@
import fs from 'fs';
import path from 'path';
// Fix type: "module" issue by using readFileSync if running as module or just basic node
// but easier to just use standard require if not module, or import fs if module.
// Package.json says type: module, so imports are fine.
try {
const errorData = JSON.parse(fs.readFileSync('storybook_errors.json', 'utf8'));
const totalFailed = Object.keys(errorData).length;
const stats = {
totalFailed,
byType: {},
commonMessages: {}
};
for (const [storyId, errors] of Object.entries(errorData)) {
for (const error of errors) {
let type = 'Unknown';
if (error.startsWith('Console:')) type = 'Console';
if (error.startsWith('PageError:')) type = 'PageError';
if (error.startsWith('NetworkFail:')) type = 'NetworkFail';
if (error.startsWith('Navigation:')) type = 'Navigation';
stats.byType[type] = (stats.byType[type] || 0) + 1;
// Extract core message for grouping
const message = error.substring(error.indexOf(':') + 1).trim();
// Truncate simple variable parts
const cleanMessage = message.split(' at ')[0].substring(0, 100);
stats.commonMessages[cleanMessage] = (stats.commonMessages[cleanMessage] || 0) + 1;
}
}
console.log('--- Error Summary ---');
console.log(`Total Stories with Errors: ${stats.totalFailed}`);
console.log('\nErrors by Type:');
console.table(stats.byType);
console.log('\nTop 10 Common Error Messages:');
const sortedMessages = Object.entries(stats.commonMessages)
.sort((a, b) => b[1] - a[1])
.slice(0, 10);
sortedMessages.forEach(([msg, count]) => {
console.log(`${count}x: ${msg}`);
});
} catch (e) {
console.error('Failed to parse errors:', e);
}

View file

@ -0,0 +1,33 @@
import { chromium } from 'playwright';
async function debugStory(storyId) {
const browser = await chromium.launch();
const page = await browser.newPage();
const storyUrl = `http://localhost:6007/iframe.html?id=${storyId}&viewMode=story`;
console.log(`Navigating to ${storyUrl}`);
const errors = [];
page.on('pageerror', err => errors.push(`PageError: ${err.message}`));
page.on('console', msg => {
if (msg.type() === 'error') errors.push(`ConsoleError: ${msg.text()}`);
});
page.on('requestfailed', request => {
errors.push(`NetworkFail: ${request.failure().errorText} ${request.url()}`);
});
try {
await page.goto(storyUrl, { waitUntil: 'networkidle' });
// Wait a bit for async errors
await page.waitForTimeout(2000);
} catch (e) {
errors.push(`NavigationError: ${e.message}`);
}
console.log('Errors found:', JSON.stringify(errors, null, 2));
await browser.close();
}
const storyId = process.argv[2] || 'components-admin-adminauditlogsview--default';
debugStory(storyId);

View file

@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""
Script to wrap MSW handler responses in the correct API format.
Converts: HttpResponse.json({ data })
To: HttpResponse.json({ success: true, data: { data } })
"""
import re
import sys
def wrap_handler_response(content):
"""
Find and wrap MSW handler responses that aren't already wrapped.
"""
# Pattern to match http.get/post/etc handlers
# This matches: http.METHOD('pattern', (...) => { return HttpResponse.json({ ... }); })
lines = content.split('\n')
result = []
i = 0
while i < len(lines):
line = lines[i]
# Check if this is a handler definition
if re.match(r'\s*http\.(get|post|put|delete|patch|all)\(', line):
# Find the return HttpResponse.json statement
handler_start = i
brace_count = 0
in_handler = False
for j in range(i, min(i + 100, len(lines))): # Look ahead up to 100 lines
current_line = lines[j]
# Track braces to know when we're inside the handler
brace_count += current_line.count('{') - current_line.count('}')
# Look for HttpResponse.json(
if 'HttpResponse.json(' in current_line:
# Check if already wrapped (has 'success: true')
# Look ahead a few lines to see if success: true exists
is_wrapped = False
for k in range(j, min(j + 5, len(lines))):
if 'success:' in lines[k] or 'success :' in lines[k]:
is_wrapped = True
break
if not is_wrapped and 'message:' not in current_line:
# This needs wrapping
# Find the opening brace after HttpResponse.json(
indent = len(current_line) - len(current_line.lstrip())
# Add the wrapper
result.append(current_line.replace(
'HttpResponse.json({',
'HttpResponse.json({\n' + ' ' * (indent + 2) + 'success: true,\n' + ' ' * (indent + 2) + 'data: {'
))
# Now we need to find the closing brace and add an extra }
json_brace_count = 1
for k in range(j + 1, len(lines)):
check_line = lines[k]
json_brace_count += check_line.count('{') - check_line.count('}')
if json_brace_count == 0:
# This is the closing brace
# Add extra closing brace before this one
result.append(' ' * (indent + 2) + '}')
result.append(check_line)
i = k + 1
break
else:
result.append(check_line)
break
else:
# Already wrapped or special case, just copy
result.append(current_line)
i = j + 1
break
elif j == i + 99 or (j > i and brace_count == 0):
# Didn't find HttpResponse.json, just copy the line
result.append(line)
i += 1
break
else:
result.append(line)
i += 1
return '\n'.join(result)
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: python wrap_msw_handlers.py <handlers_file>")
sys.exit(1)
filepath = sys.argv[1]
with open(filepath, 'r') as f:
content = f.read()
wrapped_content = wrap_handler_response(content)
with open(filepath, 'w') as f:
f.write(wrapped_content)
print(f"✅ Wrapped MSW handlers in {filepath}")

View file

@ -8,7 +8,7 @@ import { AdminAuditLogsView } from './AdminAuditLogsView';
* filtrage et détails contextuels.
*/
const meta: Meta<typeof AdminAuditLogsView> = {
title: 'Components/Admin/AdminAuditLogsView',
title: 'Components/Features/Admin/AdminAuditLogsView',
component: AdminAuditLogsView,
parameters: {
layout: 'fullscreen',

View file

@ -9,7 +9,7 @@ import { ToastProvider } from '../../components/feedback/ToastProvider';
* visualisation du trafic, queue de modération et logs système.
*/
const meta: Meta<typeof AdminDashboardView> = {
title: 'Components/Admin/AdminDashboardView',
title: 'Components/Features/Admin/AdminDashboardView',
component: AdminDashboardView,
parameters: {
layout: 'fullscreen',

View file

@ -9,7 +9,7 @@ import { ToastProvider } from '../../components/feedback/ToastProvider';
* pour pending/reviewed/resolved et actions de modération.
*/
const meta: Meta<typeof AdminModerationView> = {
title: 'Components/Admin/AdminModerationView',
title: 'Components/Features/Admin/AdminModerationView',
component: AdminModerationView,
parameters: {
layout: 'fullscreen',

View file

@ -8,7 +8,7 @@ import { AdminSettingsView } from './AdminSettingsView';
* mode maintenance et annonces globales.
*/
const meta: Meta<typeof AdminSettingsView> = {
title: 'Components/Admin/AdminSettingsView',
title: 'Components/Features/Admin/AdminSettingsView',
component: AdminSettingsView,
parameters: {
layout: 'padded',

View file

@ -8,7 +8,7 @@ import { AdminUsersView } from './AdminUsersView';
* et actions de modération (ban, suppression, rôles).
*/
const meta: Meta<typeof AdminUsersView> = {
title: 'Components/Admin/AdminUsersView',
title: 'Components/Features/Admin/AdminUsersView',
component: AdminUsersView,
parameters: {
layout: 'fullscreen',

View file

@ -25,7 +25,7 @@ const createMockUser = (overrides: Partial<User> = {}): User => ({
* avec menu d'actions contextuel.
*/
const meta: Meta<typeof UserTableRow> = {
title: 'Components/Admin/UserTableRow',
title: 'Components/Features/Admin/UserTableRow',
component: UserTableRow,
parameters: {
docs: {

View file

@ -9,7 +9,7 @@ import { BanUserModal } from './BanUserModal';
* d'un utilisateur avec raison, durée et notes internes.
*/
const meta: Meta<typeof BanUserModal> = {
title: 'Components/Admin/Modals/BanUserModal',
title: 'Components/Features/Admin/Modals/BanUserModal',
component: BanUserModal,
parameters: {
layout: 'centered',

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { TrackAnalyticsView } from './TrackAnalyticsView';
const meta: Meta<typeof TrackAnalyticsView> = {
title: 'Components/Analytics/TrackAnalyticsView',
title: 'Components/Features/Analytics/TrackAnalyticsView',
component: TrackAnalyticsView,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -37,7 +37,7 @@ const MOCK_ITEM = {
};
const meta: Meta<typeof CartItem> = {
title: 'Components/Commerce/CartItem',
title: 'Components/Features/Commerce/CartItem',
component: CartItem,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { OrderSummary } from './OrderSummary';
const meta: Meta<typeof OrderSummary> = {
title: 'Components/Commerce/OrderSummary',
title: 'Components/Features/Commerce/OrderSummary',
component: OrderSummary,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -4,7 +4,7 @@ import { WishlistView } from './WishlistView';
import { ToastProvider } from '../../components/feedback/ToastProvider';
const meta: Meta<typeof WishlistView> = {
title: 'Components/Commerce/WishlistView',
title: 'Components/Features/Commerce/WishlistView',
component: WishlistView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ActivityGraph } from './ActivityGraph';
const meta = {
title: 'Components/Dashboard/ActivityGraph',
title: 'Components/Features/Dashboard/ActivityGraph',
component: ActivityGraph,
tags: ['autodocs'],
decorators: [

View file

@ -3,7 +3,7 @@ import { StatCard } from './StatCard';
import { Activity, Music, Users, DollarSign } from 'lucide-react';
const meta = {
title: 'Components/Dashboard/StatCard',
title: 'Components/Features/Dashboard/StatCard',
component: StatCard,
tags: ['autodocs'],
decorators: [

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { TrackList } from './TrackList';
const meta = {
title: 'Components/Dashboard/TrackList',
title: 'Components/Features/Dashboard/TrackList',
component: TrackList,
tags: ['autodocs'],
} satisfies Meta<typeof TrackList>;

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { APIPlaygroundView } from './APIPlaygroundView';
const meta: Meta<typeof APIPlaygroundView> = {
title: 'Components/Developer/APIPlaygroundView',
title: 'Components/Features/Developer/APIPlaygroundView',
component: APIPlaygroundView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -3,7 +3,7 @@ import { DeveloperDashboardView } from './DeveloperDashboardView';
import { ToastProvider } from '../../components/feedback/ToastProvider';
const meta: Meta<typeof DeveloperDashboardView> = {
title: 'Components/Developer/DeveloperDashboardView',
title: 'Components/Features/Developer/DeveloperDashboardView',
component: DeveloperDashboardView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { WebhooksView } from './WebhooksView';
const meta: Meta<typeof WebhooksView> = {
title: 'Components/Developer/WebhooksView',
title: 'Components/Features/Developer/WebhooksView',
component: WebhooksView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -3,7 +3,7 @@ import { CreateAPIKeyModal } from './CreateAPIKeyModal';
import { fn } from '@storybook/test';
const meta: Meta<typeof CreateAPIKeyModal> = {
title: 'Components/Developer/Modals/CreateAPIKeyModal',
title: 'Components/Features/Developer/Modals/CreateAPIKeyModal',
component: CreateAPIKeyModal,
parameters: { layout: 'centered' },
tags: ['autodocs'],

View file

@ -16,7 +16,7 @@ const mockCourse: Course = {
};
const meta = {
title: 'Components/Education/CourseCard',
title: 'Components/Features/Education/CourseCard',
component: CourseCard,
tags: ['autodocs'],
argTypes: {

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { CourseDetailView } from './CourseDetailView';
const meta: Meta<typeof CourseDetailView> = {
title: 'Components/Education/CourseDetailView',
title: 'Components/Features/Education/CourseDetailView',
component: CourseDetailView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { CourseLearningView } from './CourseLearningView';
const meta: Meta<typeof CourseLearningView> = {
title: 'Components/Education/CourseLearningView',
title: 'Components/Features/Education/CourseLearningView',
component: CourseLearningView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { MyCoursesView } from './MyCoursesView';
const meta = {
title: 'Components/Education/MyCoursesView',
title: 'Components/Features/Education/MyCoursesView',
component: MyCoursesView,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { CertificateModal } from './CertificateModal';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Education/CertificateModal',
title: 'Components/Features/Education/CertificateModal',
component: CertificateModal,
tags: ['autodocs'],
argTypes: {

View file

@ -20,7 +20,7 @@ const mockQuiz = {
};
const meta = {
title: 'Components/Education/QuizModal',
title: 'Components/Features/Education/QuizModal',
component: QuizModal,
tags: ['autodocs'],
argTypes: {

View file

@ -28,7 +28,11 @@ const mockFilters = {
value: '',
},
],
onFilterChange: () => { },
values: {
genre: '',
year: '',
},
onChange: () => { },
};
const mockSort = {
@ -38,12 +42,13 @@ const mockSort = {
{ label: 'Most Popular', value: 'popular' },
{ label: 'A-Z', value: 'alpha' },
],
value: 'newest',
onChange: () => { },
sortBy: 'newest',
sortOrder: 'desc' as const,
onSortChange: () => { },
};
const meta: Meta<typeof FilterBar> = {
title: 'Components/Filters/FilterBar',
title: 'Components/Features/Filters/FilterBar',
component: FilterBar,
parameters: {
layout: 'padded',
@ -67,6 +72,38 @@ export const FiltersOnly: Story = {
},
};
export const Locked: Story = {
name: 'Verrouillé',
args: {
achievement: {
id: '1',
name: 'Premier pas',
description: 'Complétez votre premier cours',
icon: '🏆',
progress: 0,
maxProgress: 1,
xpReward: 100,
unlockedAt: undefined,
},
},
};
export const Unlocked: Story = {
name: 'Déverrouillé',
args: {
achievement: {
id: '2',
name: 'Expert',
description: 'Complétez 10 cours',
icon: '👑',
progress: 10,
maxProgress: 10,
xpReward: 500,
unlockedAt: new Date().toISOString(),
},
},
};
export const SortOnly: Story = {
args: {
sort: mockSort,

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { AchievementCard } from './AchievementCard';
const meta: Meta<typeof AchievementCard> = {
title: 'Components/Gamification/AchievementCard',
title: 'Components/Features/Gamification/AchievementCard',
component: AchievementCard,
parameters: { layout: 'padded' },
tags: ['autodocs'],
@ -18,5 +18,34 @@ const meta: Meta<typeof AchievementCard> = {
export default meta;
type Story = StoryObj<typeof meta>;
export const Locked: Story = { name: 'Verrouillé' };
export const Unlocked: Story = { name: 'Déverrouillé' };
export const Locked: Story = {
name: 'Verrouillé',
args: {
achievement: {
id: '1',
name: 'Premier pas',
description: 'Complétez votre premier cours',
icon: '🏆',
progress: 0,
maxProgress: 1,
xpReward: 100,
unlockedAt: undefined,
},
},
};
export const Unlocked: Story = {
name: 'Déverrouillé',
args: {
achievement: {
id: '2',
name: 'Expert',
description: 'Complétez 10 cours',
icon: '👑',
progress: 10,
maxProgress: 10,
xpReward: 500,
unlockedAt: new Date().toISOString(),
},
},
};

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { AchievementsView } from './AchievementsView';
const meta: Meta<typeof AchievementsView> = {
title: 'Components/Gamification/AchievementsView',
title: 'Components/Features/Gamification/AchievementsView',
component: AchievementsView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { LeaderboardView } from './LeaderboardView';
const meta: Meta<typeof LeaderboardView> = {
title: 'Components/Gamification/LeaderboardView',
title: 'Components/Features/Gamification/LeaderboardView',
component: LeaderboardView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { XPBar } from './XPBar';
const meta: Meta<typeof XPBar> = {
title: 'Components/Gamification/XPBar',
title: 'Components/Features/Gamification/XPBar',
component: XPBar,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -18,7 +18,7 @@ const mockItem: GearItem = {
};
const meta = {
title: 'Components/Inventory/EquipmentCard',
title: 'Components/Features/Inventory/EquipmentCard',
component: EquipmentCard,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { EquipmentDetailView } from './EquipmentDetailView';
import { ToastProvider } from '../../components/feedback/ToastProvider';
const meta: Meta<typeof EquipmentDetailView> = {
title: 'Components/Inventory/EquipmentDetailView',
title: 'Components/Features/Inventory/EquipmentDetailView',
component: EquipmentDetailView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -3,7 +3,7 @@ import { InventoryView } from './InventoryView';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Inventory/InventoryView',
title: 'Components/Features/Inventory/InventoryView',
component: InventoryView,
tags: ['autodocs'],
argTypes: {

View file

@ -63,7 +63,7 @@ export const AudioPlayer: React.FC = () => {
{/* QUEUE DRAWER */}
{showQueue && !isImmersive && (
<div className="fixed bottom-24 right-4 w-full md:w-[400px] bg-kodo-graphite/95 backdrop-blur-xl border border-kodo-steel/50 rounded-2xl shadow-2xl z-40 overflow-hidden animate-slideUp max-h-[70vh] flex flex-col ring-1 ring-white/10">
<div className="fixed bottom-24 right-4 w-full md:w-96 bg-kodo-graphite/95 backdrop-blur-xl border border-kodo-steel/50 rounded-2xl shadow-2xl z-40 overflow-hidden animate-slideUp max-h-[70vh] flex flex-col ring-1 ring-white/10">
<div className="flex items-center justify-between p-4 border-b border-kodo-steel bg-kodo-ink/80">
<h3 className="font-bold text-white text-sm tracking-wide flex items-center gap-2">
<ListMusic className="w-4 h-4 text-kodo-steel" /> PLAY QUEUE

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { DashboardLayout } from './DashboardLayout';
const meta = {
title: 'Components/Layout/DashboardLayout',
title: 'App/Layouts/DashboardLayout',
component: DashboardLayout,
tags: ['autodocs'],
parameters: {

View file

@ -50,7 +50,7 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
className="flex-1 overflow-y-auto overflow-x-hidden pt-24 pb-32 px-4 md:px-8 custom-scrollbar"
id="main-scroll-container"
>
<div className="max-w-[1600px] mx-auto w-full">
<div className="max-w-layout-content mx-auto w-full">
{children}
</div>
</main>

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Header } from './Header';
const meta = {
title: 'Components/Layout/Header',
title: 'App/Layouts/Header',
component: Header,
tags: ['autodocs'],
decorators: [

View file

@ -118,7 +118,7 @@ export function Header(_props: HeaderProps) {
onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
className="flex items-center gap-3 p-1.5 pr-4 rounded-full hover:bg-white/5 transition-all border border-transparent focus:outline-none focus:ring-2 focus:ring-kodo-cyan/50 group"
>
<div className="w-9 h-9 rounded-full bg-gradient-to-br from-kodo-cyan to-kodo-magenta p-[1.5px] shadow-lg shadow-kodo-cyan/10 group-hover:shadow-kodo-cyan/30 transition-shadow duration-300">
<div className="w-9 h-9 rounded-full bg-gradient-to-br from-kodo-cyan to-kodo-magenta p-0.5 shadow-lg shadow-kodo-cyan/10 group-hover:shadow-kodo-cyan/30 transition-shadow duration-300">
<div className="w-full h-full rounded-full bg-kodo-ink flex items-center justify-center overflow-hidden">
{/* Placeholder Avatar */}
<span className="text-xs font-bold text-white group-hover:scale-110 transition-transform duration-300">

View file

@ -23,11 +23,11 @@ export function Layout({ children }: LayoutProps) {
<main
className={cn(
'flex-1 min-h-[calc(100vh-64px)] transition-all duration-300 ease-in-out',
'flex-1 min-h-layout-main transition-all duration-300 ease-in-out',
sidebarOpen ? 'lg:ml-64' : 'ml-0',
)}
>
<div className="max-w-[1600px] mx-auto p-4 sm:p-6 lg:p-8 animate-fadeIn">
<div className="max-w-layout-content mx-auto p-4 sm:p-6 lg:p-8 animate-fadeIn">
{children}
</div>
</main>

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Navbar } from './Navbar';
const meta: Meta<typeof Navbar> = {
title: 'Components/Layout/Navbar',
title: 'App/Layouts/Navbar',
component: Navbar,
parameters: {
layout: 'fullscreen',
@ -10,7 +10,7 @@ const meta: Meta<typeof Navbar> = {
tags: ['autodocs'],
decorators: [
(Story) => (
<div className="min-h-[200px] bg-background">
<div className="min-h-layout-story bg-background">
<Story />
</div>
),

View file

@ -180,7 +180,7 @@ export const Navbar: React.FC<NavbarProps> = ({ onNavigate, onLogout }) => {
className="flex items-center gap-2 cursor-pointer group select-none"
onClick={() => setShowUserMenu(!showUserMenu)}
>
<div className="w-9 h-9 rounded-full bg-kodo-steel p-[1px] hover:ring-2 hover:ring-kodo-steel transition-all">
<div className="w-9 h-9 rounded-full bg-kodo-steel p-px hover:ring-2 hover:ring-kodo-steel transition-all">
<div className="w-full h-full rounded-full overflow-hidden">
<img
src="https://picsum.photos/100/100"

View file

@ -1,22 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Sidebar } from './Sidebar';
import { BrowserRouter } from 'react-router-dom';
const meta = {
title: 'Components/Layout/Sidebar',
title: 'App/Layouts/Sidebar',
component: Sidebar,
tags: ['autodocs'],
decorators: [
(Story) => (
<BrowserRouter>
<div className="flex h-screen bg-kodo-void">
<Story />
<div className="flex-1 p-8 text-white ml-20 lg:ml-72">
<h1>Main Content Area</h1>
<p>Resize the viewport to see responsive behavior.</p>
</div>
<div className="flex h-screen bg-kodo-void">
<Story />
<div className="flex-1 p-8 text-white ml-20 lg:ml-72">
<h1>Main Content Area</h1>
<p>Resize the viewport to see responsive behavior.</p>
</div>
</BrowserRouter>
</div>
),
],
parameters: {

View file

@ -4,7 +4,7 @@ import { PlaylistsView } from './PlaylistsView';
import { ToastProvider } from '../../feedback/ToastProvider';
const meta: Meta<typeof PlaylistsView> = {
title: 'Components/Library/Playlists/PlaylistsView',
title: 'Components/Features/Library/Playlists/PlaylistsView',
component: PlaylistsView,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -8,7 +8,7 @@ import { QueueView } from './QueueView';
* complète des tracks.
*/
const meta: Meta<typeof QueueView> = {
title: 'Components/Library/Playlists/QueueView',
title: 'Components/Features/Library/Playlists/QueueView',
component: QueueView,
parameters: {
layout: 'fullscreen',

View file

@ -9,7 +9,7 @@ import { SaveQueueAsPlaylistModal } from './SaveQueueAsPlaylistModal';
* comme nouvelle playlist.
*/
const meta: Meta<typeof SaveQueueAsPlaylistModal> = {
title: 'Components/Library/Playlists/SaveQueueAsPlaylistModal',
title: 'Components/Features/Library/Playlists/SaveQueueAsPlaylistModal',
component: SaveQueueAsPlaylistModal,
parameters: {
layout: 'centered',

View file

@ -3,7 +3,7 @@ import { LiveStreamDetailView } from './LiveStreamDetailView';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Live/LiveStreamDetailView',
title: 'Components/Features/Live/LiveStreamDetailView',
component: LiveStreamDetailView,
tags: ['autodocs'],
parameters: {

View file

@ -3,7 +3,7 @@ import { TipStreamerModal } from './TipStreamerModal';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Live/Modals/TipStreamerModal',
title: 'Components/Features/Live/Modals/TipStreamerModal',
component: TipStreamerModal,
tags: ['autodocs'],
argTypes: {

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { LicenceCard } from './LicenceCard';
const meta: Meta<typeof LicenceCard> = {
title: 'Components/Marketplace/LicenceCard',
title: 'Components/Features/Marketplace/LicenceCard',
component: LicenceCard,
parameters: { layout: 'padded' },
tags: ['autodocs'],
@ -18,6 +18,39 @@ const meta: Meta<typeof LicenceCard> = {
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic: Story = { name: 'Basic' };
export const Pro: Story = { name: 'Pro' };
export const Exclusive: Story = { name: 'Exclusive' };
const mockLicense = {
id: '1',
name: 'Standard Lease',
price: 29.99,
features: ['MP3 & WAV', '2,000 Streams', 'Non-Profit Use', '1 Music Video'],
isPopular: false,
type: 'lease',
};
export const Basic: Story = {
name: 'Basic',
args: {
license: mockLicense,
onSelect: () => { },
onInfo: () => { },
},
};
export const Pro: Story = {
name: 'Pro',
args: {
license: { ...mockLicense, name: 'Pro Lease', price: 49.99, isPopular: true },
onSelect: () => { },
onInfo: () => { },
},
};
export const Exclusive: Story = {
name: 'Exclusive',
args: {
license: { ...mockLicense, name: 'Exclusive Rights', price: 199.99, features: ['Unlimited Streams', 'Trackout Stems', 'Full Ownership'] },
onSelect: () => { },
onInfo: () => { },
},
};

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ProductDetailView } from './ProductDetailView';
const meta: Meta<typeof ProductDetailView> = {
title: 'Components/Marketplace/ProductDetailView',
title: 'Components/Features/Marketplace/ProductDetailView',
component: ProductDetailView,
parameters: { layout: 'padded' },
tags: ['autodocs'],
@ -18,6 +18,59 @@ const meta: Meta<typeof ProductDetailView> = {
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = { name: 'Par défaut' };
export const Loading: Story = { name: 'Chargement' };
export const OutOfStock: Story = { name: 'Rupture de stock' };
const mockProduct = {
id: '1',
title: 'Neon Nights',
type: 'Beat',
price: 29.99,
coverUrl: 'https://picsum.photos/400/400',
description: 'A dark synthwave track with retro vibes.',
author: 'SynthMaster',
rating: 4.8,
reviewCount: 12,
bpm: 120,
key: 'Cm',
genre: 'Synthwave',
size: '15MB',
licenses: [
{ id: '1', name: 'Lease', price: 29.99, features: ['MP3'], type: 'lease' },
{ id: '2', name: 'Premium', price: 49.99, features: ['WAV', 'Trackouts'], type: 'lease' },
],
images: ['https://picsum.photos/400/400', 'https://picsum.photos/401/401'],
reviews: [],
};
const mockSimilarProducts = [
{ ...mockProduct, id: '2', title: 'Cyber City' },
{ ...mockProduct, id: '3', title: 'Retro Future' },
];
export const Default: Story = {
name: 'Par défaut',
args: {
product: mockProduct,
similarProducts: mockSimilarProducts,
onBack: () => { },
onAddToCart: () => { },
},
};
export const Loading: Story = {
name: 'Chargement',
args: {
product: mockProduct,
similarProducts: [],
onBack: () => { },
onAddToCart: () => { },
},
};
export const OutOfStock: Story = {
name: 'Rupture de stock',
args: {
product: { ...mockProduct, available: false }, // Assuming available prop exists or similar logic
similarProducts: mockSimilarProducts,
onBack: () => { },
onAddToCart: () => { },
},
};

View file

@ -4,7 +4,7 @@ import { Button } from '../ui/button';
import { useArgs } from '@storybook/preview-api';
const meta = {
title: 'Components/Modals/CreatorModal',
title: 'Components/Features/Modals/CreatorModal',
component: CreatorModal,
tags: ['autodocs'],
parameters: {

View file

@ -3,7 +3,7 @@ import { MonitoringDashboard } from './MonitoringDashboard';
import { ToastProvider } from '../../components/feedback/ToastProvider';
const meta: Meta<typeof MonitoringDashboard> = {
title: 'Components/Monitoring/MonitoringDashboard',
title: 'Components/Features/Monitoring/MonitoringDashboard',
component: MonitoringDashboard,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -3,7 +3,7 @@ import { Breadcrumbs } from './Breadcrumbs';
import { FileText, Music } from 'lucide-react';
const meta: Meta<typeof Breadcrumbs> = {
title: 'Components/Navigation/Breadcrumbs',
title: 'Components/Features/Navigation/Breadcrumbs',
component: Breadcrumbs,
parameters: {
layout: 'padded',

View file

@ -3,7 +3,7 @@ import { Pagination } from './Pagination';
import { useState } from 'react';
const meta: Meta<typeof Pagination> = {
title: 'Components/Navigation/Pagination',
title: 'Components/Features/Navigation/Pagination',
component: Pagination,
parameters: {
layout: 'centered',

View file

@ -26,7 +26,7 @@ const mockNotifications = [
];
const meta = {
title: 'Components/Notifications/NotificationBell',
title: 'Components/Features/Notifications/NotificationBell',
component: NotificationBell,
tags: ['autodocs'],
decorators: [

View file

@ -11,7 +11,7 @@ const mockNotification = {
};
const meta = {
title: 'Components/Notifications/NotificationItem',
title: 'Components/Features/Notifications/NotificationItem',
component: NotificationItem,
tags: ['autodocs'],
argTypes: {

View file

@ -1,27 +1,15 @@
import type { Meta, StoryObj } from '@storybook/react';
import { NotificationMenu } from './NotificationMenu';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
import { ToastProvider } from '../feedback/ToastProvider';
const queryClient = new QueryClient();
const meta = {
title: 'Components/Notifications/NotificationMenu',
title: 'Components/Features/Notifications/NotificationMenu',
component: NotificationMenu,
tags: ['autodocs'],
decorators: [
(Story) => (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<ToastProvider>
<div className="flex justify-end p-20 bg-kodo-ink min-h-[400px]">
<Story />
</div>
</ToastProvider>
</BrowserRouter>
</QueryClientProvider>
<div className="flex justify-end p-20 bg-kodo-ink min-h-[400px]">
<Story />
</div>
),
],
} satisfies Meta<typeof NotificationMenu>;
@ -29,4 +17,4 @@ const meta = {
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const Default = {};

View file

@ -9,7 +9,7 @@ import { FullPlayer } from './FullPlayer';
* paroles et contrôles avancés.
*/
const meta: Meta<typeof FullPlayer> = {
title: 'Components/Player/FullPlayer',
title: 'Components/Features/Player/FullPlayer',
component: FullPlayer,
parameters: {
layout: 'fullscreen',

View file

@ -8,7 +8,7 @@ import { LyricsPanel } from './LyricsPanel';
* la lecture audio actuelle.
*/
const meta: Meta<typeof LyricsPanel> = {
title: 'Components/Player/LyricsPanel',
title: 'Components/Features/Player/LyricsPanel',
component: LyricsPanel,
parameters: {
layout: 'padded',

View file

@ -9,7 +9,7 @@ import { QueuePanel } from './QueuePanel';
* réorganisation par drag-and-drop.
*/
const meta: Meta<typeof QueuePanel> = {
title: 'Components/Player/QueuePanel',
title: 'Components/Features/Player/QueuePanel',
component: QueuePanel,
parameters: {
layout: 'padded',

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { GlobalSearchBar } from './GlobalSearchBar';
const meta: Meta<typeof GlobalSearchBar> = {
title: 'Components/Search/GlobalSearchBar',
title: 'Components/Features/Search/GlobalSearchBar',
component: GlobalSearchBar,
parameters: { layout: 'centered' },
tags: ['autodocs'],

View file

@ -8,7 +8,7 @@ const mockResults = [
];
const meta = {
title: 'Components/Search/Search',
title: 'Components/Features/Search/Search',
component: Search,
tags: ['autodocs'],
argTypes: {

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { SearchBar } from './SearchBar';
const meta = {
title: 'Components/Search/SearchBar',
title: 'Components/Features/Search/SearchBar',
component: SearchBar,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { SellerDashboardView } from './SellerDashboardView';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Seller/SellerDashboardView',
title: 'Components/Features/Seller/SellerDashboardView',
component: SellerDashboardView,
tags: ['autodocs'],
argTypes: {

View file

@ -27,7 +27,7 @@ const mockProducts: Product[] = [
];
const meta = {
title: 'Components/Seller/Modals/FlashSaleModal',
title: 'Components/Features/Seller/Modals/FlashSaleModal',
component: FlashSaleModal,
tags: ['autodocs'],
argTypes: {

View file

@ -8,7 +8,7 @@ import { AccessibilitySettingsView } from './AccessibilitySettingsView';
* taille de police, réduction des animations.
*/
const meta: Meta<typeof AccessibilitySettingsView> = {
title: 'Components/Settings/Accessibility/AccessibilitySettingsView',
title: 'Components/Features/Settings/Accessibility/AccessibilitySettingsView',
component: AccessibilitySettingsView,
parameters: {
layout: 'padded',

View file

@ -8,7 +8,7 @@ import { AccountSettings } from './AccountSettings';
* gestion email, nom d'utilisateur et suppression.
*/
const meta: Meta<typeof AccountSettings> = {
title: 'Components/Settings/Account/AccountSettings',
title: 'Components/Features/Settings/Account/AccountSettings',
component: AccountSettings,
parameters: {
layout: 'padded',

View file

@ -9,7 +9,7 @@ import { ChangeEmailModal } from './ChangeEmailModal';
* avec validation et envoi de confirmation.
*/
const meta: Meta<typeof ChangeEmailModal> = {
title: 'Components/Settings/Account/ChangeEmailModal',
title: 'Components/Features/Settings/Account/ChangeEmailModal',
component: ChangeEmailModal,
parameters: {
layout: 'centered',

View file

@ -9,7 +9,7 @@ import { ChangeUsernameModal } from './ChangeUsernameModal';
* vérification de disponibilité en temps réel.
*/
const meta: Meta<typeof ChangeUsernameModal> = {
title: 'Components/Settings/Account/ChangeUsernameModal',
title: 'Components/Features/Settings/Account/ChangeUsernameModal',
component: ChangeUsernameModal,
parameters: {
layout: 'centered',

View file

@ -8,7 +8,7 @@ import { DeleteAccountView } from './DeleteAccountView';
* avec avertissements et confirmation multiple.
*/
const meta: Meta<typeof DeleteAccountView> = {
title: 'Components/Settings/Account/DeleteAccountView',
title: 'Components/Features/Settings/Account/DeleteAccountView',
component: DeleteAccountView,
parameters: {
layout: 'padded',

View file

@ -8,7 +8,7 @@ import { AppearanceSettingsView } from './AppearanceSettingsView';
* animations et densité de l'interface.
*/
const meta: Meta<typeof AppearanceSettingsView> = {
title: 'Components/Settings/Appearance/AppearanceSettingsView',
title: 'Components/Features/Settings/Appearance/AppearanceSettingsView',
component: AppearanceSettingsView,
parameters: {
layout: 'padded',

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { DataExportView } from './DataExportView';
const meta: Meta<typeof DataExportView> = {
title: 'Components/Settings/Data/DataExportView',
title: 'Components/Features/Settings/Data/DataExportView',
component: DataExportView,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -8,7 +8,7 @@ import { SecuritySettings } from './SecuritySettings';
* des mots de passe, 2FA, et sessions actives.
*/
const meta: Meta<typeof SecuritySettings> = {
title: 'Components/Settings/Security/SecuritySettings',
title: 'Components/Features/Settings/Security/SecuritySettings',
component: SecuritySettings,
parameters: {
layout: 'padded',

View file

@ -8,7 +8,7 @@ import { SessionManagement } from './SessionManagement';
* option de déconnexion individuelle ou globale.
*/
const meta: Meta<typeof SessionManagement> = {
title: 'Components/Settings/Security/SessionManagement',
title: 'Components/Features/Settings/Security/SessionManagement',
component: SessionManagement,
parameters: {
layout: 'padded',

View file

@ -10,7 +10,7 @@ import { TwoFactorSetup } from './TwoFactorSetup';
* à deux facteurs avec QR code et codes de backup.
*/
const meta: Meta<typeof TwoFactorSetup> = {
title: 'Components/Settings/Security/TwoFactorSetup',
title: 'Components/Features/Settings/Security/TwoFactorSetup',
component: TwoFactorSetup,
parameters: {
layout: 'padded',

View file

@ -15,7 +15,7 @@ const mockComment = {
};
const meta = {
title: 'Components/Social/CommentItem',
title: 'Components/Features/Social/CommentItem',
component: CommentItem,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { fn } from '@storybook/test';
import { CreatePostModal } from './CreatePostModal';
const meta: Meta<typeof CreatePostModal> = {
title: 'Components/Social/CreatePostModal',
title: 'Components/Features/Social/CreatePostModal',
component: CreatePostModal,
parameters: { layout: 'centered' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ExploreView } from './ExploreView';
const meta: Meta<typeof ExploreView> = {
title: 'Components/Social/ExploreView',
title: 'Components/Features/Social/ExploreView',
component: ExploreView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { FeedView } from './FeedView';
const meta: Meta<typeof FeedView> = {
title: 'Components/Social/FeedView',
title: 'Components/Features/Social/FeedView',
component: FeedView,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -22,7 +22,7 @@ const mockPost = {
};
const meta = {
title: 'Components/Social/PostCard',
title: 'Components/Features/Social/PostCard',
component: PostCard,
tags: ['autodocs'],
decorators: [

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ConnectionsView } from './ConnectionsView';
const meta: Meta<typeof ConnectionsView> = {
title: 'Components/Social/ConnectionsView',
title: 'Components/Features/Social/ConnectionsView',
component: ConnectionsView,
parameters: { layout: 'padded' },
tags: ['autodocs'],

View file

@ -3,7 +3,7 @@ import { CreateGroupModal } from './CreateGroupModal';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Social/Groups/CreateGroupModal',
title: 'Components/Features/Social/Groups/CreateGroupModal',
component: CreateGroupModal,
tags: ['autodocs'],
argTypes: {

View file

@ -13,7 +13,7 @@ const mockGroup: SocialGroup = {
} as SocialGroup;
const meta = {
title: 'Components/Social/Groups/GroupCard',
title: 'Components/Features/Social/Groups/GroupCard',
component: GroupCard,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { GroupsView } from './GroupsView';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Social/Groups/GroupsView',
title: 'Components/Features/Social/Groups/GroupsView',
component: GroupsView,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { AIToolsView } from './AIToolsView';
import { ToastProvider } from '@/components/feedback/ToastProvider';
const meta = {
title: 'Components/Studio/AIToolsView',
title: 'Components/Features/Studio/AIToolsView',
component: AIToolsView,
tags: ['autodocs'],
decorators: [

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { CloudFileBrowser } from './CloudFileBrowser';
const meta = {
title: 'Components/Studio/CloudFileBrowser',
title: 'Components/Features/Studio/CloudFileBrowser',
component: CloudFileBrowser,
tags: ['autodocs'],
decorators: [

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { GoLiveView } from './GoLiveView';
const meta: Meta<typeof GoLiveView> = {
title: 'Components/Studio/GoLiveView',
title: 'Components/Features/Studio/GoLiveView',
component: GoLiveView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { ProjectsManager } from './ProjectsManager';
const meta = {
title: 'Components/Studio/ProjectsManager',
title: 'Components/Features/Studio/ProjectsManager',
component: ProjectsManager,
tags: ['autodocs'],
decorators: [

View file

@ -3,7 +3,7 @@ import { CreateProjectModal } from './CreateProjectModal';
import { fn } from '@storybook/test';
const meta: Meta<typeof CreateProjectModal> = {
title: 'Components/Studio/Projects/CreateProjectModal',
title: 'Components/Features/Studio/Projects/CreateProjectModal',
component: CreateProjectModal,
parameters: { layout: 'centered' },
tags: ['autodocs'],

View file

@ -1,29 +1,16 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ProjectDetailView } from './ProjectDetailView';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
const createMockQueryClient = () => new QueryClient({
defaultOptions: {
queries: { retry: false, staleTime: Infinity },
mutations: { retry: false },
},
});
const meta: Meta<typeof ProjectDetailView> = {
title: 'Components/Studio/Projects/ProjectDetailView',
title: 'Components/Features/Studio/Projects/ProjectDetailView',
component: ProjectDetailView,
parameters: { layout: 'fullscreen' },
tags: ['autodocs'],
decorators: [
(Story) => (
<QueryClientProvider client={createMockQueryClient()}>
<BrowserRouter>
<div className="bg-kodo-background min-h-screen">
<Story />
</div>
</BrowserRouter>
</QueryClientProvider>
<div className="bg-kodo-background min-h-screen">
<Story />
</div>
),
],
};

View file

@ -3,7 +3,7 @@ import { ThemeSwitcher } from './ThemeSwitcher';
import { useState } from 'react';
const meta = {
title: 'Components/Theme/ThemeSwitcher',
title: 'Components/Features/Theme/ThemeSwitcher',
component: ThemeSwitcher,
tags: ['autodocs'],
} satisfies Meta<typeof ThemeSwitcher>;

View file

@ -3,7 +3,7 @@ import { Alert, AlertTitle, AlertDescription } from './alert';
import { Terminal } from 'lucide-react';
const meta = {
title: 'UI/Alert',
title: 'Components/UI/Alert',
component: Alert,
tags: ['autodocs'],
argTypes: {
@ -57,7 +57,8 @@ export const WithClose: Story = {
export const ComplexContent: Story = {
render: (args) => (
<Alert {...args} icon={<Terminal className="h-4 w-4" />}>
<Alert {...args}>
<Terminal className="h-4 w-4" />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components to your app using the cli.

View file

@ -3,7 +3,7 @@ import { Button } from './button';
import { Mail, ArrowRight, Trash2, Edit } from 'lucide-react';
const meta = {
title: 'UI/Button',
title: 'Components/UI/Button',
component: Button,
tags: ['autodocs'],
argTypes: {

View file

@ -3,7 +3,7 @@ import { fn } from '@storybook/test';
import { BulkUploadModal } from './BulkUploadModal';
const meta: Meta<typeof BulkUploadModal> = {
title: 'Components/Upload/BulkUploadModal',
title: 'Components/Features/Upload/BulkUploadModal',
component: BulkUploadModal,
parameters: { layout: 'centered' },
tags: ['autodocs'],
@ -20,6 +20,41 @@ const meta: Meta<typeof BulkUploadModal> = {
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = { name: 'Par défaut' };
export const Uploading: Story = { name: 'Upload en cours' };
export const Complete: Story = { name: 'Terminé' };
const mockFiles: any[] = [
{ id: '1', file: { name: 'Track1.mp3', size: 5000000, type: 'audio/mpeg' }, progress: 50, status: 'uploading' },
{ id: '2', file: { name: 'Track2.wav', size: 15000000, type: 'audio/wav' }, progress: 100, status: 'completed' },
{ id: '3', file: { name: 'Track3.mp3', size: 4000000, type: 'audio/mpeg' }, progress: 0, status: 'pending' },
];
export const Default: Story = {
name: 'Par défaut',
args: {
files: mockFiles,
onStartUpload: () => { },
onCancelFile: () => { },
onPauseFile: () => { },
onResumeFile: () => { },
},
};
export const Uploading: Story = {
name: 'Upload en cours',
args: {
files: mockFiles.map(f => ({ ...f, status: 'uploading', progress: 45 })),
onStartUpload: () => { },
onCancelFile: () => { },
onPauseFile: () => { },
onResumeFile: () => { },
},
};
export const Complete: Story = {
name: 'Terminé',
args: {
files: mockFiles.map(f => ({ ...f, status: 'completed', progress: 100 })),
onStartUpload: () => { },
onCancelFile: () => { },
onPauseFile: () => { },
onResumeFile: () => { },
},
};

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { FileUploadZone } from './FileUploadZone';
const meta: Meta<typeof FileUploadZone> = {
title: 'Components/Upload/FileUploadZone',
title: 'Components/Features/Upload/FileUploadZone',
component: FileUploadZone,
parameters: { layout: 'centered' },
tags: ['autodocs'],

View file

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { UploadProgressBar } from './UploadProgressBar';
const meta: Meta<typeof UploadProgressBar> = {
title: 'Components/Upload/UploadProgressBar',
title: 'Components/Features/Upload/UploadProgressBar',
component: UploadProgressBar,
parameters: { layout: 'centered' },
tags: ['autodocs'],

Some files were not shown because too many files have changed in this diff Show more