mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 22:01:09 +02:00
refactor(mana): rename inventar → inventory across the codebase
The workbench-registry app id 'inventar' did not match its @mana/shared-branding MANA_APPS counterpart 'inventory', so the tier- gating join in apps/web/src/lib/app-registry/registry.ts silently failed for the inventory module — it fell into the "no MANA_APPS entry, default visible" fallback and was effectively un-gated. The codebase had also voted overwhelmingly for 'inventar' (53 files) vs 'inventory' (3 files in shared-branding), so the long-standing mismatch was just bookkeeping debt waiting to bite. Pre-release, no live data, so the cleanest fix is to align everything on the English 'inventory': - Workbench-registry id, module.config.ts appId, module folder, route folder and i18n locale folder all renamed via git mv - Standalone apps/inventar/ workspace package renamed - All imports, store identifiers (InventarEvents → InventoryEvents, INVENTAR_GUEST_SEED, inventarModuleConfig), i18n keys and href/goto paths follow the rename - The German display label "Inventar" is preserved everywhere it is a user-visible string (page titles, i18n values, toast labels) - Dexie table prefixes (invCollections, invItems, …) are unchanged - Drive-by fix: ListView.svelte was querying non-existent inventarCollections/inventarItems tables — corrected to the actual invCollections/invItems names from module.config - The "inventar ↔ inventory id mismatch" workaround comment in registry.ts is removed since the mismatch no longer exists module-registry.ts also picks up the user's parallel newsModuleConfig addition because both edits land in the same import block — keeping them split would have left the build in an inconsistent state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
51f408755c
commit
45790ffbb8
65 changed files with 173 additions and 147 deletions
59
apps/inventory/CLAUDE.md
Normal file
59
apps/inventory/CLAUDE.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Inventory
|
||||
|
||||
Configurable inventory management app - track anything with custom schemas.
|
||||
|
||||
**Web App Port:** 5190
|
||||
|
||||
## Project Overview
|
||||
|
||||
Inventory is a schema-less inventory management system built with SvelteKit. Users can create collections with custom field definitions, organize items by location and category, and view them in list/grid/table views.
|
||||
|
||||
### Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|------------|
|
||||
| Frontend | SvelteKit 2, Svelte 5 (runes), Tailwind CSS 4 |
|
||||
| State | Svelte 5 runes ($state, $derived) with localStorage persistence |
|
||||
| Icons | @mana/shared-icons (Phosphor) |
|
||||
| PWA | @vite-pwa/sveltekit + Workbox |
|
||||
| i18n | svelte-i18n (de, en) |
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- **Collections**: Groups of items with a shared schema (custom field definitions)
|
||||
- **Templates**: Predefined schemas for common item types (electronics, books, etc.)
|
||||
- **Items**: Individual inventory entries with custom field values
|
||||
- **Locations**: Hierarchical places (House > Room > Cabinet > Shelf)
|
||||
- **Categories**: Flexible categorization with hierarchy
|
||||
- **Views**: List, Grid, Table views with saved filters
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# From monorepo root
|
||||
pnpm dev:inventory:web # Start web app on port 5190
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
apps/inventory/
|
||||
├── apps/
|
||||
│ └── web/ # SvelteKit web client
|
||||
│ ├── src/
|
||||
│ │ ├── routes/
|
||||
│ │ │ ├── (auth)/ # Login flow
|
||||
│ │ │ └── (app)/ # Authenticated app
|
||||
│ │ │ ├── collections/
|
||||
│ │ │ ├── items/
|
||||
│ │ │ ├── locations/
|
||||
│ │ │ └── categories/
|
||||
│ │ └── lib/
|
||||
│ │ ├── stores/ # Svelte 5 rune stores
|
||||
│ │ ├── components/ # UI components
|
||||
│ │ ├── i18n/ # Translations
|
||||
│ │ └── data/ # Templates, defaults
|
||||
│ └── static/
|
||||
└── packages/
|
||||
└── shared/ # Shared types & constants
|
||||
```
|
||||
14
apps/inventory/package.json
Normal file
14
apps/inventory/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "inventory",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "Inventory - Configurable Inventory Management",
|
||||
"scripts": {
|
||||
"dev": "pnpm --filter @inventory/web dev",
|
||||
"dev:web": "pnpm --filter @inventory/web dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0"
|
||||
}
|
||||
22
apps/inventory/packages/shared/package.json
Normal file
22
apps/inventory/packages/shared/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "@inventory/shared",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./types": "./src/types/index.ts",
|
||||
"./constants": "./src/constants/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mana/shared-types": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
199
apps/inventory/packages/shared/src/constants/index.ts
Normal file
199
apps/inventory/packages/shared/src/constants/index.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
import type { Template, ItemStatus } from '../types/index.js';
|
||||
|
||||
export const ITEM_STATUSES: {
|
||||
value: ItemStatus;
|
||||
labelDe: string;
|
||||
labelEn: string;
|
||||
color: string;
|
||||
}[] = [
|
||||
{ value: 'owned', labelDe: 'Besitzt', labelEn: 'Owned', color: '#22c55e' },
|
||||
{ value: 'lent', labelDe: 'Verliehen', labelEn: 'Lent', color: '#f59e0b' },
|
||||
{ value: 'stored', labelDe: 'Eingelagert', labelEn: 'Stored', color: '#3b82f6' },
|
||||
{ value: 'for_sale', labelDe: 'Zu verkaufen', labelEn: 'For Sale', color: '#a855f7' },
|
||||
{ value: 'disposed', labelDe: 'Entsorgt', labelEn: 'Disposed', color: '#6b7280' },
|
||||
];
|
||||
|
||||
export const DEFAULT_TEMPLATES: Template[] = [
|
||||
{
|
||||
id: 'electronics',
|
||||
name: 'Elektronik',
|
||||
description: 'Computer, Smartphones, Gadgets',
|
||||
icon: '💻',
|
||||
category: 'tech',
|
||||
schema: {
|
||||
fields: [
|
||||
{ id: 'brand', name: 'Marke', type: 'text', order: 0 },
|
||||
{ id: 'model', name: 'Modell', type: 'text', order: 1 },
|
||||
{ id: 'serial_number', name: 'Seriennummer', type: 'text', order: 2 },
|
||||
{ id: 'purchase_date', name: 'Kaufdatum', type: 'date', order: 3 },
|
||||
{ id: 'warranty_until', name: 'Garantie bis', type: 'date', order: 4 },
|
||||
{ id: 'price', name: 'Preis', type: 'currency', currencyCode: 'EUR', order: 5 },
|
||||
{
|
||||
id: 'condition',
|
||||
name: 'Zustand',
|
||||
type: 'select',
|
||||
options: ['Neu', 'Sehr gut', 'Gut', 'Gebraucht', 'Defekt'],
|
||||
order: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'books',
|
||||
name: 'Bücher',
|
||||
description: 'Bücher, E-Books, Hörbücher',
|
||||
icon: '📚',
|
||||
category: 'media',
|
||||
schema: {
|
||||
fields: [
|
||||
{ id: 'author', name: 'Autor', type: 'text', order: 0 },
|
||||
{ id: 'isbn', name: 'ISBN', type: 'text', order: 1 },
|
||||
{ id: 'publisher', name: 'Verlag', type: 'text', order: 2 },
|
||||
{ id: 'genre', name: 'Genre', type: 'text', order: 3 },
|
||||
{ id: 'pages', name: 'Seiten', type: 'number', order: 4 },
|
||||
{ id: 'read', name: 'Gelesen', type: 'checkbox', order: 5 },
|
||||
{
|
||||
id: 'rating',
|
||||
name: 'Bewertung',
|
||||
type: 'select',
|
||||
options: ['1', '2', '3', '4', '5'],
|
||||
order: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'furniture',
|
||||
name: 'Möbel',
|
||||
description: 'Tische, Stühle, Regale',
|
||||
icon: '🪑',
|
||||
category: 'home',
|
||||
schema: {
|
||||
fields: [
|
||||
{ id: 'material', name: 'Material', type: 'text', order: 0 },
|
||||
{ id: 'dimensions', name: 'Maße', type: 'text', placeholder: 'B x H x T in cm', order: 1 },
|
||||
{ id: 'color', name: 'Farbe', type: 'text', order: 2 },
|
||||
{ id: 'room', name: 'Raum', type: 'text', order: 3 },
|
||||
{
|
||||
id: 'condition',
|
||||
name: 'Zustand',
|
||||
type: 'select',
|
||||
options: ['Neu', 'Sehr gut', 'Gut', 'Gebraucht', 'Reparaturbedürftig'],
|
||||
order: 4,
|
||||
},
|
||||
{ id: 'price', name: 'Preis', type: 'currency', currencyCode: 'EUR', order: 5 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'clothing',
|
||||
name: 'Kleidung',
|
||||
description: 'Kleidung, Schuhe, Accessoires',
|
||||
icon: '👕',
|
||||
category: 'fashion',
|
||||
schema: {
|
||||
fields: [
|
||||
{ id: 'brand', name: 'Marke', type: 'text', order: 0 },
|
||||
{ id: 'size', name: 'Größe', type: 'text', order: 1 },
|
||||
{ id: 'color', name: 'Farbe', type: 'text', order: 2 },
|
||||
{ id: 'material', name: 'Material', type: 'text', order: 3 },
|
||||
{
|
||||
id: 'season',
|
||||
name: 'Saison',
|
||||
type: 'select',
|
||||
options: ['Frühling', 'Sommer', 'Herbst', 'Winter', 'Ganzjährig'],
|
||||
order: 4,
|
||||
},
|
||||
{ id: 'price', name: 'Preis', type: 'currency', currencyCode: 'EUR', order: 5 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'tools',
|
||||
name: 'Werkzeug',
|
||||
description: 'Handwerkzeug, Elektrowerkzeug',
|
||||
icon: '🔧',
|
||||
category: 'home',
|
||||
schema: {
|
||||
fields: [
|
||||
{ id: 'brand', name: 'Marke', type: 'text', order: 0 },
|
||||
{ id: 'model', name: 'Modell', type: 'text', order: 1 },
|
||||
{
|
||||
id: 'type',
|
||||
name: 'Typ',
|
||||
type: 'select',
|
||||
options: ['Handwerkzeug', 'Elektrowerkzeug', 'Messwerkzeug', 'Sonstiges'],
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
id: 'condition',
|
||||
name: 'Zustand',
|
||||
type: 'select',
|
||||
options: ['Neu', 'Gut', 'Gebraucht', 'Defekt'],
|
||||
order: 3,
|
||||
},
|
||||
{ id: 'price', name: 'Preis', type: 'currency', currencyCode: 'EUR', order: 4 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'kitchen',
|
||||
name: 'Küche',
|
||||
description: 'Küchengeräte, Geschirr, Besteck',
|
||||
icon: '🍳',
|
||||
category: 'home',
|
||||
schema: {
|
||||
fields: [
|
||||
{ id: 'brand', name: 'Marke', type: 'text', order: 0 },
|
||||
{ id: 'material', name: 'Material', type: 'text', order: 1 },
|
||||
{
|
||||
id: 'category',
|
||||
name: 'Kategorie',
|
||||
type: 'select',
|
||||
options: ['Gerät', 'Geschirr', 'Besteck', 'Topf/Pfanne', 'Sonstiges'],
|
||||
order: 2,
|
||||
},
|
||||
{ id: 'dishwasher_safe', name: 'Spülmaschinenfest', type: 'checkbox', order: 3 },
|
||||
{ id: 'price', name: 'Preis', type: 'currency', currencyCode: 'EUR', order: 4 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'media',
|
||||
name: 'Medien',
|
||||
description: 'Filme, Musik, Spiele',
|
||||
icon: '🎬',
|
||||
category: 'media',
|
||||
schema: {
|
||||
fields: [
|
||||
{
|
||||
id: 'format',
|
||||
name: 'Format',
|
||||
type: 'select',
|
||||
options: ['DVD', 'Blu-ray', 'CD', 'Vinyl', 'Digital', 'Kassette'],
|
||||
order: 0,
|
||||
},
|
||||
{ id: 'artist', name: 'Künstler/Regisseur', type: 'text', order: 1 },
|
||||
{ id: 'genre', name: 'Genre', type: 'text', order: 2 },
|
||||
{ id: 'year', name: 'Erscheinungsjahr', type: 'number', order: 3 },
|
||||
{
|
||||
id: 'rating',
|
||||
name: 'Bewertung',
|
||||
type: 'select',
|
||||
options: ['1', '2', '3', '4', '5'],
|
||||
order: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'custom',
|
||||
name: 'Benutzerdefiniert',
|
||||
description: 'Leere Sammlung, eigene Felder definieren',
|
||||
icon: '✨',
|
||||
category: 'other',
|
||||
schema: {
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
2
apps/inventory/packages/shared/src/index.ts
Normal file
2
apps/inventory/packages/shared/src/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './types/index.js';
|
||||
export * from './constants/index.js';
|
||||
171
apps/inventory/packages/shared/src/types/index.ts
Normal file
171
apps/inventory/packages/shared/src/types/index.ts
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// Field types for configurable schemas
|
||||
export type FieldType =
|
||||
| 'text'
|
||||
| 'number'
|
||||
| 'date'
|
||||
| 'select'
|
||||
| 'tags'
|
||||
| 'checkbox'
|
||||
| 'url'
|
||||
| 'currency';
|
||||
|
||||
// Custom field definition (stored in collection schema)
|
||||
export interface FieldDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
type: FieldType;
|
||||
required?: boolean;
|
||||
defaultValue?: unknown;
|
||||
options?: string[]; // for select fields
|
||||
currencyCode?: string; // for currency fields
|
||||
placeholder?: string;
|
||||
order: number;
|
||||
}
|
||||
|
||||
// Collection schema
|
||||
export interface CollectionSchema {
|
||||
fields: FieldDefinition[];
|
||||
}
|
||||
|
||||
// Item status
|
||||
export type ItemStatus = 'owned' | 'lent' | 'stored' | 'for_sale' | 'disposed';
|
||||
|
||||
// Purchase data
|
||||
export interface PurchaseData {
|
||||
price?: number;
|
||||
currency?: string;
|
||||
date?: string;
|
||||
retailer?: string;
|
||||
warrantyExpiry?: string;
|
||||
receiptUrl?: string;
|
||||
}
|
||||
|
||||
// Item note
|
||||
export interface ItemNote {
|
||||
id: string;
|
||||
content: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// Photo
|
||||
export interface ItemPhoto {
|
||||
id: string;
|
||||
url: string;
|
||||
thumbnailUrl?: string;
|
||||
caption?: string;
|
||||
order: number;
|
||||
}
|
||||
|
||||
// Document attachment
|
||||
export interface ItemDocument {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
mimeType: string;
|
||||
size: number;
|
||||
uploadedAt: string;
|
||||
}
|
||||
|
||||
// Collection
|
||||
export interface Collection {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
color?: string;
|
||||
schema: CollectionSchema;
|
||||
templateId?: string;
|
||||
order: number;
|
||||
itemCount?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// Location (hierarchical)
|
||||
export interface Location {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
path: string;
|
||||
depth: number;
|
||||
order: number;
|
||||
children?: Location[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// Category
|
||||
export interface Category {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
name: string;
|
||||
icon?: string;
|
||||
color?: string;
|
||||
order: number;
|
||||
children?: Category[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// Item
|
||||
export interface Item {
|
||||
id: string;
|
||||
collectionId: string;
|
||||
locationId?: string;
|
||||
categoryId?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
status: ItemStatus;
|
||||
quantity: number;
|
||||
fieldValues: Record<string, unknown>;
|
||||
purchaseData?: PurchaseData;
|
||||
photos: ItemPhoto[];
|
||||
notes: ItemNote[];
|
||||
documents: ItemDocument[];
|
||||
tags: string[];
|
||||
order: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// Template definition
|
||||
export interface Template {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
schema: CollectionSchema;
|
||||
category: string;
|
||||
}
|
||||
|
||||
// Saved filter
|
||||
export interface SavedFilter {
|
||||
id: string;
|
||||
name: string;
|
||||
criteria: FilterCriteria;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface FilterCriteria {
|
||||
search?: string;
|
||||
status?: ItemStatus[];
|
||||
locationId?: string;
|
||||
categoryId?: string;
|
||||
tagIds?: string[];
|
||||
collectionId?: string;
|
||||
}
|
||||
|
||||
// View mode
|
||||
export type ViewMode = 'list' | 'grid' | 'table';
|
||||
|
||||
// Sort options
|
||||
export type SortField = 'name' | 'createdAt' | 'updatedAt' | 'status' | 'quantity';
|
||||
export type SortDirection = 'asc' | 'desc';
|
||||
|
||||
export interface SortOption {
|
||||
field: SortField;
|
||||
direction: SortDirection;
|
||||
}
|
||||
16
apps/inventory/packages/shared/tsconfig.json
Normal file
16
apps/inventory/packages/shared/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue