chore: gitignore agent-memory and remove from tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 20:17:29 +01:00
parent 0013290a19
commit d8a3c03554
10 changed files with 3 additions and 1177 deletions

View File

@@ -1,99 +0,0 @@
# tOS Backend Architecture Memory
## Project Structure
- **Location**: `/home/mehmed/Entwicklung/githubProjekte/tOS/apps/api/`
- **Framework**: NestJS 10.3.x with TypeScript strict mode
- **ORM**: Prisma 5.8.x with PostgreSQL
- **Auth**: JWT-based with Keycloak integration support
## Key Conventions
- **Language**: English for all code/comments
- **Files**: kebab-case, **Variables**: camelCase
- **API Prefix**: `/api/v1/`
- **Global Guards**: JwtAuthGuard -> RolesGuard -> PermissionsGuard
- **IDs**: CUID, **Soft Delete**: `isActive` boolean
## Security - Encryption (CRITICAL)
- `EncryptionService` in `src/common/services/` (AES-256-GCM)
- **Encrypted fields in Employee model:**
- `salary` - Stored as encrypted String (not Decimal!)
- `bankAccount` - Stored as encrypted JSON string
- Access via `findOne(id, includeSensitive=true)` for decryption
- Config: `ENCRYPTION_KEY` env variable (required in production)
## Auth Pattern
- Routes protected by default via global JwtAuthGuard
- `@Public()` for open endpoints
- `@Roles('admin', 'hr-manager')` for role-based access
- `@RequirePermissions(Permission.USERS_CREATE)` for fine-grained
- `@CurrentUser()` to get JWT payload
## Available Roles
admin, hr-manager, team-lead, employee
## Module Exports
All modules export via `index.ts` barrel files:
- `/modules/index.ts` exports: audit, dashboard, departments, user-preferences, integrations, lean, hr
- `/modules/lean/index.ts` exports: s3-planning, skill-matrix, morning-meeting
- `/modules/hr/index.ts` exports: employees, absences, time-tracking
## Health Endpoints
- Located at `src/health/` (NOT `src/modules/health/`)
- `GET /health` - Full check (database, memory, modules status)
- `GET /health/liveness` - Kubernetes liveness
- `GET /health/readiness` - Database connectivity
- `ModulesHealthIndicator` reports core/hr/lean/integrations status
## Test Infrastructure
- **Web (apps/web)**: Vitest 2.x + @testing-library/react + jsdom
- Config: `apps/web/vitest.config.ts`
- Setup: `apps/web/src/test/setup.ts` (imports @testing-library/jest-dom/vitest)
- Scripts: `test` (vitest run), `test:watch` (vitest)
- **API (apps/api)**: Jest 29.x + ts-jest + @nestjs/testing
- Config: inline in `package.json` under `jest` key
- rootDir: `src`, testRegex: `.*\\.spec\\.ts$`
- Module alias: `@/` -> `<rootDir>/`
## Phase 3: Integrations
Location: `src/modules/integrations/`
Sub-modules: credentials/, sync/, status/, jobs/
Types: PLENTYONE, ZULIP, TODOIST, FREESCOUT, NEXTCLOUD, ECODMS, GEMBADOCS
## Phase 4: LEAN
Location: `src/modules/lean/`
- `s3-planning/` - 3S/5S audit planning (permissions: S3_VIEW/CREATE/UPDATE/DELETE/MANAGE)
- `skill-matrix/` - Skills and employee skill entries
- `morning-meeting/` - SQCDM meetings, topics, actions (permissions: MEETING_VIEW/CREATE/UPDATE/DELETE)
## Phase 5: HR
Location: `src/modules/hr/`
- `employees/` - CRUD, org chart, **encrypted salary + bankAccount**
- `absences/` - Approval workflow (PENDING->APPROVED/REJECTED/CANCELLED)
- `time-tracking/` - Clock in/out, German ArbZG break compliance
### Absences Workflow
- Auto-approved: SICK, SICK_CHILD
- Vacation balance: 30 days/year, pro-rata by entry date
### Time Tracking
- German labor law breaks: >6h=30min, >9h=45min
- Monthly summary with overtime calculation
## Scripts (from root)
```bash
pnpm run dev:api # Development server
pnpm run db:migrate # Run migrations
pnpm run db:generate # Generate Prisma client
pnpm run db:seed # Seed default data
```
## Common Patterns
- Use `CommonModule` (@Global) for shared services like EncryptionService
- DTOs with class-validator for input validation
- Swagger decorators for API documentation
- `@AuditLog('Entity', 'ACTION')` for audit trail
See detailed docs in `agent-memory/backend-specialist/` for:
- [integrations.md](./integrations.md) - Integration details
- [hr-module.md](./hr-module.md) - HR module specifics
- [testing.md](./testing.md) - Test infrastructure details

View File

