diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..84103b8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +node_modules +dist +.git +.gitignore +*.log +playwright-report +test-results +coverage +e2e/.auth +.moai +.claude diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2322540 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.env b/.env index 731b3da..09e8294 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -VITE_REACT_APP_API_URL=http://localhost +VITE_REACT_APP_API_URL=https://localhost diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6ed8a82 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# URL of your Sylius API (no trailing slash) +# Dev: https://localhost +# Prod: https://api.myshop.com +VITE_REACT_APP_API_URL=https://localhost diff --git a/.env.test.example b/.env.test.example new file mode 100644 index 0000000..356ab2d --- /dev/null +++ b/.env.test.example @@ -0,0 +1,17 @@ +# Test environment variables +# Copy this file to .env.test.local and fill in the values +# Used by: make docker-test-e2e (passed via --env-file .env.test.local) + +# Base URL for Playwright E2E tests +# Local dev server (pnpm dev): https://localhost:5173 +# Docker E2E (set automatically): https://app:4174 +PLAYWRIGHT_BASE_URL=http://localhost:5173 + +# Test user credentials for E2E authentication +PLAYWRIGHT_TEST_EMAIL=test@example.com +PLAYWRIGHT_TEST_PASSWORD=your-test-password + +# Sylius API base URL +# Local (pointing to your Sylius instance): https://localhost +# Docker E2E: overridden automatically by compose.test.yml to https://api-proxy +VITE_REACT_APP_API_URL=https://localhost diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a8e5650 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,71 @@ +name: Tests + +on: + push: + branches: [main, dev, feat/**] + pull_request: + branches: [main, dev] + +jobs: + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run unit tests + run: pnpm test:unit --run + + e2e-tests: + name: E2E Tests + needs: unit-tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install Playwright browsers + run: pnpm exec playwright install --with-deps chromium + + - name: Build app + run: pnpm build + + - name: Run E2E tests + run: pnpm test:e2e + env: + PLAYWRIGHT_BASE_URL: ${{ env.PLAYWRIGHT_BASE_URL || 'http://localhost:5173' }} + PLAYWRIGHT_TEST_EMAIL: ${{ secrets.PLAYWRIGHT_TEST_EMAIL }} + PLAYWRIGHT_TEST_PASSWORD: ${{ secrets.PLAYWRIGHT_TEST_PASSWORD }} + VITE_REACT_APP_API_URL: ${{ secrets.VITE_REACT_APP_API_URL }} + CI: true + + - name: Upload Playwright report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: playwright-report/ + retention-days: 7 diff --git a/.gitignore b/.gitignore index a547bf3..88db6b0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ pnpm-debug.log* lerna-debug.log* node_modules +.pnpm-store dist dist-ssr *.local @@ -22,3 +23,15 @@ dist-ssr *.njsproj *.sln *.sw? + +# AI directories and files +.claude +.moai + +# Test artifacts +playwright-report/ +test-results/ +.auth/ + +# Local TLS certificates (generated by mkcert, machine-specific) +docker/app/certs/*.pem \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..cb2c84d --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint-staged diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..8ae9a73 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/.mcp.schema.json", + "mcpServers": { + "context7": { + "$comment": "Up-to-date documentation and code examples via Context7", + "command": "/bin/bash", + "args": ["-l", "-c", "exec npx -y @upstash/context7-mcp@latest"] + }, + "sequential-thinking": { + "$comment": "Step-by-step reasoning for complex problems", + "command": "/bin/bash", + "args": ["-l", "-c", "exec npx -y @modelcontextprotocol/server-sequential-thinking"] + }, + "shadcn": { + "command": "npx", + "args": ["shadcn@latest", "mcp"] + } + }, + "staggeredStartup": { + "enabled": true, + "delayMs": 500, + "connectionTimeout": 15000 + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..2bd4ae0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist/ +node_modules/ +pnpm-lock.yaml +*.generated.* +schema/ +public/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..eeb4765 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 2, + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..220e0f5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,129 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Vitest v4 unit test suite — 48 tests across schemas, utilities, and UI components (SPEC-TEST-001) + - `src/schemas/__tests__/` — auth, account, review schema validation (28 tests) + - `src/lib/__tests__/` — `formError`, `applyServerErrors`, `clearServerErrors` utilities (6 tests) + - `src/components/ui/__tests__/` — `FieldError`, `PasswordStrength` components (14 tests) +- Playwright v1.58 E2E test suite with Page Object Model pattern (SPEC-TEST-001) + - Auth flows: login, register, logout + - Form validation: inline errors, onBlur/onSubmit behaviour + - Account pages: profile update, change password + - Auth fixture with JWT seed via Sylius API (`e2e/fixtures/auth.ts`) +- Docker multi-stage test infrastructure (`docker/Dockerfile.test`) with isolated stages per concern (SPEC-TEST-001) +- `docker/compose.test.yml` — fully self-contained test environment (unit-tests, app-serve, e2e-tests services) +- `docker/compose.yml` — React dev container with hot-reload (node:22-alpine) +- `docker/Dockerfile` — multi-stage app image (dev, builder, production) +- `Makefile` — unified command interface for development, Docker, and all test scenarios +- GitHub Actions CI workflow (`.github/workflows/test.yml`) — unit job (blocking) then E2E job on `main`, `dev`, and `feat/**` branches +- `FieldError` component (`src/components/ui/field-error.tsx`) — accessible inline field error with `id`, `aria-live` (SPEC-UPDATE-001) +- `applyServerErrors` utility — maps Sylius API error payloads to TanStack Form server errors (SPEC-UPDATE-001) +- `clearServerErrors` utility — clears `onServer` errors before re-submission to unblock `canSubmit` (SPEC-UPDATE-001) +- `onBlur` validators on all form fields using the same Zod schemas as `onSubmit` (SPEC-UPDATE-001) +- `aria-describedby` linking all inputs to their `FieldError` component (SPEC-UPDATE-001) + +### Changed + +- All form fields updated with `aria-invalid` driven by TanStack Form field state (SPEC-UPDATE-001) +- `submitForm()` utility extended to call `clearServerErrors` before submission (SPEC-UPDATE-001) + +## [0.4.0] - 2026-03-09 + +### Added + +- `@tanstack/react-form` v1.28.4 and `zod` v3.25.76 as production dependencies (SPEC-REFACTOR-001) +- `src/schemas/` directory with centralized Zod schemas: `auth.ts`, `address.ts`, `account.ts`, `review.ts`, `index.ts` +- `formError()` utility in `src/lib/utils.ts` — safely extracts error message from TanStack Form v1 error values (handles both Zod `ZodIssue` objects and plain strings) +- `noValidate` attribute on all form elements — disables browser-native HTML5 validation in favour of TanStack Form inline errors +- `aria-invalid` attribute on all validated inputs and `SelectTrigger` components across 10 form pages (accessibility improvement) +- Form-level pre-fill timing guard (`canInitialize`) on `AddressPage` — waits for async customer/order data before populating form fields + +### Changed + +- All 10 form pages migrated from raw `useState` to TanStack Form `useForm` + `form.Field` render-prop pattern (SPEC-REFACTOR-001) + - Auth pages: `LoginPage`, `RegisterPage`, `ForgotPasswordPage`, `ResetPasswordPage` + - Account pages: `ProfilePage`, `ChangePasswordPage`, `AddAddressPage`, `EditAddressPage` + - Checkout: `AddressPage` + - Product: `AddReviewPage` +- `AddressForm` refactored to receive TanStack Form field API via render-prop — parent `useForm` instance now controls validation and submission for account address pages +- `AddressPage` nested address fields (`billingAddress.*`, `shippingAddress.*`) validated via form-level `validators.onSubmit` using `addressSchema.safeParse()` with `{ fields: {} }` error map format +- `RegisterPage` password strength indicator and show/hide toggles preserved; strength score computed via `useMemo` from `useStore`-subscribed password value +- Zod schemas replace all hand-written `if` checks, `validateAddress()` function, and `Record` form data objects; TypeScript types derived via `z.infer<>` enforce type safety between form values and API payloads + +### Removed + +- `validateAddress()` ad-hoc validation function (replaced by `addressSchema`) +- All `useState`-based manual field tracking and error state across migrated pages +- `@tanstack/zod-form-adapter` runtime dependency (TanStack Form v1 uses native StandardSchema — adapter installed but not used) + +## [0.3.0] - 2026-03-01 + +### Added + +- Prettier v3 with `prettier-plugin-tailwindcss` for automatic Tailwind class sorting +- Husky v9 pre-commit git hook running lint-staged automatically +- lint-staged: runs `eslint --fix` + `prettier --write` on staged `.ts/.tsx` files +- `.editorconfig` for consistent editor settings (UTF-8, LF, 2-space indent) +- `pnpm format` script for manual full-codebase formatting + +### Changed + +- ESLint config updated: added `eslint-config-prettier` (disables formatting rules conflicting with Prettier) +- ESLint config: `react-refresh/only-export-components` disabled for `src/components/ui/` and `src/context/` (legitimate multi-export pattern) +- `react-hooks/exhaustive-deps` warnings fixed in `ProductList.tsx` and `ProductPage.tsx` by moving fetch functions inside `useEffect`/`useCallback` +- Entire codebase reformatted with Prettier baseline (formatting-only, no logic changes) + +## [0.2.0] - 2026-03-01 + +### Added + +- TailwindCSS v4 via `@tailwindcss/vite` Vite plugin +- shadcn/ui component library with Base UI (`base-vega` style) +- New UI components: `Button`, `Input`, `Textarea`, `Select`, `Table`, `Badge`, `Alert`, `Checkbox`, `Accordion`, `Breadcrumb`, `Card`, `Sheet`, `NavigationMenu`, `DropdownMenu`, `Separator`, `Label`, `Sonner` toasts +- `@base-ui/react` as the primitives layer (replaces Radix UI) +- Mobile navigation via shadcn/ui `Sheet` component (React-controlled, replaces Bootstrap offcanvas) +- Desktop navigation via shadcn/ui `NavigationMenu` with hover dropdowns +- Flash messages replaced by Sonner toast notifications +- `input-group`, `navigation-menu`, `separator`, `textarea` shadcn/ui components added +- `@theme` block with brand color token `--color-primary: #22B99A` and Bootstrap-compatible `--breakpoint-lg: 992px` +- `.link-reset` utility class ported to Tailwind `@layer components` +- Proper TypeScript types for cart/order product rows (removes all `any` types) +- pnpm as the package manager + +### Changed + +- All UI styling migrated from Bootstrap 5 to TailwindCSS v4 +- All native HTML elements (``, `