diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 0000000..655b8a2 --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,45 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + env: { + node: true, + es2022: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'prettier', + ], + plugins: ['@typescript-eslint', 'prettier'], + rules: { + 'prettier/prettier': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-non-null-assertion': 'warn', + }, + ignorePatterns: [ + 'node_modules/', + 'dist/', + 'build/', + '.turbo/', + 'coverage/', + '*.config.js', + '*.config.ts', + ], +}; diff --git a/frontend/.gitkeep b/frontend/.gitkeep new file mode 100644 index 0000000..dabf36a --- /dev/null +++ b/frontend/.gitkeep @@ -0,0 +1 @@ +# Workspace root diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..32a2397 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..829a2ea --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,143 @@ +# Effigenix Frontend + +TypeScript monorepo containing Terminal User Interface (TUI) and shared packages for the Effigenix ERP system. + +## Structure + +``` +frontend/ +├── apps/ +│ └── cli/ # Terminal UI (Ink) +│ +└── packages/ # Shared packages (reusable for WebUI) + ├── api-client/ # HTTP client for Backend API + ├── types/ # TypeScript types (generated from OpenAPI) + ├── validation/ # Zod schemas + └── config/ # Shared configuration +``` + +## Prerequisites + +- Node.js 20+ +- pnpm 9+ +- Backend running at http://localhost:8080 + +## Getting Started + +### Install Dependencies + +```bash +pnpm install +``` + +### Development + +```bash +# Run TUI in dev mode +pnpm run dev + +# Run tests +pnpm test + +# Type check +pnpm run typecheck + +# Lint code +pnpm run lint + +# Format code +pnpm run format +``` + +### Build + +```bash +# Build all packages +pnpm run build +``` + +## Package Development + +### Adding a New Package + +```bash +cd packages +mkdir my-package +cd my-package +pnpm init +``` + +Then update `package.json`: +- Set name to `@effigenix/my-package` +- Add `"main": "./dist/index.js"` +- Add `"types": "./dist/index.d.ts"` +- Add build script: `"build": "tsup src/index.ts"` + +### Using Packages + +```json +{ + "dependencies": { + "@effigenix/types": "workspace:*", + "@effigenix/api-client": "workspace:*" + } +} +``` + +## Type Generation + +Types are automatically generated from the backend OpenAPI spec: + +```bash +# Start backend first +cd ../backend +mvn spring-boot:run + +# Generate types +cd ../frontend/packages/types +pnpm run generate:types +``` + +## Technology Stack + +- **TypeScript** - Type-safe JavaScript +- **React** - UI library (Ink for terminal) +- **pnpm** - Fast, efficient package manager +- **Zod** - Runtime validation + type inference +- **Axios** - HTTP client with interceptors +- **tsup** - Zero-config TypeScript bundler +- **vitest** - Fast unit testing + +## Monorepo Commands + +```bash +# Install dependencies in root +pnpm install + +# Run command in specific package +pnpm run --filter @effigenix/cli dev + +# Run command in all packages +pnpm run --recursive build + +# Add dependency to specific package +pnpm add axios --filter @effigenix/api-client +``` + +## Scripts + +- `pnpm run build` - Build all packages +- `pnpm run dev` - Run CLI TUI in dev mode +- `pnpm test` - Run all tests +- `pnpm run typecheck` - Type check all packages +- `pnpm run lint` - Lint all code +- `pnpm run format` - Format all code + +## Documentation + +See individual package READMEs for detailed documentation: +- [API Client](packages/api-client/README.md) +- [Types](packages/types/README.md) +- [Validation](packages/validation/README.md) +- [Config](packages/config/README.md) +- [CLI TUI](apps/cli/README.md) diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..297e58e --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,31 @@ +{ + "name": "@effigenix/root", + "version": "0.1.0", + "private": true, + "description": "Effigenix ERP Frontend Monorepo", + "scripts": { + "build": "pnpm run --recursive build", + "dev": "pnpm run --filter @effigenix/cli dev", + "test": "pnpm run --recursive test", + "test:coverage": "pnpm run --recursive test:coverage", + "typecheck": "pnpm run --recursive typecheck", + "lint": "eslint . --ext .ts,.tsx", + "lint:fix": "eslint . --ext .ts,.tsx --fix", + "format": "prettier --write \"**/*.{ts,tsx,json,md}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,json,md}\"" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "prettier": "^3.2.5", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=20.0.0", + "pnpm": ">=9.0.0" + }, + "packageManager": "pnpm@9.15.0" +} diff --git a/frontend/packages/types/README.md b/frontend/packages/types/README.md new file mode 100644 index 0000000..d415ac6 --- /dev/null +++ b/frontend/packages/types/README.md @@ -0,0 +1,173 @@ +# @effigenix/types + +TypeScript types for the Effigenix ERP system. Types are automatically generated from the backend OpenAPI specification and extended with UI-specific types. + +## Features + +- **Auto-generated from OpenAPI** - Types are generated from the backend's OpenAPI spec +- **Type-safe** - Compile-time type checking for all API interactions +- **Custom UI types** - Additional types for UI state management +- **Clean exports** - Organized wrapper files for easy consumption + +## Installation + +This package is part of the Effigenix frontend monorepo and is not published separately. + +```bash +pnpm add @effigenix/types --filter my-package +``` + +## Usage + +### Importing Types + +```typescript +import { + // Auth types + LoginRequest, + LoginResponse, + TokenStorage, + + // User types + UserDTO, + CreateUserRequest, + UpdateUserRequest, + + // Role types + RoleDTO, + Permission, + + // Common types + ApiError, + ValidationError, + + // Enums + UserStatus, + LoadingState, + Screen +} from '@effigenix/types'; +``` + +### Example: Type-safe API request + +```typescript +import { LoginRequest, LoginResponse } from '@effigenix/types'; + +async function login(request: LoginRequest): Promise { + // TypeScript ensures the request matches the backend's expected structure + const response = await fetch('/api/auth/login', { + method: 'POST', + body: JSON.stringify(request), + }); + + return response.json() as Promise; +} +``` + +## Type Generation + +Types are automatically generated from the backend's OpenAPI specification. + +### Prerequisites + +1. Backend must be running at `http://localhost:8080` +2. OpenAPI spec must be available at `http://localhost:8080/api-docs` + +### Generate Types + +```bash +# Start backend first +cd ../../../backend +mvn spring-boot:run + +# In another terminal, generate types +cd frontend/packages/types +pnpm run generate:types +``` + +This will: +1. Fetch the OpenAPI spec from the backend +2. Generate TypeScript types in `src/generated/api.ts` +3. Types are automatically used by wrapper files (`auth.ts`, `user.ts`, etc.) + +### Build Process + +```bash +# Generate types + build package +pnpm run build + +# Type check only +pnpm run typecheck + +# Watch mode for development +pnpm run dev +``` + +## Structure + +``` +src/ +├── generated/ +│ └── api.ts # AUTO-GENERATED from OpenAPI (DO NOT EDIT) +├── auth.ts # Authentication types (wrapper + custom) +├── user.ts # User management types (wrapper + custom) +├── role.ts # Role and permission types (wrapper + custom) +├── common.ts # Common types (wrapper + custom) +├── enums.ts # Enums and constants +└── index.ts # Main export file +``` + +## Type Categories + +### Generated Types (from OpenAPI) + +These types are automatically generated and should not be edited manually: + +- `LoginRequest`, `LoginResponse` +- `UserDTO`, `CreateUserRequest`, `UpdateUserRequest` +- `RoleDTO`, `Permission` +- `ErrorResponse` + +### Custom UI Types + +These types are defined manually for UI-specific needs: + +- `TokenStorage` - Token storage structure +- `UserListFilters` - User list filtering options +- `UserFormData` - Form data for user creation/editing +- `LoadingState` - UI loading states +- `Screen` - TUI screen navigation + +## CI/CD + +In CI/CD pipelines, ensure types are generated before building: + +```bash +# Start backend in background +docker-compose up -d backend + +# Wait for backend to be ready +./scripts/wait-for-backend.sh + +# Generate types +cd frontend/packages/types +pnpm run generate:types + +# Build +pnpm run build +``` + +## Development Workflow + +1. Make changes to backend DTOs +2. Restart backend +3. Run `pnpm run generate:types` in this package +4. TypeScript will catch any breaking changes in dependent packages +5. Update wrapper files if needed (add new custom types) + +## Notes + +- The `generated/api.ts` file should **never** be edited manually +- Wrapper files (`auth.ts`, `user.ts`, etc.) re-export types with clean names +- Custom types should be added to wrapper files, not `generated/api.ts` +- Run type generation before building to ensure types are up-to-date diff --git a/frontend/packages/types/package.json b/frontend/packages/types/package.json new file mode 100644 index 0000000..64f8c4a --- /dev/null +++ b/frontend/packages/types/package.json @@ -0,0 +1,34 @@ +{ + "name": "@effigenix/types", + "version": "0.1.0", + "description": "TypeScript types for Effigenix ERP (auto-generated from OpenAPI)", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "generate:types": "openapi-typescript http://localhost:8080/api-docs -o src/generated/api.ts", + "build": "pnpm run generate:types && tsup", + "dev": "tsup --watch", + "typecheck": "tsc --noEmit", + "clean": "rm -rf dist .turbo" + }, + "devDependencies": { + "@types/node": "^20.11.0", + "openapi-typescript": "^7.4.2", + "tsup": "^8.0.1", + "typescript": "^5.3.3" + }, + "tsup": { + "entry": ["src/index.ts"], + "format": ["esm"], + "dts": true, + "clean": true, + "sourcemap": true, + "splitting": false + } +} diff --git a/frontend/packages/types/src/auth.ts b/frontend/packages/types/src/auth.ts new file mode 100644 index 0000000..3dea270 --- /dev/null +++ b/frontend/packages/types/src/auth.ts @@ -0,0 +1,31 @@ +/** + * Authentication-related types + * Re-exports types from the auto-generated OpenAPI schema + */ + +import type { components } from './generated/api'; + +// Login +export type LoginRequest = components['schemas']['LoginRequest']; +export type LoginResponse = components['schemas']['LoginResponse']; + +// Token Refresh +export type RefreshTokenRequest = components['schemas']['RefreshTokenRequest']; + +// Session Token +export type SessionToken = components['schemas']['SessionToken']; + +/** + * Custom types not in OpenAPI spec + */ + +export interface TokenStorage { + accessToken: string; + refreshToken: string; + expiresAt: string; // ISO 8601 timestamp +} + +export interface AuthConfig { + apiBaseUrl: string; + auth?: TokenStorage; +} diff --git a/frontend/packages/types/src/common.ts b/frontend/packages/types/src/common.ts new file mode 100644 index 0000000..b8199f4 --- /dev/null +++ b/frontend/packages/types/src/common.ts @@ -0,0 +1,40 @@ +/** + * Common types used across the application + * Re-exports types from the auto-generated OpenAPI schema + */ + +import type { components } from './generated/api'; + +// Error handling +export type ErrorResponse = components['schemas']['ErrorResponse']; + +/** + * Custom common types + */ + +export interface ValidationError { + field: string; + message: string; + code?: string; +} + +export interface ApiError { + message: string; + code?: string; + status?: number; + validationErrors?: ValidationError[]; +} + +export interface PaginationParams { + page?: number; + size?: number; + sort?: string; +} + +export interface PagedResponse { + content: T[]; + totalElements: number; + totalPages: number; + size: number; + number: number; +} diff --git a/frontend/packages/types/src/enums.ts b/frontend/packages/types/src/enums.ts new file mode 100644 index 0000000..5ac670c --- /dev/null +++ b/frontend/packages/types/src/enums.ts @@ -0,0 +1,36 @@ +/** + * Enums and constants + */ + +export enum UserStatus { + ACTIVE = 'ACTIVE', + LOCKED = 'LOCKED', +} + +export enum TokenType { + ACCESS = 'ACCESS', + REFRESH = 'REFRESH', +} + +/** + * UI-specific enums + */ + +export enum LoadingState { + IDLE = 'IDLE', + LOADING = 'LOADING', + SUCCESS = 'SUCCESS', + ERROR = 'ERROR', +} + +export enum Screen { + LOGIN = 'LOGIN', + MAIN_MENU = 'MAIN_MENU', + USER_LIST = 'USER_LIST', + USER_CREATE = 'USER_CREATE', + USER_DETAIL = 'USER_DETAIL', + USER_EDIT = 'USER_EDIT', + ROLE_LIST = 'ROLE_LIST', + ROLE_ASSIGN = 'ROLE_ASSIGN', + CHANGE_PASSWORD = 'CHANGE_PASSWORD', +} diff --git a/frontend/packages/types/src/generated/api.ts b/frontend/packages/types/src/generated/api.ts new file mode 100644 index 0000000..2484244 --- /dev/null +++ b/frontend/packages/types/src/generated/api.ts @@ -0,0 +1,22 @@ +// This file is AUTO-GENERATED by openapi-typescript +// DO NOT EDIT manually - run 'pnpm run generate:types' to regenerate +// +// To generate types, start the backend first: +// cd backend && mvn spring-boot:run +// Then run: +// pnpm run generate:types + +/** + * This file will contain the OpenAPI-generated types from the backend. + * After running the generate command, it will export: + * - components.schemas (all DTOs) + * - paths (all API endpoints) + */ + +export interface components { + schemas: Record; +} + +export interface paths { + [key: string]: unknown; +} diff --git a/frontend/packages/types/src/index.ts b/frontend/packages/types/src/index.ts new file mode 100644 index 0000000..b9333a6 --- /dev/null +++ b/frontend/packages/types/src/index.ts @@ -0,0 +1,16 @@ +/** + * @effigenix/types + * + * TypeScript types for Effigenix ERP system + * Auto-generated from OpenAPI specification + custom UI types + */ + +// Re-export all types +export * from './auth'; +export * from './user'; +export * from './role'; +export * from './common'; +export * from './enums'; + +// Re-export generated types for advanced usage +export type { components, paths } from './generated/api'; diff --git a/frontend/packages/types/src/role.ts b/frontend/packages/types/src/role.ts new file mode 100644 index 0000000..b9fff54 --- /dev/null +++ b/frontend/packages/types/src/role.ts @@ -0,0 +1,21 @@ +/** + * Role and Permission types + * Re-exports types from the auto-generated OpenAPI schema + */ + +import type { components } from './generated/api'; + +// Role DTOs +export type RoleDTO = components['schemas']['RoleDTO']; +export type RoleName = components['schemas']['RoleName']; +export type Permission = components['schemas']['Permission']; + +/** + * Custom types for UI + */ + +export interface RoleWithUsers { + role: RoleDTO; + userCount: number; + users?: Array<{ id: string; username: string }>; +} diff --git a/frontend/packages/types/src/user.ts b/frontend/packages/types/src/user.ts new file mode 100644 index 0000000..14d63b1 --- /dev/null +++ b/frontend/packages/types/src/user.ts @@ -0,0 +1,36 @@ +/** + * User Management types + * Re-exports types from the auto-generated OpenAPI schema + */ + +import type { components } from './generated/api'; + +// User DTOs +export type UserDTO = components['schemas']['UserDTO']; +export type CreateUserRequest = components['schemas']['CreateUserRequest']; +export type UpdateUserRequest = components['schemas']['UpdateUserRequest']; + +// Password Management +export type ChangePasswordRequest = components['schemas']['ChangePasswordRequest']; + +// Role Assignment +export type AssignRoleRequest = components['schemas']['AssignRoleRequest']; + +/** + * Custom types for UI state + */ + +export interface UserListFilters { + status?: 'ACTIVE' | 'LOCKED'; + roleId?: string; + branchId?: string; + search?: string; +} + +export interface UserFormData { + username: string; + email: string; + password: string; + roleIds: string[]; + branchId?: string; +} diff --git a/frontend/packages/types/tsconfig.json b/frontend/packages/types/tsconfig.json new file mode 100644 index 0000000..510f9b8 --- /dev/null +++ b/frontend/packages/types/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] +} diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml new file mode 100644 index 0000000..e9b0dad --- /dev/null +++ b/frontend/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'apps/*' + - 'packages/*' diff --git a/frontend/tsconfig.base.json b/frontend/tsconfig.base.json new file mode 100644 index 0000000..98d5caa --- /dev/null +++ b/frontend/tsconfig.base.json @@ -0,0 +1,50 @@ +{ + "compilerOptions": { + // Type Checking + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "exactOptionalPropertyTypes": true, + + // Modules + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + + // Emit + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "composite": true, + "incremental": true, + "noEmit": false, + + // JavaScript Support + "allowJs": false, + "checkJs": false, + + // Interop Constraints + "isolatedModules": true, + "verbatimModuleSyntax": true, + "forceConsistentCasingInFileNames": true, + + // Language and Environment + "target": "ES2022", + "lib": ["ES2022"], + "jsx": "react-jsx", + + // Completeness + "skipLibCheck": true + }, + "exclude": [ + "node_modules", + "dist", + "build", + ".turbo", + "coverage" + ] +}