@@ -1,65 +0,0 @@
# tOS Test Infrastructure
## Frontend (apps/web) - Vitest
### Configuration
- `vitest.config.ts` at project root of apps/web
- Uses `@vitejs/plugin-react` for JSX transform
- jsdom environment for DOM testing
- `globals: true` so `describe/it/expect` are global
- Path alias `@` -> `./src` matching tsconfig
- CSS disabled in tests (`css: false`)
- Setup file: `src/test/setup.ts` imports `@testing-library/jest-dom/vitest`
### Dependencies (devDependencies)
- vitest ^2.1.8
- @testing-library/react ^16.1.0
- @testing-library/jest-dom ^6.6.3
- @testing-library/user-event ^14.5.2
- @vitejs/plugin-react ^4.3.4
- jsdom ^25.0.1
### Test Files
- `src/lib/utils.test.ts` - Tests for cn(), getInitials(), capitalize(), truncate(), safeJsonParse(), generateId(), isServer(), isClient()
- `src/components/ui/badge.test.tsx` - Badge component rendering with all variants
- `src/hooks/use-toast.test.ts` - Tests reducer logic and toast() function
### Run Commands
```bash
pnpm --filter web test # vitest run
pnpm --filter web test:watch # vitest (watch mode)
```
## Backend (apps/api) - Jest
### Configuration
- Inline in `package.json` under `"jest"` key
- ts-jest transform for TypeScript
- Node test environment
- Module alias: `@/` -> `<rootDir>/` (rootDir = src)
- Test regex: `.*\.spec\.ts$`
### Dependencies (already present)
- jest ^29.7.0
- ts-jest ^29.1.1
- @nestjs/testing ^10.3.0
- @types/jest ^29.5.11
- supertest ^6.3.3
### Test Files
- `src/health/health.controller.spec.ts` - Health controller (check, liveness, readiness)
- `src/common/services/encryption.service.spec.ts` - EncryptionService (encrypt/decrypt, objects, empty strings, generateKey, init)
### Test Patterns for NestJS
- Use `Test.createTestingModule()` for DI setup
- Mock all dependencies with `{ provide: X, useValue: mockX }`
- Call `module.get<T>(T)` to get the instance under test
- For services with `onModuleInit()`, call it manually in `beforeEach`
- Use `jest.clearAllMocks()` in `afterEach`
### Run Commands
```bash
pnpm --filter api test # jest
pnpm --filter api test:watch # jest --watch
pnpm --filter api test:cov # jest --coverage
```

View File

@@ -1,52 +0,0 @@
# tOS Infrastructure Memory
## Docker Stack
- **Location**: `/home/mehmed/Entwicklung/githubProjekte/tOS/docker/`
- **Compose file**: `docker-compose.yml` (name: tos)
- **Services**: PostgreSQL 16, Redis 7, Keycloak 24.0
- **Network**: `tos-network` (bridge)
- **Volumes**: `tos-postgres-data`, `tos-redis-data`
## Ports (Default)
| Service | Port |
|------------|------|
| PostgreSQL | 5432 |
| Redis | 6379 |
| Keycloak | 8080 |
| API | 3001 |
| Frontend | 3000 |
## Keycloak Configuration
- **Realm**: `tOS`
- **Clients**: `tos-frontend` (public), `tos-backend` (confidential)
- **Roles Hierarchy**:
- admin -> hr-manager, manager, department_head, team-lead, employee
- hr-manager -> employee
- manager -> department_head, employee
- department_head -> team-lead, employee
- team-lead -> employee
- **Test Users**: admin, manager, depthead, employee, hrmanager, teamlead
- **Default passwords**: `<username>123` (temporary)
## Environment Variables
- **Root `.env.example`**: Application config (NextAuth, Keycloak, API keys)
- **Docker `.env.example`**: Container config (ports, credentials)
- **Critical Production Secrets**:
- `ENCRYPTION_KEY` - 32 bytes for credential encryption
- `JWT_SECRET` - API token signing
- `NEXTAUTH_SECRET` - Session encryption
- `KEYCLOAK_BACKEND_CLIENT_SECRET`
## Package Scripts
```bash
pnpm docker:up # Start infrastructure
pnpm docker:down # Stop infrastructure
pnpm docker:logs # View logs
pnpm docker:reset # Destroy volumes and restart
pnpm dev # Start dev servers
```
## Known Issues / Lessons Learned
- Keycloak 24+ (UBI9) has no curl; use bash TCP redirect for health checks
- Realm import: file must be at `/opt/keycloak/data/import/` with `--import-realm` flag
- Health check `start_period` should be 90s+ for Keycloak (Java startup)

View File

