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:
Till JS 2026-04-09 15:50:24 +02:00
parent 51f408755c
commit 45790ffbb8
65 changed files with 173 additions and 147 deletions

View file

@ -1,14 +0,0 @@
{
"name": "inventar",
"version": "1.0.0",
"private": true,
"description": "Inventar - Configurable Inventory Management",
"scripts": {
"dev": "pnpm --filter @inventar/web dev",
"dev:web": "pnpm --filter @inventar/web dev"
},
"devDependencies": {
"typescript": "^5.9.3"
},
"packageManager": "pnpm@9.15.0"
}

View file

@ -1,4 +1,4 @@
# Inventar
# Inventory
Configurable inventory management app - track anything with custom schemas.
@ -6,7 +6,7 @@ Configurable inventory management app - track anything with custom schemas.
## Project Overview
Inventar 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.
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
@ -31,13 +31,13 @@ Inventar is a schema-less inventory management system built with SvelteKit. User
```bash
# From monorepo root
pnpm dev:inventar:web # Start web app on port 5190
pnpm dev:inventory:web # Start web app on port 5190
```
## Project Structure
```
apps/inventar/
apps/inventory/
├── apps/
│ └── web/ # SvelteKit web client
│ ├── src/

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

View file

@ -1,5 +1,5 @@
{
"name": "@inventar/shared",
"name": "@inventory/shared",
"version": "1.0.0",
"private": true,
"type": "module",

View file

@ -34,7 +34,7 @@ const sections: Section[] = [
{ name: 'ManaCalendar', icon: 'ph:calendar-bold', tagline: 'Kalender', url: 'https://calendar.mana.how' },
{ name: 'Kontakte', icon: 'ph:address-book-bold', tagline: 'Kontakte', url: 'https://contacts.mana.how' },
{ name: 'ManaStorage', icon: 'ph:cloud-bold', tagline: 'Cloud-Speicher', url: 'https://storage.mana.how' },
{ name: 'Inventar', icon: 'ph:package-bold', tagline: 'Inventar', url: 'https://inventar.mana.how' },
{ name: 'Inventar', icon: 'ph:package-bold', tagline: 'Inventar', url: 'https://inventory.mana.how' },
],
},
{

View file

@ -64,7 +64,7 @@ const APP_SUBDOMAINS = new Set([
'picture',
'calc',
'citycorners',
'inventar',
'inventory',
'times',
'uload',
'memoro',

View file

@ -40,6 +40,7 @@ import {
Calculator,
Lightning,
Sparkle,
Newspaper,
} from '@mana/shared-icons';
// ── Apps with entity capabilities ───────────────────────────
@ -565,13 +566,13 @@ registerApp({
});
registerApp({
id: 'inventar',
id: 'inventory',
name: 'Inventar',
color: '#78716C',
icon: Package,
views: {
list: { load: () => import('$lib/modules/inventar/ListView.svelte') },
detail: { load: () => import('$lib/modules/inventar/views/DetailView.svelte') },
list: { load: () => import('$lib/modules/inventory/ListView.svelte') },
detail: { load: () => import('$lib/modules/inventory/views/DetailView.svelte') },
},
});
@ -669,3 +670,24 @@ registerApp({
list: { load: () => import('$lib/modules/playground/ListView.svelte') },
},
});
registerApp({
id: 'news',
name: 'News',
color: '#10B981',
icon: Newspaper,
views: {
list: { load: () => import('$lib/modules/news/ListView.svelte') },
},
contextMenuActions: [
{
id: 'open-feed',
label: 'Feed öffnen',
icon: Plus,
action: () =>
window.dispatchEvent(
new CustomEvent('mana:quick-action', { detail: { app: 'news', action: 'open' } })
),
},
],
});

View file

@ -91,10 +91,9 @@ export function getAllApps(): AppDescriptor[] {
*
* Returns null when there is no matching MANA_APPS entry. Internal-only
* tools that exist in the workbench but not in MANA_APPS (`automations`,
* `playground`, the `inventar` `inventory` id mismatch) fall into this
* case `getAccessibleApps` then treats them as visible by default
* rather than hiding them for everyone, since the alternative is a
* silent regression for founders/devs.
* `playground`) fall into this case `getAccessibleApps` then treats
* them as visible by default rather than hiding them for everyone, since
* the alternative is a silent regression for founders/devs.
*/
function getAppRequiredTier(appId: string): AccessTier | null {
const branding = MANA_APPS.find((a) => a.id === appId);

View file

@ -138,7 +138,7 @@ describe('module-registry — pre-refactor snapshot', () => {
music: ['songs', 'mukkePlaylists', 'playlistSongs', 'mukkeProjects', 'markers', 'songTags'],
storage: ['files', 'storageFolders', 'fileTags'],
presi: ['presiDecks', 'slides', 'presiDeckTags'],
inventar: ['invCollections', 'invItems', 'invLocations', 'invCategories', 'invItemTags'],
inventory: ['invCollections', 'invItems', 'invLocations', 'invCategories', 'invItemTags'],
photos: ['albums', 'albumItems', 'photoFavorites', 'photoMediaTags'],
skilltree: ['skills', 'activities', 'achievements', 'skillTags'],
citycorners: ['cities', 'ccLocations', 'ccFavorites', 'ccLocationTags'],

View file

@ -61,7 +61,7 @@ import { zitareModuleConfig } from '$lib/modules/zitare/module.config';
import { musicModuleConfig } from '$lib/modules/music/module.config';
import { storageModuleConfig } from '$lib/modules/storage/module.config';
import { presiModuleConfig } from '$lib/modules/presi/module.config';
import { inventarModuleConfig } from '$lib/modules/inventar/module.config';
import { inventoryModuleConfig } from '$lib/modules/inventory/module.config';
import { photosModuleConfig } from '$lib/modules/photos/module.config';
import { skilltreeModuleConfig } from '$lib/modules/skilltree/module.config';
import { citycornersModuleConfig } from '$lib/modules/citycorners/module.config';
@ -84,6 +84,7 @@ import { financeModuleConfig } from '$lib/modules/finance/module.config';
import { placesModuleConfig } from '$lib/modules/places/module.config';
import { playgroundModuleConfig } from '$lib/modules/playground/module.config';
import { whoModuleConfig } from '$lib/modules/who/module.config';
import { newsModuleConfig } from '$lib/modules/news/module.config';
export const MODULE_CONFIGS: readonly ModuleConfig[] = [
manaCoreConfig,
@ -100,7 +101,7 @@ export const MODULE_CONFIGS: readonly ModuleConfig[] = [
musicModuleConfig,
storageModuleConfig,
presiModuleConfig,
inventarModuleConfig,
inventoryModuleConfig,
photosModuleConfig,
skilltreeModuleConfig,
citycornersModuleConfig,
@ -123,6 +124,7 @@ export const MODULE_CONFIGS: readonly ModuleConfig[] = [
placesModuleConfig,
playgroundModuleConfig,
whoModuleConfig,
newsModuleConfig,
];
// ─── Derived Maps ──────────────────────────────────────────

View file

@ -38,7 +38,7 @@ function registerLocale(lang: SupportedLocale) {
presi,
uload,
times,
inventar,
inventory,
photos,
nutriphi,
planta,
@ -49,6 +49,7 @@ function registerLocale(lang: SupportedLocale) {
guides,
help,
cycles,
news,
] = await Promise.all([
import(`./locales/apps/${lang}.json`),
import(`./locales/common/${lang}.json`),
@ -72,7 +73,7 @@ function registerLocale(lang: SupportedLocale) {
import(`./locales/presi/${lang}.json`),
import(`./locales/uload/${lang}.json`),
import(`./locales/times/${lang}.json`),
import(`./locales/inventar/${lang}.json`),
import(`./locales/inventory/${lang}.json`),
import(`./locales/photos/${lang}.json`),
import(`./locales/nutriphi/${lang}.json`),
import(`./locales/planta/${lang}.json`),
@ -83,6 +84,7 @@ function registerLocale(lang: SupportedLocale) {
import(`./locales/guides/${lang}.json`),
import(`./locales/help/${lang}.json`),
import(`./locales/cycles/${lang}.json`),
import(`./locales/news/${lang}.json`),
]);
return {
@ -108,7 +110,7 @@ function registerLocale(lang: SupportedLocale) {
presi: presi.default,
uload: uload.default,
times: times.default,
inventar: inventar.default,
inventory: inventory.default,
photos: photos.default,
nutriphi: nutriphi.default,
planta: planta.default,
@ -119,6 +121,7 @@ function registerLocale(lang: SupportedLocale) {
guides: guides.default,
help: help.default,
cycles: cycles.default,
news: news.default,
};
});
}

View file

@ -18,7 +18,7 @@
"nutriphi": "NutriPhi",
"planta": "Planta",
"presi": "Presi",
"inventar": "Inventar",
"inventory": "Inventar",
"memoro": "Memoro",
"questions": "Recherche",
"skilltree": "Skills",

View file

@ -18,7 +18,7 @@
"nutriphi": "NutriPhi",
"planta": "Planta",
"presi": "Presi",
"inventar": "Inventory",
"inventory": "Inventory",
"memoro": "Memoro",
"questions": "Research",
"skilltree": "Skills",

View file

@ -18,7 +18,7 @@
"nutriphi": "NutriPhi",
"planta": "Planta",
"presi": "Presi",
"inventar": "Inventario",
"inventory": "Inventario",
"memoro": "Memoro",
"questions": "Investigación",
"skilltree": "Skills",

View file

@ -18,7 +18,7 @@
"nutriphi": "NutriPhi",
"planta": "Planta",
"presi": "Presi",
"inventar": "Inventaire",
"inventory": "Inventaire",
"memoro": "Memoro",
"questions": "Recherche",
"skilltree": "Skills",

View file

@ -18,7 +18,7 @@
"nutriphi": "NutriPhi",
"planta": "Planta",
"presi": "Presi",
"inventar": "Inventario",
"inventory": "Inventario",
"memoro": "Memoro",
"questions": "Ricerca",
"skilltree": "Skills",

View file

@ -1,5 +1,5 @@
<!--
Inventar — Workbench ListView
Inventory — Workbench ListView
Collections and items overview.
-->
<script lang="ts">
@ -12,12 +12,12 @@
let { navigate }: ViewProps = $props();
const collectionsQuery = useLiveQueryWithDefault(async () => {
const all = await db.table<LocalCollection>('inventarCollections').orderBy('order').toArray();
const all = await db.table<LocalCollection>('invCollections').orderBy('order').toArray();
return all.filter((c) => !c.deletedAt);
}, [] as LocalCollection[]);
const itemsQuery = useLiveQueryWithDefault(async () => {
const all = await db.table<LocalItem>('inventarItems').toArray();
const all = await db.table<LocalItem>('invItems').toArray();
return all.filter((i) => !i.deletedAt);
}, [] as LocalItem[]);

View file

@ -20,7 +20,7 @@ const DEMO_COLLECTION_ID = 'demo-electronics';
const DEMO_LOCATION_ID = 'demo-home';
const DEMO_CATEGORY_ID = 'demo-tech';
export const INVENTAR_GUEST_SEED = {
export const INVENTORY_GUEST_SEED = {
invCollections: [
{
id: DEMO_COLLECTION_ID,

View file

@ -1,7 +1,7 @@
/**
* Inventar constants templates and status definitions.
*
* Inlined from @inventar/shared since the unified app does not depend on it.
* Inlined from @inventory/shared since the unified app does not depend on it.
*/
import type { ItemStatus } from './queries';

View file

@ -49,6 +49,6 @@ export {
invItemTable,
invLocationTable,
invCategoryTable,
INVENTAR_GUEST_SEED,
INVENTORY_GUEST_SEED,
} from './collections';
export type { LocalCollection, LocalItem, LocalLocation, LocalCategory } from './types';

View file

@ -1,7 +1,7 @@
import type { ModuleConfig } from '$lib/data/module-registry';
export const inventarModuleConfig: ModuleConfig = {
appId: 'inventar',
export const inventoryModuleConfig: ModuleConfig = {
appId: 'inventory',
tables: [
{ name: 'invCollections', syncName: 'collections' },
{ name: 'invItems', syncName: 'items' },

View file

@ -9,7 +9,7 @@ import { db } from '$lib/data/database';
import { decryptRecords } from '$lib/data/crypto';
import type { LocalCollection, LocalItem, LocalLocation, LocalCategory } from './types';
// ─── Shared Types (inline to avoid @inventar/shared dependency) ───
// ─── Shared Types (inline to avoid @inventory/shared dependency) ───
export interface Collection {
id: string;

View file

@ -8,7 +8,7 @@
import { invCategoryTable } from '../collections';
import { toCategory } from '../queries';
import type { LocalCategory } from '../types';
import { InventarEvents } from '@mana/shared-utils/analytics';
import { InventoryEvents } from '@mana/shared-utils/analytics';
export const categoriesStore = {
async create(data: { name: string; icon?: string; color?: string; parentId?: string }) {
@ -25,7 +25,7 @@ export const categoriesStore = {
order: siblings.length,
};
await invCategoryTable.add(newLocal);
InventarEvents.categoryCreated();
InventoryEvents.categoryCreated();
return toCategory(newLocal);
},
@ -49,6 +49,6 @@ export const categoriesStore = {
for (const deleteId of idsToDelete) {
await invCategoryTable.update(deleteId, { deletedAt: now, updatedAt: now });
}
InventarEvents.categoryDeleted();
InventoryEvents.categoryDeleted();
},
};

View file

@ -8,7 +8,7 @@
import { invCollectionTable } from '../collections';
import { toCollection } from '../queries';
import type { LocalCollection } from '../types';
import { InventarEvents } from '@mana/shared-utils/analytics';
import { InventoryEvents } from '@mana/shared-utils/analytics';
export const collectionsStore = {
async create(data: {
@ -33,7 +33,7 @@ export const collectionsStore = {
itemCount: 0,
};
await invCollectionTable.add(newLocal);
InventarEvents.collectionCreated();
InventoryEvents.collectionCreated();
return toCollection(newLocal);
},
@ -52,7 +52,7 @@ export const collectionsStore = {
deletedAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
InventarEvents.collectionDeleted();
InventoryEvents.collectionDeleted();
},
async reorder(orderedIds: string[]) {

View file

@ -9,7 +9,7 @@ import { invItemTable } from '../collections';
import { toItem } from '../queries';
import type { LocalItem } from '../types';
import type { ItemStatus } from '../queries';
import { InventarEvents } from '@mana/shared-utils/analytics';
import { InventoryEvents } from '@mana/shared-utils/analytics';
import { encryptRecord } from '$lib/data/crypto';
export const itemsStore = {
@ -49,7 +49,7 @@ export const itemsStore = {
const plaintextSnapshot = toItem(newLocal);
await encryptRecord('invItems', newLocal);
await invItemTable.add(newLocal);
InventarEvents.itemCreated();
InventoryEvents.itemCreated();
return plaintextSnapshot;
},
@ -76,7 +76,7 @@ export const itemsStore = {
};
await encryptRecord('invItems', diff);
await invItemTable.update(id, diff);
InventarEvents.itemUpdated();
InventoryEvents.itemUpdated();
},
async delete(id: string) {
@ -84,7 +84,7 @@ export const itemsStore = {
deletedAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
InventarEvents.itemDeleted();
InventoryEvents.itemDeleted();
},
async deleteByCollection(collectionId: string) {

View file

@ -8,7 +8,7 @@
import { invLocationTable } from '../collections';
import { toLocation } from '../queries';
import type { LocalLocation } from '../types';
import { InventarEvents } from '@mana/shared-utils/analytics';
import { InventoryEvents } from '@mana/shared-utils/analytics';
function buildPath(locations: LocalLocation[], parentId?: string): string {
if (!parentId) return '';
@ -42,7 +42,7 @@ export const locationsStore = {
order: siblings.length,
};
await invLocationTable.add(newLocal);
InventarEvents.locationCreated();
InventoryEvents.locationCreated();
return toLocation(newLocal);
},
@ -66,6 +66,6 @@ export const locationsStore = {
for (const deleteId of idsToDelete) {
await invLocationTable.update(deleteId, { deletedAt: now, updatedAt: now });
}
InventarEvents.locationDeleted();
InventoryEvents.locationDeleted();
},
};

View file

@ -1,5 +1,5 @@
/**
* Uinventar Tags Uses shared global tags + module-specific junction table.
* Inventory Tags Uses shared global tags + module-specific junction table.
*/
import { db } from '$lib/data/database';

View file

@ -6,7 +6,7 @@ import { createViewStore } from '@mana/shared-stores';
import type { ViewMode, FilterCriteria } from '../queries';
export const viewStore = createViewStore<ViewMode, FilterCriteria>({
storagePrefix: 'inventar',
storagePrefix: 'inventory',
defaultViewMode: 'list',
defaultSort: { field: 'name', direction: 'asc' },
hasActiveFilters: (f) =>

View file

@ -17,7 +17,7 @@ const SPLIT_APP_ID_LIST = [
'music',
'storage',
'presi',
'inventar',
'inventory',
'photos',
'skilltree',
'citycorners',

View file

@ -6,8 +6,8 @@
useAllItems,
useAllLocations,
useAllCategories,
} from '$lib/modules/inventar/queries';
import { viewStore } from '$lib/modules/inventar/stores/view.svelte';
} from '$lib/modules/inventory/queries';
import { viewStore } from '$lib/modules/inventory/stores/view.svelte';
let { children }: { children: Snippet } = $props();

View file

@ -1,14 +1,14 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { getContext } from 'svelte';
import { collectionsStore } from '$lib/modules/inventar/stores/collections.svelte';
import { itemsStore } from '$lib/modules/inventar/stores/items.svelte';
import { collectionsStore } from '$lib/modules/inventory/stores/collections.svelte';
import { itemsStore } from '$lib/modules/inventory/stores/items.svelte';
import {
getSortedCollections,
getItemCountByCollection,
getTotalItemCount,
} from '$lib/modules/inventar/queries';
import type { Collection, Item } from '$lib/modules/inventar/queries';
} from '$lib/modules/inventory/queries';
import type { Collection, Item } from '$lib/modules/inventory/queries';
import { Plus, Trash } from '@mana/shared-icons';
const collectionsCtx: { readonly value: Collection[] } = getContext('collections');
@ -19,7 +19,7 @@
}
function handleCollectionClick(collection: Collection) {
goto(`/inventar/collections/${collection.id}`);
goto(`/inventory/collections/${collection.id}`);
}
function handleDelete(e: Event, id: string) {
@ -50,7 +50,7 @@
</p>
</div>
<a
href="/inventar/collections/new"
href="/inventory/collections/new"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
>
<Plus size={20} />
@ -69,7 +69,7 @@
Erstelle deine erste Sammlung, um loszulegen.
</p>
<a
href="/inventar/collections/new"
href="/inventory/collections/new"
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
>
Neue Sammlung

View file

@ -2,8 +2,8 @@
import { _ } from 'svelte-i18n';
import { Plus } from '@mana/shared-icons';
import { getContext } from 'svelte';
import { categoriesStore } from '$lib/modules/inventar/stores/categories.svelte';
import type { Category } from '$lib/modules/inventar/queries';
import { categoriesStore } from '$lib/modules/inventory/stores/categories.svelte';
import type { Category } from '$lib/modules/inventory/queries';
const categoriesCtx: { readonly value: Category[] } = getContext('categories');

View file

@ -3,18 +3,18 @@
import { goto } from '$app/navigation';
import { CaretLeft, PencilSimple, Plus, Trash } from '@mana/shared-icons';
import { getContext } from 'svelte';
import { itemsStore } from '$lib/modules/inventar/stores/items.svelte';
import { viewStore } from '$lib/modules/inventar/stores/view.svelte';
import { itemsStore } from '$lib/modules/inventory/stores/items.svelte';
import { viewStore } from '$lib/modules/inventory/stores/view.svelte';
import {
getCollectionById,
getItemsByCollection,
getSortedItems,
} from '$lib/modules/inventar/queries';
import type { Collection, Item, ItemStatus } from '$lib/modules/inventar/queries';
import FieldRenderer from '$lib/modules/inventar/components/fields/FieldRenderer.svelte';
import FieldEditor from '$lib/modules/inventar/components/fields/FieldEditor.svelte';
import StatusBadge from '$lib/modules/inventar/components/StatusBadge.svelte';
import ViewModeToggle from '$lib/modules/inventar/components/ViewModeToggle.svelte';
} from '$lib/modules/inventory/queries';
import type { Collection, Item, ItemStatus } from '$lib/modules/inventory/queries';
import FieldRenderer from '$lib/modules/inventory/components/fields/FieldRenderer.svelte';
import FieldEditor from '$lib/modules/inventory/components/fields/FieldEditor.svelte';
import StatusBadge from '$lib/modules/inventory/components/StatusBadge.svelte';
import ViewModeToggle from '$lib/modules/inventory/components/ViewModeToggle.svelte';
const collectionsCtx: { readonly value: Collection[] } = getContext('collections');
const itemsCtx: { readonly value: Item[] } = getContext('items');
@ -59,7 +59,7 @@
{#if !collection}
<div class="text-center py-16">
<p class="text-[hsl(var(--muted-foreground))]">Sammlung nicht gefunden</p>
<a href="/inventar" class="mt-4 text-[hsl(var(--primary))]">Zuruck</a>
<a href="/inventory" class="mt-4 text-[hsl(var(--primary))]">Zuruck</a>
</div>
{:else}
<div class="space-y-6">
@ -67,7 +67,7 @@
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<a
href="/inventar"
href="/inventory"
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
>
<CaretLeft size={20} />
@ -83,7 +83,7 @@
<div class="flex items-center gap-2">
<ViewModeToggle current={viewStore.viewMode} onchange={(m) => viewStore.setViewMode(m)} />
<a
href="/inventar/collections/{collection.id}/edit"
href="/inventory/collections/{collection.id}/edit"
class="rounded-lg border border-[hsl(var(--border))] p-2 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
>
<PencilSimple size={16} />
@ -166,8 +166,8 @@
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{#each sortedItems as item (item.id)}
<div
onclick={() => goto(`/inventar/items/${item.id}`)}
onkeydown={(e) => e.key === 'Enter' && goto(`/inventar/items/${item.id}`)}
onclick={() => goto(`/inventory/items/${item.id}`)}
onkeydown={(e) => e.key === 'Enter' && goto(`/inventory/items/${item.id}`)}
role="button"
tabindex="0"
class="group cursor-pointer rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-left"
@ -221,7 +221,7 @@
{#each sortedItems as item (item.id)}
<tr
class="cursor-pointer border-b border-[hsl(var(--border))] transition-colors hover:bg-[hsl(var(--accent)/0.05)]"
onclick={() => goto(`/inventar/items/${item.id}`)}
onclick={() => goto(`/inventory/items/${item.id}`)}
>
<td class="px-4 py-2.5 font-medium text-[hsl(var(--foreground))]">{item.name}</td>
<td class="px-4 py-2.5"><StatusBadge status={item.status} /></td>
@ -248,8 +248,8 @@
<div class="space-y-2">
{#each sortedItems as item (item.id)}
<div
onclick={() => goto(`/inventar/items/${item.id}`)}
onkeydown={(e) => e.key === 'Enter' && goto(`/inventar/items/${item.id}`)}
onclick={() => goto(`/inventory/items/${item.id}`)}
onkeydown={(e) => e.key === 'Enter' && goto(`/inventory/items/${item.id}`)}
role="button"
tabindex="0"
class="group flex w-full cursor-pointer items-center gap-4 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-left transition-colors hover:border-[hsl(var(--primary)/0.3)]"

View file

@ -3,11 +3,11 @@
import { goto } from '$app/navigation';
import { CaretLeft } from '@mana/shared-icons';
import { getContext } from 'svelte';
import { collectionsStore } from '$lib/modules/inventar/stores/collections.svelte';
import { getCollectionById } from '$lib/modules/inventar/queries';
import type { Collection } from '$lib/modules/inventar/queries';
import type { CollectionSchema } from '$lib/modules/inventar/constants';
import SchemaEditor from '$lib/modules/inventar/components/fields/SchemaEditor.svelte';
import { collectionsStore } from '$lib/modules/inventory/stores/collections.svelte';
import { getCollectionById } from '$lib/modules/inventory/queries';
import type { Collection } from '$lib/modules/inventory/queries';
import type { CollectionSchema } from '$lib/modules/inventory/constants';
import SchemaEditor from '$lib/modules/inventory/components/fields/SchemaEditor.svelte';
const collectionsCtx: { readonly value: Collection[] } = getContext('collections');
@ -38,7 +38,7 @@
icon: icon || undefined,
schema,
});
goto(`/inventar/collections/${collection.id}`);
goto(`/inventory/collections/${collection.id}`);
}
const inputClass =
@ -55,7 +55,7 @@
<div class="mx-auto max-w-2xl space-y-6">
<div class="flex items-center gap-3">
<button
onclick={() => goto(`/inventar/collections/${collection.id}`)}
onclick={() => goto(`/inventory/collections/${collection.id}`)}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
>
<CaretLeft size={20} />
@ -94,7 +94,7 @@
<div class="flex justify-end gap-3 pt-4">
<button
onclick={() => goto(`/inventar/collections/${collection.id}`)}
onclick={() => goto(`/inventory/collections/${collection.id}`)}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))]"
>
Abbrechen

View file

@ -1,13 +1,13 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { CaretLeft } from '@mana/shared-icons';
import { collectionsStore } from '$lib/modules/inventar/stores/collections.svelte';
import { collectionsStore } from '$lib/modules/inventory/stores/collections.svelte';
import {
DEFAULT_TEMPLATES,
type Template,
type CollectionSchema,
} from '$lib/modules/inventar/constants';
import SchemaEditor from '$lib/modules/inventar/components/fields/SchemaEditor.svelte';
} from '$lib/modules/inventory/constants';
import SchemaEditor from '$lib/modules/inventory/components/fields/SchemaEditor.svelte';
let step = $state<'template' | 'details'>('template');
let selectedTemplate = $state<Template | null>(null);
@ -35,7 +35,7 @@
schema,
templateId: selectedTemplate?.id,
});
goto('/inventar');
goto('/inventory');
}
const inputClass =
@ -50,7 +50,7 @@
<div class="flex items-center gap-3">
<button
onclick={() =>
step === 'details' && selectedTemplate ? (step = 'template') : goto('/inventar')}
step === 'details' && selectedTemplate ? (step = 'template') : goto('/inventory')}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
>
<CaretLeft size={20} />
@ -127,7 +127,7 @@
<!-- Actions -->
<div class="flex justify-end gap-3 pt-4">
<button
onclick={() => goto('/inventar')}
onclick={() => goto('/inventory')}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))]"
>
Abbrechen

View file

@ -4,24 +4,24 @@
import { goto } from '$app/navigation';
import { CaretLeft } from '@mana/shared-icons';
import { getContext } from 'svelte';
import { itemsStore } from '$lib/modules/inventar/stores/items.svelte';
import { itemsStore } from '$lib/modules/inventory/stores/items.svelte';
import {
getItemById,
getCollectionById,
getLocationById,
getLocationFullPath,
getCategoryById,
} from '$lib/modules/inventar/queries';
} from '$lib/modules/inventory/queries';
import type {
Collection,
Item,
Location,
Category,
ItemStatus,
} from '$lib/modules/inventar/queries';
import FieldRenderer from '$lib/modules/inventar/components/fields/FieldRenderer.svelte';
import FieldEditor from '$lib/modules/inventar/components/fields/FieldEditor.svelte';
import StatusBadge from '$lib/modules/inventar/components/StatusBadge.svelte';
} from '$lib/modules/inventory/queries';
import FieldRenderer from '$lib/modules/inventory/components/fields/FieldRenderer.svelte';
import FieldEditor from '$lib/modules/inventory/components/fields/FieldEditor.svelte';
import StatusBadge from '$lib/modules/inventory/components/StatusBadge.svelte';
const collectionsCtx: { readonly value: Collection[] } = getContext('collections');
const itemsCtx: { readonly value: Item[] } = getContext('items');
@ -90,7 +90,7 @@
async function deleteItem() {
if (!item || !confirm('Item endgultig loschen?')) return;
await itemsStore.delete(item.id);
goto(collection ? `/inventar/collections/${collection.id}` : '/inventar');
goto(collection ? `/inventory/collections/${collection.id}` : '/inventory');
}
const inputClass =
@ -104,7 +104,7 @@
{#if !item}
<div class="text-center py-16">
<p class="text-[hsl(var(--muted-foreground))]">Item nicht gefunden</p>
<a href="/inventar" class="mt-4 text-[hsl(var(--primary))]">Zuruck</a>
<a href="/inventory" class="mt-4 text-[hsl(var(--primary))]">Zuruck</a>
</div>
{:else}
<div class="mx-auto max-w-2xl space-y-6">
@ -112,7 +112,8 @@
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<button
onclick={() => goto(collection ? `/inventar/collections/${collection.id}` : '/inventar')}
onclick={() =>
goto(collection ? `/inventory/collections/${collection.id}` : '/inventory')}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
>
<CaretLeft size={20} />

View file

@ -1,8 +1,8 @@
<script lang="ts">
import { getContext } from 'svelte';
import { locationsStore } from '$lib/modules/inventar/stores/locations.svelte';
import { getLocationTree } from '$lib/modules/inventar/queries';
import type { Location } from '$lib/modules/inventar/queries';
import { locationsStore } from '$lib/modules/inventory/stores/locations.svelte';
import { getLocationTree } from '$lib/modules/inventory/queries';
import type { Location } from '$lib/modules/inventory/queries';
import { Plus } from '@mana/shared-icons';
const locationsCtx: { readonly value: Location[] } = getContext('locations');

View file

@ -1,9 +1,9 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { getContext } from 'svelte';
import { getFilteredItems, getCollectionById } from '$lib/modules/inventar/queries';
import type { Collection, Item } from '$lib/modules/inventar/queries';
import StatusBadge from '$lib/modules/inventar/components/StatusBadge.svelte';
import { getFilteredItems, getCollectionById } from '$lib/modules/inventory/queries';
import type { Collection, Item } from '$lib/modules/inventory/queries';
import StatusBadge from '$lib/modules/inventory/components/StatusBadge.svelte';
import { MagnifyingGlass } from '@mana/shared-icons';
const collectionsCtx: { readonly value: Collection[] } = getContext('collections');
@ -41,7 +41,7 @@
<div class="space-y-2">
{#each results as item (item.id)}
<button
onclick={() => goto(`/inventar/items/${item.id}`)}
onclick={() => goto(`/inventory/items/${item.id}`)}
class="flex w-full items-center gap-4 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-left transition-colors hover:border-[hsl(var(--primary)/0.3)]"
>
<div class="flex-1">

View file

@ -810,7 +810,7 @@ services:
# REMOVED standalone web containers — now served by unified mana-web container (mana.how):
# chat-web, todo-web, zitare-web, calendar-web, clock-web, contacts-web,
# storage-web, presi-web, cards-web, nutriphi-web, skilltree-web, photos-web,
# music-web, citycorners-web, picture-web, inventar-web, calc-web, times-web,
# music-web, citycorners-web, picture-web, inventory-web, calc-web, times-web,
# uload-web, memoro-web
# picture-backend: REMOVED — replaced by Hono server (apps/picture/apps/server)

View file

@ -242,7 +242,7 @@ scrape_configs:
- https://mana.how/notes
- https://mana.how/habits
- https://mana.how/guides
- https://mana.how/inventar
- https://mana.how/inventory
- https://mana.how/playground
# Standalone games (separate containers)
- https://whopxl.mana.how

View file

@ -133,7 +133,7 @@ Pure CRUD apps use mana-sync directly.
| 5022 | citycorners-web | *(local-first only)* |
| 5023 | cards-web | 3036 cards-server |
| 5024 | mukke-web | 3037 mukke-server |
| 5025 | inventar-web | *(local-first only)* |
| 5025 | inventory-web | *(local-first only)* |
| 5026 | context-web | *(local-first only)* |
| 5027 | questions-web | *(local-first only)* |
| 5028 | planta-web | 3039 planta-server |

View file

@ -26,7 +26,7 @@ const apps = [
{ name: 'music', url: `${BASE}:5189` },
{ name: 'citycorners', url: `${BASE}:5190` },
{ name: 'picture', url: `${BASE}:5174` },
{ name: 'inventar', url: `${BASE}:5191` },
{ name: 'inventory', url: `${BASE}:5191` },
];
// When testing against production, use subdomains

View file

@ -108,8 +108,8 @@
"dev:photos:app": "pnpm dev:photos:web",
"dev:photos:full": "concurrently -n auth,sync,web -c blue,magenta,cyan \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:photos:web\"",
"dev:tags-test": "concurrently -n auth,sync,api -c blue,magenta,yellow \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:api\"",
"inventar:dev": "turbo run dev --filter=inventar...",
"dev:inventar:web": "pnpm --filter @inventar/web dev",
"inventory:dev": "turbo run dev --filter=inventory...",
"dev:inventory:web": "pnpm --filter @inventory/web dev",
"times:dev": "turbo run dev --filter=times...",
"dev:times:web": "pnpm --filter @times/web dev",
"dev:times:full": "concurrently -n auth,sync,web -c blue,magenta,cyan \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:times:web\"",
@ -246,7 +246,7 @@
"dev:skilltree:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:skilltree:web\"",
"dev:photos:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:photos:web\"",
"dev:citycorners:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:citycorners:web\"",
"dev:inventar:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:inventar:web\"",
"dev:inventory:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:inventory:web\"",
"dev:times:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:times:web\"",
"dev:calc:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:calc:web\"",
"dev:manavoxel:local": "concurrently -n sync,web -c magenta,cyan \"pnpm dev:sync\" \"pnpm dev:manavoxel:web\"",

View file

@ -61,7 +61,7 @@
music: ['Musik machen, einfach so', 'Quelloffen & unabhängig', 'Privat by Design'],
storage: ['Deine Dateien, dein Tresor', 'Quelloffen & unabhängig', 'Privat by Design'],
times: ['Zeiterfassung ohne Overhead', 'Quelloffen & unabhängig', 'Privat by Design'],
inventar: ['Alles im Überblick behalten', 'Quelloffen & unabhängig', 'Privat by Design'],
inventory: ['Alles im Überblick behalten', 'Quelloffen & unabhängig', 'Privat by Design'],
uload: ['Links kürzen & verwalten', 'Quelloffen & unabhängig', 'Privat by Design'],
news: ['Nachrichten, kuratiert für dich', 'Quelloffen & unabhängig', 'Privat by Design'],
arcade: ['Spiele direkt im Browser', 'Quelloffen & unabhängig', 'Privat by Design'],
@ -94,7 +94,7 @@
music: ['Make music, just like that', 'Open-source & independent', 'Private by design'],
storage: ['Your files, your vault', 'Open-source & independent', 'Private by design'],
times: ['Time tracking without overhead', 'Open-source & independent', 'Private by design'],
inventar: ['Keep track of everything', 'Open-source & independent', 'Private by design'],
inventory: ['Keep track of everything', 'Open-source & independent', 'Private by design'],
uload: ['Shorten & manage links', 'Open-source & independent', 'Private by design'],
news: ['News, curated for you', 'Open-source & independent', 'Private by design'],
arcade: ['Games right in your browser', 'Open-source & independent', 'Private by design'],

View file

@ -17,7 +17,7 @@
* }
*
* export const viewStore = createViewStore<MyViewMode, MyFilters>({
* storagePrefix: 'inventar',
* storagePrefix: 'inventory',
* defaultViewMode: 'list',
* defaultSort: { field: 'name', direction: 'asc' },
* hasActiveFilters: (f) => !!(f.search || f.status?.length || f.tagIds?.length),
@ -40,7 +40,7 @@ export interface SavedFilter<F> {
}
export interface ViewStoreConfig<V extends string, F extends object> {
/** Prefix for localStorage keys (e.g. 'inventar' → 'inventar_view_mode') */
/** Prefix for localStorage keys (e.g. 'inventory' → 'inventory_view_mode') */
storagePrefix: string;
/** Default view mode */
defaultViewMode: V;

View file

@ -167,7 +167,7 @@ const track = {
memoro: createModuleTracker('memoro'),
app: createModuleTracker('app'),
calc: createModuleTracker('calc'),
inventar: createModuleTracker('inventar'),
inventory: createModuleTracker('inventory'),
moodlit: createModuleTracker('moodlit'),
citycorners: createModuleTracker('citycorners'),
};
@ -570,18 +570,18 @@ export const CalcEvents = {
};
/**
* Inventar App Events
* Inventory App Events
*/
export const InventarEvents = {
itemCreated: () => track.inventar('item_created'),
itemUpdated: () => track.inventar('item_updated'),
itemDeleted: () => track.inventar('item_deleted'),
collectionCreated: () => track.inventar('collection_created'),
collectionDeleted: () => track.inventar('collection_deleted'),
categoryCreated: () => track.inventar('category_created'),
categoryDeleted: () => track.inventar('category_deleted'),
locationCreated: () => track.inventar('location_created'),
locationDeleted: () => track.inventar('location_deleted'),
export const InventoryEvents = {
itemCreated: () => track.inventory('item_created'),
itemUpdated: () => track.inventory('item_updated'),
itemDeleted: () => track.inventory('item_deleted'),
collectionCreated: () => track.inventory('collection_created'),
collectionDeleted: () => track.inventory('collection_deleted'),
categoryCreated: () => track.inventory('category_created'),
categoryDeleted: () => track.inventory('category_deleted'),
locationCreated: () => track.inventory('location_created'),
locationDeleted: () => track.inventory('location_deleted'),
};
/**

View file

@ -190,7 +190,7 @@ export const MANA_APP_INDEX: Record<string, number> = {
photos: 12,
skilltree: 13,
citycorners: 14,
inventar: 15,
inventory: 15,
times: 16,
nutriphi: 17,
planta: 18,

View file

@ -199,7 +199,6 @@ get_tier_badge() {
case "$appid" in
mana.how) appid="mana" ;;
manadeck) appid="cards" ;;
inventar) appid="inventory" ;;
esac
echo "$TIER_APPS" | while IFS='|' read -r id name tier st; do

View file

@ -120,7 +120,7 @@ const PROJECT_META: Record<string, { name: string; icon: string }> = {
picture: { name: 'ManaPicture', icon: '🎨' },
zitare: { name: 'Zitare', icon: '✨' },
presi: { name: 'Presi', icon: '📊' },
inventar: { name: 'Inventar', icon: '📦' },
inventory: { name: 'Inventar', icon: '📦' },
nutriphi: { name: 'Nutriphi', icon: '🥗' },
planta: { name: 'Planta', icon: '🌱' },
storage: { name: 'Storage', icon: '☁️' },