1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 08:29:36 +01:00

feat: initialize frontend monorepo with pnpm workspace and types package

- Add pnpm workspace configuration with apps/ and packages/
- Create @effigenix/types package with OpenAPI type generation setup
- Add TypeScript strict mode configuration
- Configure ESLint and Prettier for code quality
- Add wrapper files for clean type exports (auth, user, role, common)
- Add custom UI types and enums
This commit is contained in:
Sebastian Frick 2026-02-17 22:13:18 +01:00
parent c2c48a03e8
commit 3ab2c1a57e
17 changed files with 702 additions and 0 deletions

45
frontend/.eslintrc.js Normal file
View file

@ -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',
],
};

1
frontend/.gitkeep Normal file
View file

@ -0,0 +1 @@
# Workspace root

10
frontend/.prettierrc Normal file
View file

@ -0,0 +1,10 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"endOfLine": "lf"
}

143
frontend/README.md Normal file
View file

@ -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)

31
frontend/package.json Normal file
View file

@ -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"
}

View file

@ -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<LoginResponse> {
// 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<LoginResponse>;
}
```
## 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

View file

@ -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
}
}

View file

@ -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;
}

View file

@ -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<T> {
content: T[];
totalElements: number;
totalPages: number;
size: number;
number: number;
}

View file

@ -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',
}

View file

@ -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<string, unknown>;
}
export interface paths {
[key: string]: unknown;
}

View file

@ -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';

View file

@ -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 }>;
}

View file

@ -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;
}

View file

@ -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"]
}

View file

@ -0,0 +1,3 @@
packages:
- 'apps/*'
- 'packages/*'

View file

@ -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"
]
}