@@ -1,325 +0,0 @@
# tOS Frontend - Agent Memory
## Project Overview
- **Type**: Enterprise Web Dashboard (Next.js 14+ App Router)
- **Location**: `/home/mehmed/Entwicklung/githubProjekte/tOS/apps/web/`
- **Language**: Code/comments in English, UI in German (default) via i18n
## Tech Stack
- Next.js 14+ with App Router
- TypeScript (strict mode)
- Tailwind CSS + shadcn/ui (new-york style)
- next-themes for dark/light mode
- next-intl for i18n (de default, en supported)
- NextAuth with Keycloak provider
- Framer Motion for animations
- Zustand for client state (sidebar-store, dashboard-store)
- TanStack Query for server state
- TanStack Table for data tables
- Recharts for chart components
- dnd-kit for drag and drop
- date-fns for date formatting
- Lucide React for icons
## File Structure
```
apps/web/src/
app/[locale]/ # Locale-based routing
(auth)/ # Protected routes with sidebar layout
dashboard/ # Widget-based dashboard
settings/ # profile/, preferences/, security/, notifications/
admin/ # users/, departments/, integrations/ - role protected
lean/ # 3s-planning/, morning-meeting/, skill-matrix/
hr/ # employees/, time-tracking/, absences/
integrations/ # Overview + [type]/ dynamic routes
login/ # Public login page
components/
ui/ # shadcn/ui + DataTable, Badge, Tabs, Switch, etc.
layout/ # Sidebar (collapsible sub-nav), Header
dashboard/ # Widget system (container, grid, registry)
widgets/ # clock, welcome, quick-actions, stats, calendar, activity
integrations/ # orders, chat, tasks, tickets, files, documents
integrations/ # Shared: status-badge, integration-card, connection-test-button
charts/ # bar-chart, line-chart, pie-chart, chart-container
providers/ # SessionProvider, ThemeProvider, QueryProvider
hooks/ # use-toast, use-media-query, use-mounted
integrations/ # use-orders, use-messages, use-tasks, etc.
lib/ # utils, api, auth, motion variants
stores/ # sidebar-store, dashboard-store
types/ # User, UserRole, Department, WidgetConfig, integrations.ts
```
## Key Patterns
### 1. Server/Client Component Split
- `page.tsx` = Server Component (metadata only)
- `*-content.tsx` = Client Component (receives locale prop)
### 2. Widget System (Phase 2)
- Registry pattern: `widget-registry.ts` defines all widget types
- Drag & drop: dnd-kit with SortableContext
- Persistence: Zustand store with localStorage
- Each widget: WidgetContainer wrapper + specific content
### 3. Role-Based Navigation
- NavItem has optional `requiredRoles: UserRole[]`
- Sidebar filters items via `filterNavItems(items, userRoles)`
- Roles: admin, manager, department_head, employee
### 4. DataTable Pattern
- Generic component: `DataTable<TData, TValue>`
- Use `DataTableColumnHeader` for sortable columns
- Features: search, pagination, column visibility, row selection
### 5. Chart Components
- Wrap Recharts in `ChartContainer` for consistent styling
- Support loading states, empty states
- Theme-aware colors via CSS variables
### 6. Integration Hooks (Phase 3)
- TanStack Query with 30s staleTime
- Mock data for development, TODO comments for API replacement
- Query key factories: `ordersKeys`, `messagesKeys`, etc.
- Optimistic updates for complete/toggle actions
### 7. Integration Widgets
- Widget categories: `integrations` added to registry
- requiredRoles: ['manager', 'admin'] for sensitive widgets
- Each widget uses WidgetContainer + specific hook
## Sidebar Behavior
- Expanded: 240px, Collapsed: 64px
- Collapsible sub-navigation with ChevronDown animation
- Tooltip shows sub-nav when collapsed
## i18n Keys
- Flat structure with nested objects: `widgets.clock.name`
- Use ASCII for German (oe, ae, ue) to avoid encoding issues
## Dependencies Added (Phase 2)
- @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities
- @tanstack/react-table
- @radix-ui/react-checkbox, collapsible, popover, select, tabs
- recharts, date-fns
## Dependencies Added (Phase 3)
- @radix-ui/react-switch (for Switch component)
## Integration Types (Phase 3+)
- `plenty-one`: PlentyONE (e-commerce) - OrdersWidget
- `zulip`: ZULIP (chat) - ChatWidget
- `todoist`: Todoist (tasks) - TasksWidget
- `freescout`: FreeScout (helpdesk) - TicketsWidget
- `nextcloud`: Nextcloud (files) - FilesWidget
- `ecodms`: ecoDMS (documents) - DocumentsWidget
- `gembadocs`: GembaDocs (audits/compliance) - GembaDocsWidget
## LEAN Module Structure
### File Organization
```
apps/web/src/
app/[locale]/(auth)/lean/
page.tsx # LEAN overview
s3-planning/
page.tsx # S3 plans overview
[departmentId]/page.tsx # Department detail view
morning-meeting/
page.tsx # Meetings overview + calendar
morning-meeting-overview-content.tsx
[departmentId]/
page.tsx # Department SQCDM board
department-meeting-content.tsx
skill-matrix/
page.tsx # Overview with department cards
skill-matrix-overview-content.tsx
[departmentId]/
page.tsx # Department matrix grid + gap analysis
department-skill-matrix-content.tsx
components/lean/
s3/ # S3 Planning components
index.ts # Barrel export
s3-status-cell.tsx # Colored week cell
s3-status-modal.tsx # Status edit dialog
s3-category-card.tsx # Category with week grid
s3-progress-chart.tsx # Pie chart stats
s3-plan-overview.tsx # Plans list with filters
s3-department-view.tsx # Week calendar view
morning-meeting/ # Morning Meeting components
index.ts # Barrel export
meeting-board.tsx # Main board with all 5 SQCDM columns
sqcdm-column.tsx # Single S/Q/C/D/M column
kpi-card.tsx # KPI with traffic light + trend
meeting-timer.tsx # Timer with start/stop, countdown mode
action-list.tsx # Action items container with filters
action-item.tsx # Single action with status/assignee
hooks/lean/
index.ts # Barrel export
use-s3-plans.ts # Plans CRUD + types
use-s3-status.ts # Status updates
use-meetings.ts # Meeting CRUD + topics/actions
use-meeting-timer.ts # Timer logic (elapsed/countdown)
```
### S3 Status Types & Colors
- `NOT_APPLICABLE` (gray): Not started
- `YELLOW`: In progress
- `GREEN`: Completed
- `RED`: Problem/Issue
### S3 Type Categories
- `SEIRI`: Sort
- `SEITON`: Set in Order
- `SEISO`: Shine
### API Endpoints (Backend)
- `GET /lean/s3/plans` - List with filters (year, month, departmentId)
- `GET /lean/s3/plans/:id` - Single plan with categories/statuses
- `PUT /lean/s3/status/:id` - Update status entry
### Skill Matrix Module
```
components/lean/skill-matrix/
index.ts # Barrel export
skill-level-badge.tsx # Level badge with color coding (0-4)
skill-cell.tsx # Matrix cell with quick-edit popover
skill-matrix-grid.tsx # Full grid (employees x skills)
skill-gap-chart.tsx # Bar chart for gap analysis
hooks/lean/
use-skills.ts # Skills CRUD + categories
use-skill-matrix.ts # Matrix data + gap analysis
```
### Skill Level Colors
- `0` (gray): No knowledge
- `1` (red): Basics
- `2` (yellow): Independent
- `3` (green): Expert
- `4` (blue): Can train
### Skill Matrix API Endpoints (Backend)
- `GET /lean/skills` - List with filters
- `GET /lean/skills/department/:id` - Skills for department
- `GET /lean/skill-matrix/:departmentId` - Full matrix
- `GET /lean/skill-matrix/gaps/:departmentId` - Gap analysis
- `POST /lean/skill-matrix/entries` - Create entry
- `PUT /lean/skill-matrix/entries/:id` - Update entry
- `POST /lean/skill-matrix/entries/bulk` - Bulk upsert
## HR Module (Phase 5)
### File Structure
```
apps/web/src/
app/[locale]/(auth)/hr/
page.tsx # HR overview with stats
hr-overview-content.tsx # Client component
employees/
page.tsx # Employee list
employees-content.tsx
[id]/
page.tsx # Employee details
employee-detail-content.tsx
new/
page.tsx # New employee form
new-employee-content.tsx
org-chart/
page.tsx # Organization chart
org-chart-content.tsx
components/hr/employees/
index.ts # Barrel export
employee-card.tsx # Quick overview card
employee-list.tsx # DataTable with filters
employee-form.tsx # Create/edit form
org-chart.tsx # Hierarchical tree view
hooks/hr/
index.ts # Barrel export
use-employees.ts # CRUD + types + mock data
```
### Employee Types
- `EmploymentStatus`: active, inactive, on_leave, terminated
- `ContractType`: full_time, part_time, mini_job, intern, trainee, freelance
- Full Employee interface with address, emergency contact
### Employee Status Colors
- `active` (green)
- `inactive` (gray)
- `on_leave` (yellow)
- `terminated` (red)
### i18n Keys (hr namespace)
- `hr.title`, `hr.description`
- `hr.employees.*` - List/detail pages
- `hr.stats.*` - Dashboard statistics
- `hr.employeeStatus.*` - Status translations
- `hr.contractType.*` - Contract type translations
- `hr.form.*` - Form sections
- `hr.tabs.*` - Detail view tabs
- `hr.toast.*` - Toast notifications
### Dependencies Added (Phase 5)
- react-day-picker (for Calendar component)
- react-hook-form + zod (already present)
- @radix-ui/react-progress (for Progress component)
## HR Time Tracking Module (Phase 5)
### File Structure
```
apps/web/src/
app/[locale]/(auth)/hr/
time-tracking/
page.tsx # Time tracking overview
time-tracking-content.tsx # Time clock + entries
[employeeId]/
page.tsx # Employee time account
employee-time-account-content.tsx
absences/
page.tsx # Absences overview
absences-content.tsx # Balance + requests
calendar/
page.tsx # Team calendar view
absence-calendar-content.tsx
requests/
page.tsx # Manager approval view
absence-requests-content.tsx
components/hr/
time-tracking/
index.ts # Barrel export
time-clock.tsx # Web clock (clock in/out, breaks)
time-entry-list.tsx # List of time entries
time-entry-form.tsx # Correction request dialog
time-summary.tsx # Monthly summary with progress
absences/
index.ts # Barrel export
absence-calendar.tsx # Monthly calendar with absences
absence-request-form.tsx # Create absence request dialog
absence-card.tsx # Single absence with status
absence-approval-list.tsx # Pending requests for managers
vacation-balance.tsx # Vacation quota display
hooks/hr/
use-time-tracking.ts # Clock in/out, entries, summary
use-absences.ts # Requests, balance, calendar
types/hr.ts # TimeEntry, Absence, VacationBalance types
```
### Time Tracking Types
- `TimeEntryStatus`: CLOCKED_IN, ON_BREAK, CLOCKED_OUT
- `TimeEntryType`: REGULAR, OVERTIME, CORRECTED
- `TIME_STATUS_INFO`: Color mapping for status badges
### Absence Types
- `AbsenceType`: VACATION, SICK, HOME_OFFICE, SPECIAL_LEAVE, UNPAID_LEAVE, TRAINING
- `AbsenceRequestStatus`: PENDING, APPROVED, REJECTED, CANCELLED
- `ABSENCE_TYPE_INFO`: Color + icon mapping
- `ABSENCE_STATUS_INFO`: Status color mapping
### i18n Keys (hr namespace)
- `hr.timeTracking.*` - Clock, entries, summary
- `hr.absences.*` - Balance, requests, calendar, approvals
## Related Files
- [shared-package.md](./shared-package.md) - @tos/shared integration guide & differences
- [component-patterns.md](./component-patterns.md) - Code examples

