1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 19:10:22 +01:00

feat(frontend): TypeScript-Monorepo mit Terminal-UI für Effigenix ERP

Monorepo-Setup (pnpm workspaces) mit vier shared Packages und einer TUI-App:

Shared Packages:
- @effigenix/types: TypeScript-DTOs (UserDTO, RoleDTO, AuthDTO, Enums)
- @effigenix/config: API-Konfiguration und Shared Constants
- @effigenix/validation: Zod-Schemas für Username, E-Mail und Passwort
- @effigenix/api-client: axios-Client mit JWT-Handling (proaktiver + reaktiver
  Token-Refresh), AuthInterceptor, ErrorInterceptor, Resources für auth/users/roles

TUI (apps/cli, Ink 5 / React):
- Authentication: Login/Logout, Session-Restore beim Start, JWT-Refresh
- User Management: Liste, Anlage (Zod-Inline-Validation), Detailansicht,
  Passwort ändern, Sperren/Entsperren mit ConfirmDialog
- Role Management: Liste, Detailansicht, Zuweisen/Entfernen per RoleSelectList (↑↓)
- UX: SuccessDisplay (Auto-Dismiss 3 s), ConfirmDialog (J/N),
  FormInput mit Inline-Fehlern, StatusBar mit API-URL
- Layout: Fullscreen-Modus (alternate screen buffer), Header mit eingeloggtem User
- Tests: vitest + ink-testing-library (15 Tests)
This commit is contained in:
Sebastian Frick 2026-02-18 12:23:11 +01:00
parent 87123df2e4
commit bbe9e87c33
65 changed files with 6955 additions and 1 deletions

View file

@ -0,0 +1,43 @@
/**
* Auth resource: login, logout, refresh
*/
import type { AxiosInstance } from 'axios';
import { API_PATHS } from '@effigenix/config';
export interface LoginRequest {
username: string;
password: string;
}
export interface LoginResponse {
accessToken: string;
tokenType: string;
expiresIn: number;
expiresAt: string;
refreshToken: string;
}
export interface RefreshTokenRequest {
refreshToken: string;
}
export function createAuthResource(client: AxiosInstance) {
return {
async login(request: LoginRequest): Promise<LoginResponse> {
const response = await client.post<LoginResponse>(API_PATHS.auth.login, request);
return response.data;
},
async logout(): Promise<void> {
await client.post(API_PATHS.auth.logout);
},
async refresh(request: RefreshTokenRequest): Promise<LoginResponse> {
const response = await client.post<LoginResponse>(API_PATHS.auth.refresh, request);
return response.data;
},
};
}
export type AuthResource = ReturnType<typeof createAuthResource>;

View file

@ -0,0 +1,23 @@
/**
* Roles resource: list all roles
*/
import type { AxiosInstance } from 'axios';
import { API_PATHS } from '@effigenix/config';
export interface RoleDTO {
id: string;
name: string;
permissions: string[];
}
export function createRolesResource(client: AxiosInstance) {
return {
async list(): Promise<RoleDTO[]> {
const response = await client.get<RoleDTO[]>(API_PATHS.roles.base);
return response.data;
},
};
}
export type RolesResource = ReturnType<typeof createRolesResource>;

View file

@ -0,0 +1,94 @@
/**
* Users resource: CRUD, lock/unlock, roles, password
*/
import type { AxiosInstance } from 'axios';
import { API_PATHS } from '@effigenix/config';
export interface UserDTO {
id: string;
username: string;
email: string;
roles: RoleDTO[];
branchId?: string;
status: 'ACTIVE' | 'LOCKED';
createdAt: string;
lastLogin?: string;
}
export interface RoleDTO {
id: string;
name: string;
permissions: string[];
}
export interface CreateUserRequest {
username: string;
email: string;
password: string;
roleNames: string[];
branchId?: string;
}
export interface UpdateUserRequest {
email?: string;
branchId?: string;
}
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
}
export interface AssignRoleRequest {
roleName: string;
}
export function createUsersResource(client: AxiosInstance) {
return {
async list(): Promise<UserDTO[]> {
const response = await client.get<UserDTO[]>(API_PATHS.users.base);
return response.data;
},
async getById(id: string): Promise<UserDTO> {
const response = await client.get<UserDTO>(API_PATHS.users.byId(id));
return response.data;
},
async create(request: CreateUserRequest): Promise<UserDTO> {
const response = await client.post<UserDTO>(API_PATHS.users.base, request);
return response.data;
},
async update(id: string, request: UpdateUserRequest): Promise<UserDTO> {
const response = await client.put<UserDTO>(API_PATHS.users.byId(id), request);
return response.data;
},
async lock(id: string): Promise<UserDTO> {
const response = await client.post<UserDTO>(API_PATHS.users.lock(id));
return response.data;
},
async unlock(id: string): Promise<UserDTO> {
const response = await client.post<UserDTO>(API_PATHS.users.unlock(id));
return response.data;
},
async assignRole(id: string, request: AssignRoleRequest): Promise<UserDTO> {
const response = await client.post<UserDTO>(API_PATHS.users.roles(id), request);
return response.data;
},
async removeRole(id: string, roleName: string): Promise<void> {
await client.delete(`${API_PATHS.users.roles(id)}/${encodeURIComponent(roleName)}`);
},
async changePassword(id: string, request: ChangePasswordRequest): Promise<void> {
await client.put(API_PATHS.users.password(id), request);
},
};
}
export type UsersResource = ReturnType<typeof createUsersResource>;