forked from cardosofelipe/fast-next-template
Implement the main dashboard / projects list page for Syndarix as the landing page after login. The implementation includes: Dashboard Components: - QuickStats: Overview cards showing active projects, agents, issues, approvals - ProjectsSection: Grid/list view with filtering and sorting controls - ProjectCardGrid: Rich project cards for grid view - ProjectRowList: Compact rows for list view - ActivityFeed: Real-time activity sidebar with connection status - PerformanceCard: Performance metrics display - EmptyState: Call-to-action for new users - ProjectStatusBadge: Status indicator with icons - ComplexityIndicator: Visual complexity dots - ProgressBar: Accessible progress bar component Features: - Projects grid/list view with view mode toggle - Filter by status (all, active, paused, completed, archived) - Sort by recent, name, progress, or issues - Quick stats overview with counts - Real-time activity feed sidebar with live/reconnecting status - Performance metrics card - Create project button linking to wizard - Responsive layout for mobile/desktop - Loading skeleton states - Empty state for new users API Integration: - useProjects hook for fetching projects (mock data until backend ready) - useDashboardStats hook for statistics - TanStack Query for caching and data fetching Testing: - 37 unit tests covering all dashboard components - E2E test suite for dashboard functionality - Accessibility tests (keyboard nav, aria attributes, heading hierarchy) Technical: - TypeScript strict mode compliance - ESLint passing - WCAG AA accessibility compliance - Mobile-first responsive design - Dark mode support via semantic tokens - Follows design system guidelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
155 lines
4.9 KiB
TypeScript
155 lines
4.9 KiB
TypeScript
/**
|
|
* Tests for wizard constants and utility functions
|
|
*/
|
|
|
|
import {
|
|
complexityOptions,
|
|
clientModeOptions,
|
|
autonomyOptions,
|
|
getTotalSteps,
|
|
getStepLabels,
|
|
getDisplayStep,
|
|
WIZARD_STEPS,
|
|
} from '@/components/projects/wizard/constants';
|
|
|
|
describe('complexityOptions', () => {
|
|
it('has 4 options', () => {
|
|
expect(complexityOptions).toHaveLength(4);
|
|
});
|
|
|
|
it('includes script with skipConfig: true', () => {
|
|
const script = complexityOptions.find((o) => o.id === 'script');
|
|
expect(script).toBeDefined();
|
|
expect(script?.skipConfig).toBe(true);
|
|
});
|
|
|
|
it('has other options with skipConfig: false', () => {
|
|
const others = complexityOptions.filter((o) => o.id !== 'script');
|
|
expect(others.every((o) => o.skipConfig === false)).toBe(true);
|
|
});
|
|
|
|
it('has correct timelines', () => {
|
|
const script = complexityOptions.find((o) => o.id === 'script');
|
|
const simple = complexityOptions.find((o) => o.id === 'simple');
|
|
const medium = complexityOptions.find((o) => o.id === 'medium');
|
|
const complex = complexityOptions.find((o) => o.id === 'complex');
|
|
|
|
expect(script?.scope).toContain('Minutes to 1-2 hours');
|
|
expect(simple?.scope).toContain('2-3 days');
|
|
expect(medium?.scope).toContain('2-3 weeks');
|
|
expect(complex?.scope).toContain('2-3 months');
|
|
});
|
|
});
|
|
|
|
describe('clientModeOptions', () => {
|
|
it('has 2 options', () => {
|
|
expect(clientModeOptions).toHaveLength(2);
|
|
});
|
|
|
|
it('includes technical and auto modes', () => {
|
|
const ids = clientModeOptions.map((o) => o.id);
|
|
expect(ids).toContain('technical');
|
|
expect(ids).toContain('auto');
|
|
});
|
|
});
|
|
|
|
describe('autonomyOptions', () => {
|
|
it('has 3 options', () => {
|
|
expect(autonomyOptions).toHaveLength(3);
|
|
});
|
|
|
|
it('includes all autonomy levels', () => {
|
|
const ids = autonomyOptions.map((o) => o.id);
|
|
expect(ids).toContain('full_control');
|
|
expect(ids).toContain('milestone');
|
|
expect(ids).toContain('autonomous');
|
|
});
|
|
|
|
it('has valid approval matrices', () => {
|
|
autonomyOptions.forEach((option) => {
|
|
expect(option.approvals).toHaveProperty('codeChanges');
|
|
expect(option.approvals).toHaveProperty('issueUpdates');
|
|
expect(option.approvals).toHaveProperty('architectureDecisions');
|
|
expect(option.approvals).toHaveProperty('sprintPlanning');
|
|
expect(option.approvals).toHaveProperty('deployments');
|
|
});
|
|
});
|
|
|
|
it('full_control requires all approvals', () => {
|
|
const fullControl = autonomyOptions.find((o) => o.id === 'full_control');
|
|
expect(Object.values(fullControl!.approvals).every(Boolean)).toBe(true);
|
|
});
|
|
|
|
it('autonomous only requires architecture and deployments', () => {
|
|
const autonomous = autonomyOptions.find((o) => o.id === 'autonomous');
|
|
expect(autonomous!.approvals.codeChanges).toBe(false);
|
|
expect(autonomous!.approvals.issueUpdates).toBe(false);
|
|
expect(autonomous!.approvals.architectureDecisions).toBe(true);
|
|
expect(autonomous!.approvals.sprintPlanning).toBe(false);
|
|
expect(autonomous!.approvals.deployments).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('getTotalSteps', () => {
|
|
it('returns 6 for non-script mode', () => {
|
|
expect(getTotalSteps(false)).toBe(6);
|
|
});
|
|
|
|
it('returns 4 for script mode', () => {
|
|
expect(getTotalSteps(true)).toBe(4);
|
|
});
|
|
});
|
|
|
|
describe('getStepLabels', () => {
|
|
it('returns 6 labels for non-script mode', () => {
|
|
const labels = getStepLabels(false);
|
|
expect(labels).toHaveLength(6);
|
|
expect(labels).toEqual([
|
|
'Basic Info',
|
|
'Complexity',
|
|
'Client Mode',
|
|
'Autonomy',
|
|
'Agent Chat',
|
|
'Review',
|
|
]);
|
|
});
|
|
|
|
it('returns 4 labels for script mode (no Client Mode or Autonomy)', () => {
|
|
const labels = getStepLabels(true);
|
|
expect(labels).toHaveLength(4);
|
|
expect(labels).toEqual(['Basic Info', 'Complexity', 'Agent Chat', 'Review']);
|
|
});
|
|
});
|
|
|
|
describe('getDisplayStep', () => {
|
|
it('returns actual step for non-script mode', () => {
|
|
expect(getDisplayStep(1, false)).toBe(1);
|
|
expect(getDisplayStep(2, false)).toBe(2);
|
|
expect(getDisplayStep(3, false)).toBe(3);
|
|
expect(getDisplayStep(4, false)).toBe(4);
|
|
expect(getDisplayStep(5, false)).toBe(5);
|
|
expect(getDisplayStep(6, false)).toBe(6);
|
|
});
|
|
|
|
it('maps steps correctly for script mode', () => {
|
|
// Steps 1 and 2 stay the same
|
|
expect(getDisplayStep(1, true)).toBe(1);
|
|
expect(getDisplayStep(2, true)).toBe(2);
|
|
// Step 5 (Agent Chat) becomes display step 3
|
|
expect(getDisplayStep(5, true)).toBe(3);
|
|
// Step 6 (Review) becomes display step 4
|
|
expect(getDisplayStep(6, true)).toBe(4);
|
|
});
|
|
});
|
|
|
|
describe('WIZARD_STEPS', () => {
|
|
it('has correct step numbers', () => {
|
|
expect(WIZARD_STEPS.BASIC_INFO).toBe(1);
|
|
expect(WIZARD_STEPS.COMPLEXITY).toBe(2);
|
|
expect(WIZARD_STEPS.CLIENT_MODE).toBe(3);
|
|
expect(WIZARD_STEPS.AUTONOMY).toBe(4);
|
|
expect(WIZARD_STEPS.AGENT_CHAT).toBe(5);
|
|
expect(WIZARD_STEPS.REVIEW).toBe(6);
|
|
});
|
|
});
|