View File

@@ -1,204 +0,0 @@
# Component Patterns - tOS Frontend
## Layout Component Pattern
```tsx
// src/components/layout/sidebar.tsx
'use client';
import { usePathname } from 'next/navigation';
import { motion } from 'framer-motion';
import { useSidebarStore } from '@/stores/sidebar-store';
export function Sidebar({ locale }: { locale: string }) {
const { isExpanded, toggleSidebar } = useSidebarStore();
const pathname = usePathname();
return (
<motion.aside
initial={false}
animate={{ width: isExpanded ? 240 : 64 }}
transition={{ duration: 0.2, ease: 'easeInOut' }}
className="fixed left-0 top-0 z-40 h-screen border-r bg-sidebar"
>
{/* Content */}
</motion.aside>
);
}
```
## Page with Metadata Pattern
```tsx
// page.tsx (Server Component)
import { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { ContentComponent } from './content-component';
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations('namespace');
return { title: t('title') };
}
export default function Page() {
return <ContentComponent />;
}
// content-component.tsx (Client Component)
'use client';
import { useTranslations } from 'next-intl';
export function ContentComponent() {
const t = useTranslations('namespace');
return <div>{t('key')}</div>;
}
```
## Zustand Store Pattern
```tsx
// src/stores/sidebar-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface SidebarState {
isExpanded: boolean;
toggleSidebar: () => void;
}
export const useSidebarStore = create<SidebarState>()(
persist(
(set) => ({
isExpanded: true,
toggleSidebar: () => set((state) => ({ isExpanded: !state.isExpanded })),
}),
{
name: 'tos-sidebar-state',
partialize: (state) => ({ isExpanded: state.isExpanded }),
}
)
);
```
## Animation Pattern with Framer Motion
```tsx
import { motion } from 'framer-motion';
// Staggered list animation
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ staggerChildren: 0.1 }}
>
{items.map((item, index) => (
<motion.div
key={item.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.3, ease: 'easeOut' }}
>
{item.content}
</motion.div>
))}
</motion.div>
```
## shadcn/ui Component with Variants
```tsx
// src/components/ui/button.tsx
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground',
outline: 'border border-input bg-background hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
},
size: {
default: 'h-9 px-4 py-2',
sm: 'h-8 px-3 text-xs',
lg: 'h-10 px-8',
icon: 'h-9 w-9',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
```
## Provider Composition Pattern
```tsx
// src/components/providers/index.tsx
'use client';
export function Providers({ children }: { children: ReactNode }) {
return (
<SessionProvider>
<QueryProvider>
<ThemeProvider>
{children}
</ThemeProvider>
</QueryProvider>
</SessionProvider>
);
}
```
## i18n Usage Pattern
```tsx
// In components
import { useTranslations } from 'next-intl';
export function Component() {
const t = useTranslations('namespace');
return <span>{t('key', { param: 'value' })}</span>;
}
// In server components
import { getTranslations } from 'next-intl/server';
export async function ServerComponent() {
const t = await getTranslations('namespace');
return <span>{t('key')}</span>;
}
```
## Protected Route Layout Pattern
```tsx
// src/app/[locale]/(auth)/layout.tsx
'use client';
export default function AuthLayout({ children, params: { locale } }) {
const { isExpanded } = useSidebarStore();
return (
<div className="min-h-screen bg-background">
<div className="hidden lg:block">
<Sidebar locale={locale} />
</div>
<MobileSidebar locale={locale} />
<div className={cn(
'flex min-h-screen flex-col transition-[margin-left] duration-200',
'ml-0',
isExpanded ? 'lg:ml-[240px]' : 'lg:ml-[64px]'
)}>
<Header locale={locale} />
<main className="flex-1 p-4 md:p-6 lg:p-8">
<PageTransition>{children}</PageTransition>
</main>
</div>
</div>
);
}
```

