diff --git a/.gitignore b/.gitignore index 9bc7a33..e9e3d00 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,7 @@ pnpm-debug.log* *storybook.log # Storybook build output -storybook-static/ \ No newline at end of file +storybook-static/ + +# 3rd party +vendors/ diff --git a/Dockerfile b/Dockerfile index ed1ca1c..4d18ade 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,24 @@ # Build stage FROM node:20-alpine AS builder WORKDIR /app -COPY package.json pnpm-lock.yaml ./ -RUN npm install -g pnpm && pnpm install + +# Copy workspace configuration and root package files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ + +# Install pnpm +RUN npm install -g pnpm + +# Copy all package.json files to enable proper dependency resolution COPY packages/ui-kit/package.json ./packages/ui-kit/ -WORKDIR /app/packages/ui-kit +COPY packages/showcase/package.json ./packages/showcase/ + +# Install all dependencies for the workspace RUN pnpm install -WORKDIR /app + +# Copy all source code COPY . . + +# Build all packages RUN pnpm build # Production stage diff --git a/docs/project_plan.md b/docs/project_plan.md index f5ea895..87522ce 100644 --- a/docs/project_plan.md +++ b/docs/project_plan.md @@ -56,7 +56,7 @@ A pragmatic breakdown into **four one‑week sprints** plus a preparatory **Spri | --- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------ | | 3.1 | Wrap **TanStack Table** into `DataTable` with pagination, resize. | Story with 50 rows paginates; Playwright test clicks next page. | ✓ | | 3.2 | Build **MainLayout** with TopBar + LeftNav + Breadcrumb. | Storybook viewport test at 1280 & 1024 px shows responsive collapse. | ✓ | -| 3.3 | Implement Toast system (`useToast`) + StatusBadge. | Vitest renders Toast, axe-core passes. | PR | +| 3.3 | Implement Toast system (`useToast`) + StatusBadge. | Vitest renders Toast, axe-core passes. | ✓ | | 3.4 | Sample showcase: login page + dashboard + customers table route. | E2E Playwright run (login → dashboard) green in CI. | | | 3.5 | Add i18n infrastructure (`react-i18next`) with `en`, `de` locales. | Storybook toolbar allows locale switch; renders German labels. | | | 3.6 | **SQLite seed script** – generate 100 customers & 2 users; hook `pnpm run seed` in showcase. | Script executes without error; Playwright test logs in with `admin` credentials, verifies 100 customers paginated. | | diff --git a/docs/task-planning/task-3.4-sample-showcase.md b/docs/task-planning/task-3.4-sample-showcase.md new file mode 100644 index 0000000..5a835b8 --- /dev/null +++ b/docs/task-planning/task-3.4-sample-showcase.md @@ -0,0 +1,169 @@ +# Task 3.4: Sample Showcase - Login Page + Dashboard + Customers Table Route + +## Task Description + +Implement a sample showcase application that demonstrates the UI-Kit components in action. This includes creating actual application routes with login functionality, a dashboard, and a customers table view using the existing UI components. + +## Task Planning + +| Task Description | Definition of Done (DoD) | Status | +| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------- | +| Set up showcase application structure | - Showcase app directory structure exists
- React Router v7 configured
- Basic routing setup complete | Complete | +| Create login page using AuthShell | - Login page renders with AuthShell layout
- Login form uses UI-Kit form components
- Basic authentication logic implemented | Checking | +| Implement dashboard page using MainLayout | - Dashboard page renders with MainLayout
- Displays summary cards and navigation
- Uses existing UI-Kit components | Open | +| Create customers table route with DataTable | - Customers page displays using DataTable component
- Shows paginated customer data
- Navigation from dashboard works | Open | +| Add routing and navigation | - React Router navigation between pages
- Protected routes for authenticated content
- Breadcrumb navigation works | Open | +| Implement basic authentication state | - Login/logout functionality
- Session persistence
- Route protection based on auth state | Open | +| Add sample data and mock API | - Customer data generation
- Mock authentication endpoints
- API integration with DataTable | Open | +| Create E2E tests | - Playwright tests for login flow
- Navigation test (login → dashboard → customers)
- Tests pass in CI | Open | +| Integrate showcase with UI-Kit build | - Showcase app builds successfully
- Uses published UI-Kit components
- No circular dependencies | Open | + +## Implementation Details + +### 1. Application Structure + +Create a sample showcase application that demonstrates the UI-Kit in a realistic scenario: + +``` +showcase/ + src/ + pages/ + LoginPage.tsx # Using AuthShell + form components + DashboardPage.tsx # Using MainLayout + stats components + CustomersPage.tsx # Using MainLayout + DataTable + components/ + ProtectedRoute.tsx # Route protection wrapper + Navigation.tsx # App navigation using UI-Kit components + hooks/ + useAuth.tsx # Authentication state management + services/ + api.ts # Mock API for data + auth.ts # Authentication logic + types/ + Customer.ts # Customer data types + Auth.ts # Authentication types +``` + +### 2. Pages Implementation + +**Login Page:** + +- Use `AuthShell` layout from UI-Kit +- Login form with `TextInput` and `Button` components +- Form validation using React Hook Form + Zod +- Error handling with `Toast` notifications +- Responsive design following UI-Kit patterns + +**Dashboard Page:** + +- Use `MainLayout` with navigation and breadcrumbs +- Summary cards showing customer statistics +- Quick action buttons using UI-Kit buttons +- Navigation to customers page +- Use `StatusBadge` for status indicators + +**Customers Page:** + +- Use `MainLayout` for consistent navigation +- `DataTable` component showing customer list +- Pagination, sorting, and filtering +- Customer actions (view, edit buttons) +- Breadcrumb navigation + +### 3. Technical Requirements + +**Routing:** + +- React Router v7 with data loaders +- Protected routes requiring authentication +- Proper error boundaries and 404 handling +- Clean URL structure + +**State Management:** + +- Authentication state using Zustand (from UI-Kit) +- Customer data fetching and caching +- Form state management with React Hook Form + +**Data Layer:** + +- Mock customer data (50+ records) +- Simulated API responses with realistic delays +- Pagination and filtering logic +- Authentication simulation + +### 4. Testing Strategy + +**E2E Tests (Playwright):** + +- Login flow: invalid credentials → error, valid credentials → dashboard +- Navigation: login → dashboard → customers → back to dashboard +- DataTable interactions: pagination, sorting +- Logout functionality +- Responsive behavior testing + +**Integration:** + +- Component integration with real data +- Form submission and validation flows +- Navigation state persistence +- Error handling scenarios + +## Definition of Done + +**Original DoD:** "E2E Playwright run (login → dashboard) green in CI." + +**Expanded DoD:** + +- ✅ Showcase application builds and runs without errors +- ✅ Login page functional with form validation +- ✅ Dashboard page displays with navigation +- ✅ Customers page shows DataTable with pagination +- ✅ E2E Playwright tests pass: login → dashboard → customers flow +- ✅ All UI-Kit components integrated properly +- ✅ Responsive design works on different screen sizes +- ✅ Authentication flow works end-to-end +- ✅ CI pipeline runs tests successfully + +## Technical Dependencies + +### Required UI-Kit Components: + +- `AuthShell` (login page layout) +- `AppShell` (dashboard and customers layout) +- `DataTable` (customers table) +- `TextInput`, `Button` (forms) +- `Toast` (notifications) +- `StatusBadge` (status indicators) + +### External Dependencies: + +- React Router v7 (routing) +- React Hook Form + Zod (forms) +- Playwright (E2E testing) +- Mock data generation utilities + +### Infrastructure: + +- Vite setup for showcase app +- Build integration with UI-Kit +- CI/CD pipeline updates +- Development server configuration + +## Success Metrics + +1. **Functionality:** All user flows work end-to-end +2. **Performance:** Pages load within 2 seconds +3. **Accessibility:** axe-core tests pass on all pages +4. **Testing:** 100% E2E test coverage for critical paths +5. **Integration:** UI-Kit components work seamlessly +6. **Maintainability:** Clean, documented code structure + +## Next Steps After Completion + +This showcase will serve as: + +- Reference implementation for UI-Kit usage +- Testing ground for new components +- Demo for stakeholders and users +- Foundation for future showcase extensions (task 3.5: i18n, task 3.6: SQLite integration) diff --git a/eslint.config.js b/eslint.config.js index d75db38..3d5e518 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -7,7 +7,7 @@ import uiKitRules from './packages/eslint-plugin-ui-kit-rules/index.js'; export default [ // global ignores – applied before other configs { - ignores: ['packages/ui-kit/dist/**', '**/node_modules/**', '**/storybook-static/**'] + ignores: ['packages/ui-kit/dist/**', '**/node_modules/**', '**/storybook-static/**', 'vendors/**', 'packages/showcase/dist/**', '**/dist/**'] }, js.configs.recommended, ...tseslint.configs.recommended, diff --git a/packages/showcase/eslint.config.js b/packages/showcase/eslint.config.js new file mode 100644 index 0000000..af60e58 --- /dev/null +++ b/packages/showcase/eslint.config.js @@ -0,0 +1,42 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; + +export default [ + // global ignores – applied before other configs + { + ignores: ['dist/**', 'node_modules/**', '../../vendors/**'] + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['src/**/*.{js,jsx,ts,tsx}', 'tailwind.config.js', 'vite.config.ts', 'postcss.config.js'], + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module', + globals: { + module: 'readonly', + require: 'readonly', + process: 'readonly', + console: 'readonly', + window: 'readonly', + document: 'readonly', + navigator: 'readonly' + } + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh + }, + rules: { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true } + ], + '@typescript-eslint/no-require-imports': 'off' + } + } +]; \ No newline at end of file diff --git a/packages/showcase/index.html b/packages/showcase/index.html new file mode 100644 index 0000000..2051f6f --- /dev/null +++ b/packages/showcase/index.html @@ -0,0 +1,16 @@ + + + + + + + + UI-Kit Showcase + + + +
+ + + + \ No newline at end of file diff --git a/packages/showcase/package.json b/packages/showcase/package.json new file mode 100644 index 0000000..bea572b --- /dev/null +++ b/packages/showcase/package.json @@ -0,0 +1,48 @@ +{ + "name": "@org/showcase", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint .", + "preview": "vite preview", + "test": "vitest run", + "test:e2e": "playwright test" + }, + "dependencies": { + "@hookform/resolvers": "^3.6.1", + "@org/ui-kit": "workspace:*", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hook-form": "^7.56.4", + "react-router": "^7.0.0", + "react-router-dom": "^7.0.0", + "zod": "^3.25.7", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@eslint/js": "^9.27.0", + "@playwright/test": "^1.52.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.5", + "@vitejs/plugin-react": "^4.4.1", + "autoprefixer": "^10.4.21", + "daisyui": "^5.0.35", + "eslint": "^9.27.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "jsdom": "^26.1.0", + "postcss": "^8.5.3", + "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7", + "typescript": "^5.8.3", + "typescript-eslint": "^8.32.1", + "vite": "^5.4.19", + "vitest": "^1.6.1" + } +} diff --git a/packages/showcase/postcss.config.js b/packages/showcase/postcss.config.js new file mode 100644 index 0000000..1b364dd --- /dev/null +++ b/packages/showcase/postcss.config.js @@ -0,0 +1,9 @@ +import tailwindcss from 'tailwindcss'; +import autoprefixer from 'autoprefixer'; + +export default { + plugins: [ + tailwindcss, + autoprefixer, + ], +}; \ No newline at end of file diff --git a/packages/showcase/src/App.test.tsx b/packages/showcase/src/App.test.tsx new file mode 100644 index 0000000..fb2266b --- /dev/null +++ b/packages/showcase/src/App.test.tsx @@ -0,0 +1,20 @@ +import { describe, it, expect } from 'vitest'; +import { render } from '@testing-library/react'; +import { BrowserRouter } from 'react-router'; +import { ToastProvider } from '@org/ui-kit'; +import { LoginPage } from './pages/LoginPage'; + +describe('Showcase App', () => { + it('renders LoginPage without crashing', () => { + render( + + + + + + ); + + // Check that the component renders + expect(document.body).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/packages/showcase/src/components/ProtectedRoute.tsx b/packages/showcase/src/components/ProtectedRoute.tsx new file mode 100644 index 0000000..67c1ece --- /dev/null +++ b/packages/showcase/src/components/ProtectedRoute.tsx @@ -0,0 +1,16 @@ +import { Navigate } from 'react-router-dom'; +import { useAuth } from '../hooks/useAuth'; + +interface ProtectedRouteProps { + children: React.ReactNode; +} + +export function ProtectedRoute({ children }: ProtectedRouteProps) { + const { isAuthenticated } = useAuth(); + + if (!isAuthenticated) { + return ; + } + + return <>{children}; +} \ No newline at end of file diff --git a/packages/showcase/src/hooks/useAuth.tsx b/packages/showcase/src/hooks/useAuth.tsx new file mode 100644 index 0000000..3c72889 --- /dev/null +++ b/packages/showcase/src/hooks/useAuth.tsx @@ -0,0 +1,84 @@ +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; +import type { AuthState, LoginCredentials } from '../types/Auth'; + +interface AuthStore extends AuthState { + login: (credentials: LoginCredentials) => Promise; + logout: () => void; + clearError: () => void; +} + +// Mock users for demo +const MOCK_USERS = [ + { + id: '1', + email: 'admin@example.com', + name: 'Admin User', + role: 'admin' as const, + password: 'admin123', + }, + { + id: '2', + email: 'user@example.com', + name: 'Demo User', + role: 'user' as const, + password: 'user123', + }, +]; + +export const useAuth = create()( + persist( + (set) => ({ + user: null, + isAuthenticated: false, + isLoading: false, + error: null, + + login: async (credentials: LoginCredentials) => { + set({ isLoading: true, error: null }); + + // Simulate API delay + await new Promise(resolve => setTimeout(resolve, 1000)); + + const user = MOCK_USERS.find( + u => u.email === credentials.email && u.password === credentials.password + ); + + if (user) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { password, ...userWithoutPassword } = user; + set({ + user: userWithoutPassword, + isAuthenticated: true, + isLoading: false, + error: null + }); + } else { + set({ + error: 'Invalid email or password', + isLoading: false + }); + } + }, + + logout: () => { + set({ + user: null, + isAuthenticated: false, + error: null + }); + }, + + clearError: () => { + set({ error: null }); + }, + }), + { + name: 'auth-storage', + partialize: (state) => ({ + user: state.user, + isAuthenticated: state.isAuthenticated + }), + } + ) +); \ No newline at end of file diff --git a/packages/showcase/src/index.css b/packages/showcase/src/index.css new file mode 100644 index 0000000..af9d14b --- /dev/null +++ b/packages/showcase/src/index.css @@ -0,0 +1,132 @@ +@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap'); +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Force DaisyUI component generation - ensure these classes are available */ +/* .btn .btn-primary .btn-secondary .card .input .modal .navbar .drawer */ + +/* Nexadash-inspired design tokens */ +:root { + /* Typography */ + --font-plus-jakarta: 'Plus Jakarta Sans', sans-serif; + + /* Color palette inspired by nexadash */ + --color-primary: #335cff; + --color-primary-hover: #2A4DD7; + --color-black: #171718; + --color-white: #ffffff; + + /* Gray palette */ + --color-gray-100: #fafbfc; + --color-gray-200: #f9fafb; + --color-gray-300: #e2e8f0; + --color-gray-400: #f5f7fa; + --color-gray-500: #b9bec6; + --color-gray-600: #9ca3af; + --color-gray-700: #6b7280; + --color-gray: #525866; + + /* Status colors */ + --color-success: #22c55e; + --color-success-light: #dcfce7; + --color-warning: #eab308; + --color-danger: #ef4444; + --color-danger-light: #fee2e2; + + /* Theme colors */ + --color-light-theme: #f4f7ff; + --color-light-orange: #ffedd5; + --color-light-blue: #e0f2fe; + --color-light-purple: #f3e8ff; + + /* Shadows */ + --shadow-3xl: 0 1px 2px 0 rgba(95, 74, 46, 0.08), 0 0 0 1px rgba(227, 225, 222, 0.4); + --shadow-sm: 0 1px 2px 0 rgba(113, 116, 152, 0.1); + + /* Shadcn/UI variables mapped to nexadash tokens */ + --primary: var(--color-primary); + --primary-foreground: var(--color-white); + + --secondary: var(--color-gray-200); + --secondary-foreground: var(--color-gray); + + --accent: var(--color-light-theme); + --accent-foreground: var(--color-primary); + + --destructive: var(--color-danger); + --destructive-foreground: var(--color-white); + + /* UI elements */ + --background: var(--color-white); + --foreground: var(--color-black); + --card: var(--color-white); + --card-foreground: var(--color-black); + --popover: var(--color-white); + --popover-foreground: var(--color-black); + + /* Borders and inputs */ + --border: var(--color-gray-300); + --input: var(--color-gray-300); + --ring: var(--color-primary); + + /* Status colors for UI */ + --success: var(--color-success); + --success-foreground: var(--color-white); + --warning: var(--color-warning); + --warning-foreground: var(--color-white); + + /* Radius */ + --radius: 0.5rem; +} + +.dark { + /* Dark mode adjustments */ + --background: var(--color-black); + --foreground: var(--color-white); + --card: #1a1a1b; + --card-foreground: var(--color-white); + --popover: #1a1a1b; + --popover-foreground: var(--color-white); + --border: #2a2a2b; + --input: #2a2a2b; +} + +@layer base { + * { + @apply border-border; + } + + body { + @apply bg-background text-foreground; + font-family: var(--font-plus-jakarta); + } + + button { + @apply cursor-pointer; + } +} + +/* Nexadash-inspired component styles */ +@layer components { + .nexadash-card { + @apply bg-white rounded-lg; + box-shadow: var(--shadow-3xl); + } + + .nexadash-button-primary { + @apply bg-primary text-white hover:bg-[#2A4DD7] px-4 py-2 rounded-lg font-medium text-sm transition-colors duration-300; + } + + .nexadash-button-outline { + @apply ring-1 ring-inset ring-primary bg-white text-primary hover:bg-light-theme px-4 py-2 rounded-lg font-medium text-sm transition-colors duration-300; + } + + .nexadash-sidebar-nav { + @apply text-gray flex items-center gap-2.5 px-5 py-2.5 text-sm font-medium leading-tight transition hover:text-black; + } + + .nexadash-sidebar-nav.active { + @apply !text-black bg-light-theme; + } +} \ No newline at end of file diff --git a/packages/showcase/src/main.tsx b/packages/showcase/src/main.tsx new file mode 100644 index 0000000..6224ae7 --- /dev/null +++ b/packages/showcase/src/main.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import { ToastProvider } from '@org/ui-kit'; +import './index.css'; + +import { LoginPage } from './pages/LoginPage'; +import { DashboardPage } from './pages/DashboardPage'; +import { CustomersPage } from './pages/CustomersPage'; +import { ProtectedRoute } from './components/ProtectedRoute'; + +// Set DaisyUI theme on HTML element +document.documentElement.setAttribute('data-theme', 'light'); + +const router = createBrowserRouter([ + { + path: '/', + element: , + }, + { + path: '/login', + element: , + }, + { + path: '/dashboard', + element: ( + + + + ), + }, + { + path: '/customers', + element: ( + + + + ), + }, +]); + +const container = document.getElementById('root'); +if (!container) throw new Error('Failed to find the root element'); + +const root = createRoot(container); +root.render( + + + + + +); \ No newline at end of file diff --git a/packages/showcase/src/pages/CustomersPage.tsx b/packages/showcase/src/pages/CustomersPage.tsx new file mode 100644 index 0000000..3f2d88d --- /dev/null +++ b/packages/showcase/src/pages/CustomersPage.tsx @@ -0,0 +1,346 @@ +import { useNavigate } from 'react-router-dom'; +import { AppShell, Button, DataTable } from '@org/ui-kit'; +import { useAuth } from '../hooks/useAuth'; +import { HomeIcon, UsersIcon, FileTextIcon, SettingsIcon, BarChartIcon, ArrowLeftIcon, UserPlusIcon, DownloadIcon, FilterIcon } from 'lucide-react'; + +interface Customer { + id: string; + name: string; + email: string; + phone: string; + status: 'Active' | 'Inactive' | 'Pending'; + policies: number; + joinDate: string; +} + +interface TableCellProps { + row: { + original: Customer; + }; +} + +export function CustomersPage() { + const navigate = useNavigate(); + const { user, logout } = useAuth(); + + const handleLogout = () => { + logout(); + navigate('/login'); + }; + + // Navigation items for the sidebar + const navItems = [ + { + id: 'dashboard', + label: 'Dashboard', + icon: , + href: '/dashboard', + isActive: false, + }, + { + id: 'customers', + label: 'Customers', + icon: , + href: '/customers', + isActive: true, + }, + { + id: 'policies', + label: 'Policies', + icon: , + href: '#', + isActive: false, + }, + { + id: 'reports', + label: 'Reports', + icon: , + href: '#', + isActive: false, + }, + { + id: 'settings', + label: 'Settings', + icon: , + href: '#', + isActive: false, + }, + ]; + + // User actions for the top bar + const userActions = ( +
+
+
+ Welcome, {user?.name || 'Demo User'}! +
+ +
+ ); + + // Logo for the top bar + const logo = ( +
+
+ UI +
+
+ Insurance Portal +
Customer Management
+
+
+ ); + + // Breadcrumbs for the page + const breadcrumbs = [ + { label: 'Dashboard', href: '/dashboard' }, + { label: 'Customers', href: '/customers' }, + ]; + + // Sample customer data + const customers: Customer[] = [ + { + id: '1', + name: 'John Smith', + email: 'john.smith@email.com', + phone: '+1 (555) 123-4567', + status: 'Active', + policies: 3, + joinDate: '2023-01-15', + }, + { + id: '2', + name: 'Sarah Johnson', + email: 'sarah.j@email.com', + phone: '+1 (555) 234-5678', + status: 'Active', + policies: 2, + joinDate: '2023-02-20', + }, + { + id: '3', + name: 'Michael Brown', + email: 'michael.brown@email.com', + phone: '+1 (555) 345-6789', + status: 'Inactive', + policies: 1, + joinDate: '2022-11-10', + }, + { + id: '4', + name: 'Emily Davis', + email: 'emily.davis@email.com', + phone: '+1 (555) 456-7890', + status: 'Active', + policies: 4, + joinDate: '2023-03-05', + }, + { + id: '5', + name: 'David Wilson', + email: 'david.wilson@email.com', + phone: '+1 (555) 567-8901', + status: 'Pending', + policies: 0, + joinDate: '2024-01-12', + }, + ]; + + const columns = [ + { + header: 'Customer', + accessorKey: 'name', + cell: ({ row }: TableCellProps) => ( +
+
+ + {row.original.name.split(' ').map((n: string) => n[0]).join('')} + +
+
+
{row.original.name}
+
{row.original.email}
+
+
+ ), + }, + { + header: 'Phone', + accessorKey: 'phone', + cell: ({ row }: TableCellProps) => ( + {row.original.phone} + ), + }, + { + header: 'Status', + accessorKey: 'status', + cell: ({ row }: TableCellProps) => { + const status = row.original.status; + const statusColors = { + Active: 'bg-success-light text-success', + Inactive: 'bg-gray-200 text-gray', + Pending: 'bg-light-orange text-warning', + }; + return ( + + {status} + + ); + }, + }, + { + header: 'Policies', + accessorKey: 'policies', + cell: ({ row }: TableCellProps) => ( + {row.original.policies} + ), + }, + { + header: 'Join Date', + accessorKey: 'joinDate', + cell: ({ row }: TableCellProps) => ( + {new Date(row.original.joinDate).toLocaleDateString()} + ), + }, + { + header: 'Actions', + id: 'actions', + cell: () => ( +
+ + +
+ ), + }, + ]; + + const activeCustomers = customers.filter(c => c.status === 'Active').length; + const pendingCustomers = customers.filter(c => c.status === 'Pending').length; + const totalPolicies = customers.reduce((sum, c) => sum + c.policies, 0); + + return ( + +
+ {/* Page Header */} +
+
+
+ +
+

Customers

+

Manage your customer database and relationships

+
+
+ + + +
+
+ + {/* Stats Cards */} +
+
+
+
+

Active Customers

+

{activeCustomers}

+
+
+ +
+
+
+
+
+
+

Pending Approvals

+

{pendingCustomers}

+
+
+ +
+
+
+
+
+
+

Total Policies

+

{totalPolicies}

+
+
+ +
+
+
+
+ + {/* Customer Table */} +
+
+
+
+

Customer List

+

Manage and view all customer information

+
+
+ Showing {customers.length} customers +
+
+
+
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/packages/showcase/src/pages/DashboardPage.tsx b/packages/showcase/src/pages/DashboardPage.tsx new file mode 100644 index 0000000..47b4537 --- /dev/null +++ b/packages/showcase/src/pages/DashboardPage.tsx @@ -0,0 +1,310 @@ +import { useNavigate } from 'react-router-dom'; +import { AppShell, Button } from '@org/ui-kit'; +import { useAuth } from '../hooks/useAuth'; +import { HomeIcon, UsersIcon, FileTextIcon, SettingsIcon, BarChartIcon, TrendingUpIcon, AlertCircleIcon, DollarSignIcon, CalendarCheck, Download, Ellipsis, TrendingDown } from 'lucide-react'; + +export function DashboardPage() { + const navigate = useNavigate(); + const { user, logout } = useAuth(); + + const handleLogout = () => { + logout(); + navigate('/login'); + }; + + // Navigation items for the sidebar + const navItems = [ + { + id: 'dashboard', + label: 'Dashboard', + icon: , + href: '/dashboard', + isActive: true, + }, + { + id: 'customers', + label: 'Customers', + icon: , + href: '/customers', + isActive: false, + }, + { + id: 'policies', + label: 'Policies', + icon: , + href: '#', + isActive: false, + }, + { + id: 'reports', + label: 'Reports', + icon: , + href: '#', + isActive: false, + }, + { + id: 'settings', + label: 'Settings', + icon: , + href: '#', + isActive: false, + }, + ]; + + // User actions for the top bar + const userActions = ( +
+
+
+ Welcome, {user?.name || 'Demo User'}! +
+ +
+ ); + + // Logo for the top bar + const logo = ( +
+
+ UI +
+
+ Insurance Portal +
Dashboard Overview
+
+
+ ); + + const stats = [ + { + label: 'Total Customers', + value: '1,234', + change: '+12%', + status: 'success' as const, + icon: , + bgColor: 'bg-light-blue', + iconColor: 'text-primary' + }, + { + label: 'Active Policies', + value: '856', + change: '+13%', + status: 'success' as const, + icon: , + bgColor: 'bg-success-light', + iconColor: 'text-success' + }, + { + label: 'Pending Claims', + value: '23', + change: '-8%', + status: 'warning' as const, + icon: , + bgColor: 'bg-light-orange', + iconColor: 'text-warning' + }, + { + label: 'Monthly Revenue', + value: '$45,678', + change: '-3%', + status: 'error' as const, + icon: , + bgColor: 'bg-light-purple', + iconColor: 'text-primary' + }, + ]; + + return ( + +
+ {/* Page Heading */} +
+

Dashboard

+

Welcome back! Here's what's happening with your business today.

+
+ + {/* Main Content */} +
+ {/* Sales Overview Card */} +
+
+
+
+
+

+ Sales Overview +

+

+ 10 March 2024 - 10 April 2024 +

+
+ + 10 Mar, 2024 - 10 Apr, 2024 +
+
+
+

+ $75,485.57 +

+
+
+ + 15.15% +
+ + + $150.48 Increased + +
+
+
+
+
+ +

Chart placeholder

+
+
+
+ + {/* Stats Grid */} +
+ {stats.map((stat, index) => ( +
+
+
+ {stat.icon} +
+
+
+

{stat.label}

+

{stat.value}

+
+ {stat.change.startsWith('+') ? ( + + ) : ( + + )} + + {stat.change} + +
+
+
+ ))} +
+
+
+ +
+ {/* Recent Activity */} +
+
+
+
+

Recent Activity

+

Latest updates and notifications

+
+ +
+
+
+
+
+

New customer registration

+

John Smith joined the platform

+ 2h ago +
+
+
+
+
+

Policy renewal processed

+

Auto insurance policy #AI-2024-001

+ 4h ago +
+
+
+
+
+

Claim requires attention

+

Vehicle damage claim #VD-2024-089

+ 6h ago +
+
+
+
+
+ + {/* Quick Actions */} +
+
+
+
+

Quick Actions

+

Frequently used operations

+
+ +
+
+ + + + +
+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/packages/showcase/src/pages/LoginPage.tsx b/packages/showcase/src/pages/LoginPage.tsx new file mode 100644 index 0000000..f0cec6c --- /dev/null +++ b/packages/showcase/src/pages/LoginPage.tsx @@ -0,0 +1,87 @@ +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { AuthShell, Button, TextInput, useToast } from '@org/ui-kit'; +import { useAuth } from '../hooks/useAuth'; +import type { LoginCredentials } from '../types/Auth'; + +const loginSchema = z.object({ + email: z.string().email('Please enter a valid email address'), + password: z.string().min(6, 'Password must be at least 6 characters'), +}); + +export function LoginPage() { + const navigate = useNavigate(); + const { login, isAuthenticated, isLoading, error, clearError } = useAuth(); + const { error: showError } = useToast(); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(loginSchema), + }); + + useEffect(() => { + if (isAuthenticated) { + navigate('/dashboard'); + } + }, [isAuthenticated, navigate]); + + useEffect(() => { + if (error) { + showError('Login Failed', error); + clearError(); + } + }, [error, showError, clearError]); + + const onSubmit = async (data: LoginCredentials) => { + await login(data); + }; + + return ( + +
+
+

Welcome Back

+

Sign in to your account

+
+ +
+ + + + + + + +
+

Demo credentials:

+

Admin: admin@example.com / admin123

+

User: user@example.com / user123

+
+
+
+ ); +} \ No newline at end of file diff --git a/packages/showcase/src/services/mockData.ts b/packages/showcase/src/services/mockData.ts new file mode 100644 index 0000000..a84d8e3 --- /dev/null +++ b/packages/showcase/src/services/mockData.ts @@ -0,0 +1,114 @@ +import type { Customer } from '../types/Customer'; + +export const mockCustomers: Customer[] = [ + { + id: '1', + name: 'John Smith', + email: 'john.smith@email.com', + company: 'Acme Corp', + status: 'active', + joinDate: '2023-01-15', + lastActivity: '2024-01-10', + totalOrders: 12, + totalValue: 45600, + }, + { + id: '2', + name: 'Sarah Johnson', + email: 'sarah.johnson@techstart.com', + company: 'TechStart Inc', + status: 'active', + joinDate: '2023-03-22', + lastActivity: '2024-01-08', + totalOrders: 8, + totalValue: 32400, + }, + { + id: '3', + name: 'Michael Brown', + email: 'michael.brown@globalco.com', + company: 'Global Solutions', + status: 'pending', + joinDate: '2023-12-01', + lastActivity: '2024-01-05', + totalOrders: 3, + totalValue: 12800, + }, + { + id: '4', + name: 'Emily Davis', + email: 'emily.davis@innovate.com', + company: 'Innovate Labs', + status: 'active', + joinDate: '2023-06-10', + lastActivity: '2024-01-09', + totalOrders: 15, + totalValue: 67200, + }, + { + id: '5', + name: 'David Wilson', + email: 'david.wilson@enterprise.com', + company: 'Enterprise Systems', + status: 'inactive', + joinDate: '2022-11-30', + lastActivity: '2023-12-15', + totalOrders: 25, + totalValue: 89500, + }, + { + id: '6', + name: 'Lisa Anderson', + email: 'lisa.anderson@startup.com', + company: 'Startup Ventures', + status: 'active', + joinDate: '2023-08-14', + lastActivity: '2024-01-07', + totalOrders: 6, + totalValue: 23400, + }, + { + id: '7', + name: 'Robert Taylor', + email: 'robert.taylor@megacorp.com', + company: 'MegaCorp Industries', + status: 'active', + joinDate: '2023-02-28', + lastActivity: '2024-01-06', + totalOrders: 18, + totalValue: 78900, + }, + { + id: '8', + name: 'Jennifer Martinez', + email: 'jennifer.martinez@solutions.com', + company: 'Smart Solutions', + status: 'pending', + joinDate: '2023-11-20', + lastActivity: '2024-01-04', + totalOrders: 2, + totalValue: 8600, + }, + { + id: '9', + name: 'Christopher Lee', + email: 'christopher.lee@dynamics.com', + company: 'Dynamic Systems', + status: 'active', + joinDate: '2023-04-05', + lastActivity: '2024-01-03', + totalOrders: 11, + totalValue: 41200, + }, + { + id: '10', + name: 'Amanda White', + email: 'amanda.white@future.com', + company: 'Future Tech', + status: 'active', + joinDate: '2023-07-18', + lastActivity: '2024-01-02', + totalOrders: 9, + totalValue: 35700, + }, +]; \ No newline at end of file diff --git a/packages/showcase/src/test-setup.ts b/packages/showcase/src/test-setup.ts new file mode 100644 index 0000000..1817ca7 --- /dev/null +++ b/packages/showcase/src/test-setup.ts @@ -0,0 +1,2 @@ +// Test setup file for vitest +// Basic setup without jest-dom for now \ No newline at end of file diff --git a/packages/showcase/src/types/Auth.ts b/packages/showcase/src/types/Auth.ts new file mode 100644 index 0000000..1326112 --- /dev/null +++ b/packages/showcase/src/types/Auth.ts @@ -0,0 +1,18 @@ +export interface User { + id: string; + email: string; + name: string; + role: 'admin' | 'user'; +} + +export interface LoginCredentials { + email: string; + password: string; +} + +export interface AuthState { + user: User | null; + isAuthenticated: boolean; + isLoading: boolean; + error: string | null; +} \ No newline at end of file diff --git a/packages/showcase/src/types/Customer.ts b/packages/showcase/src/types/Customer.ts new file mode 100644 index 0000000..fb74e24 --- /dev/null +++ b/packages/showcase/src/types/Customer.ts @@ -0,0 +1,11 @@ +export interface Customer { + id: string; + name: string; + email: string; + company: string; + status: 'active' | 'inactive' | 'pending'; + joinDate: string; + lastActivity: string; + totalOrders: number; + totalValue: number; +} \ No newline at end of file diff --git a/packages/showcase/src/types/ui-kit.d.ts b/packages/showcase/src/types/ui-kit.d.ts new file mode 100644 index 0000000..05ebc45 --- /dev/null +++ b/packages/showcase/src/types/ui-kit.d.ts @@ -0,0 +1,102 @@ +declare module '@org/ui-kit' { + import { ReactNode } from 'react'; + + export interface User { + id: string; + email: string; + name: string; + role: 'admin' | 'user'; + } + + export interface ButtonProps { + children: ReactNode; + onClick?: () => void; + type?: 'button' | 'submit' | 'reset'; + className?: string; + disabled?: boolean; + variant?: 'default' | 'outline' | 'ghost'; + size?: 'sm' | 'md' | 'lg'; + } + + export interface TextInputProps { + label?: string; + type?: string; + placeholder?: string; + error?: string; + name?: string; + value?: string; + onChange?: (e: React.ChangeEvent) => void; + onBlur?: (e: React.FocusEvent) => void; + } + + export interface StatusBadgeProps { + children: ReactNode; + status: 'active' | 'inactive' | 'pending' | 'success' | 'warning' | 'error'; + } + + export interface DataTableProps { + data: unknown[]; + columns: unknown[]; + searchable?: boolean; + pagination?: { + pageSize: number; + showSizeSelector?: boolean; + }; + } + + export interface NavItem { + id: string; + label: string; + icon?: ReactNode; + href?: string; + isActive?: boolean; + onClick?: () => void; + children?: NavItem[]; + isGroup?: boolean; + isExpanded?: boolean; + } + + export interface BreadcrumbItem { + label: string; + href?: string; + isActive?: boolean; + } + + export interface AppShellProps { + children?: ReactNode; + logo?: ReactNode; + navItems?: NavItem[]; + topNavItems?: ReactNode; + userActions?: ReactNode; + breadcrumbs?: BreadcrumbItem[]; + fixedHeader?: boolean; + defaultCollapsed?: boolean; + className?: string; + fixedWidth?: boolean; + } + + export interface AuthShellProps { + children: ReactNode; + } + + export interface ToastProviderProps { + children: ReactNode; + } + + export interface UseToastReturn { + toast: (options: Record) => string; + success: (title: string, description?: string) => string; + error: (title: string, description?: string) => string; + warning: (title: string, description?: string) => string; + info: (title: string, description?: string) => string; + } + + export const Button: React.FC; + export const TextInput: React.ForwardRefExoticComponent; + export const StatusBadge: React.FC; + export const DataTable: React.FC; + export const AppShell: React.FC; + export const AuthShell: React.FC; + export const ToastProvider: React.FC; + export const useToast: () => UseToastReturn; +} \ No newline at end of file diff --git a/packages/showcase/tailwind.config.js b/packages/showcase/tailwind.config.js new file mode 100644 index 0000000..57a1af1 --- /dev/null +++ b/packages/showcase/tailwind.config.js @@ -0,0 +1,118 @@ +import daisyui from "daisyui"; +import tailwindcssAnimate from "tailwindcss-animate"; + +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + safelist: [ + // Ensure DaisyUI component classes are always generated + 'btn', 'btn-primary', 'btn-secondary', 'btn-accent', 'btn-neutral', + 'card', 'card-body', 'card-title', 'card-actions', + 'input', 'input-primary', 'input-secondary', + 'modal', 'modal-box', 'modal-backdrop', + 'navbar', 'drawer', 'drawer-content', 'drawer-side', + 'bg-base-100', 'bg-base-200', 'bg-base-300', + 'text-base-content', 'text-primary', 'text-secondary', + // Add missing utility classes + 'bg-primary', 'bg-secondary', 'bg-accent', + 'text-primary-foreground', 'text-secondary-foreground', 'text-accent-foreground', + 'border-primary', 'border-secondary', 'border-accent', + // Nexadash-inspired classes + 'bg-light-theme', 'text-gray', 'shadow-3xl' + ], + darkMode: ['class', 'class'], + theme: { + extend: { + fontFamily: { + 'plus-jakarta': ['Plus Jakarta Sans', 'sans-serif'], + sans: ['Plus Jakarta Sans', 'sans-serif'], + }, + colors: { + border: "var(--border)", + input: "var(--input)", + ring: "var(--ring)", + background: "var(--background)", + foreground: "var(--foreground)", + primary: { + DEFAULT: "var(--primary)", + foreground: "var(--primary-foreground)", + hover: "var(--color-primary-hover)", + }, + secondary: { + DEFAULT: "var(--secondary)", + foreground: "var(--secondary-foreground)", + }, + accent: { + DEFAULT: "var(--accent)", + foreground: "var(--accent-foreground)", + }, + destructive: { + DEFAULT: "var(--destructive)", + foreground: "var(--destructive-foreground)", + }, + card: { + DEFAULT: "var(--card)", + foreground: "var(--card-foreground)", + }, + popover: { + DEFAULT: "var(--popover)", + foreground: "var(--popover-foreground)", + }, + success: { + DEFAULT: "var(--success)", + foreground: "var(--success-foreground)", + light: "var(--color-success-light)", + }, + warning: { + DEFAULT: "var(--warning)", + foreground: "var(--warning-foreground)", + }, + danger: { + DEFAULT: "var(--color-danger)", + light: "var(--color-danger-light)", + }, + // Nexadash color palette + gray: { + DEFAULT: "var(--color-gray)", + 100: "var(--color-gray-100)", + 200: "var(--color-gray-200)", + 300: "var(--color-gray-300)", + 400: "var(--color-gray-400)", + 500: "var(--color-gray-500)", + 600: "var(--color-gray-600)", + 700: "var(--color-gray-700)", + }, + 'light-theme': "var(--color-light-theme)", + 'light-orange': "var(--color-light-orange)", + 'light-blue': "var(--color-light-blue)", + 'light-purple': "var(--color-light-purple)", + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + boxShadow: { + sm: "var(--shadow-sm)", + '3xl': "var(--shadow-3xl)", + DEFAULT: "var(--shadow)", + md: "var(--shadow-md)", + lg: "var(--shadow-lg)", + }, + }, + }, + plugins: [daisyui, tailwindcssAnimate], + daisyui: { + themes: ["light", "dark"], + darkTheme: "dark", + base: true, + styled: true, + utils: true, + prefix: "", + logs: false, + themeRoot: ":root", + }, +} \ No newline at end of file diff --git a/packages/showcase/tsconfig.json b/packages/showcase/tsconfig.json new file mode 100644 index 0000000..6823565 --- /dev/null +++ b/packages/showcase/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"], + "exclude": ["../../vendors/**"], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/packages/showcase/tsconfig.node.json b/packages/showcase/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/packages/showcase/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/showcase/vite.config.ts b/packages/showcase/vite.config.ts new file mode 100644 index 0000000..992a905 --- /dev/null +++ b/packages/showcase/vite.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + build: { + outDir: 'dist', + sourcemap: true, + }, + server: { + port: 3000, + open: true, + }, +}); \ No newline at end of file diff --git a/packages/showcase/vitest.config.ts b/packages/showcase/vitest.config.ts new file mode 100644 index 0000000..1c807f0 --- /dev/null +++ b/packages/showcase/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + setupFiles: ['./src/test-setup.ts'], + }, +}); \ No newline at end of file diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index e9c33c0..4e669cc 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -3,15 +3,16 @@ "version": "0.1.0", "private": true, "type": "module", - "main": "./dist/index.js", - "module": "./dist/index.js", + "main": "./dist/ui-kit.umd.cjs", + "module": "./dist/ui-kit.js", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } + "import": "./dist/ui-kit.js", + "require": "./dist/ui-kit.umd.cjs" + }, + "./dist/style.css": "./dist/style.css" }, "files": [ "dist/**" diff --git a/packages/ui-kit/src/index.ts b/packages/ui-kit/src/index.ts index 513b0fb..2779e65 100644 --- a/packages/ui-kit/src/index.ts +++ b/packages/ui-kit/src/index.ts @@ -4,6 +4,7 @@ export * from './components' // Layout components - using named exports to avoid conflicts export { AppShell, + AuthShell, MinimalShell, WizardShell } from './layout' diff --git a/packages/ui-kit/tsconfig.json b/packages/ui-kit/tsconfig.json index ac90e3d..20f1f0f 100644 --- a/packages/ui-kit/tsconfig.json +++ b/packages/ui-kit/tsconfig.json @@ -6,10 +6,12 @@ "module": "ESNext", "skipLibCheck": true, "moduleResolution": "bundler", - "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, - "noEmit": true, + "noEmit": false, + "declaration": true, + "declarationMap": true, + "outDir": "dist", "jsx": "react-jsx", "strict": true, "noUnusedLocals": true, @@ -21,7 +23,8 @@ }, "types": ["vitest/globals", "@testing-library/jest-dom", "node"] }, - "include": ["src", "vitest.config.ts"], + "include": ["src"], + "exclude": ["src/**/*.test.tsx", "src/**/*.stories.tsx", "../../vendors/**"], "references": [ { "path": "./tsconfig.node.json" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39a4c92..9b9a59b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -126,6 +126,91 @@ importers: packages/eslint-plugin-ui-kit-rules: {} + packages/showcase: + dependencies: + '@hookform/resolvers': + specifier: ^3.6.1 + version: 3.10.0(react-hook-form@7.56.4(react@19.1.0)) + '@org/ui-kit': + specifier: workspace:* + version: link:../ui-kit + react: + specifier: ^19.1.0 + version: 19.1.0 + react-dom: + specifier: ^19.1.0 + version: 19.1.0(react@19.1.0) + react-hook-form: + specifier: ^7.56.4 + version: 7.56.4(react@19.1.0) + react-router: + specifier: ^7.0.0 + version: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-router-dom: + specifier: ^7.0.0 + version: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + zod: + specifier: ^3.25.7 + version: 3.25.7 + zustand: + specifier: ^5.0.4 + version: 5.0.4(@types/react@19.1.4)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) + devDependencies: + '@eslint/js': + specifier: ^9.27.0 + version: 9.27.0 + '@playwright/test': + specifier: ^1.52.0 + version: 1.52.0 + '@types/react': + specifier: ^19.1.4 + version: 19.1.4 + '@types/react-dom': + specifier: ^19.1.5 + version: 19.1.5(@types/react@19.1.4) + '@vitejs/plugin-react': + specifier: ^4.4.1 + version: 4.4.1(vite@5.4.19(@types/node@22.15.19)) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.3) + daisyui: + specifier: ^5.0.35 + version: 5.0.35 + eslint: + specifier: ^9.27.0 + version: 9.27.0(jiti@2.4.2) + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-react-refresh: + specifier: ^0.4.20 + version: 0.4.20(eslint@9.27.0(jiti@2.4.2)) + postcss: + specifier: ^8.5.3 + version: 8.5.3 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.17) + typescript: + specifier: ^5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: ^8.32.1 + version: 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + vite: + specifier: ^5.4.19 + version: 5.4.19(@types/node@22.15.19) + vitest: + specifier: ^1.6.1 + version: 1.6.1(@types/node@22.15.19)(jsdom@26.1.0) + packages/ui-kit: dependencies: '@hookform/resolvers': @@ -1466,6 +1551,11 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@hookform/resolvers@3.10.0': + resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} + peerDependencies: + react-hook-form: ^7.0.0 + '@hookform/resolvers@5.0.1': resolution: {integrity: sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==} peerDependencies: @@ -3275,6 +3365,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + core-js-compat@3.42.0: resolution: {integrity: sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==} @@ -5493,6 +5587,23 @@ packages: '@types/react': optional: true + react-router-dom@7.6.0: + resolution: {integrity: sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.6.0: + resolution: {integrity: sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -5686,6 +5797,9 @@ packages: set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -7871,6 +7985,10 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 + '@hookform/resolvers@3.10.0(react-hook-form@7.56.4(react@19.1.0))': + dependencies: + react-hook-form: 7.56.4(react@19.1.0) + '@hookform/resolvers@5.0.1(react-hook-form@7.56.4(react@19.1.0))': dependencies: '@standard-schema/utils': 0.3.0 @@ -9940,6 +10058,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.0.2: {} + core-js-compat@3.42.0: dependencies: browserslist: 4.24.5 @@ -12552,6 +12672,20 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + react-router-dom@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-router: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + + react-router@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + cookie: 1.0.2 + react: 19.1.0 + set-cookie-parser: 2.7.1 + optionalDependencies: + react-dom: 19.1.0(react@19.1.0) + react-style-singleton@2.2.3(@types/react@19.1.4)(react@19.1.0): dependencies: get-nonce: 1.0.1 @@ -12777,6 +12911,8 @@ snapshots: set-blocking@2.0.0: {} + set-cookie-parser@2.7.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4