View File

@@ -1,69 +0,0 @@
# @tos/shared Package Integration
## Package Location
`packages/shared/` - Built with tsup, outputs CJS + ESM + types to `dist/`
## Available Exports
### Utils (`@tos/shared` or `@tos/shared/utils`)
- `formatDate(date, options?)` - German locale, 2-digit format (dd.MM.yyyy)
- `formatTime(date, options?)` - German locale, HH:mm
- `formatDateTime(date)` - Combined date + time
- `calculateWorkingDays(start, end)` - Excludes weekends
- `minutesToHoursAndMinutes(mins)` - Returns { hours, minutes }
- `formatMinutesToTime(mins)` - Returns "HH:MM" string
- `getInitials(firstName, lastName)` - Two-arg version, returns "FL"
- `getFullName(firstName, lastName)` - Returns "First Last"
- `slugify(str)` - URL-safe slug
- `isDefined<T>(value)` - Type guard for non-null/undefined
- `sleep(ms)` - Promise-based delay
### Constants (`@tos/shared`)
- `DEFAULT_VACATION_DAYS`, `MIN_VACATION_DAYS`
- `STANDARD_WORKING_HOURS_PER_WEEK/DAY`, `MAX_WORKING_HOURS_PER_DAY`
- `DEFAULT_BREAK_MINUTES`, `REQUIRED_BREAK_MINUTES`, `EXTENDED_BREAK_MINUTES`
- `SKILL_LEVELS`, `S3_CATEGORIES`, `SQCDM_CATEGORIES`
- `HTTP_STATUS`, `DATE_FORMATS`
- `API_VERSION`, `API_PREFIX`, `DEFAULT_PAGE_SIZE`, `MAX_PAGE_SIZE`
- `PERMISSIONS`, `ROLE_PERMISSIONS`
### Types (`@tos/shared` or `@tos/shared/types`)
- `User`, `UserRole`, `UserPreferences`, `DashboardWidgetConfig`, `NotificationPreferences`
- `CreateUserDto`, `UpdateUserDto`
- `Department`, `DepartmentCode`, `DepartmentWithStats`, `CreateDepartmentDto`, `UpdateDepartmentDto`
- `Employee`, `ContractType`, `TimeEntry`, `TimeEntryType`, `Absence`, `AbsenceType`, `ApprovalStatus`
- `AuthUser`, `TokenPayload`, `LoginRequest`, `AuthResponse`, `Permission`
- Skill Matrix: `Skill`, `SkillMatrixEntry`, `SkillMatrix`, `SkillGapAnalysis`, etc.
## Integration Patterns
### Web App (apps/web)
- `transpilePackages: ['@tos/shared']` in next.config.mjs (transpiles source directly)
- `"@tos/shared": "workspace:*"` in package.json
- Import directly: `import { getInitials } from '@tos/shared'`
### API App (apps/api)
- `"@tos/shared": "workspace:*"` in package.json
- Uses built dist (CJS) - requires `pnpm --filter @tos/shared build` first
- Import: `import { calculateWorkingDays, DEFAULT_VACATION_DAYS } from '@tos/shared'`
## Key Differences: Local vs Shared
### getInitials
- **Shared**: `getInitials(firstName: string, lastName: string)` - two args
- **Web local** (`@/lib/utils`): `getInitials(name: string)` - single full name string, splits on space
- Both coexist; use shared in components with firstName/lastName, local in header with full name
### Date Formatting
- **Shared**: `formatDate()` uses `{ day: '2-digit', month: '2-digit', year: 'numeric' }`, hardcoded `de-DE`
- **Web local**: `formatDate()` uses `{ year: 'numeric', month: 'long', day: 'numeric' }`, accepts locale param
- **Web local**: `formatDateShort()` is equivalent to shared `formatDate()` but accepts locale param
- NOT interchangeable - different output format
### sleep
- Identical in both - web local now re-exports from `@tos/shared`
### ApprovalStatus / ContractType
- **Shared**: UPPERCASE (`'PENDING'`, `'FULL_TIME'`)
- **Web types**: lowercase (`'pending'`, `'full_time'`)
- NOT interchangeable - different casing convention

View File

@@ -1,169 +0,0 @@
# Integration Specialist Memory - tOS Project
## Project Structure
- **API Location**: `/apps/api/src/`
- **Integrations Module**: `/apps/api/src/modules/integrations/`
- **Connectors**: `/apps/api/src/modules/integrations/connectors/`
## Phase 3 Connector Implementation (Completed)
### Base Infrastructure
- `base-connector.ts`: Abstract base class with axios, retry logic, rate limiting
- `errors/integration.errors.ts`: Custom error classes (Auth, Connection, RateLimit, API, Config, Validation)
### Implemented Connectors
#### PlentyONE (`/connectors/plentyone/`)
- **Auth**: OAuth2 Client Credentials flow
- **Token Management**: Auto-refresh with 5-minute buffer
- **Endpoints**: Orders, Stock, Statistics/Revenue
- **Rate Limiting**: 60s timeout (PlentyONE can be slow)
- **API Base**: `{baseUrl}/rest`
#### ZULIP (`/connectors/zulip/`)
- **Auth**: Basic Auth (email + API key)
- **Endpoints**: Messages, Streams, Users
- **API Base**: `{baseUrl}/api/v1`
- **Note**: Form-encoded POST requests for some endpoints
#### Todoist (`/connectors/todoist/`)
- **Auth**: Bearer Token
- **Endpoints**: Tasks, Projects, Sections, Labels
- **API Base**: `https://api.todoist.com/rest/v2`
- **Note**: Uses X-Request-Id header for idempotency
#### FreeScout (`/connectors/freescout/`)
- **Auth**: API Key via `X-FreeScout-API-Key` header
- **Endpoints**: Conversations, Mailboxes, Customers, Tags
- **API Base**: `{baseUrl}/api`
- **API Docs**: https://github.com/freescout-helpdesk/freescout/wiki/API
#### Nextcloud (`/connectors/nextcloud/`)
- **Auth**: Basic Auth (username + app-password)
- **Endpoints**: WebDAV Files, OCS Share API, CalDAV Calendar (basic)
- **API Bases**: `{baseUrl}/remote.php/dav/files/{user}` (WebDAV), `{baseUrl}/ocs/v2.php` (OCS)
- **Note**: WebDAV uses PROPFIND/MKCOL/MOVE/COPY; OCS needs `OCS-APIRequest: true` header
#### ecoDMS (`/connectors/ecodms/`)
- **Auth**: Session-based (login returns session ID, use `X-Session-Id` header)
- **Endpoints**: Documents, Folders, Classifications, Search
- **API Base**: `{baseUrl}/api/v1`
- **Note**: Session auto-refresh before expiry; supports file upload with FormData
## Environment Variables
```bash
# PlentyONE
PLENTYONE_BASE_URL=
PLENTYONE_CLIENT_ID=
PLENTYONE_CLIENT_SECRET=
# ZULIP
ZULIP_BASE_URL=
ZULIP_EMAIL=
ZULIP_API_KEY=
# Todoist
TODOIST_API_TOKEN=
# FreeScout
FREESCOUT_API_URL=
FREESCOUT_API_KEY=
# Nextcloud
NEXTCLOUD_URL=
NEXTCLOUD_USERNAME=
NEXTCLOUD_PASSWORD=
# ecoDMS
ECODMS_API_URL=
ECODMS_USERNAME=
ECODMS_PASSWORD=
ECODMS_API_VERSION=v1
```
## Key Patterns
### Retry Logic
- Exponential backoff with jitter (0.5-1.5x multiplier)
- Default: 3 retries, 1s initial delay, 30s max delay
- Retry on: 429, 500, 502, 503, 504
### Error Handling
- `IntegrationConnectionError`: Network issues (retryable)
- `IntegrationAuthError`: 401 responses (not retryable)
- `IntegrationRateLimitError`: 429 responses (retryable after delay)
- `IntegrationApiError`: Other API errors (retryable if 5xx)
### Module Structure
Each connector follows pattern:
1. `{name}.types.ts` - TypeScript interfaces
2. `{name}.connector.ts` - Low-level API client
3. `{name}.service.ts` - Business logic layer
4. `{name}.controller.ts` - HTTP endpoints
5. `{name}.module.ts` - NestJS module
6. `dto/*.dto.ts` - Request/Response DTOs
## API Endpoints
```
GET /integrations - Overview all integrations
GET /integrations/:type/status - Integration status
GET /integrations/:type/health - Health check
POST /integrations/plentyone/test
GET /integrations/plentyone/orders
GET /integrations/plentyone/orders/:id
GET /integrations/plentyone/stock
GET /integrations/plentyone/stats
POST /integrations/zulip/test
GET /integrations/zulip/messages
POST /integrations/zulip/messages
GET /integrations/zulip/streams
GET /integrations/zulip/users
POST /integrations/todoist/test
GET /integrations/todoist/tasks
POST /integrations/todoist/tasks
PUT /integrations/todoist/tasks/:id
DELETE /integrations/todoist/tasks/:id
GET /integrations/todoist/projects
# FreeScout
POST /integrations/freescout/test
GET /integrations/freescout/conversations
GET /integrations/freescout/conversations/:id
POST /integrations/freescout/conversations
POST /integrations/freescout/conversations/:id/reply
GET /integrations/freescout/mailboxes
GET /integrations/freescout/customers
GET /integrations/freescout/tags
# Nextcloud
POST /integrations/nextcloud/test
GET /integrations/nextcloud/files
GET /integrations/nextcloud/files/*path
POST /integrations/nextcloud/files/upload
DELETE /integrations/nextcloud/files/*path
GET /integrations/nextcloud/calendar/events
# ecoDMS
POST /integrations/ecodms/test
GET /integrations/ecodms/documents
GET /integrations/ecodms/documents/:id
POST /integrations/ecodms/documents/search
GET /integrations/ecodms/documents/:id/download
GET /integrations/ecodms/folders
GET /integrations/ecodms/classifications
```
## Dependencies
- `axios`: ^1.6.0 (HTTP client)
- axios types included in axios package
## Notes
- All connectors check `isConfigured()` before operations
- `getMissingConfig()` returns list of missing env vars
- Logging in development mode by default
- All API responses transformed to DTOs

View File

@@ -1,123 +0,0 @@
# tOS Project Code Review Memory
## Project Overview
- **Type:** Enterprise Web Operating System with HR, LEAN modules
- **Stack:** Next.js 14 + NestJS 10 + Prisma + PostgreSQL + Keycloak
- **Structure:** pnpm monorepo with turbo (apps/web, apps/api, packages/shared)
## Code Standards (from PLAN.md)
| Area | Language | Convention |
|------|----------|------------|
| Code & Comments | English | Required |
| UI Text | German (via i18n) | next-intl |
| Variables | camelCase | `employeeId` |
| Components | PascalCase | `DashboardWidget` |
| Files | kebab-case | `user-service.ts` |
| Constants | UPPER_SNAKE_CASE | `MAX_VACATION_DAYS` |
## Critical Patterns Found
### Authentication
- Global `JwtAuthGuard` with `@Public()` opt-out decorator
- Global `RolesGuard` with `@Roles()` decorator
- Keycloak integration via NextAuth (frontend) and passport-jwt (backend)
- **IMPORTANT:** Always verify JWT signatures, never just decode
- Guards registered in correct order: JWT -> Roles -> Permissions (app.module.ts L74-88)
- JWT strategy validates user exists and is active on every request
### Frontend Architecture
- **Server/Client split:** Server page.tsx -> Client *-content.tsx
- **State:** Zustand (sidebar, dashboard), TanStack Query (server state)
- **i18n:** next-intl with de.json/en.json, default locale = "de"
- **Dashboard:** dnd-kit widget grid, widget registry pattern
- **Styling:** Tailwind + shadcn/ui, HSL CSS vars, dark mode via class
### Integration Architecture (Phase 3 - Disabled)
- **Location:** `apps/api/integrations.backup/`
- Only PlentyONE/Zulip/Todoist extend BaseConnector
- FreeScout/Nextcloud/ecoDMS/GembaDocs have independent implementations
### Keycloak Client ID Mismatch (CRITICAL - RECURRING)
- Realm: `tos-frontend` / `tos-backend`
- apps/api/.env: `tos-api` | apps/web/.env: `tos-nextauth` (MISMATCH)
## Key Issues to Watch
1. **params API:** Mixed sync/async patterns across pages
2. **Hardcoded German:** dashboard-content.tsx, widget-grid.tsx bypass i18n
3. **Mock Data:** HR hooks (employees, time-tracking, absences) use mock data
4. **Type Conflicts:** types/index.ts vs types/hr.ts have conflicting exports
5. **Auth Layout:** `(auth)/layout.tsx` is `'use client'` -- blocks SSR
6. **Error pages:** Link to `/dashboard` without locale prefix
7. **ENCRYPTION_KEY:** `optional()` in validation -- must be required
### Backend-Specific Issues (from Phase 7 Review)
8. **Morning Meeting Permissions:** ALL endpoints use DASHBOARD_VIEW -- any user can CRUD meetings
9. **Private method access:** time-tracking.controller.ts L237-239 uses bracket notation `this.service['privateMethod']`
10. **Prisma cleanDatabase():** Uses Promise.all ignoring FK constraints (prisma.service.ts L48-61)
11. **Roles guard leaks info:** Error messages reveal required role names (roles.guard.ts L31-33)
12. **enableImplicitConversion:** In ValidationPipe (main.ts L44) -- can cause type coercion bugs
13. **Break tracking via string parsing:** time-tracking uses note field for break detection (L216-219, L257-258)
14. **Audit interceptor:** oldData always undefined (audit.interceptor.ts L60), salary not sanitized (L131)
15. **Unregistered interceptors:** LoggingInterceptor and TimeoutInterceptor defined but never registered
16. **N+1 queries:** skill-entries.service.ts analyzeGaps (L515-523), s3-planning findAll (L173-178)
17. **bulkUpsert not transactional:** skill-entries.service.ts L429-476 -- partial failures possible
18. **No backdating limit:** Manual time entries have no restriction on how far back entries can be created
19. **Holiday calculation missing:** absences calculateWorkingDays does not account for public holidays
20. **Vacation carry-over TODO:** BUrlG compliance gap (absences.service.ts L979)
## File Locations
| Purpose | Path |
|---------|------|
| Prisma Schema | `apps/api/prisma/schema.prisma` |
| Frontend Types | `apps/web/src/types/` |
| HR Hooks | `apps/web/src/hooks/hr/` |
| LEAN Hooks | `apps/web/src/hooks/lean/` |
| Dashboard | `apps/web/src/components/dashboard/` |
| i18n Messages | `apps/web/messages/` |
| Integration Backup | `apps/api/integrations.backup/` |
| Auth Guards | `apps/api/src/auth/guards/` |
| Auth Permissions | `apps/api/src/auth/permissions/` |
| Encryption Service | `apps/api/src/common/services/encryption.service.ts` |
| HR Employees | `apps/api/src/modules/hr/employees/` |
| HR Time Tracking | `apps/api/src/modules/hr/time-tracking/` |
| HR Absences | `apps/api/src/modules/hr/absences/` |
| LEAN Skill Matrix | `apps/api/src/modules/lean/skill-matrix/` |
| LEAN Morning Meeting | `apps/api/src/modules/lean/morning-meeting/` |
| LEAN S3 Planning | `apps/api/src/modules/lean/s3-planning/` |
| Audit Module | `apps/api/src/modules/audit/` |
| Config Validation | `apps/api/src/config/config.validation.ts` |
## Review History
### Phase 7 Full Backend Review (2026-02-06)
- **Overall: 7.8/10** | 3 Critical, 14 Important issues
- Scores: Auth 8 | Prisma 7 | HR/Emp 8 | HR/Time 7 | HR/Abs 8
- LEAN/Skill 8 | LEAN/Morning 7 | LEAN/S3 8 | Dashboard 8
- Common 8 | Users 8 | Audit 7 | app.module 9
- See: `phase7-backend-review.md`
### Phase 6 Frontend Review (2026-02-06)
- 5 Critical, 9 Important, detailed 10-area review
- See: `phase6-frontend-review.md`
### Infrastructure + Integration Review (2026-02-06)
- Docker 7/10 | Keycloak 7/10 | Env 4/10 | Integration 6/10
- See: `infra-integration-review.md`
### Phase 5 Review (2026-02-05)
- HR modules: Employees, Time Tracking, Absences
### Phase 3 Review (2026-02-05)
- Integration connectors reviewed; module now disabled
### Phase 1 Review (2024)
- JWT validation, type sync, CORS, Keycloak fixes applied
## Backend Architecture Notes
- Global guards chain: JwtAuthGuard -> RolesGuard -> PermissionsGuard
- Response envelope via TransformInterceptor: `{success, data, timestamp}`
- Global HttpExceptionFilter catches all exceptions, no internal leaks
- AES-256-GCM encryption for salary + bank accounts (fixed salt issue noted)
- Audit via decorator `@AuditLog()` + global AuditInterceptor
- Permissions enum uses entity:action format (e.g., `employees:read`)
- DEFAULT_ROLE_PERMISSIONS maps roles to permission arrays

View File

@@ -1,71 +0,0 @@
# Phase 7 - Full Backend Code Review (2026-02-06)
## Overall Score: 7.8/10
## Module Scores
| Module | Score | Key Issue |
|--------|-------|-----------|
| Auth (guards, strategy, decorators) | 8/10 | Role names leaked in error messages |
| Prisma (service, module) | 7/10 | cleanDatabase() ignores FK constraints |
| HR/Employees | 8/10 | `as any` type casts for encrypted data |
| HR/Time Tracking | 7/10 | Break tracking via string parsing, private method access |
| HR/Absences | 8/10 | Missing holiday calc, vacation carry-over TODO |
| LEAN/Skill Matrix | 8/10 | N+1 in analyzeGaps, non-transactional bulkUpsert |
| LEAN/Morning Meeting | 7/10 | ALL endpoints use DASHBOARD_VIEW permission |
| LEAN/S3 Planning | 8/10 | File upload MIME not validated, N+1 in findAll |
| Dashboard | 8/10 | Hardcoded role strings |
| Common (filters, interceptors) | 8/10 | Logging/Timeout interceptors never registered |
| Users | 8/10 | GET /users has no permission restriction |
| Audit | 7/10 | oldData always undefined, salary not sanitized |
| app.module + main.ts | 9/10 | enableImplicitConversion risk |
## Critical Issues (3)
### 1. Morning Meeting Permission Escalation
- **File:** `apps/api/src/modules/lean/morning-meeting/morning-meeting.controller.ts`
- **Lines:** 47, 59, 66, 79, 99, 120, 149, 159, 170, 183, 193, 211, 221, 234, 249, 259, 270
- **Issue:** ALL endpoints (including create, update, delete) use `Permission.DASHBOARD_VIEW`
- **Impact:** Any authenticated user with dashboard access can create/modify/delete morning meetings
### 2. Private Method Access via Bracket Notation
- **File:** `apps/api/src/modules/hr/time-tracking/time-tracking.controller.ts`
- **Lines:** 237-239
- **Issue:** `this.timeTrackingService['getEmployeeByUserId'](user.sub)` accesses private method
- **Impact:** Circumvents TypeScript access modifiers, fragile to refactoring
### 3. Prisma cleanDatabase() with Promise.all
- **File:** `apps/api/src/prisma/prisma.service.ts`
- **Lines:** 48-61
- **Issue:** Deletes all tables in parallel ignoring foreign key constraints
- **Impact:** Can fail in production/staging if FK constraints exist; should use sequential deletion or raw SQL truncate cascade
## Important Issues (14)
1. **Roles guard leaks role names** - roles.guard.ts L31-33
2. **Permissions guard leaks permission names** - permissions.guard.ts
3. **enableImplicitConversion in ValidationPipe** - main.ts L44
4. **Break tracking via note string parsing** - time-tracking.service.ts L216-219, L257-258
5. **No backdating limit** for manual time entries
6. **Holiday calculation missing** in absences calculateWorkingDays
7. **Vacation carry-over not implemented** - absences.service.ts L979 (BUrlG)
8. **Event queue in-memory only** - absences.service.ts L1414
9. **N+1 query in analyzeGaps** - skill-entries.service.ts L515-523
10. **bulkUpsert not transactional** - skill-entries.service.ts L429-476
11. **File upload MIME not validated** - s3-planning.controller.ts
12. **LoggingInterceptor + TimeoutInterceptor never registered**
13. **GET /users no permission check** - users.controller.ts L88-97
14. **Audit oldData always undefined** - audit.interceptor.ts L60
## Positive Observations
- JWT verification (not just decode) with user existence check on every request
- AES-256-GCM encryption for sensitive employee data (salary, bank account)
- Consistent error response format via global HttpExceptionFilter
- Response envelope pattern via TransformInterceptor
- ArbZG break rules correctly implemented in time tracking
- Comprehensive absence workflow with approval chain
- Global ValidationPipe with whitelist + forbidNonWhitelisted
- Proper soft delete patterns for employees
- Well-structured module hierarchy (HrModule -> sub-modules)
- Swagger/OpenAPI documentation on all endpoints

3
.gitignore vendored
View File

@@ -17,6 +17,9 @@ build/
.env.*.local
!.env.example
# Claude Code
.claude/agent-memory/
# IDE
.idea/
.vscode/