From a91a6076cc6dd8ceb4649268f7e410e2bfb1eb9c Mon Sep 17 00:00:00 2001 From: Till JS Date: Sun, 12 Apr 2026 18:59:44 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20rename=20planta=20=E2=86=92=20plant?= =?UTF-8?q?s,=20clean=20up=20codebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename planta module to plants everywhere (routes, modules, API, branding, i18n, docker, docs, shared packages) - Fix package name collisions: @mana/credits-service, @mana/subscriptions-service (unblocks turbo) - Extract layout composables: use-ai-tier-items, use-sync-status-items, RouteTierGate (layout 1345→1015 lines) - Create shared DB pool for apps/api (lib/db.ts), migrate 5 modules - Add automations module queries.ts with useAllAutomations/useEnabledAutomations - Remove debug console.log statements from production code - Rename storage display name: Ablage → Speicher Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/guidelines/hono-server.md | 2 +- .claude/scheduled_tasks.lock | 1 + README.md | 2 +- apps/api/src/index.ts | 4 +- apps/api/src/lib/db.ts | 32 ++ apps/api/src/modules/news/routes.ts | 8 +- .../src/modules/{planta => plants}/routes.ts | 10 +- apps/api/src/modules/presi/routes.ts | 8 +- apps/api/src/modules/research/schema.ts | 8 +- apps/api/src/modules/todo/routes.ts | 8 +- apps/api/src/modules/traces/routes.ts | 7 +- apps/mana/apps/web/src/hooks.server.ts | 2 +- .../apps/web/src/lib/app-registry/apps.ts | 8 +- .../components/layout/RouteTierGate.svelte | 81 ++++ .../layout/use-ai-tier-items.svelte.ts | 226 +++++++++++ .../layout/use-sync-status-items.svelte.ts | 88 +++++ .../lib/components/observatory/data/layout.ts | 2 +- .../components/observatory/data/mockData.ts | 4 +- .../observatory/ui/DetailPanel.svelte | 2 +- .../components/observatory/ui/LakeCard.svelte | 2 +- .../components/voice/use-local-stt.svelte.ts | 19 - .../apps/web/src/lib/data/DATA_LAYER_AUDIT.md | 4 +- .../apps/web/src/lib/data/crypto/registry.ts | 4 +- apps/mana/apps/web/src/lib/data/database.ts | 2 +- .../web/src/lib/data/module-registry.test.ts | 2 +- .../apps/web/src/lib/data/module-registry.ts | 4 +- .../apps/web/src/lib/data/seed-registry.ts | 4 +- .../web/src/lib/data/time-blocks/types.ts | 2 +- apps/mana/apps/web/src/lib/i18n/index.ts | 6 +- .../web/src/lib/i18n/locales/apps/de.json | 4 +- .../web/src/lib/i18n/locales/apps/en.json | 2 +- .../web/src/lib/i18n/locales/apps/es.json | 2 +- .../web/src/lib/i18n/locales/apps/fr.json | 2 +- .../web/src/lib/i18n/locales/apps/it.json | 2 +- .../i18n/locales/{planta => plants}/de.json | 2 +- .../i18n/locales/{planta => plants}/en.json | 2 +- .../i18n/locales/{planta => plants}/es.json | 2 +- .../i18n/locales/{planta => plants}/fr.json | 2 +- .../i18n/locales/{planta => plants}/it.json | 2 +- .../web/src/lib/modules/automations/index.ts | 2 + .../src/lib/modules/automations/queries.ts | 63 +++ .../core/widgets/PlantWateringWidget.svelte | 6 +- .../lib/modules/nutriphi/mutations.test.ts | 2 +- .../web/src/lib/modules/nutriphi/queries.ts | 2 +- .../{planta => plants}/ListView.svelte | 22 +- .../src/lib/modules/{planta => plants}/api.ts | 6 +- .../modules/{planta => plants}/collections.ts | 4 +- .../lib/modules/{planta => plants}/index.ts | 6 +- .../{planta => plants}/module.config.ts | 4 +- .../{planta => plants}/mutations.test.ts | 8 +- .../modules/{planta => plants}/mutations.ts | 12 +- .../{planta => plants}/queries.test.ts | 2 +- .../lib/modules/{planta => plants}/queries.ts | 2 +- .../{planta => plants}/quick-input-adapter.ts | 2 +- .../{planta => plants}/stores/tags.svelte.ts | 2 +- .../lib/modules/{planta => plants}/types.ts | 2 +- .../{planta => plants}/utils/plant-parser.ts | 2 +- .../{planta => plants}/utils/syntax-help.ts | 4 +- .../views/DetailView.svelte | 104 ++--- .../apps/web/src/lib/quick-input/registry.ts | 2 +- .../apps/web/src/lib/splitscreen/registry.ts | 2 +- apps/mana/apps/web/src/lib/triggers/loader.ts | 2 +- .../apps/web/src/lib/types/dashboard.test.ts | 2 +- apps/mana/apps/web/src/lib/types/dashboard.ts | 6 +- .../apps/web/src/routes/(app)/+layout.svelte | 363 +----------------- .../web/src/routes/(app)/credits/+page.svelte | 2 +- .../routes/(app)/nutriphi/[id]/+page.svelte | 2 +- .../(app)/{planta => plants}/+layout.svelte | 10 +- .../(app)/{planta => plants}/+page.svelte | 36 +- .../{planta => plants}/[id]/+page.svelte | 78 ++-- .../(app)/{planta => plants}/add/+page.svelte | 34 +- .../{planta => plants}/tags/+page.svelte | 6 +- apps/{planta => plants}/CLAUDE.md | 16 +- apps/{planta => plants}/package.json | 2 +- cloudflared-config.yml | 2 +- docker-compose.dev.yml | 4 +- docker-compose.macmini.yml | 10 +- .../grafana/dashboards/master-overview.json | 2 +- .../grafana/dashboards/system-overview.json | 2 +- docker/prometheus/prometheus.yml | 4 +- docs/EXTERNAL_SERVICES.md | 2 +- docs/LOCAL_DEVELOPMENT.md | 2 +- docs/MONITORING.md | 2 +- docs/PORT_SCHEMA.md | 4 +- docs/PRE_LAUNCH_CLEANUP.md | 2 +- docs/REFACTORING_AUDIT_2026_04.md | 12 +- docs/TECHNOLOGY_AUDIT_2026_03.md | 4 +- docs/future/MODULE_IDEAS.md | 4 +- package.json | 10 +- packages/credits/src/operations.ts | 6 +- packages/help/src/mana-faq.ts | 4 +- .../src/components/GuestWelcomeModal.svelte | 4 +- packages/shared-branding/src/app-icons.ts | 4 +- packages/shared-branding/src/config.ts | 6 +- packages/shared-branding/src/index.ts | 2 +- .../{PlantaLogo.svelte => PlantsLogo.svelte} | 2 +- packages/shared-branding/src/logos/index.ts | 2 +- packages/shared-branding/src/mana-apps.ts | 8 +- packages/shared-branding/src/types.ts | 2 +- packages/shared-storage/README.md | 2 +- packages/shared-storage/src/factory.ts | 4 +- packages/shared-storage/src/index.ts | 2 +- packages/shared-storage/src/types.ts | 2 +- .../shared-stores/src/reminder-scheduler.ts | 2 +- packages/shared-types/src/ai-schemas.ts | 4 +- packages/shared-utils/src/analytics.ts | 14 +- packages/spiral-db/src/schema.ts | 2 +- services/mana-auth/src/services/user-data.ts | 2 +- services/mana-credits/package.json | 2 +- services/mana-subscriptions/package.json | 2 +- 110 files changed, 831 insertions(+), 707 deletions(-) create mode 100644 .claude/scheduled_tasks.lock create mode 100644 apps/api/src/lib/db.ts rename apps/api/src/modules/{planta => plants}/routes.ts (94%) create mode 100644 apps/mana/apps/web/src/lib/components/layout/RouteTierGate.svelte create mode 100644 apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts create mode 100644 apps/mana/apps/web/src/lib/components/layout/use-sync-status-items.svelte.ts rename apps/mana/apps/web/src/lib/i18n/locales/{planta => plants}/de.json (99%) rename apps/mana/apps/web/src/lib/i18n/locales/{planta => plants}/en.json (99%) rename apps/mana/apps/web/src/lib/i18n/locales/{planta => plants}/es.json (99%) rename apps/mana/apps/web/src/lib/i18n/locales/{planta => plants}/fr.json (99%) rename apps/mana/apps/web/src/lib/i18n/locales/{planta => plants}/it.json (99%) create mode 100644 apps/mana/apps/web/src/lib/modules/automations/queries.ts rename apps/mana/apps/web/src/lib/modules/{planta => plants}/ListView.svelte (88%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/api.ts (94%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/collections.ts (96%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/index.ts (91%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/module.config.ts (76%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/mutations.test.ts (97%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/mutations.ts (97%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/queries.test.ts (99%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/queries.ts (99%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/quick-input-adapter.ts (98%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/stores/tags.svelte.ts (84%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/types.ts (98%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/utils/plant-parser.ts (99%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/utils/syntax-help.ts (83%) rename apps/mana/apps/web/src/lib/modules/{planta => plants}/views/DetailView.svelte (85%) rename apps/mana/apps/web/src/routes/(app)/{planta => plants}/+layout.svelte (91%) rename apps/mana/apps/web/src/routes/(app)/{planta => plants}/+page.svelte (80%) rename apps/mana/apps/web/src/routes/(app)/{planta => plants}/[id]/+page.svelte (74%) rename apps/mana/apps/web/src/routes/(app)/{planta => plants}/add/+page.svelte (71%) rename apps/mana/apps/web/src/routes/(app)/{planta => plants}/tags/+page.svelte (89%) rename apps/{planta => plants}/CLAUDE.md (54%) rename apps/{planta => plants}/package.json (90%) rename packages/shared-branding/src/logos/{PlantaLogo.svelte => PlantsLogo.svelte} (78%) diff --git a/.claude/guidelines/hono-server.md b/.claude/guidelines/hono-server.md index 817c83d01..55d0989f6 100644 --- a/.claude/guidelines/hono-server.md +++ b/.claude/guidelines/hono-server.md @@ -85,7 +85,7 @@ uploadRoutes.post('/avatar', async (c) => { Only servers that need their own database use Drizzle. Most apps rely on mana-sync for data persistence. -**Servers with Drizzle:** chat, todo, moodlit, context, planta, presi, traces, uload, wisekeep, news +**Servers with Drizzle:** chat, todo, moodlit, context, plants, presi, traces, uload, wisekeep, news **Servers without Drizzle (mana-sync only):** calendar, contacts, cards, mukke, nutriphi, picture, questions, storage diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 000000000..e95fde175 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"7b3ce34c-30d3-4f38-8ce5-3d170ce48f50","pid":91068,"acquiredAt":1775918818294} \ No newline at end of file diff --git a/README.md b/README.md index 4581d966c..a11f2aefc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Monorepo containing all Mana projects — a self-hosted multi-app ecosystem with | **cards** | Card/deck management | NestJS backend, Expo mobile, SvelteKit web | | **zitare** | Daily inspiration quotes | NestJS backend, Expo mobile, SvelteKit web, Astro landing | | **mukke** | Music player | NestJS backend, SvelteKit web | -| **planta** | Plant care tracker | NestJS backend, SvelteKit web | +| **plants** | Plant care tracker | NestJS backend, SvelteKit web | | **storage** | Cloud storage | NestJS backend, SvelteKit web | | **questions** | Q&A with web search | SvelteKit web | | **skilltree** | Skill tree visualization | NestJS backend, SvelteKit web | diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index be013c72a..5c29cf259 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -24,7 +24,7 @@ import { contextRoutes } from './modules/context/routes'; import { pictureRoutes } from './modules/picture/routes'; import { storageRoutes } from './modules/storage/routes'; import { todoRoutes } from './modules/todo/routes'; -import { plantaRoutes } from './modules/planta/routes'; +import { plantsRoutes } from './modules/plants/routes'; import { nutriphiRoutes } from './modules/nutriphi/routes'; import { guidesRoutes } from './modules/guides/routes'; import { moodlitRoutes } from './modules/moodlit/routes'; @@ -56,7 +56,7 @@ app.route('/api/v1/context', contextRoutes); app.route('/api/v1/picture', pictureRoutes); app.route('/api/v1/storage', storageRoutes); app.route('/api/v1/todo', todoRoutes); -app.route('/api/v1/planta', plantaRoutes); +app.route('/api/v1/plants', plantsRoutes); app.route('/api/v1/nutriphi', nutriphiRoutes); app.route('/api/v1/guides', guidesRoutes); app.route('/api/v1/moodlit', moodlitRoutes); diff --git a/apps/api/src/lib/db.ts b/apps/api/src/lib/db.ts new file mode 100644 index 000000000..9d810111d --- /dev/null +++ b/apps/api/src/lib/db.ts @@ -0,0 +1,32 @@ +/** + * Shared database connection pool for mana-api. + * + * All modules share a single connection pool instead of each creating + * their own. The pool is created lazily on first call and reused. + * + * Usage: + * ```ts + * import { getConnection } from '../../lib/db'; + * import { drizzle } from 'drizzle-orm/postgres-js'; + * + * const db = drizzle(getConnection(), { schema: { ... } }); + * ``` + */ + +import postgres from 'postgres'; + +const DATABASE_URL = + process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; + +let pool: ReturnType | null = null; + +/** + * Returns the shared postgres connection pool. + * Created lazily with sensible defaults (max 10 connections). + */ +export function getConnection() { + if (!pool) { + pool = postgres(DATABASE_URL, { max: 10, idle_timeout: 30 }); + } + return pool; +} diff --git a/apps/api/src/modules/news/routes.ts b/apps/api/src/modules/news/routes.ts index 335b069c7..82879799f 100644 --- a/apps/api/src/modules/news/routes.ts +++ b/apps/api/src/modules/news/routes.ts @@ -14,16 +14,12 @@ import { Hono } from 'hono'; import { Readability } from '@mozilla/readability'; import { JSDOM } from 'jsdom'; import { drizzle } from 'drizzle-orm/postgres-js'; -import postgres from 'postgres'; import { sql } from 'drizzle-orm'; +import { getConnection } from '../../lib/db'; // ─── DB Connection (reads from news.curated_articles) ────── -const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; - -const connection = postgres(DATABASE_URL, { max: 10 }); -const db = drizzle(connection); +const db = drizzle(getConnection()); // ─── Extract Service (Readability fallback for ad-hoc URLs) ─ diff --git a/apps/api/src/modules/planta/routes.ts b/apps/api/src/modules/plants/routes.ts similarity index 94% rename from apps/api/src/modules/planta/routes.ts rename to apps/api/src/modules/plants/routes.ts index d61064790..d82a387c4 100644 --- a/apps/api/src/modules/planta/routes.ts +++ b/apps/api/src/modules/plants/routes.ts @@ -1,5 +1,5 @@ /** - * Planta module — Photo upload + AI plant identification. + * Plants module — Photo upload + AI plant identification. * * CRUD for plants, photos, watering is handled by mana-sync. This * module owns the server-only operations: photo upload to mana-media @@ -61,7 +61,7 @@ routes.post('/photos/upload', async (c) => { try { const { uploadImageToMedia } = await import('../../lib/media'); const buffer = await file.arrayBuffer(); - const result = await uploadImageToMedia(buffer, file.name, { app: 'planta', userId }); + const result = await uploadImageToMedia(buffer, file.name, { app: 'plants', userId }); return c.json( { @@ -73,7 +73,7 @@ routes.post('/photos/upload', async (c) => { 201 ); } catch (err) { - logger.error('planta.upload_failed', { + logger.error('plants.upload_failed', { error: err instanceof Error ? err.message : String(err), }); return c.json({ error: 'Upload failed' }, 500); @@ -107,11 +107,11 @@ routes.post('/analysis/identify', async (c) => { }); return c.json(envelope(object)); } catch (err) { - logger.error('planta.analysis_failed', { + logger.error('plants.analysis_failed', { error: err instanceof Error ? err.message : String(err), }); return c.json({ error: 'Analysis failed' }, 500); } }); -export { routes as plantaRoutes }; +export { routes as plantsRoutes }; diff --git a/apps/api/src/modules/presi/routes.ts b/apps/api/src/modules/presi/routes.ts index fcfd0a4f9..ad974fcb3 100644 --- a/apps/api/src/modules/presi/routes.ts +++ b/apps/api/src/modules/presi/routes.ts @@ -12,7 +12,7 @@ import { HTTPException } from 'hono/http-exception'; import { authMiddleware } from '@mana/shared-hono/auth'; import type { AuthVariables } from '@mana/shared-hono'; import { drizzle } from 'drizzle-orm/postgres-js'; -import postgres from 'postgres'; +import { getConnection } from '../../lib/db'; import { decks, slides, @@ -25,11 +25,7 @@ import { // ─── DB Connection ───────────────────────────────────────── -const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; - -const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20 }); -const db = drizzle(connection, { +const db = drizzle(getConnection(), { schema: { decks, slides, diff --git a/apps/api/src/modules/research/schema.ts b/apps/api/src/modules/research/schema.ts index 9b4ceacb2..06007cdd6 100644 --- a/apps/api/src/modules/research/schema.ts +++ b/apps/api/src/modules/research/schema.ts @@ -13,11 +13,8 @@ */ import { drizzle } from 'drizzle-orm/postgres-js'; -import postgres from 'postgres'; import { pgSchema, uuid, text, timestamp, integer, jsonb } from 'drizzle-orm/pg-core'; - -const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; +import { getConnection } from '../../lib/db'; export const researchSchema = pgSchema('research'); @@ -58,8 +55,7 @@ export const sources = researchSchema.table('sources', { createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), }); -const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20 }); -export const db = drizzle(connection, { schema: { researchResults, sources } }); +export const db = drizzle(getConnection(), { schema: { researchResults, sources } }); export type ResearchResult = typeof researchResults.$inferSelect; export type Source = typeof sources.$inferSelect; diff --git a/apps/api/src/modules/todo/routes.ts b/apps/api/src/modules/todo/routes.ts index 111045607..e4567d953 100644 --- a/apps/api/src/modules/todo/routes.ts +++ b/apps/api/src/modules/todo/routes.ts @@ -17,8 +17,8 @@ import { rrulestr } from 'rrule'; import { z } from 'zod'; import { eq, and, asc, sql } from 'drizzle-orm'; import { drizzle } from 'drizzle-orm/postgres-js'; -import postgres from 'postgres'; import { serviceAuthMiddleware, type AuthVariables } from '@mana/shared-hono'; +import { getConnection } from '../../lib/db'; import { pgSchema, uuid, @@ -33,9 +33,6 @@ import { // ─── DB Schema (minimal, server-only) ────────────────────── -const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; - const todoSchema = pgSchema('todo'); const tasks = todoSchema.table('tasks', { @@ -88,8 +85,7 @@ const reminders = todoSchema.table( }) ); -const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20 }); -const db = drizzle(connection, { schema: { tasks, projects, reminders } }); +const db = drizzle(getConnection(), { schema: { tasks, projects, reminders } }); // ─── Routes ──────────────────────────────────────────────── diff --git a/apps/api/src/modules/traces/routes.ts b/apps/api/src/modules/traces/routes.ts index 6b8c697f8..3c22c6b0d 100644 --- a/apps/api/src/modules/traces/routes.ts +++ b/apps/api/src/modules/traces/routes.ts @@ -10,7 +10,7 @@ import { Hono } from 'hono'; import { logger, type AuthVariables } from '@mana/shared-hono'; import { eq, and } from 'drizzle-orm'; import { drizzle } from 'drizzle-orm/postgres-js'; -import postgres from 'postgres'; +import { getConnection } from '../../lib/db'; import { pgSchema, uuid, @@ -23,8 +23,6 @@ import { // ─── DB Schema ────────────────────────────────────────────── -const DATABASE_URL = - process.env.DATABASE_URL ?? 'postgresql://mana:devpassword@localhost:5432/mana_platform'; const LLM_URL = process.env.MANA_LLM_URL || 'http://localhost:3025'; const tracesSchema = pgSchema('traces'); @@ -116,8 +114,7 @@ const guidePois = tracesSchema.table('guide_pois', { createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), }); -const connection = postgres(DATABASE_URL, { max: 5, idle_timeout: 20 }); -const db = drizzle(connection, { schema: { locations, cities, pois, guides, guidePois } }); +const db = drizzle(getConnection(), { schema: { locations, cities, pois, guides, guidePois } }); // ─── Routes ───────────────────────────────────────────────── diff --git a/apps/mana/apps/web/src/hooks.server.ts b/apps/mana/apps/web/src/hooks.server.ts index c5f9c01d5..f5e0a60ed 100644 --- a/apps/mana/apps/web/src/hooks.server.ts +++ b/apps/mana/apps/web/src/hooks.server.ts @@ -56,7 +56,7 @@ const APP_SUBDOMAINS = new Set([ 'contacts', 'zitare', 'skilltree', - 'planta', + 'plants', 'cards', 'storage', 'presi', diff --git a/apps/mana/apps/web/src/lib/app-registry/apps.ts b/apps/mana/apps/web/src/lib/app-registry/apps.ts index d8427bccf..e7b595aea 100644 --- a/apps/mana/apps/web/src/lib/app-registry/apps.ts +++ b/apps/mana/apps/web/src/lib/app-registry/apps.ts @@ -605,13 +605,13 @@ registerApp({ }); registerApp({ - id: 'planta', - name: 'Planta', + id: 'plants', + name: 'Plants', color: '#16A34A', icon: Plant, views: { - list: { load: () => import('$lib/modules/planta/ListView.svelte') }, - detail: { load: () => import('$lib/modules/planta/views/DetailView.svelte') }, + list: { load: () => import('$lib/modules/plants/ListView.svelte') }, + detail: { load: () => import('$lib/modules/plants/views/DetailView.svelte') }, }, }); diff --git a/apps/mana/apps/web/src/lib/components/layout/RouteTierGate.svelte b/apps/mana/apps/web/src/lib/components/layout/RouteTierGate.svelte new file mode 100644 index 000000000..ab74e97ad --- /dev/null +++ b/apps/mana/apps/web/src/lib/components/layout/RouteTierGate.svelte @@ -0,0 +1,81 @@ + + + +
+
+

+ {appName} +

+
🔒
+

+ {isDE + ? 'Diese App ist aktuell in der geschlossenen ' + : 'This app is currently in closed '}{requiredTierLabel}{isDE + ? '-Phase.' + : ' phase.'} +

+
+
+ {isDE ? 'Dein Zugang:' : 'Your access:'} + {userTierLabel} +
+
+ {isDE ? 'Benötigt:' : 'Required:'} + {requiredTierLabel} +
+
+
+ + {#if !authStore.isAuthenticated} + + {/if} +
+
+
diff --git a/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts b/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts new file mode 100644 index 000000000..036601ea2 --- /dev/null +++ b/apps/mana/apps/web/src/lib/components/layout/use-ai-tier-items.svelte.ts @@ -0,0 +1,226 @@ +/** + * AI Tier Selector — composable for the PillNavigation AI dropdown. + * + * Manages LLM tier toggles (browser/server/cloud) and STT model + * selection. Returns reactive PillDropdownItem arrays + labels. + */ + +import { goto } from '$app/navigation'; +import type { PillDropdownItem } from '@mana/shared-ui'; +import { llmSettingsState, updateLlmSettings, type LlmTier } from '@mana/shared-llm'; +import { isLocalLlmSupported, getLocalLlmStatus, loadLocalLlm } from '@mana/local-llm'; +import { + getLocalSttStatus, + loadLocalStt, + isLocalSttSupported, + MODELS as STT_MODELS, + DEFAULT_MODEL as STT_DEFAULT_MODEL, + type ModelKey as SttModelKey, +} from '@mana/local-stt'; + +const TIER_TOGGLE_LIST: Array<{ tier: LlmTier; shortLabel: string; icon: string }> = [ + { tier: 'browser', shortLabel: 'Lokal (Gemma 4)', icon: 'robot' }, + { tier: 'mana-server', shortLabel: 'Server (Gemma 4)', icon: 'globe' }, + { tier: 'cloud', shortLabel: 'Cloud (Gemini)', icon: 'cloud' }, +]; + +export function useAiTierItems() { + const webgpuSupported = isLocalLlmSupported(); + const localLlmStatus = getLocalLlmStatus(); + const sttSupported = isLocalSttSupported(); + const localSttStatus = getLocalSttStatus(); + let selectedSttModel = $state(STT_DEFAULT_MODEL); + const llmSettings = $derived(llmSettingsState.current); + + function toggleAiTier(tier: LlmTier) { + const current = llmSettings.allowedTiers; + const next = current.includes(tier) + ? current.filter((t: LlmTier) => t !== tier) + : [...current, tier]; + updateLlmSettings({ allowedTiers: next }); + } + + function buildLlmStatusItem(): PillDropdownItem { + const s = localLlmStatus.current; + const state = s.state; + let label: string; + let icon: string; + let danger = false; + let disabled = false; + + switch (state) { + case 'ready': + label = 'Geladen'; + icon = 'checkCircle'; + disabled = true; + break; + case 'downloading': + label = `Lade… ${((s as { progress: number }).progress * 100).toFixed(0)}%`; + icon = 'clock'; + disabled = true; + break; + case 'loading': + label = 'Initialisiere…'; + icon = 'clock'; + disabled = true; + break; + case 'checking': + label = 'Prüfe…'; + icon = 'clock'; + disabled = true; + break; + case 'error': + label = 'Fehler — erneut versuchen'; + icon = 'bell'; + danger = true; + break; + default: + label = 'Modell laden'; + icon = 'cloud'; + } + + return { + id: 'ai-browser-status', + label, + icon, + group: 'local-llm', + danger, + disabled, + progress: state === 'downloading' ? (s as { progress: number }).progress : undefined, + onClick: !disabled ? () => void loadLocalLlm() : undefined, + }; + } + + function buildSttStatusItem(): PillDropdownItem { + const s = localSttStatus.current; + const state = s.state; + let label: string; + let icon: string; + let danger = false; + let disabled = false; + + switch (state) { + case 'ready': + label = 'STT bereit'; + icon = 'checkCircle'; + disabled = true; + break; + case 'downloading': + label = `STT Lade… ${((s as { progress: number }).progress * 100).toFixed(0)}%`; + icon = 'clock'; + disabled = true; + break; + case 'loading': + label = 'STT lädt…'; + icon = 'clock'; + disabled = true; + break; + case 'checking': + label = 'STT prüft…'; + icon = 'clock'; + disabled = true; + break; + case 'error': + label = 'STT Fehler'; + icon = 'bell'; + danger = true; + break; + default: + label = 'STT Modell laden'; + icon = 'mic'; + } + + return { + id: 'stt-status', + label, + icon, + danger, + disabled, + progress: state === 'downloading' ? (s as { progress: number }).progress : undefined, + onClick: !disabled ? () => void loadLocalStt(selectedSttModel) : undefined, + }; + } + + const items = $derived([ + // Tier toggles + ...TIER_TOGGLE_LIST.filter((t) => t.tier !== 'browser' || webgpuSupported).map((t) => { + const isActive = llmSettings.allowedTiers.includes(t.tier); + return { + id: `ai-tier-${t.tier}`, + label: t.shortLabel, + icon: isActive ? 'checkCircle' : t.icon, + active: isActive, + onClick: () => toggleAiTier(t.tier), + ...(t.tier === 'browser' ? { group: 'local-llm' } : {}), + }; + }), + // Browser model status (grouped with the "Lokal" toggle) + ...(llmSettings.allowedTiers.includes('browser') && webgpuSupported + ? [buildLlmStatusItem()] + : []), + // STT section + { id: 'stt-divider', label: '', divider: true }, + ...(sttSupported + ? (Object.entries(STT_MODELS) as [SttModelKey, (typeof STT_MODELS)[SttModelKey]][]).map( + ([key, model]) => { + const isSelected = selectedSttModel === key; + return { + id: `stt-model-${key}`, + label: model.displayName, + icon: isSelected ? 'checkCircle' : 'mic', + active: isSelected, + onClick: () => { + selectedSttModel = key; + void loadLocalStt(key); + }, + }; + } + ) + : []), + ...(sttSupported ? [buildSttStatusItem()] : []), + // Settings link + { id: 'ai-divider', label: '', divider: true }, + { + id: 'ai-settings', + label: 'KI-Einstellungen', + icon: 'settings', + onClick: () => goto('/settings#ai-options'), + }, + ]); + + const label = $derived.by(() => { + const active = llmSettings.allowedTiers; + if (active.length === 0) return 'Aus'; + const sorted = [...active].sort( + (a, b) => + TIER_TOGGLE_LIST.findIndex((t) => t.tier === a) - + TIER_TOGGLE_LIST.findIndex((t) => t.tier === b) + ); + const first = TIER_TOGGLE_LIST.find((t) => t.tier === sorted[0]); + return first ? first.shortLabel.split(' (')[0] : 'KI'; + }); + + const icon = $derived.by(() => { + const active = llmSettings.allowedTiers; + if (active.length === 0) return 'power'; + const sorted = [...active].sort( + (a, b) => + TIER_TOGGLE_LIST.findIndex((t) => t.tier === a) - + TIER_TOGGLE_LIST.findIndex((t) => t.tier === b) + ); + const first = TIER_TOGGLE_LIST.find((t) => t.tier === sorted[0]); + return first ? first.icon : 'cpu'; + }); + + return { + get items() { + return items; + }, + get label() { + return label; + }, + get icon() { + return icon; + }, + }; +} diff --git a/apps/mana/apps/web/src/lib/components/layout/use-sync-status-items.svelte.ts b/apps/mana/apps/web/src/lib/components/layout/use-sync-status-items.svelte.ts new file mode 100644 index 000000000..bb51fb176 --- /dev/null +++ b/apps/mana/apps/web/src/lib/components/layout/use-sync-status-items.svelte.ts @@ -0,0 +1,88 @@ +/** + * Sync Status Dropdown — composable for the PillNavigation sync pill. + * + * Returns reactive PillDropdownItems showing sync subscription state + * (active / paused / inactive) with links to settings and credits. + */ + +import { goto } from '$app/navigation'; +import type { PillDropdownItem } from '@mana/shared-ui'; +import { syncBilling } from '$lib/stores/sync-billing.svelte'; + +export function useSyncStatusItems() { + const items = $derived.by(() => { + const result: PillDropdownItem[] = []; + + if (syncBilling.active) { + result.push({ + id: 'sync-active', + label: 'Cloud Sync aktiv', + icon: 'cloud', + active: true, + disabled: true, + }); + if (syncBilling.nextChargeAt) { + const date = new Date(syncBilling.nextChargeAt).toLocaleDateString('de-DE', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }); + result.push({ + id: 'sync-next', + label: `Nächste Abbuchung: ${date}`, + icon: 'calendar', + disabled: true, + }); + } + } else if (syncBilling.paused) { + result.push({ + id: 'sync-paused', + label: 'Sync pausiert — Credits aufladen', + icon: 'bell', + onClick: () => goto('/credits?tab=packages'), + }); + } else { + result.push({ + id: 'sync-inactive', + label: 'Sync aktivieren', + icon: 'cloud', + onClick: () => goto('/settings/sync'), + }); + result.push({ + id: 'sync-info', + label: 'Nur lokal — ab 30 Credits/Monat', + icon: 'creditCard', + disabled: true, + }); + } + + result.push({ id: 'sync-divider', label: '', divider: true }); + result.push({ + id: 'sync-settings', + label: 'Sync-Einstellungen', + icon: 'settings', + onClick: () => goto('/settings/sync'), + }); + + return result; + }); + + const label = $derived( + syncBilling.loading + ? '...' + : syncBilling.active + ? 'Sync' + : syncBilling.paused + ? 'Pausiert' + : 'Lokal' + ); + + return { + get items() { + return items; + }, + get label() { + return label; + }, + }; +} diff --git a/apps/mana/apps/web/src/lib/components/observatory/data/layout.ts b/apps/mana/apps/web/src/lib/components/observatory/data/layout.ts index ae6038274..a32b589dd 100644 --- a/apps/mana/apps/web/src/lib/components/observatory/data/layout.ts +++ b/apps/mana/apps/web/src/lib/components/observatory/data/layout.ts @@ -160,7 +160,7 @@ export const APP_POSITIONS: Record = { diff --git a/apps/mana/apps/web/src/lib/components/voice/use-local-stt.svelte.ts b/apps/mana/apps/web/src/lib/components/voice/use-local-stt.svelte.ts index 6a1b9f48e..458e607b2 100644 --- a/apps/mana/apps/web/src/lib/components/voice/use-local-stt.svelte.ts +++ b/apps/mana/apps/web/src/lib/components/voice/use-local-stt.svelte.ts @@ -151,16 +151,8 @@ export function useLocalStt(options?: { language?: string }): LocalSttHandle { const capturedRate = sampleRate; cleanup(); - console.log( - '[local-stt] Captured', - capturedChunks.length, - 'chunks, sample rate:', - capturedRate - ); - if (capturedChunks.length === 0) { error = 'Keine Audiodaten aufgenommen.'; - console.warn('[local-stt] No audio chunks captured'); state = 'idle'; return; } @@ -180,25 +172,14 @@ export function useLocalStt(options?: { language?: string }): LocalSttHandle { // Resample to 16 kHz if needed const audio = capturedRate === 16000 ? merged : resample(merged, capturedRate, 16000); - const durationSec = audio.length / 16000; - console.log('[local-stt] Audio ready:', { - originalSamples: merged.length, - resampledSamples: audio.length, - durationSec: durationSec.toFixed(1), - sampleRate: capturedRate, - maxAmplitude: Math.max(...Array.from(audio.slice(0, 16000)).map(Math.abs)), - }); - const result = await transcribe({ audio, language: options?.language, onChunk: (t: string) => { partial += t; - console.log('[local-stt] Chunk:', t); }, }); - console.log('[local-stt] Result:', result); text = result.text.trim(); } catch (e) { error = e instanceof Error ? e.message : String(e); diff --git a/apps/mana/apps/web/src/lib/data/DATA_LAYER_AUDIT.md b/apps/mana/apps/web/src/lib/data/DATA_LAYER_AUDIT.md index 23aaec99e..e4aa51790 100644 --- a/apps/mana/apps/web/src/lib/data/DATA_LAYER_AUDIT.md +++ b/apps/mana/apps/web/src/lib/data/DATA_LAYER_AUDIT.md @@ -35,7 +35,7 @@ | 3 | Client Wire-up: vault-client, record-helpers, layout integration | ✅ | `354cbcb17` | | 4 | Pilot: notes table mit 8 End-to-End Tests | ✅ | `bed08a1aa` | | 5 | Rollout: chat, dreams, memoro, contacts, cycles, finance | ✅ | `af92720a6` | -| 6.1 | Rollout: cards, presi, inventar, planta | ✅ | `73f294b29` | +| 6.1 | Rollout: cards, presi, inventar, plants | ✅ | `73f294b29` | | 6.2 + 6.3 | Settings UI (`/settings/security`) + Encryption Intro Banner | ✅ | `6b8e2c717` | | Roundup | DATA_LAYER_AUDIT roll-up vor Phase 7 | ✅ | `4bdf4238c` | | 7.1 | timeBlocks-Hub: tasks + calendar.events + timeBlocks (mit Habits-Coupling) | ✅ | `c875b4e96` | @@ -396,7 +396,7 @@ Unlock-Flow (Login auf neuem Gerät): | presi | `presiDecks` | `title`, `description` | 6.1 | | | `slides` | `content` (SlideContent JSON) | 6.1 | | inventar | `invItems` | `description` (name + notes-array bleiben plaintext) | 6.1 | -| planta | `plants` | `name`, `careNotes`, `temperature`, `soilType` | 6.1 | +| plants | `plants` | `name`, `careNotes`, `temperature`, `soilType` | 6.1 | | **todo** | **`tasks`** | **`title`, `description`, `subtasks`, `metadata`** | **7.1** | | **calendar** | **`events`** | **`title`, `description`, `location`** | **7.1** | | **time-blocks** | **`timeBlocks`** | **`title`, `description`** (Cross-Module-Hub für todo/calendar/habits/times) | **7.1** | diff --git a/apps/mana/apps/web/src/lib/data/crypto/registry.ts b/apps/mana/apps/web/src/lib/data/crypto/registry.ts index 622dbdca1..a9811ec20 100644 --- a/apps/mana/apps/web/src/lib/data/crypto/registry.ts +++ b/apps/mana/apps/web/src/lib/data/crypto/registry.ts @@ -148,11 +148,11 @@ export const ENCRYPTION_REGISTRY: Record = { // - photoMediaId / photoUrl / photoThumbnailUrl: opaque pointers to // mana-media; the URL alone is not PII (anyone with the URL // already has the bytes), and CAS-deduped media IDs leak no user - // content. Same rationale planta uses for plantPhotos. + // content. Same rationale plants uses for plantPhotos. // - confidence (float 0-1): pure metadata about the AI run. meals: { enabled: true, fields: ['description', 'portionSize', 'foods'] }, - // ─── Planta ────────────────────────────────────────────── + // ─── Plants ────────────────────────────────────────────── // `name` is NOT in the schema index for plants (only isActive + // healthStatus), so encrypting it is safe. LocalPlant uses // `careNotes` (no separate `notes`) plus the user-typed metadata. diff --git a/apps/mana/apps/web/src/lib/data/database.ts b/apps/mana/apps/web/src/lib/data/database.ts index a782eece5..e91eb3a9c 100644 --- a/apps/mana/apps/web/src/lib/data/database.ts +++ b/apps/mana/apps/web/src/lib/data/database.ts @@ -182,7 +182,7 @@ db.version(1).stores({ nutriFavorites: 'id, mealType, usageCount', mealTags: 'id, mealId, tagId, [mealId+tagId]', - // ─── Planta (appId: 'planta') ─── + // ─── Plants (appId: 'plants') ─── plants: 'id, isActive, healthStatus', plantPhotos: 'id, plantId, isPrimary, [plantId+isPrimary]', wateringSchedules: 'id, plantId, nextWateringAt', diff --git a/apps/mana/apps/web/src/lib/data/module-registry.test.ts b/apps/mana/apps/web/src/lib/data/module-registry.test.ts index 032a70c2b..2b4c53c87 100644 --- a/apps/mana/apps/web/src/lib/data/module-registry.test.ts +++ b/apps/mana/apps/web/src/lib/data/module-registry.test.ts @@ -156,7 +156,7 @@ describe('module-registry — pre-refactor snapshot', () => { context: ['contextSpaces', 'documents', 'documentTags'], questions: ['qCollections', 'questions', 'answers', 'questionTags'], nutriphi: ['meals', 'goals', 'nutriFavorites', 'mealTags'], - planta: ['plants', 'plantPhotos', 'wateringSchedules', 'wateringLogs', 'plantTags'], + plants: ['plants', 'plantPhotos', 'wateringSchedules', 'wateringLogs', 'plantTags'], uload: ['links', 'uloadTags', 'uloadFolders', 'linkTags'], calc: ['calculations', 'savedFormulas'], moodlit: ['moods', 'sequences', 'moodTags'], diff --git a/apps/mana/apps/web/src/lib/data/module-registry.ts b/apps/mana/apps/web/src/lib/data/module-registry.ts index ca2e5f3fc..ed236b508 100644 --- a/apps/mana/apps/web/src/lib/data/module-registry.ts +++ b/apps/mana/apps/web/src/lib/data/module-registry.ts @@ -69,7 +69,7 @@ import { timesModuleConfig } from '$lib/modules/times/module.config'; import { contextModuleConfig } from '$lib/modules/context/module.config'; import { questionsModuleConfig } from '$lib/modules/questions/module.config'; import { nutriphiModuleConfig } from '$lib/modules/nutriphi/module.config'; -import { plantaModuleConfig } from '$lib/modules/planta/module.config'; +import { plantsModuleConfig } from '$lib/modules/plants/module.config'; import { uloadModuleConfig } from '$lib/modules/uload/module.config'; import { calcModuleConfig } from '$lib/modules/calc/module.config'; import { moodlitModuleConfig } from '$lib/modules/moodlit/module.config'; @@ -113,7 +113,7 @@ export const MODULE_CONFIGS: readonly ModuleConfig[] = [ contextModuleConfig, questionsModuleConfig, nutriphiModuleConfig, - plantaModuleConfig, + plantsModuleConfig, uloadModuleConfig, calcModuleConfig, moodlitModuleConfig, diff --git a/apps/mana/apps/web/src/lib/data/seed-registry.ts b/apps/mana/apps/web/src/lib/data/seed-registry.ts index cba89fa4c..ea05c4cc0 100644 --- a/apps/mana/apps/web/src/lib/data/seed-registry.ts +++ b/apps/mana/apps/web/src/lib/data/seed-registry.ts @@ -27,7 +27,7 @@ import { SKILLTREE_GUEST_SEED } from '$lib/modules/skilltree/collections'; import { TODO_GUEST_SEED } from '$lib/modules/todo/collections'; import { NOTES_GUEST_SEED } from '$lib/modules/notes/collections'; import { TIMES_GUEST_SEED } from '$lib/modules/times/collections'; -import { PLANTA_GUEST_SEED } from '$lib/modules/planta/collections'; +import { PLANTS_GUEST_SEED } from '$lib/modules/plants/collections'; import { DRINK_GUEST_SEED } from '$lib/modules/drink/collections'; /** @@ -60,7 +60,7 @@ register(SKILLTREE_GUEST_SEED); register(TODO_GUEST_SEED); register(NOTES_GUEST_SEED); register(TIMES_GUEST_SEED); -register(PLANTA_GUEST_SEED); +register(PLANTS_GUEST_SEED); register(DRINK_GUEST_SEED); /** diff --git a/apps/mana/apps/web/src/lib/data/time-blocks/types.ts b/apps/mana/apps/web/src/lib/data/time-blocks/types.ts index 7c340c6e4..ba3bf0095 100644 --- a/apps/mana/apps/web/src/lib/data/time-blocks/types.ts +++ b/apps/mana/apps/web/src/lib/data/time-blocks/types.ts @@ -38,7 +38,7 @@ export type TimeBlockSourceModule = | 'habits' | 'events' | 'body' - | 'planta' + | 'plants' | 'dreams' | 'skilltree' | 'cycles' diff --git a/apps/mana/apps/web/src/lib/i18n/index.ts b/apps/mana/apps/web/src/lib/i18n/index.ts index 6a9be78e0..6a7c9442a 100644 --- a/apps/mana/apps/web/src/lib/i18n/index.ts +++ b/apps/mana/apps/web/src/lib/i18n/index.ts @@ -41,7 +41,7 @@ function registerLocale(lang: SupportedLocale) { inventory, photos, nutriphi, - planta, + plants, skilltree, citycorners, calc, @@ -77,7 +77,7 @@ function registerLocale(lang: SupportedLocale) { import(`./locales/inventory/${lang}.json`), import(`./locales/photos/${lang}.json`), import(`./locales/nutriphi/${lang}.json`), - import(`./locales/planta/${lang}.json`), + import(`./locales/plants/${lang}.json`), import(`./locales/skilltree/${lang}.json`), import(`./locales/citycorners/${lang}.json`), import(`./locales/calc/${lang}.json`), @@ -115,7 +115,7 @@ function registerLocale(lang: SupportedLocale) { inventory: inventory.default, photos: photos.default, nutriphi: nutriphi.default, - planta: planta.default, + plants: plants.default, skilltree: skilltree.default, citycorners: citycorners.default, calc: calc.default, diff --git a/apps/mana/apps/web/src/lib/i18n/locales/apps/de.json b/apps/mana/apps/web/src/lib/i18n/locales/apps/de.json index 188936e5c..e4c48c35b 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/apps/de.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/apps/de.json @@ -14,9 +14,9 @@ "picture": "Bilder", "music": "Musik", "photos": "Fotos", - "storage": "Ablage", + "storage": "Speicher", "nutriphi": "NutriPhi", - "planta": "Planta", + "plants": "Pflanzen", "presi": "Presi", "inventory": "Inventar", "memoro": "Memoro", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/apps/en.json b/apps/mana/apps/web/src/lib/i18n/locales/apps/en.json index 4f0a9c6bf..03c0654c2 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/apps/en.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/apps/en.json @@ -16,7 +16,7 @@ "photos": "Photos", "storage": "Storage", "nutriphi": "NutriPhi", - "planta": "Planta", + "plants": "Plants", "presi": "Presi", "inventory": "Inventory", "memoro": "Memoro", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/apps/es.json b/apps/mana/apps/web/src/lib/i18n/locales/apps/es.json index 63320679c..336ef4fe9 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/apps/es.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/apps/es.json @@ -16,7 +16,7 @@ "photos": "Fotos", "storage": "Almacén", "nutriphi": "NutriPhi", - "planta": "Planta", + "plants": "Plantas", "presi": "Presi", "inventory": "Inventario", "memoro": "Memoro", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/apps/fr.json b/apps/mana/apps/web/src/lib/i18n/locales/apps/fr.json index 5aa7d57f3..fcf0c50ca 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/apps/fr.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/apps/fr.json @@ -16,7 +16,7 @@ "photos": "Photos", "storage": "Stockage", "nutriphi": "NutriPhi", - "planta": "Planta", + "plants": "Plantes", "presi": "Presi", "inventory": "Inventaire", "memoro": "Memoro", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/apps/it.json b/apps/mana/apps/web/src/lib/i18n/locales/apps/it.json index a7fe3e41b..92a818a4a 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/apps/it.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/apps/it.json @@ -16,7 +16,7 @@ "photos": "Foto", "storage": "Archivio", "nutriphi": "NutriPhi", - "planta": "Planta", + "plants": "Piante", "presi": "Presi", "inventory": "Inventario", "memoro": "Memoro", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/planta/de.json b/apps/mana/apps/web/src/lib/i18n/locales/plants/de.json similarity index 99% rename from apps/mana/apps/web/src/lib/i18n/locales/planta/de.json rename to apps/mana/apps/web/src/lib/i18n/locales/plants/de.json index ec3670c9d..588831002 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/planta/de.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/plants/de.json @@ -1,6 +1,6 @@ { "app": { - "name": "Planta", + "name": "Plants", "loading": "Laden...", "tagline": "Pflanzenpflege leicht gemacht" }, diff --git a/apps/mana/apps/web/src/lib/i18n/locales/planta/en.json b/apps/mana/apps/web/src/lib/i18n/locales/plants/en.json similarity index 99% rename from apps/mana/apps/web/src/lib/i18n/locales/planta/en.json rename to apps/mana/apps/web/src/lib/i18n/locales/plants/en.json index c9cf4c313..4c12607e4 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/planta/en.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/plants/en.json @@ -1,6 +1,6 @@ { "app": { - "name": "Planta", + "name": "Plants", "loading": "Loading...", "tagline": "Plant care made easy" }, diff --git a/apps/mana/apps/web/src/lib/i18n/locales/planta/es.json b/apps/mana/apps/web/src/lib/i18n/locales/plants/es.json similarity index 99% rename from apps/mana/apps/web/src/lib/i18n/locales/planta/es.json rename to apps/mana/apps/web/src/lib/i18n/locales/plants/es.json index cd183a8f5..55c1183db 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/planta/es.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/plants/es.json @@ -1,6 +1,6 @@ { "app": { - "name": "Planta", + "name": "Plants", "loading": "Cargando...", "tagline": "Cuidado de plantas simplificado" }, diff --git a/apps/mana/apps/web/src/lib/i18n/locales/planta/fr.json b/apps/mana/apps/web/src/lib/i18n/locales/plants/fr.json similarity index 99% rename from apps/mana/apps/web/src/lib/i18n/locales/planta/fr.json rename to apps/mana/apps/web/src/lib/i18n/locales/plants/fr.json index 158fa740a..79b25dafc 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/planta/fr.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/plants/fr.json @@ -1,6 +1,6 @@ { "app": { - "name": "Planta", + "name": "Plants", "loading": "Chargement...", "tagline": "L'entretien des plantes simplifié" }, diff --git a/apps/mana/apps/web/src/lib/i18n/locales/planta/it.json b/apps/mana/apps/web/src/lib/i18n/locales/plants/it.json similarity index 99% rename from apps/mana/apps/web/src/lib/i18n/locales/planta/it.json rename to apps/mana/apps/web/src/lib/i18n/locales/plants/it.json index 20c3a99a1..4138ddd8d 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/planta/it.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/plants/it.json @@ -1,6 +1,6 @@ { "app": { - "name": "Planta", + "name": "Plants", "loading": "Caricamento...", "tagline": "Cura delle piante semplificata" }, diff --git a/apps/mana/apps/web/src/lib/modules/automations/index.ts b/apps/mana/apps/web/src/lib/modules/automations/index.ts index b5d048127..a4ff467e8 100644 --- a/apps/mana/apps/web/src/lib/modules/automations/index.ts +++ b/apps/mana/apps/web/src/lib/modules/automations/index.ts @@ -1,4 +1,6 @@ export { automationsStore } from './stores/automations.svelte'; export { automationTable } from './collections'; +export { useAllAutomations, useEnabledAutomations, toAutomation } from './queries'; +export type { Automation } from './queries'; export type { LocalAutomation, SourceOption, ActionOption } from './types'; export { SOURCE_OPTIONS, ACTION_OPTIONS, CONDITION_OPS } from './types'; diff --git a/apps/mana/apps/web/src/lib/modules/automations/queries.ts b/apps/mana/apps/web/src/lib/modules/automations/queries.ts new file mode 100644 index 000000000..9ac32a158 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/automations/queries.ts @@ -0,0 +1,63 @@ +/** + * Reactive Queries for Automations module. + * + * Uses useLiveQueryWithDefault on the unified database. + */ + +import { useLiveQueryWithDefault } from '@mana/local-store/svelte'; +import { automationTable } from './collections'; +import type { LocalAutomation } from './types'; + +// ─── Type Converter ────────────────────────────────────── + +export interface Automation { + id: string; + name: string; + enabled: boolean; + sourceApp: string; + sourceCollection: string; + sourceOp: 'insert' | 'update'; + conditionField?: string; + conditionOp?: string; + conditionValue?: string; + targetApp: string; + targetAction: string; + targetParams?: Record; + createdAt: string; + updatedAt: string; +} + +export function toAutomation(local: LocalAutomation): Automation { + return { + id: local.id, + name: local.name, + enabled: local.enabled, + sourceApp: local.sourceApp, + sourceCollection: local.sourceCollection, + sourceOp: local.sourceOp, + conditionField: local.conditionField, + conditionOp: local.conditionOp, + conditionValue: local.conditionValue, + targetApp: local.targetApp, + targetAction: local.targetAction, + targetParams: local.targetParams, + createdAt: local.createdAt ?? new Date().toISOString(), + updatedAt: local.updatedAt ?? new Date().toISOString(), + }; +} + +// ─── Live Queries ──────────────────────────────────────── + +export function useAllAutomations() { + return useLiveQueryWithDefault(async () => { + const locals = await automationTable.toArray(); + return locals.filter((a) => !a.deletedAt).map(toAutomation); + }, [] as Automation[]); +} + +export function useEnabledAutomations() { + return useLiveQueryWithDefault(async () => { + const locals = await automationTable.toArray(); + return locals.filter((a) => !a.deletedAt && a.enabled).map(toAutomation); + }, [] as Automation[]); +} diff --git a/apps/mana/apps/web/src/lib/modules/core/widgets/PlantWateringWidget.svelte b/apps/mana/apps/web/src/lib/modules/core/widgets/PlantWateringWidget.svelte index 57b31b6fc..8116309e4 100644 --- a/apps/mana/apps/web/src/lib/modules/core/widgets/PlantWateringWidget.svelte +++ b/apps/mana/apps/web/src/lib/modules/core/widgets/PlantWateringWidget.svelte @@ -124,7 +124,7 @@
🌱

Noch keine Pflanzen angelegt.

Pflanze hinzufügen @@ -140,7 +140,7 @@ - + Alle Pflanzen anzeigen {/if} diff --git a/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts b/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts index 4a78618bd..63ed85269 100644 --- a/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts +++ b/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts @@ -15,7 +15,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; // Database hooks call into funnel-tracking + trigger registry on every write. // They reach for browser-only globals (localStorage), so stub them the same -// way the planta tests do. +// way the plants tests do. vi.mock('$lib/stores/funnel-tracking', () => ({ trackFirstContent: vi.fn() })); vi.mock('$lib/triggers/registry', () => ({ fire: vi.fn() })); vi.mock('$lib/triggers/inline-suggest', () => ({ diff --git a/apps/mana/apps/web/src/lib/modules/nutriphi/queries.ts b/apps/mana/apps/web/src/lib/modules/nutriphi/queries.ts index 757988dec..e8c869dc5 100644 --- a/apps/mana/apps/web/src/lib/modules/nutriphi/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/nutriphi/queries.ts @@ -53,7 +53,7 @@ export function useAllMeals() { /** * Look up a single meal by id and decrypt it. Used by the detail page, * which inlines its own useLiveQueryWithDefault wrapper so the querier - * can capture the route param directly (matches planta DetailView pattern). + * can capture the route param directly (matches plants DetailView pattern). */ export async function loadMealById(id: string): Promise { const local = await db.table('meals').get(id); diff --git a/apps/mana/apps/web/src/lib/modules/planta/ListView.svelte b/apps/mana/apps/web/src/lib/modules/plants/ListView.svelte similarity index 88% rename from apps/mana/apps/web/src/lib/modules/planta/ListView.svelte rename to apps/mana/apps/web/src/lib/modules/plants/ListView.svelte index 705bdbf70..737630b1b 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/plants/ListView.svelte @@ -1,5 +1,5 @@ - p.id} emptyTitle={$_('planta.list.empty')}> + p.id} emptyTitle={$_('plants.list.empty')}> {#snippet toolbar()}
@@ -88,14 +88,14 @@ - {$_('planta.create.save', { default: 'Pflanze anlegen' })} + {$_('plants.create.save', { default: 'Pflanze anlegen' })} {/if} {/snippet} {#snippet header()} - {$_('planta.list.count', { values: { count: plants.length } })} + {$_('plants.list.count', { values: { count: plants.length } })} {#if dueForWatering.length > 0} {$_('planta.list.dueWatering', { values: { count: dueForWatering.length } })}{$_('plants.list.dueWatering', { values: { count: dueForWatering.length } })} {/if} {#if needsAttention.length > 0} {$_('planta.list.needsCare', { values: { count: needsAttention.length } })}{$_('plants.list.needsCare', { values: { count: needsAttention.length } })} {/if} {/snippet} @@ -153,7 +153,7 @@ {#if schedule}

- {$_('planta.list.everyXDays', { values: { days: schedule.frequencyDays } })} + {$_('plants.list.everyXDays', { values: { days: schedule.frequencyDays } })}

{/if} diff --git a/apps/mana/apps/web/src/lib/modules/planta/api.ts b/apps/mana/apps/web/src/lib/modules/plants/api.ts similarity index 94% rename from apps/mana/apps/web/src/lib/modules/planta/api.ts rename to apps/mana/apps/web/src/lib/modules/plants/api.ts index 3f7ea5483..2c57b61bd 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/api.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/api.ts @@ -1,5 +1,5 @@ /** - * Planta — server-only API client + * Plants — server-only API client * * CRUD lives in IndexedDB + sync. This module talks to mana-api for the * two server-only operations: photo upload (S3 via mana-media) and AI @@ -53,7 +53,7 @@ export async function uploadPlantPhoto(file: File, plantId: string): Promise { - const res = await fetch(`${getManaApiUrl()}/api/v1/planta/analysis/identify`, { + const res = await fetch(`${getManaApiUrl()}/api/v1/plants/analysis/identify`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/apps/mana/apps/web/src/lib/modules/planta/collections.ts b/apps/mana/apps/web/src/lib/modules/plants/collections.ts similarity index 96% rename from apps/mana/apps/web/src/lib/modules/planta/collections.ts rename to apps/mana/apps/web/src/lib/modules/plants/collections.ts index e5cdc4d93..bb2637d01 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/collections.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/collections.ts @@ -1,5 +1,5 @@ /** - * Planta module — collection accessors and guest seed data. + * Plants module — collection accessors and guest seed data. * * Tables are already defined in the unified database (database.ts): * plants, plantPhotos, wateringSchedules, wateringLogs @@ -19,7 +19,7 @@ export const wateringLogTable = db.table('wateringLogs'); const DEMO_PLANT_ID = 'demo-monstera'; -export const PLANTA_GUEST_SEED = { +export const PLANTS_GUEST_SEED = { plants: [ { id: DEMO_PLANT_ID, diff --git a/apps/mana/apps/web/src/lib/modules/planta/index.ts b/apps/mana/apps/web/src/lib/modules/plants/index.ts similarity index 91% rename from apps/mana/apps/web/src/lib/modules/planta/index.ts rename to apps/mana/apps/web/src/lib/modules/plants/index.ts index 98103a748..466673d25 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/index.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/index.ts @@ -1,5 +1,5 @@ /** - * Planta module — barrel exports. + * Plants module — barrel exports. */ // Collections & seed data @@ -8,7 +8,7 @@ export { plantPhotoTable, wateringScheduleTable, wateringLogTable, - PLANTA_GUEST_SEED, + PLANTS_GUEST_SEED, } from './collections'; // Types @@ -65,4 +65,4 @@ export { type ParsedPlant, type ParsedPlantWithIds, } from './utils/plant-parser'; -export { PLANTA_SYNTAX } from './utils/syntax-help'; +export { PLANTS_SYNTAX } from './utils/syntax-help'; diff --git a/apps/mana/apps/web/src/lib/modules/planta/module.config.ts b/apps/mana/apps/web/src/lib/modules/plants/module.config.ts similarity index 76% rename from apps/mana/apps/web/src/lib/modules/planta/module.config.ts rename to apps/mana/apps/web/src/lib/modules/plants/module.config.ts index 9879c6e19..5e0dc79e0 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/module.config.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/module.config.ts @@ -1,7 +1,7 @@ import type { ModuleConfig } from '$lib/data/module-registry'; -export const plantaModuleConfig: ModuleConfig = { - appId: 'planta', +export const plantsModuleConfig: ModuleConfig = { + appId: 'plants', tables: [ { name: 'plants' }, { name: 'plantPhotos' }, diff --git a/apps/mana/apps/web/src/lib/modules/planta/mutations.test.ts b/apps/mana/apps/web/src/lib/modules/plants/mutations.test.ts similarity index 97% rename from apps/mana/apps/web/src/lib/modules/planta/mutations.test.ts rename to apps/mana/apps/web/src/lib/modules/plants/mutations.test.ts index b2451e530..7b0624824 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/mutations.test.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/mutations.test.ts @@ -1,7 +1,7 @@ /** - * Integration tests for planta mutations against a real (fake) IndexedDB. + * Integration tests for plants mutations against a real (fake) IndexedDB. * - * Focus: wateringMutations.logWatering — the most consequential planta + * Focus: wateringMutations.logWatering — the most consequential plants * write because it (a) appends a log and (b) re-anchors the schedule's * nextWateringAt, which drives every "needs water" badge in the UI. */ @@ -10,7 +10,7 @@ import 'fake-indexeddb/auto'; import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('@mana/shared-utils/analytics', () => ({ - PlantaEvents: { + PlantsEvents: { plantCreated: vi.fn(), plantDeleted: vi.fn(), plantWatered: vi.fn(), @@ -37,7 +37,7 @@ const wateringSchedules = () => db.table('wateringSchedul beforeEach(async () => { setCurrentUserId('test-user'); - // Planta `plants` table is encrypted; install a real Web Crypto key + // Plants `plants` table is encrypted; install a real Web Crypto key // so any incidental reads/writes to it succeed. Watering tables // themselves are plaintext, but the test harness still requires the // vault to be unlocked because shared hooks call into the provider. diff --git a/apps/mana/apps/web/src/lib/modules/planta/mutations.ts b/apps/mana/apps/web/src/lib/modules/plants/mutations.ts similarity index 97% rename from apps/mana/apps/web/src/lib/modules/planta/mutations.ts rename to apps/mana/apps/web/src/lib/modules/plants/mutations.ts index 2c7ff6afd..451caeb13 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/mutations.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/mutations.ts @@ -1,5 +1,5 @@ /** - * Planta — Mutation Helpers (Local-First) + * Plants — Mutation Helpers (Local-First) * * All writes go to IndexedDB first, sync handles the rest. Mutations throw * on failure so UI callers can surface errors via toasts. @@ -7,7 +7,7 @@ import { db } from '$lib/data/database'; import { toPlant, toWateringSchedule } from './queries'; -import { PlantaEvents } from '@mana/shared-utils/analytics'; +import { PlantsEvents } from '@mana/shared-utils/analytics'; import { encryptRecord, decryptRecord } from '$lib/data/crypto'; import { createBlock } from '$lib/data/time-blocks/service'; import { uploadPlantPhoto, identifyPlant, type IdentifyResult } from './api'; @@ -45,7 +45,7 @@ export const plantMutations = { const plaintextSnapshot = toPlant(newLocal); await encryptRecord('plants', newLocal); await db.table('plants').add(newLocal); - PlantaEvents.plantCreated(); + PlantsEvents.plantCreated(); return plaintextSnapshot; }, @@ -77,7 +77,7 @@ export const plantMutations = { deletedAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }); - PlantaEvents.plantDeleted(); + PlantsEvents.plantDeleted(); }, /** @@ -139,7 +139,7 @@ export const wateringMutations = { endDate: now, kind: 'logged', type: 'watering', - sourceModule: 'planta', + sourceModule: 'plants', sourceId: logEntry.id, title: `${plantName} gegossen`, color: '#06b6d4', @@ -159,7 +159,7 @@ export const wateringMutations = { }); } - PlantaEvents.plantWatered(); + PlantsEvents.plantWatered(); }, async updateSchedule(plantId: string, frequencyDays: number): Promise { diff --git a/apps/mana/apps/web/src/lib/modules/planta/queries.test.ts b/apps/mana/apps/web/src/lib/modules/plants/queries.test.ts similarity index 99% rename from apps/mana/apps/web/src/lib/modules/planta/queries.test.ts rename to apps/mana/apps/web/src/lib/modules/plants/queries.test.ts index bcbb28f11..6c4d0503c 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/queries.test.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/queries.test.ts @@ -1,5 +1,5 @@ /** - * Pure-function tests for planta queries. + * Pure-function tests for plants queries. * * Covers the watering date math that drives every "needs water" badge in * the UI — getting this wrong silently causes bad reminders, so it's worth diff --git a/apps/mana/apps/web/src/lib/modules/planta/queries.ts b/apps/mana/apps/web/src/lib/modules/plants/queries.ts similarity index 99% rename from apps/mana/apps/web/src/lib/modules/planta/queries.ts rename to apps/mana/apps/web/src/lib/modules/plants/queries.ts index 411500e13..d47226a4f 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/queries.ts @@ -1,5 +1,5 @@ /** - * Reactive Queries & Pure Helpers for Planta + * Reactive Queries & Pure Helpers for Plants * * Uses Dexie liveQuery to automatically re-render when IndexedDB changes * (local writes, sync updates, other tabs). Components call these hooks diff --git a/apps/mana/apps/web/src/lib/modules/planta/quick-input-adapter.ts b/apps/mana/apps/web/src/lib/modules/plants/quick-input-adapter.ts similarity index 98% rename from apps/mana/apps/web/src/lib/modules/planta/quick-input-adapter.ts rename to apps/mana/apps/web/src/lib/modules/plants/quick-input-adapter.ts index 234670235..46ad2d649 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/quick-input-adapter.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/quick-input-adapter.ts @@ -1,5 +1,5 @@ /** - * Planta QuickInputBar Adapter + * Plants QuickInputBar Adapter */ import type { InputBarAdapter } from '$lib/quick-input/types'; diff --git a/apps/mana/apps/web/src/lib/modules/planta/stores/tags.svelte.ts b/apps/mana/apps/web/src/lib/modules/plants/stores/tags.svelte.ts similarity index 84% rename from apps/mana/apps/web/src/lib/modules/planta/stores/tags.svelte.ts rename to apps/mana/apps/web/src/lib/modules/plants/stores/tags.svelte.ts index 986058c5a..e5d4a0cdc 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/stores/tags.svelte.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/stores/tags.svelte.ts @@ -1,5 +1,5 @@ /** - * Planta Tags — Uses shared global tags + module-specific junction table. + * Plants Tags — Uses shared global tags + module-specific junction table. */ import { db } from '$lib/data/database'; diff --git a/apps/mana/apps/web/src/lib/modules/planta/types.ts b/apps/mana/apps/web/src/lib/modules/plants/types.ts similarity index 98% rename from apps/mana/apps/web/src/lib/modules/planta/types.ts rename to apps/mana/apps/web/src/lib/modules/plants/types.ts index 732e1754e..667302af9 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/types.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/types.ts @@ -1,5 +1,5 @@ /** - * Planta module types for the unified app. + * Plants module types for the unified app. */ import type { BaseRecord } from '@mana/local-store'; diff --git a/apps/mana/apps/web/src/lib/modules/planta/utils/plant-parser.ts b/apps/mana/apps/web/src/lib/modules/plants/utils/plant-parser.ts similarity index 99% rename from apps/mana/apps/web/src/lib/modules/planta/utils/plant-parser.ts rename to apps/mana/apps/web/src/lib/modules/plants/utils/plant-parser.ts index 2d0f87916..227f22531 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/utils/plant-parser.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/utils/plant-parser.ts @@ -1,5 +1,5 @@ /** - * Plant Parser for Planta Module + * Plant Parser for Plants Module * * Extends the base parser with plant-specific patterns: * - Scientific names (italic Latin names) diff --git a/apps/mana/apps/web/src/lib/modules/planta/utils/syntax-help.ts b/apps/mana/apps/web/src/lib/modules/plants/utils/syntax-help.ts similarity index 83% rename from apps/mana/apps/web/src/lib/modules/planta/utils/syntax-help.ts rename to apps/mana/apps/web/src/lib/modules/plants/utils/syntax-help.ts index b1f32246b..bc1cf5651 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/utils/syntax-help.ts +++ b/apps/mana/apps/web/src/lib/modules/plants/utils/syntax-help.ts @@ -1,9 +1,9 @@ /** - * Planta-specific syntax help patterns + * Plants-specific syntax help patterns */ import type { SyntaxGroup } from '@mana/shared-ui'; -export const PLANTA_SYNTAX: SyntaxGroup[] = [ +export const PLANTS_SYNTAX: SyntaxGroup[] = [ { title: 'Pflanzen', items: [ diff --git a/apps/mana/apps/web/src/lib/modules/planta/views/DetailView.svelte b/apps/mana/apps/web/src/lib/modules/plants/views/DetailView.svelte similarity index 85% rename from apps/mana/apps/web/src/lib/modules/planta/views/DetailView.svelte rename to apps/mana/apps/web/src/lib/modules/plants/views/DetailView.svelte index 92ddd3dfb..451a8e78b 100644 --- a/apps/mana/apps/web/src/lib/modules/planta/views/DetailView.svelte +++ b/apps/mana/apps/web/src/lib/modules/plants/views/DetailView.svelte @@ -1,5 +1,5 @@ @@ -92,7 +92,7 @@ showTagPicker = false; } catch (err) { console.error('add tag failed:', err); - toast.error($_('planta.errors.saveFailed')); + toast.error($_('plants.errors.saveFailed')); } } @@ -101,7 +101,7 @@ await plantTagOps.removeTag(plantId, tagId); } catch (err) { console.error('remove tag failed:', err); - toast.error($_('planta.errors.saveFailed')); + toast.error($_('plants.errors.saveFailed')); } } @@ -117,7 +117,7 @@ detail.blur(); try { await plantMutations.update(plantId, { - name: editName.trim() || detail.entity?.name || $_('planta.plant.unnamed'), + name: editName.trim() || detail.entity?.name || $_('plants.plant.unnamed'), scientificName: editScientificName.trim() || undefined, careNotes: editCareNotes.trim() || undefined, lightRequirements: editLightRequirements || undefined, @@ -133,7 +133,7 @@ }); } catch (err) { console.error('plant save failed:', err); - toast.error($_('planta.errors.saveFailed')); + toast.error($_('plants.errors.saveFailed')); } } @@ -146,7 +146,7 @@ }); } catch (err) { console.error('plant select save failed:', err); - toast.error($_('planta.errors.saveFailed')); + toast.error($_('plants.errors.saveFailed')); } } @@ -161,10 +161,10 @@ uploading = true; try { await photoMutations.upload(plantId, file); - toast.success($_('planta.success.photoUploaded')); + toast.success($_('plants.success.photoUploaded')); } catch (err) { console.error('photo upload failed:', err); - toast.error($_('planta.errors.uploadFailed')); + toast.error($_('plants.errors.uploadFailed')); } finally { uploading = false; if (fileInput) fileInput.value = ''; @@ -174,7 +174,7 @@ async function handleIdentify() { const primary = photos[0]; if (!primary) { - toast.error($_('planta.errors.identifyFailed')); + toast.error($_('plants.errors.identifyFailed')); return; } identifying = true; @@ -182,10 +182,10 @@ try { const result = await photoMutations.identify(primary.id); identifyResult = result; - toast.success($_('planta.success.identified')); + toast.success($_('plants.success.identified')); } catch (err) { console.error('identify failed:', err); - toast.error($_('planta.errors.identifyFailed')); + toast.error($_('plants.errors.identifyFailed')); } finally { identifying = false; } @@ -195,11 +195,11 @@ if (!identifyResult) return; try { await plantMutations.applyIdentification(plantId, identifyResult, { overwrite: false }); - toast.success($_('planta.success.plantSaved')); + toast.success($_('plants.success.plantSaved')); identifyResult = null; } catch (err) { console.error('apply identification failed:', err); - toast.error($_('planta.errors.saveFailed')); + toast.error($_('plants.errors.saveFailed')); } } @@ -208,7 +208,7 @@ await photoMutations.setPrimary(plantId, photoId); } catch (err) { console.error('set primary failed:', err); - toast.error($_('planta.errors.saveFailed')); + toast.error($_('plants.errors.saveFailed')); } } @@ -217,7 +217,7 @@ await photoMutations.remove(photoId); } catch (err) { console.error('remove photo failed:', err); - toast.error($_('planta.errors.deleteFailed')); + toast.error($_('plants.errors.deleteFailed')); } } @@ -225,14 +225,14 @@ detail.deleteWithUndo({ - label: $_('planta.success.plantDeleted'), + label: $_('plants.success.plantDeleted'), delete: deletePlant, goBack, })} @@ -243,75 +243,75 @@ bind:value={editName} onfocus={detail.focus} onblur={saveField} - placeholder={$_('planta.plant.namePlaceholder')} + placeholder={$_('plants.plant.namePlaceholder')} />
- {$_('planta.plant.scientificName')} + {$_('plants.plant.scientificName')}
- {$_('planta.plant.species')} + {$_('plants.plant.species')}
- {$_('planta.plant.state')} + {$_('plants.plant.state')}
- {$_('planta.plant.light')} + {$_('plants.plant.light')}
- {$_('planta.plant.wateringDays')} + {$_('plants.plant.wateringDays')}
- {$_('planta.plant.acquired')} + {$_('plants.plant.acquired')}
- +
- +
{#if photos.length > 0} {/if}
@@ -367,7 +367,7 @@ /> {#if photos.length === 0} -

{$_('planta.photo.noPhotos')}

+

{$_('plants.photo.noPhotos')}

{:else}
{#each photos as photo (photo.id)} @@ -379,7 +379,7 @@ type="button" class="photo-btn" onclick={() => handleSetPrimary(photo.id)} - title={$_('planta.photo.primary')} + title={$_('plants.photo.primary')} > ★ @@ -388,7 +388,7 @@ type="button" class="photo-btn danger" onclick={() => handleRemovePhoto(photo.id)} - title={$_('planta.common.delete')} + title={$_('plants.common.delete')} > × @@ -401,14 +401,14 @@ {#if identifyResult}
- {$_('planta.identify.resultTitle')} + {$_('plants.identify.resultTitle')}
{#if identifyResult.scientificName}

- {$_('planta.plant.scientificName')}: + {$_('plants.plant.scientificName')}: {identifyResult.scientificName}

{/if} @@ -417,7 +417,7 @@ {/if} {#if identifyResult.confidence !== undefined}

- {$_('planta.identify.confidence')}: {Math.round(identifyResult.confidence * 100)}% + {$_('plants.identify.confidence')}: {Math.round(identifyResult.confidence * 100)}%

{/if} {#if identifyResult.wateringAdvice} @@ -441,7 +441,7 @@ type="button" class="tag-remove" onclick={() => handleRemoveTag(tag.id)} - aria-label={$_('planta.common.delete')} + aria-label={$_('plants.common.delete')} > × @@ -468,7 +468,7 @@ {#if wateringLogs.length > 0}
- +
    {#each wateringLogs as log (log.id)}
  • @@ -484,10 +484,10 @@
    {$_('planta.plant.created')}: {new Date(plant.createdAt ?? '').toLocaleDateString()}{$_('plants.plant.created')}: {new Date(plant.createdAt ?? '').toLocaleDateString()} {#if plant.updatedAt} - {$_('planta.plant.edited')}: {new Date(plant.updatedAt).toLocaleDateString()} + {$_('plants.plant.edited')}: {new Date(plant.updatedAt).toLocaleDateString()} {/if}
    {/snippet} diff --git a/apps/mana/apps/web/src/lib/quick-input/registry.ts b/apps/mana/apps/web/src/lib/quick-input/registry.ts index fb370fe05..be19704c0 100644 --- a/apps/mana/apps/web/src/lib/quick-input/registry.ts +++ b/apps/mana/apps/web/src/lib/quick-input/registry.ts @@ -12,7 +12,7 @@ const registry = new Map Promise>([ ['/calendar', () => import('$lib/modules/calendar/quick-input-adapter')], ['/contacts', () => import('$lib/modules/contacts/quick-input-adapter')], ['/times', () => import('$lib/modules/times/quick-input-adapter')], - ['/planta', () => import('$lib/modules/planta/quick-input-adapter')], + ['/plants', () => import('$lib/modules/plants/quick-input-adapter')], ['/nutriphi', () => import('$lib/modules/nutriphi/quick-input-adapter')], ]); diff --git a/apps/mana/apps/web/src/lib/splitscreen/registry.ts b/apps/mana/apps/web/src/lib/splitscreen/registry.ts index 07eb4b305..10f39aa2d 100644 --- a/apps/mana/apps/web/src/lib/splitscreen/registry.ts +++ b/apps/mana/apps/web/src/lib/splitscreen/registry.ts @@ -25,7 +25,7 @@ const SPLIT_APP_ID_LIST = [ 'context', 'questions', 'nutriphi', - 'planta', + 'plants', 'uload', 'calc', 'moodlit', diff --git a/apps/mana/apps/web/src/lib/triggers/loader.ts b/apps/mana/apps/web/src/lib/triggers/loader.ts index 18c1aeec5..b19cb5985 100644 --- a/apps/mana/apps/web/src/lib/triggers/loader.ts +++ b/apps/mana/apps/web/src/lib/triggers/loader.ts @@ -59,7 +59,7 @@ export async function loadAutomations(): Promise { }); } - if (active.length > 0) { + if (import.meta.env.DEV && active.length > 0) { console.log(`[Triggers] Loaded ${active.length} automation(s)`); } } diff --git a/apps/mana/apps/web/src/lib/types/dashboard.test.ts b/apps/mana/apps/web/src/lib/types/dashboard.test.ts index dbc1af3b3..4aedd576c 100644 --- a/apps/mana/apps/web/src/lib/types/dashboard.test.ts +++ b/apps/mana/apps/web/src/lib/types/dashboard.test.ts @@ -54,7 +54,7 @@ describe('WIDGET_REGISTRY', () => { 'context', 'mana-auth', 'nutriphi', - 'planta', + 'plants', 'cycles', undefined, ]; diff --git a/apps/mana/apps/web/src/lib/types/dashboard.ts b/apps/mana/apps/web/src/lib/types/dashboard.ts index 0416bd075..2947a4bfc 100644 --- a/apps/mana/apps/web/src/lib/types/dashboard.ts +++ b/apps/mana/apps/web/src/lib/types/dashboard.ts @@ -27,7 +27,7 @@ export type WidgetType = | 'context-docs' // Context: recent documents & spaces | 'active-timer' // Times: running timer | 'nutrition-progress' // NutriPhi: today's calorie progress - | 'plant-watering' // Planta: plants due for watering + | 'plant-watering' // Plants: plants due for watering | 'day-timeline' // TimeBlocks: chronological day timeline | 'activity-feed' // TimeBlocks: recent activity across modules | 'cycles' // Cycles: current phase + days until next period @@ -131,7 +131,7 @@ export interface WidgetMeta { | 'context' | 'times' | 'nutriphi' - | 'planta' + | 'plants' | 'cycles' | 'body' | 'mana-auth'; @@ -318,7 +318,7 @@ export const WIDGET_REGISTRY: WidgetMeta[] = [ icon: '🌱', defaultSize: 'small', allowMultiple: false, - requiredBackend: 'planta', + requiredBackend: 'plants', }, { type: 'day-timeline', diff --git a/apps/mana/apps/web/src/routes/(app)/+layout.svelte b/apps/mana/apps/web/src/routes/(app)/+layout.svelte index dd87eb3fe..7beb41d38 100644 --- a/apps/mana/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/mana/apps/web/src/routes/(app)/+layout.svelte @@ -42,16 +42,9 @@ import { linkLocalStore, linkMutations } from '@mana/shared-links'; import { manaStore } from '$lib/data/local-store'; import { startLlmQueue, stopLlmQueue } from '$lib/llm-queue'; - import { llmSettingsState, updateLlmSettings, tierLabel, type LlmTier } from '@mana/shared-llm'; - import { isLocalLlmSupported, getLocalLlmStatus, loadLocalLlm } from '@mana/local-llm'; - import { - getLocalSttStatus, - loadLocalStt, - isLocalSttSupported, - MODELS as STT_MODELS, - DEFAULT_MODEL as STT_DEFAULT_MODEL, - type ModelKey as SttModelKey, - } from '@mana/local-stt'; + import { useAiTierItems } from '$lib/components/layout/use-ai-tier-items.svelte'; + import { useSyncStatusItems } from '$lib/components/layout/use-sync-status-items.svelte'; + import RouteTierGate from '$lib/components/layout/RouteTierGate.svelte'; import { useLocalStt } from '$lib/components/voice/use-local-stt.svelte'; import { Microphone, Stop } from '@mana/shared-icons'; import { @@ -186,274 +179,10 @@ let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale)); // ── AI Tier Selector (PillNav dropdown) ───────────────── - const webgpuSupported = isLocalLlmSupported(); - const localLlmStatus = getLocalLlmStatus(); - const sttSupported = isLocalSttSupported(); - const localSttStatus = getLocalSttStatus(); - let selectedSttModel = $state(STT_DEFAULT_MODEL); - const llmSettings = $derived(llmSettingsState.current); - - function toggleAiTier(tier: LlmTier) { - const current = llmSettings.allowedTiers; - const next = current.includes(tier) - ? current.filter((t: LlmTier) => t !== tier) - : [...current, tier]; - updateLlmSettings({ allowedTiers: next }); - } - - const TIER_TOGGLE_LIST: Array<{ tier: LlmTier; shortLabel: string; icon: string }> = [ - { tier: 'browser', shortLabel: 'Lokal (Gemma 4)', icon: 'robot' }, - { tier: 'mana-server', shortLabel: 'Server (Gemma 4)', icon: 'globe' }, - { tier: 'cloud', shortLabel: 'Cloud (Gemini)', icon: 'cloud' }, - ]; - - let aiTierItems = $derived([ - // Tier toggles — browser tier item and its model-status buddy share a - // group so PillDropdownBar renders them as a paired pill. - ...TIER_TOGGLE_LIST.filter((t) => t.tier !== 'browser' || webgpuSupported).map((t) => { - const isActive = llmSettings.allowedTiers.includes(t.tier); - return { - id: `ai-tier-${t.tier}`, - label: t.shortLabel, - icon: isActive ? 'checkCircle' : t.icon, - active: isActive, - onClick: () => toggleAiTier(t.tier), - ...(t.tier === 'browser' ? { group: 'local-llm' } : {}), - }; - }), - // Browser model status / load button (grouped with the "Lokal" toggle). - // Handles all LoadingStatus states so the user sees feedback during - // download, initialization, and on error (e.g. worker crash). - ...(llmSettings.allowedTiers.includes('browser') && webgpuSupported - ? [ - (() => { - const s = localLlmStatus.current; - const state = s.state; - let label: string; - let icon: string; - let danger = false; - let disabled = false; - - switch (state) { - case 'ready': - label = 'Geladen'; - icon = 'checkCircle'; - disabled = true; - break; - case 'downloading': - label = `Lade… ${((s as { progress: number }).progress * 100).toFixed(0)}%`; - icon = 'clock'; - disabled = true; - break; - case 'loading': - label = 'Initialisiere…'; - icon = 'clock'; - disabled = true; - break; - case 'checking': - label = 'Prüfe…'; - icon = 'clock'; - disabled = true; - break; - case 'error': - label = 'Fehler — erneut versuchen'; - icon = 'bell'; - danger = true; - break; - default: - label = 'Modell laden'; - icon = 'cloud'; - } - - return { - id: 'ai-browser-status', - label, - icon, - group: 'local-llm', - danger, - disabled, - progress: state === 'downloading' ? (s as { progress: number }).progress : undefined, - onClick: !disabled ? () => void loadLocalLlm() : undefined, - }; - })(), - ] - : []), - // ── STT section ────────────────────────────────── - { id: 'stt-divider', label: '', divider: true }, - // STT model selector — each model is a pill, active = currently selected - ...(sttSupported - ? (Object.entries(STT_MODELS) as [SttModelKey, (typeof STT_MODELS)[SttModelKey]][]).map( - ([key, model]) => { - const isSelected = selectedSttModel === key; - return { - id: `stt-model-${key}`, - label: model.displayName, - icon: isSelected ? 'checkCircle' : 'mic', - active: isSelected, - onClick: () => { - selectedSttModel = key; - void loadLocalStt(key); - }, - }; - } - ) - : []), - // STT model status (grouped with selected model) - ...(sttSupported - ? [ - (() => { - const s = localSttStatus.current; - const state = s.state; - let label: string; - let icon: string; - let danger = false; - let disabled = false; - - switch (state) { - case 'ready': - label = 'STT bereit'; - icon = 'checkCircle'; - disabled = true; - break; - case 'downloading': - label = `STT Lade… ${((s as { progress: number }).progress * 100).toFixed(0)}%`; - icon = 'clock'; - disabled = true; - break; - case 'loading': - label = 'STT lädt…'; - icon = 'clock'; - disabled = true; - break; - case 'checking': - label = 'STT prüft…'; - icon = 'clock'; - disabled = true; - break; - case 'error': - label = 'STT Fehler'; - icon = 'bell'; - danger = true; - break; - default: - label = 'STT Modell laden'; - icon = 'mic'; - } - - return { - id: 'stt-status', - label, - icon, - danger, - disabled, - progress: state === 'downloading' ? (s as { progress: number }).progress : undefined, - onClick: !disabled ? () => void loadLocalStt(selectedSttModel) : undefined, - }; - })(), - ] - : []), - // Divider + settings link - { id: 'ai-divider', label: '', divider: true }, - { - id: 'ai-settings', - label: 'KI-Einstellungen', - icon: 'settings', - onClick: () => goto('/settings#ai-options'), - }, - ]); - - let currentAiTierLabel = $derived.by(() => { - const active = llmSettings.allowedTiers; - if (active.length === 0) return 'Aus'; - // Show the first (privacy-sorted) tier's short name - const sorted = [...active].sort( - (a, b) => - TIER_TOGGLE_LIST.findIndex((t) => t.tier === a) - - TIER_TOGGLE_LIST.findIndex((t) => t.tier === b) - ); - const first = TIER_TOGGLE_LIST.find((t) => t.tier === sorted[0]); - return first ? first.shortLabel.split(' (')[0] : 'KI'; - }); - - let currentAiTierIcon = $derived.by(() => { - const active = llmSettings.allowedTiers; - if (active.length === 0) return 'power'; - const sorted = [...active].sort( - (a, b) => - TIER_TOGGLE_LIST.findIndex((t) => t.tier === a) - - TIER_TOGGLE_LIST.findIndex((t) => t.tier === b) - ); - const first = TIER_TOGGLE_LIST.find((t) => t.tier === sorted[0]); - return first ? first.icon : 'cpu'; - }); + const aiTier = useAiTierItems(); // ── Sync status dropdown ──────────────────────────────── - let syncStatusItems = $derived.by(() => { - const items: import('@mana/shared-ui').PillDropdownItem[] = []; - - if (syncBilling.active) { - items.push({ - id: 'sync-active', - label: 'Cloud Sync aktiv', - icon: 'cloud', - active: true, - disabled: true, - }); - if (syncBilling.nextChargeAt) { - const date = new Date(syncBilling.nextChargeAt).toLocaleDateString('de-DE', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - }); - items.push({ - id: 'sync-next', - label: `Nächste Abbuchung: ${date}`, - icon: 'calendar', - disabled: true, - }); - } - } else if (syncBilling.paused) { - items.push({ - id: 'sync-paused', - label: 'Sync pausiert — Credits aufladen', - icon: 'bell', - onClick: () => goto('/credits?tab=packages'), - }); - } else { - items.push({ - id: 'sync-inactive', - label: 'Sync aktivieren', - icon: 'cloud', - onClick: () => goto('/settings/sync'), - }); - items.push({ - id: 'sync-info', - label: 'Nur lokal — ab 30 Credits/Monat', - icon: 'creditCard', - disabled: true, - }); - } - - items.push({ id: 'sync-divider', label: '', divider: true }); - items.push({ - id: 'sync-settings', - label: 'Sync-Einstellungen', - icon: 'settings', - onClick: () => goto('/settings/sync'), - }); - - return items; - }); - - let currentSyncLabel = $derived( - syncBilling.loading - ? '...' - : syncBilling.active - ? 'Sync' - : syncBilling.paused - ? 'Pausiert' - : 'Lokal' - ); + const syncStatus = useSyncStatusItems(); // ── User / Guest awareness ────────────────────────────── let userEmail = $derived( @@ -817,11 +546,10 @@ $effect(() => { const t = localStt.text; const e = localStt.error; - if (e) { + if (import.meta.env.DEV && e) { console.warn('[layout-stt] Error:', e); } if (t) { - console.log('[layout-stt] Transcribed text:', t); sttInjectedText = t; } }); @@ -1082,12 +810,12 @@ primaryColor="#6366f1" showAppSwitcher={true} showAiTierSelector={true} - {aiTierItems} - {currentAiTierLabel} - {currentAiTierIcon} + aiTierItems={aiTier.items} + currentAiTierLabel={aiTier.label} + currentAiTierIcon={aiTier.icon} showSyncStatus={authStore.isAuthenticated} - {syncStatusItems} - {currentSyncLabel} + syncStatusItems={syncStatus.items} + currentSyncLabel={syncStatus.label} {appItems} {userEmail} settingsHref="/settings" @@ -1121,70 +849,11 @@ >
    {#if routeBlocked && routeAppId} - -
    -
    -

    - {routeAppId.name} -

    -
    🔒
    -

    - {($locale || 'de') === 'de' - ? 'Diese App ist aktuell in der geschlossenen ' - : 'This app is currently in closed '}{routeTierLabels.required}{($locale || 'de') === 'de' ? '-Phase.' : ' phase.'} -

    -
    -
    - {($locale || 'de') === 'de' ? 'Dein Zugang:' : 'Your access:'} - {routeTierLabels.user} -
    -
    - {($locale || 'de') === 'de' ? 'Benötigt:' : 'Required:'} - {routeTierLabels.required} -
    -
    -
    - - {#if !authStore.isAuthenticated} - - {/if} -
    -
    -
    + {:else} {@render children()} {/if} diff --git a/apps/mana/apps/web/src/routes/(app)/credits/+page.svelte b/apps/mana/apps/web/src/routes/(app)/credits/+page.svelte index 8a8c957aa..cddd88cd6 100644 --- a/apps/mana/apps/web/src/routes/(app)/credits/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/credits/+page.svelte @@ -68,7 +68,7 @@ cards: 'Cards', nutriphi: 'NutriPhi', picture: 'Picture', - planta: 'Planta', + plants: 'Plants', presi: 'Presi', questions: 'Questions', skilltree: 'SkillTree', diff --git a/apps/mana/apps/web/src/routes/(app)/nutriphi/[id]/+page.svelte b/apps/mana/apps/web/src/routes/(app)/nutriphi/[id]/+page.svelte index eea25a8e5..ed85ce8d0 100644 --- a/apps/mana/apps/web/src/routes/(app)/nutriphi/[id]/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/nutriphi/[id]/+page.svelte @@ -9,7 +9,7 @@ import { ArrowLeft, Trash } from '@mana/shared-icons'; // Inline the live query so the closure captures page.params.id directly - // (matches the planta DetailView pattern). + // (matches the plants DetailView pattern). const mealQuery = useLiveQueryWithDefault( () => (page.params.id ? loadMealById(page.params.id) : Promise.resolve(null)), null as MealWithNutrition | null diff --git a/apps/mana/apps/web/src/routes/(app)/planta/+layout.svelte b/apps/mana/apps/web/src/routes/(app)/plants/+layout.svelte similarity index 91% rename from apps/mana/apps/web/src/routes/(app)/planta/+layout.svelte rename to apps/mana/apps/web/src/routes/(app)/plants/+layout.svelte index ecc97025a..72930599e 100644 --- a/apps/mana/apps/web/src/routes/(app)/planta/+layout.svelte +++ b/apps/mana/apps/web/src/routes/(app)/plants/+layout.svelte @@ -1,8 +1,8 @@ @@ -18,7 +18,7 @@ toWateringSchedule, toWateringLog, useAllTags, - } from '$lib/modules/planta/queries'; + } from '$lib/modules/plants/queries'; import type { LocalPlant, LocalPlantPhoto, @@ -29,7 +29,7 @@ PlantPhoto, WateringSchedule, WateringLog, - } from '$lib/modules/planta/types'; + } from '$lib/modules/plants/types'; let { children }: { children: Snippet } = $props(); diff --git a/apps/mana/apps/web/src/routes/(app)/planta/+page.svelte b/apps/mana/apps/web/src/routes/(app)/plants/+page.svelte similarity index 80% rename from apps/mana/apps/web/src/routes/(app)/planta/+page.svelte rename to apps/mana/apps/web/src/routes/(app)/plants/+page.svelte index a8239502c..b2eb72f46 100644 --- a/apps/mana/apps/web/src/routes/(app)/planta/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/plants/+page.svelte @@ -3,15 +3,15 @@ import { _ } from 'svelte-i18n'; import { getContext } from 'svelte'; import { toast } from '$lib/stores/toast.svelte'; - import { wateringMutations } from '$lib/modules/planta/mutations'; + import { wateringMutations } from '$lib/modules/plants/mutations'; import { getActivePlants, getPrimaryPhoto, getScheduleForPlant, getDaysUntilWatering, isWateringOverdue, - } from '$lib/modules/planta/queries'; - import type { Plant, PlantPhoto, WateringSchedule } from '$lib/modules/planta/types'; + } from '$lib/modules/plants/queries'; + import type { Plant, PlantPhoto, WateringSchedule } from '$lib/modules/plants/types'; const allPlants: { readonly value: Plant[] } = getContext('plants'); const allPlantPhotos: { readonly value: PlantPhoto[] } = getContext('plantPhotos'); @@ -35,9 +35,9 @@ if (!schedule) return ''; const days = getDaysUntilWatering(schedule); if (days === null) return ''; - if (days < 0) return $_('planta.watering.overdue'); - if (days === 0) return $_('planta.watering.today'); - return $_('planta.watering.daysUntil', { values: { days } }); + if (days < 0) return $_('plants.watering.overdue'); + if (days === 0) return $_('plants.watering.today'); + return $_('plants.watering.daysUntil', { values: { days } }); } function shouldShowWaterButton(plantId: string): boolean { @@ -51,30 +51,30 @@ e.stopPropagation(); try { await wateringMutations.logWatering(plantId); - toast.success($_('planta.success.plantWatered')); + toast.success($_('plants.success.plantWatered')); } catch (err) { console.error('logWatering failed:', err); - toast.error($_('planta.errors.wateringFailed')); + toast.error($_('plants.errors.wateringFailed')); } } - {$_('planta.nav.plants')} - Planta + {$_('plants.nav.plants')} - Plants
    -

    {$_('planta.nav.plants')}

    - {$_('planta.plant.add')} +

    {$_('plants.nav.plants')}

    + {$_('plants.plant.add')}
    {#if plants.length === 0}
    🌱
    -

    {$_('planta.plant.noPlants')}

    -

    {$_('planta.app.tagline')}

    - {$_('planta.plant.addFirst')} +

    {$_('plants.plant.noPlants')}

    +

    {$_('plants.app.tagline')}

    + {$_('plants.plant.addFirst')}
    {:else}
    @@ -87,11 +87,11 @@ role="link" tabindex="0" class="card plant-card text-left" - onclick={() => goto(`/planta/${plant.id}`)} + onclick={() => goto(`/plants/${plant.id}`)} onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); - goto(`/planta/${plant.id}`); + goto(`/plants/${plant.id}`); } }} > @@ -118,9 +118,9 @@ type="button" class="absolute top-2 right-2 rounded-full bg-blue-500 px-3 py-1 text-xs text-white hover:bg-blue-600" onclick={(e) => handleWater(plant.id, e)} - title={$_('planta.watering.water')} + title={$_('plants.watering.water')} > - {$_('planta.watering.water')} + {$_('plants.watering.water')} {/if}
    diff --git a/apps/mana/apps/web/src/routes/(app)/planta/[id]/+page.svelte b/apps/mana/apps/web/src/routes/(app)/plants/[id]/+page.svelte similarity index 74% rename from apps/mana/apps/web/src/routes/(app)/planta/[id]/+page.svelte rename to apps/mana/apps/web/src/routes/(app)/plants/[id]/+page.svelte index 9e25af3bb..1a51cd992 100644 --- a/apps/mana/apps/web/src/routes/(app)/planta/[id]/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/plants/[id]/+page.svelte @@ -4,14 +4,14 @@ import { _ } from 'svelte-i18n'; import { getContext } from 'svelte'; import { toast } from '$lib/stores/toast.svelte'; - import { plantMutations, wateringMutations } from '$lib/modules/planta/mutations'; + import { plantMutations, wateringMutations } from '$lib/modules/plants/mutations'; import { getPlantById, getPhotosForPlant, getScheduleForPlant, getLogsForPlant, - } from '$lib/modules/planta/queries'; - import type { Plant, PlantPhoto, WateringSchedule, WateringLog } from '$lib/modules/planta/types'; + } from '$lib/modules/plants/queries'; + import type { Plant, PlantPhoto, WateringSchedule, WateringLog } from '$lib/modules/plants/types'; const allPlants: { readonly value: Plant[] } = getContext('plants'); const allPlantPhotos: { readonly value: PlantPhoto[] } = getContext('plantPhotos'); @@ -36,10 +36,10 @@ watering = true; try { await wateringMutations.logWatering(plant.id); - toast.success($_('planta.success.plantWatered')); + toast.success($_('plants.success.plantWatered')); } catch (err) { console.error('logWatering failed:', err); - toast.error($_('planta.errors.wateringFailed')); + toast.error($_('plants.errors.wateringFailed')); } finally { watering = false; } @@ -47,15 +47,15 @@ async function handleDelete() { if (!plant) return; - if (!confirm($_('planta.plant.confirmDelete'))) return; + if (!confirm($_('plants.plant.confirmDelete'))) return; try { await plantMutations.delete(plant.id); - toast.success($_('planta.success.plantDeleted')); - goto('/planta'); + toast.success($_('plants.success.plantDeleted')); + goto('/plants'); } catch (err) { console.error('delete plant failed:', err); - toast.error($_('planta.errors.deleteFailed')); + toast.error($_('plants.errors.deleteFailed')); } } @@ -75,48 +75,48 @@ } function getHealthText(status: string | null | undefined): string { - if (status === 'needs_attention') return $_('planta.health.needsAttention'); - if (status === 'sick') return $_('planta.health.sick'); - return $_('planta.health.healthy'); + if (status === 'needs_attention') return $_('plants.health.needsAttention'); + if (status === 'sick') return $_('plants.health.sick'); + return $_('plants.health.healthy'); } function getLightText(light: string | null | undefined): string { switch (light) { case 'low': - return $_('planta.light.low'); + return $_('plants.light.low'); case 'medium': - return $_('planta.light.medium'); + return $_('plants.light.medium'); case 'bright': - return $_('planta.light.bright'); + return $_('plants.light.bright'); case 'direct': - return $_('planta.light.direct'); + return $_('plants.light.direct'); default: - return $_('planta.common.none'); + return $_('plants.common.none'); } } function getHumidityText(humidity: string | null | undefined): string { switch (humidity) { case 'low': - return $_('planta.humidity.low'); + return $_('plants.humidity.low'); case 'medium': - return $_('planta.humidity.medium'); + return $_('plants.humidity.medium'); case 'high': - return $_('planta.humidity.high'); + return $_('plants.humidity.high'); default: - return $_('planta.common.none'); + return $_('plants.common.none'); } } - {plant?.name || $_('planta.app.name')} - Planta + {plant?.name || $_('plants.app.name')} - Plants {#if !plant}
    -

    {$_('planta.plant.notFound')}

    - {$_('planta.nav.plants')} +

    {$_('plants.plant.notFound')}

    + {$_('plants.nav.plants')}
    {:else}
    @@ -153,28 +153,28 @@
    -

    {$_('planta.plant.careNotes')}

    +

    {$_('plants.plant.careNotes')}

    -

    {$_('planta.plant.light')}

    +

    {$_('plants.plant.light')}

    {getLightText(plant.lightRequirements)}

    -

    {$_('planta.watering.water')}

    +

    {$_('plants.watering.water')}

    {plant.wateringFrequencyDays - ? $_('planta.list.everyXDays', { values: { days: plant.wateringFrequencyDays } }) - : $_('planta.common.none')} + ? $_('plants.list.everyXDays', { values: { days: plant.wateringFrequencyDays } }) + : $_('plants.common.none')}

    -

    {$_('planta.humidity.medium')}

    +

    {$_('plants.humidity.medium')}

    {getHumidityText(plant.humidity)}

    {#if plant.careNotes}
    -

    {$_('planta.plant.careNotes')}

    +

    {$_('plants.plant.careNotes')}

    {plant.careNotes}

    {/if} @@ -183,14 +183,14 @@
    -

    {$_('planta.nav.watering')}

    +

    {$_('plants.nav.watering')}

    @@ -198,11 +198,11 @@ {#if wateringSchedule}
    -

    {$_('planta.watering.lastWatered')}

    +

    {$_('plants.watering.lastWatered')}

    {formatDate(wateringSchedule.lastWateredAt)}

    -

    {$_('planta.watering.nextWatering')}

    +

    {$_('plants.watering.nextWatering')}

    {formatDate(wateringSchedule.nextWateringAt)}

    @@ -210,11 +210,11 @@ {#if wateringHistory.length > 0}
    -

    {$_('planta.watering.watered')}

    +

    {$_('plants.watering.watered')}

      {#each wateringHistory.slice(0, 5) as log (log.id)}
    • - {$_('planta.watering.watered')} + {$_('plants.watering.watered')} {formatDate(log.wateredAt)}
    • {/each} @@ -225,9 +225,9 @@
      - {$_('planta.nav.plants')} + {$_('plants.nav.plants')}
    diff --git a/apps/mana/apps/web/src/routes/(app)/planta/add/+page.svelte b/apps/mana/apps/web/src/routes/(app)/plants/add/+page.svelte similarity index 71% rename from apps/mana/apps/web/src/routes/(app)/planta/add/+page.svelte rename to apps/mana/apps/web/src/routes/(app)/plants/add/+page.svelte index bae5c0408..549355f27 100644 --- a/apps/mana/apps/web/src/routes/(app)/planta/add/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/plants/add/+page.svelte @@ -2,7 +2,7 @@ import { goto } from '$app/navigation'; import { _ } from 'svelte-i18n'; import { toast } from '$lib/stores/toast.svelte'; - import { plantMutations } from '$lib/modules/planta/mutations'; + import { plantMutations } from '$lib/modules/plants/mutations'; let plantName = $state(''); let scientificName = $state(''); @@ -12,7 +12,7 @@ async function savePlant() { if (!plantName.trim()) { - error = $_('planta.errors.saveFailed'); + error = $_('plants.errors.saveFailed'); return; } @@ -25,23 +25,23 @@ scientificName: scientificName.trim() || undefined, commonName: commonName.trim() || undefined, }); - toast.success($_('planta.success.plantAdded')); - goto(`/planta/${plant.id}`); + toast.success($_('plants.success.plantAdded')); + goto(`/plants/${plant.id}`); } catch (err) { console.error('Failed to create plant:', err); - error = $_('planta.errors.saveFailed'); - toast.error($_('planta.errors.saveFailed')); + error = $_('plants.errors.saveFailed'); + toast.error($_('plants.errors.saveFailed')); saving = false; } } - {$_('planta.plant.add')} - Planta + {$_('plants.plant.add')} - Plants
    -

    {$_('planta.plant.add')}

    +

    {$_('plants.plant.add')}

    {#if error}
    @@ -52,40 +52,40 @@
    @@ -95,14 +95,14 @@ class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-white border-r-transparent" > {:else} - {$_('planta.common.save')} + {$_('plants.common.save')} {/if}
    diff --git a/apps/mana/apps/web/src/routes/(app)/planta/tags/+page.svelte b/apps/mana/apps/web/src/routes/(app)/plants/tags/+page.svelte similarity index 89% rename from apps/mana/apps/web/src/routes/(app)/planta/tags/+page.svelte rename to apps/mana/apps/web/src/routes/(app)/plants/tags/+page.svelte index f605de905..288dc624d 100644 --- a/apps/mana/apps/web/src/routes/(app)/planta/tags/+page.svelte +++ b/apps/mana/apps/web/src/routes/(app)/plants/tags/+page.svelte @@ -7,17 +7,17 @@ - Tags | Planta + Tags | Plants

    Tags

    - {$_('planta.app.tagline')} + {$_('plants.app.tagline')}

    {#if tagsCtx.value.length === 0} -

    {$_('planta.list.empty')}

    +

    {$_('plants.list.empty')}

    {:else}
    {#each tagsCtx.value as tag} diff --git a/apps/planta/CLAUDE.md b/apps/plants/CLAUDE.md similarity index 54% rename from apps/planta/CLAUDE.md rename to apps/plants/CLAUDE.md index 086387b77..1d0423615 100644 --- a/apps/planta/CLAUDE.md +++ b/apps/plants/CLAUDE.md @@ -1,22 +1,22 @@ -# Planta — consolidated into the unified Mana app +# Plants — consolidated into the unified Mana app This product was migrated into the unified Mana monorepo. The legacy -per-product `apps/planta/apps/backend/` and `apps/planta/apps/web/` +per-product `apps/plants/apps/backend/` and `apps/plants/apps/web/` directories have been removed. Active code now lives in: -- **Backend compute routes**: [`apps/api/src/modules/planta/routes.ts`](../api/src/modules/planta/routes.ts) (Gemini Vision plant analysis, S3 upload) -- **Frontend module** (local-first): [`apps/mana/apps/web/src/lib/modules/planta/`](../mana/apps/web/src/lib/modules/planta/) -- **Web route**: [`apps/mana/apps/web/src/routes/(app)/planta/`](../mana/apps/web/src/routes/(app)/planta/) +- **Backend compute routes**: [`apps/api/src/modules/plants/routes.ts`](../api/src/modules/plants/routes.ts) (Gemini Vision plant analysis, S3 upload) +- **Frontend module** (local-first): [`apps/mana/apps/web/src/lib/modules/plants/`](../mana/apps/web/src/lib/modules/plants/) +- **Web route**: [`apps/mana/apps/web/src/routes/(app)/plants/`](../mana/apps/web/src/routes/(app)/plants/) For monorepo-wide patterns (auth, sync, encryption, services), see the [root `CLAUDE.md`](../../CLAUDE.md) and [`apps/mana/CLAUDE.md`](../mana/CLAUDE.md). -The previous standalone "Planta Project Guide" describing a per-product +The previous standalone "Plants Project Guide" describing a per-product backend with its own database, schema, and watering scheduler was deleted in the audit cleanup of 2026-04-09 — it had been inaccurate since the consolidation. Pre-consolidation reference is in git history. -> **Note:** The orphaned `apps/planta/packages/shared/` package was +> **Note:** The orphaned `apps/plants/packages/shared/` package was > removed on 2026-04-09 — it had zero consumers across the repo. The -> remaining `apps/planta/package.json` is kept as a placeholder so the +> remaining `apps/plants/package.json` is kept as a placeholder so the > directory still surfaces in IDEs and as a doc anchor. diff --git a/apps/planta/package.json b/apps/plants/package.json similarity index 90% rename from apps/planta/package.json rename to apps/plants/package.json index ad0d90911..f8518b0c8 100644 --- a/apps/planta/package.json +++ b/apps/plants/package.json @@ -1,5 +1,5 @@ { - "name": "planta", + "name": "plants", "version": "0.1.0", "private": true, "description": "Planta - Plant Documentation & Care App", diff --git a/cloudflared-config.yml b/cloudflared-config.yml index 9ee626211..2e837dbcd 100644 --- a/cloudflared-config.yml +++ b/cloudflared-config.yml @@ -54,7 +54,7 @@ ingress: service: http://localhost:5000 - hostname: skilltree.mana.how service: http://localhost:5000 - - hostname: planta.mana.how + - hostname: plants.mana.how service: http://localhost:5000 - hostname: cards.mana.how service: http://localhost:5000 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index e8ae7a727..ed055cafc 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -108,12 +108,12 @@ services: mc mb --ignore-existing myminio/contacts-storage; mc mb --ignore-existing myminio/storage-storage; mc mb --ignore-existing myminio/inventory-storage; - mc mb --ignore-existing myminio/planta-storage; + mc mb --ignore-existing myminio/plants-storage; mc mb --ignore-existing myminio/projectdoc-storage; mc mb --ignore-existing myminio/music-storage; mc anonymous set download myminio/mana-storage; mc anonymous set download myminio/picture-storage; - mc anonymous set download myminio/planta-storage; + mc anonymous set download myminio/plants-storage; mc anonymous set download myminio/inventory-storage; mc ilm rule add --expire-days 90 myminio/chat-storage --prefix 'tmp/' 2>/dev/null || true; mc ilm rule add --expire-days 30 myminio/calendar-storage --prefix 'tmp/' 2>/dev/null || true; diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index 78a4d8687..54e24ebfe 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -144,7 +144,7 @@ services: environment: MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin} - MINIO_API_CORS_ALLOW_ORIGIN: https://music.mana.how,https://mana.how,https://picture.mana.how,https://storage.mana.how,https://planta.mana.how,https://contacts.mana.how,https://chat.mana.how,https://nutriphi.mana.how,https://photos.mana.how + MINIO_API_CORS_ALLOW_ORIGIN: https://music.mana.how,https://mana.how,https://picture.mana.how,https://storage.mana.how,https://plants.mana.how,https://contacts.mana.how,https://chat.mana.how,https://nutriphi.mana.how,https://photos.mana.how volumes: - /Volumes/ManaData/minio:/data ports: @@ -178,12 +178,12 @@ services: mc mb --ignore-existing myminio/storage-storage; mc mb --ignore-existing myminio/inventory-storage; mc mb --ignore-existing myminio/music-storage; - mc mb --ignore-existing myminio/planta-storage; + mc mb --ignore-existing myminio/plants-storage; mc mb --ignore-existing myminio/projectdoc-storage; mc mb --ignore-existing myminio/mail-storage; mc anonymous set download myminio/mana-storage; mc anonymous set download myminio/picture-storage; - mc anonymous set download myminio/planta-storage; + mc anonymous set download myminio/plants-storage; mc anonymous set download myminio/inventory-storage; mc ilm rule add --expire-days 90 myminio/chat-storage --prefix 'tmp/' 2>/dev/null || true; mc ilm rule add --expire-days 30 myminio/calendar-storage --prefix 'tmp/' 2>/dev/null || true; @@ -314,7 +314,7 @@ services: STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} STRIPE_WEBHOOK_SECRET: ${STRIPE_CREDITS_WEBHOOK_SECRET:-} BASE_URL: https://credits.mana.how - CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://picture.mana.how,https://todo.mana.how,https://zitare.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://cards.mana.how,https://presi.mana.how,https://storage.mana.how,https://nutriphi.mana.how,https://planta.mana.how,https://music.mana.how,https://context.mana.how,https://photos.mana.how,https://questions.mana.how,https://calc.mana.how + CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://picture.mana.how,https://todo.mana.how,https://zitare.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://cards.mana.how,https://presi.mana.how,https://storage.mana.how,https://nutriphi.mana.how,https://plants.mana.how,https://music.mana.how,https://context.mana.how,https://photos.mana.how,https://questions.mana.how,https://calc.mana.how ports: - "3002:3002" healthcheck: @@ -412,7 +412,7 @@ services: DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_platform MANA_AUTH_URL: http://mana-auth:3001 MANA_SERVICE_KEY: ${MANA_SERVICE_KEY} - CORS_ORIGINS: https://mana.how,https://calc.mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://cards.mana.how,https://music.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://planta.mana.how,https://presi.mana.how,https://questions.mana.how,https://storage.mana.how,https://todo.mana.how,https://zitare.mana.how + CORS_ORIGINS: https://mana.how,https://calc.mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://cards.mana.how,https://music.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://plants.mana.how,https://presi.mana.how,https://questions.mana.how,https://storage.mana.how,https://todo.mana.how,https://zitare.mana.how ports: - "3062:3062" healthcheck: diff --git a/docker/grafana/dashboards/master-overview.json b/docker/grafana/dashboards/master-overview.json index 335471d55..5ba1eaf22 100644 --- a/docker/grafana/dashboards/master-overview.json +++ b/docker/grafana/dashboards/master-overview.json @@ -661,7 +661,7 @@ "type": "prometheus", "uid": "${datasource}" }, - "expr": "up{job=\"planta-backend\"}", + "expr": "up{job=\"plants-backend\"}", "legendFormat": "Planta", "refId": "Q" }, diff --git a/docker/grafana/dashboards/system-overview.json b/docker/grafana/dashboards/system-overview.json index 4465ea5f8..ae4810cea 100644 --- a/docker/grafana/dashboards/system-overview.json +++ b/docker/grafana/dashboards/system-overview.json @@ -615,7 +615,7 @@ }, { "datasource": { "type": "prometheus", "uid": "${datasource}" }, - "expr": "up{job=\"planta-backend\"}", + "expr": "up{job=\"plants-backend\"}", "legendFormat": "Planta", "refId": "Q" }, diff --git a/docker/prometheus/prometheus.yml b/docker/prometheus/prometheus.yml index d595055fb..f4f44e7b1 100644 --- a/docker/prometheus/prometheus.yml +++ b/docker/prometheus/prometheus.yml @@ -99,7 +99,7 @@ scrape_configs: scrape_interval: 30s # NOTE: Individual app backends (chat, todo, calendar, contacts, storage, - # nutriphi, music, planta, picture) have been REMOVED — all migrated to + # nutriphi, music, plants, picture) have been REMOVED — all migrated to # local-first architecture. Only uload-server and memoro-server remain. # Mana LLM Gateway (Ollama + Google Fallback) @@ -227,7 +227,7 @@ scrape_configs: - https://mana.how/storage - https://mana.how/presi - https://mana.how/nutriphi - - https://mana.how/planta + - https://mana.how/plants - https://mana.how/calc - https://mana.how/zitare - https://mana.how/cards diff --git a/docs/EXTERNAL_SERVICES.md b/docs/EXTERNAL_SERVICES.md index d7635aae8..5bd830d0e 100644 --- a/docs/EXTERNAL_SERVICES.md +++ b/docs/EXTERNAL_SERVICES.md @@ -103,7 +103,7 @@ Legende: LOCAL = Self-hosted | CLOUD = Externer Dienst | FREE = Kostenlos - `picture-storage` - AI-generierte Bilder - `chat-storage` - User-Uploads - `cards-storage` - Card/Deck Assets -- `planta-storage` - Pflanzenfotos +- `plants-storage` - Pflanzenfotos - `nutriphi-storage` - Mahlzeitenfotos - `presi-storage` - Präsentationsfolien - `calendar-storage` - Kalender-Anhänge diff --git a/docs/LOCAL_DEVELOPMENT.md b/docs/LOCAL_DEVELOPMENT.md index b3f3bcf5e..b9a53b17d 100644 --- a/docs/LOCAL_DEVELOPMENT.md +++ b/docs/LOCAL_DEVELOPMENT.md @@ -160,7 +160,7 @@ pnpm setup:db:uload # Setup uload database + schema pnpm setup:db:context # Setup context database + schema pnpm setup:db:storage # Setup storage database + schema pnpm setup:db:mukke # Setup mukke database + schema -pnpm setup:db:planta # Setup planta database + schema +pnpm setup:db:plants # Setup plants database + schema pnpm setup:db:nutriphi # Setup nutriphi database + schema pnpm setup:db:questions # Setup questions database + schema pnpm setup:db:traces # Setup traces database + schema diff --git a/docs/MONITORING.md b/docs/MONITORING.md index c2499c22e..e42b41204 100644 --- a/docs/MONITORING.md +++ b/docs/MONITORING.md @@ -36,7 +36,7 @@ All monitoring tools are publicly accessible - no login required (except GlitchT | Photos | https://stats.mana.how/share/dc201d685f784716a0b8587376eca7a1/photos-webapp | | Storage | https://stats.mana.how/share/392ff51d11f14f0c9d556af1402a3ee6/storage-webapp | | NutriPhi | https://stats.mana.how/share/33dfae72f8e24aaa8008cbbceeaf072d/nutriphi-webapp | -| Planta | https://stats.mana.how/share/1e83a8a67fa84d3995455c21dedbe3a2/planta-webapp | +| Planta | https://stats.mana.how/share/1e83a8a67fa84d3995455c21dedbe3a2/plants-webapp | | Presi | https://stats.mana.how/share/a1eb8d1fa4d543e6b97ac41351fe1c6f/presi-webapp | | Skilltree | https://stats.mana.how/share/5de13e0895ae4a69aa2a834f985be14d/skilltree-webapp | | Cards | https://stats.mana.how/share/1c1d54c4782943e58dde0a6db7c86ec6/cards-webapp | diff --git a/docs/PORT_SCHEMA.md b/docs/PORT_SCHEMA.md index 0121459bb..13e02b8ad 100644 --- a/docs/PORT_SCHEMA.md +++ b/docs/PORT_SCHEMA.md @@ -104,7 +104,7 @@ Pure CRUD apps use mana-sync directly. | 3036 | cards-server | Hono/Bun | AI card generation | | 3037 | mukke-server | Hono/Bun | Audio processing, BPM, ID3 tags | | 3038 | nutriphi-server | Hono/Bun | Gemini meal analysis | -| 3039 | planta-server | Hono/Bun | Gemini plant analysis | +| 3039 | plants-server | Hono/Bun | Gemini plant analysis | | 3040 | presi-server | Hono/Bun | Share links | | 3041-3059 | *(reserved)* | | | @@ -137,7 +137,7 @@ Pure CRUD apps use mana-sync directly. | 5025 | inventory-web | *(local-first only)* | | 5026 | context-web | *(local-first only)* | | 5027 | questions-web | *(local-first only)* | -| 5028 | planta-web | 3039 planta-server | +| 5028 | plants-web | 3039 plants-server | | 5029 | moodlit-web | *(future)* | | 5030-5049 | *(reserved)* | | diff --git a/docs/PRE_LAUNCH_CLEANUP.md b/docs/PRE_LAUNCH_CLEANUP.md index 8ca2677df..7778cd7c1 100644 --- a/docs/PRE_LAUNCH_CLEANUP.md +++ b/docs/PRE_LAUNCH_CLEANUP.md @@ -289,7 +289,7 @@ needed them. removed. - `~/.cloudflared/config.yml` on the Mac Mini lost its 16 dead ingress routes (`chat-api`, `todo-api`, `calendar-api`, `clock-api`, `clock-bot`, - `contacts-api`, `zitare-api`, `skilltree-api`, `planta-api`, `cards-api`, + `contacts-api`, `zitare-api`, `skilltree-api`, `plants-api`, `cards-api`, `storage-api`, `presi-api`, `nutriphi-api`, `photos-api`, `mukke-api`, `picture-api`). The tunnel was reloaded via `kill -HUP `. - After reload, every former 502 returns 404 from the Cloudflare edge diff --git a/docs/REFACTORING_AUDIT_2026_04.md b/docs/REFACTORING_AUDIT_2026_04.md index 6a5979e1b..76acba705 100644 --- a/docs/REFACTORING_AUDIT_2026_04.md +++ b/docs/REFACTORING_AUDIT_2026_04.md @@ -33,7 +33,7 @@ nicht zentralisiert. ### 2. ✅ 8× Recursive Turbo Anti-Pattern fixen -**Wo:** `apps/{uload,context,moodlit,planta,storage,news,questions}/package.json` +**Wo:** `apps/{uload,context,moodlit,plants,storage,news,questions}/package.json` + `games/arcade/package.json` (im Audit-Sweep zusätzlich gefunden) **Problem:** Diese package.json enthielten `"dev": "turbo run dev"`. @@ -49,9 +49,9 @@ gefundene Dead-Code: auf nicht-existierende `@context/web` / `@context/server` Packages — entfernt, nur `dev:mobile` bleibt (das einzige real existierende Sub-Package). -- `apps/planta/package.json` hatte `dev:web`, `dev:server`, `db:push`, - `db:studio`, `db:seed` Filter auf nicht-existierende `@planta/web` / - `@planta/server` Packages — entfernt. `apps/planta/` enthält nur ein +- `apps/plants/package.json` hatte `dev:web`, `dev:server`, `db:push`, + `db:studio`, `db:seed` Filter auf nicht-existierende `@plants/web` / + `@plants/server` Packages — entfernt. `apps/plants/` enthält nur ein leeres `packages/shared/` (siehe Audit-Item #11/#18). - Analog ist `apps/storage/` und `apps/questions/` nur ein Stub mit `packages/shared/` bzw. nichts — die CLAUDE.md-Files referenzieren @@ -86,7 +86,7 @@ hatten die Infrastruktur bereits — sie wurde nur nicht benutzt. - `packages/shared-hono/src/index.ts`: Re-exportiert `logger` aus `@mana/shared-logger`, damit Module ohne extra dependency darauf zugreifen können. -- 7 `console.error` Aufrufe in `apps/api/src/modules/{guides,planta, +- 7 `console.error` Aufrufe in `apps/api/src/modules/{guides,plants, nutriphi,traces}/routes.ts` durch `logger.error('module.event_name', { error: ... })` ersetzt. Event-Namen folgen `.` Konvention für Filterbarkeit in Sentry/JSON-Logs. @@ -373,7 +373,7 @@ nicht mehr existiert. `apps/uload/apps/server`. Beide deployed via `docker-compose.macmini.yml`. - Alle anderen 17 (`calendar`, `chat`, `todo`, `contacts`, `picture`, - `cards`, `planta`, `nutriphi`, `news`, `traces`, `presi`, `storage`, + `cards`, `plants`, `nutriphi`, `news`, `traces`, `presi`, `storage`, `music`, `moodlit`, `context`, `guides`, `questions`) wurden zu `apps/api/src/modules/{name}/routes.ts` migriert. Ihre `apps/server/` Verzeichnisse existieren nicht mehr. diff --git a/docs/TECHNOLOGY_AUDIT_2026_03.md b/docs/TECHNOLOGY_AUDIT_2026_03.md index a28ca1de5..7988d70ef 100644 --- a/docs/TECHNOLOGY_AUDIT_2026_03.md +++ b/docs/TECHNOLOGY_AUDIT_2026_03.md @@ -64,7 +64,7 @@ Jeder einzelne Backend-Service: | Kategorie | Backends | Source Files | Empfehlung | |-----------|----------|-------------|------------| | **Komplex (behalten)** | Chat, Todo, Calendar, Contacts, Storage, Mukke | 60-89 pro Backend | Eigenstaendige Services | -| **AI-spezialisiert (behalten)** | Picture, Planta, NutriPhi | 30-50 pro Backend | Eigene AI-Pipelines | +| **AI-spezialisiert (behalten)** | Picture, Plants, NutriPhi | 30-50 pro Backend | Eigene AI-Pipelines | | **Triviales CRUD (konsolidieren)** | Zitare (20!), Clock (31), Presi, CityCorners, Questions, Context | 20-35 pro Backend | -> 1 Content Service | | **Eliminieren** | Photos (mana-media Proxy), Skilltree (Frontend-only) | minimal | Backend loeschen | @@ -558,7 +558,7 @@ Diese Technologie-Entscheidungen sind gut und sollten beibehalten werden: ### Apps (24) -calendar, chat, citycorners, clock, contacts, context, docs, inventar, mana, cards, matrix, mukke, nutriphi, photos, picture, planta, playground, presi, questions, skilltree, storage, todo, traces, zitare +calendar, chat, citycorners, clock, contacts, context, docs, inventar, mana, cards, matrix, mukke, nutriphi, photos, picture, plants, playground, presi, questions, skilltree, storage, todo, traces, zitare ### Services (17) diff --git a/docs/future/MODULE_IDEAS.md b/docs/future/MODULE_IDEAS.md index 76886a754..d96515427 100644 --- a/docs/future/MODULE_IDEAS.md +++ b/docs/future/MODULE_IDEAS.md @@ -19,7 +19,7 @@ recommendation. **Productivity:** todo, calendar, contacts, notes, habits, times, timeblocks, events **Knowledge & learning:** cards, zitare, guides, questions, skilltree, memoro, context -**Health & self:** nutriphi, cycles, dreams, moodlit, planta +**Health & self:** nutriphi, cycles, dreams, moodlit, plants **Media & creative:** chat, picture, presi, music, photos, storage, uload **Data & tools:** finance, calc, inventory, places, citycorners, who, news, links, tags, playground @@ -104,7 +104,7 @@ recommendation. - **kids** *(ZK)* — Milestones, illnesses, growth, photos - **pets** — Vet appointments, vaccinations, feeding, weight -- **plants-care** — Extension of `planta`: watering plan, fertilizing, repotting +- **plants-care** — Extension of `plants`: watering plan, fertilizing, repotting ## Health & Body (additional) diff --git a/package.json b/package.json index 6c18b8940..9c9b76552 100644 --- a/package.json +++ b/package.json @@ -155,10 +155,10 @@ "dev:citycorners:app": "pnpm dev:citycorners:web", "dev:citycorners:full": "concurrently -n auth,sync,web -c blue,magenta,cyan \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:citycorners:web\"", "deploy:landing:citycorners": "pnpm --filter @citycorners/landing build && npx wrangler pages deploy apps/citycorners/apps/landing/dist --project-name=citycorners-landing", - "planta:dev": "turbo run dev --filter=planta...", - "dev:planta:web": "pnpm --filter @planta/web dev", - "dev:planta:app": "concurrently -n api,web -c yellow,cyan \"pnpm dev:api\" \"pnpm dev:planta:web\"", - "dev:planta:full": "concurrently -n auth,sync,api -c blue,magenta,yellow \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:api\"", + "plants:dev": "turbo run dev --filter=plants...", + "dev:plants:web": "pnpm --filter @plants/web dev", + "dev:plants:app": "concurrently -n api,web -c yellow,cyan \"pnpm dev:api\" \"pnpm dev:plants:web\"", + "dev:plants:full": "concurrently -n auth,sync,api -c blue,magenta,yellow \"pnpm dev:auth\" \"pnpm dev:sync\" \"pnpm dev:api\"", "nutriphi:dev": "turbo run dev --filter=nutriphi...", "dev:nutriphi:web": "pnpm --filter @nutriphi/web dev", "dev:nutriphi:landing": "pnpm --filter @nutriphi/landing dev", @@ -237,7 +237,7 @@ "dev:music:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", "dev:nutriphi:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", "dev:picture:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", - "dev:planta:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", + "dev:plants:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", "dev:questions:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", "dev:storage:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", "dev:presi:local": "concurrently -n sync,api -c magenta,yellow \"pnpm dev:sync\" \"pnpm dev:api\"", diff --git a/packages/credits/src/operations.ts b/packages/credits/src/operations.ts index 55316fb8e..1537206f6 100644 --- a/packages/credits/src/operations.ts +++ b/packages/credits/src/operations.ts @@ -44,7 +44,7 @@ export enum CreditOperationType { // Zitare - AI explanations AI_QUOTE_EXPLANATION = 'ai_quote_explanation', - // Planta - Plant analysis + // Plants - Plant analysis AI_PLANT_ANALYSIS = 'ai_plant_analysis', // Traces - City guide generation @@ -243,12 +243,12 @@ export const OPERATION_METADATA: Record app: 'zitare', }, - // Planta + // Plants [CreditOperationType.AI_PLANT_ANALYSIS]: { name: 'Plant Analysis', description: 'Identify and analyze a plant from a photo', category: CreditCategory.AI, - app: 'planta', + app: 'plants', }, // Traces diff --git a/packages/help/src/mana-faq.ts b/packages/help/src/mana-faq.ts index 731bfb984..ba5497b15 100644 --- a/packages/help/src/mana-faq.ts +++ b/packages/help/src/mana-faq.ts @@ -59,8 +59,8 @@ export function getManaFAQs(locale: string): FAQItem[] { id: 'faq-mana-use', question: isDE ? 'Wofür wird Mana verwendet?' : 'What is Mana used for?', answer: isDE - ? '

    Mana wird für Premium-Funktionen innerhalb der Mana-Apps verwendet:

    • Chat: Cloud-KI-Modelle (Claude, GPT, DeepSeek) — lokale Modelle sind kostenlos
    • Picture: KI-Bildgenerierungen (nach 3 kostenlosen Generierungen)
    • Context: KI-Textgenerierung und -analyse
    • Weitere Apps: KI-gestützte Features in Planta, Questions, etc.

    Basis-Funktionen wie Aufgaben, Kalender, Kontakte, Dateien und Chats mit lokalen Modellen sind immer kostenlos.

    ' - : '

    Mana is used for premium features within Mana apps:

    • Chat: Cloud AI models (Claude, GPT, DeepSeek) — local models are free
    • Picture: AI image generations (after 3 free generations)
    • Context: AI text generation and analysis
    • More apps: AI-powered features in Planta, Questions, etc.

    Core features like tasks, calendar, contacts, files, and chats with local models are always free.

    ', + ? '

    Mana wird für Premium-Funktionen innerhalb der Mana-Apps verwendet:

    • Chat: Cloud-KI-Modelle (Claude, GPT, DeepSeek) — lokale Modelle sind kostenlos
    • Picture: KI-Bildgenerierungen (nach 3 kostenlosen Generierungen)
    • Context: KI-Textgenerierung und -analyse
    • Weitere Apps: KI-gestützte Features in Plants, Questions, etc.

    Basis-Funktionen wie Aufgaben, Kalender, Kontakte, Dateien und Chats mit lokalen Modellen sind immer kostenlos.

    ' + : '

    Mana is used for premium features within Mana apps:

    • Chat: Cloud AI models (Claude, GPT, DeepSeek) — local models are free
    • Picture: AI image generations (after 3 free generations)
    • Context: AI text generation and analysis
    • More apps: AI-powered features in Plants, Questions, etc.

    Core features like tasks, calendar, contacts, files, and chats with local models are always free.

    ', category: 'billing', order: 92, language: isDE ? 'de' : 'en', diff --git a/packages/shared-auth-ui/src/components/GuestWelcomeModal.svelte b/packages/shared-auth-ui/src/components/GuestWelcomeModal.svelte index d1af67561..a6b6ad0e8 100644 --- a/packages/shared-auth-ui/src/components/GuestWelcomeModal.svelte +++ b/packages/shared-auth-ui/src/components/GuestWelcomeModal.svelte @@ -53,7 +53,7 @@ calc: ['Rechnen ohne Ablenkung', 'Quelloffen & unabhängig', 'Privat by Design'], guides: ['Anleitungen, die funktionieren', 'Quelloffen & unabhängig', 'Privat by Design'], citycorners: ['Entdecke deine Stadt', 'Quelloffen & unabhängig', 'Privat by Design'], - planta: ['Pflanzenpflege leicht gemacht', 'Quelloffen & unabhängig', 'Privat by Design'], + plants: ['Pflanzenpflege leicht gemacht', 'Quelloffen & unabhängig', 'Privat by Design'], photos: ['Deine Fotos, deine Galerie', 'Quelloffen & unabhängig', 'Privat by Design'], questions: ['Recherche mit System', 'Quelloffen & unabhängig', 'Privat by Design'], context: ['Dein Wissen, strukturiert', 'Quelloffen & unabhängig', 'Privat by Design'], @@ -86,7 +86,7 @@ calc: ['Calculate without distraction', 'Open-source & independent', 'Private by design'], guides: ['Guides that actually work', 'Open-source & independent', 'Private by design'], citycorners: ['Discover your city', 'Open-source & independent', 'Private by design'], - planta: ['Plant care made simple', 'Open-source & independent', 'Private by design'], + plants: ['Plant care made simple', 'Open-source & independent', 'Private by design'], photos: ['Your photos, your gallery', 'Open-source & independent', 'Private by design'], questions: ['Research with structure', 'Open-source & independent', 'Private by design'], context: ['Your knowledge, organized', 'Open-source & independent', 'Private by design'], diff --git a/packages/shared-branding/src/app-icons.ts b/packages/shared-branding/src/app-icons.ts index 94dcc87af..06af76be1 100644 --- a/packages/shared-branding/src/app-icons.ts +++ b/packages/shared-branding/src/app-icons.ts @@ -117,7 +117,7 @@ export const APP_ICONS = { photos: svgToDataUrl( `` ), - planta: svgToDataUrl( + plants: svgToDataUrl( `` ), skilltree: svgToDataUrl( @@ -153,7 +153,7 @@ export const APP_ICONS = { body: svgToDataUrl( // Dumbbell + heart-pulse hybrid: training (barbell) + body (pulse line). // Red→orange gradient to set it apart from the green health-adjacent - // modules (planta, nutriphi) and the pink cycles icon. + // modules (plants, nutriphi) and the pink cycles icon. `` ), firsts: svgToDataUrl( diff --git a/packages/shared-branding/src/config.ts b/packages/shared-branding/src/config.ts index b43f6e62b..febdfea4c 100644 --- a/packages/shared-branding/src/config.ts +++ b/packages/shared-branding/src/config.ts @@ -246,9 +246,9 @@ export const APP_BRANDING: Record = { logoStroke: true, logoStrokeWidth: 1.5, }, - planta: { - id: 'planta', - name: 'Planta', + plants: { + id: 'plants', + name: 'Plants', tagline: 'Plant Care Assistant', primaryColor: '#22c55e', secondaryColor: '#4ade80', diff --git a/packages/shared-branding/src/index.ts b/packages/shared-branding/src/index.ts index 8ee17f9fa..05dc458fb 100644 --- a/packages/shared-branding/src/index.ts +++ b/packages/shared-branding/src/index.ts @@ -33,7 +33,7 @@ export { ClockLogo, QuestionsLogo, SkillTreeLogo, - PlantaLogo, + PlantsLogo, LightWriteLogo, MusicLogo, ContextLogo, diff --git a/packages/shared-branding/src/logos/PlantaLogo.svelte b/packages/shared-branding/src/logos/PlantsLogo.svelte similarity index 78% rename from packages/shared-branding/src/logos/PlantaLogo.svelte rename to packages/shared-branding/src/logos/PlantsLogo.svelte index 0ea5c1008..cc2af7835 100644 --- a/packages/shared-branding/src/logos/PlantaLogo.svelte +++ b/packages/shared-branding/src/logos/PlantsLogo.svelte @@ -10,4 +10,4 @@ let { size = 55, color, class: className = '' }: Props = $props(); - + diff --git a/packages/shared-branding/src/logos/index.ts b/packages/shared-branding/src/logos/index.ts index ecdc6c177..2ea851d36 100644 --- a/packages/shared-branding/src/logos/index.ts +++ b/packages/shared-branding/src/logos/index.ts @@ -20,7 +20,7 @@ export { default as InventoryLogo } from './InventoryLogo.svelte'; export { default as ClockLogo } from './ClockLogo.svelte'; export { default as QuestionsLogo } from './QuestionsLogo.svelte'; export { default as SkillTreeLogo } from './SkillTreeLogo.svelte'; -export { default as PlantaLogo } from './PlantaLogo.svelte'; +export { default as PlantsLogo } from './PlantsLogo.svelte'; export { default as LightWriteLogo } from './LightWriteLogo.svelte'; export { default as MusicLogo } from './MusicLogo.svelte'; export { default as ContextLogo } from './ContextLogo.svelte'; diff --git a/packages/shared-branding/src/mana-apps.ts b/packages/shared-branding/src/mana-apps.ts index 12bb5a019..d871d87fa 100644 --- a/packages/shared-branding/src/mana-apps.ts +++ b/packages/shared-branding/src/mana-apps.ts @@ -531,17 +531,17 @@ export const MANA_APPS: ManaApp[] = [ requiredTier: 'guest', }, { - id: 'planta', - name: 'Planta', + id: 'plants', + name: 'Plants', description: { - de: 'Pflanzenpflege', + de: 'Pflanzen', en: 'Plant Care', }, longDescription: { de: 'Verwalte deine Pflanzen mit Gießplänen, Fotos und Pflegeprotokollen.', en: 'Manage your plants with watering schedules, photos, and care logs.', }, - icon: APP_ICONS.planta, + icon: APP_ICONS.plants, color: '#22c55e', comingSoon: false, status: 'beta', diff --git a/packages/shared-branding/src/types.ts b/packages/shared-branding/src/types.ts index d07461e6c..6732dd2eb 100644 --- a/packages/shared-branding/src/types.ts +++ b/packages/shared-branding/src/types.ts @@ -21,7 +21,7 @@ export type AppId = | 'inventory' | 'questions' | 'skilltree' - | 'planta' + | 'plants' | 'lightwrite' | 'context' | 'music' diff --git a/packages/shared-storage/README.md b/packages/shared-storage/README.md index c768d53e7..ec6d9c853 100644 --- a/packages/shared-storage/README.md +++ b/packages/shared-storage/README.md @@ -28,7 +28,7 @@ Each app gets its own isolated bucket, created automatically by `minio-init`: | `mail-storage` | Mail | Email attachments | | `inventory-storage` | Inventory | Product photos | | `music-storage` | Music | Music tracks, beats, covers | -| `planta-storage` | Planta | Plant photos | +| `plants-storage` | Planta | Plant photos | | `projectdoc-storage` | ProjectDoc | Document files | ## Usage diff --git a/packages/shared-storage/src/factory.ts b/packages/shared-storage/src/factory.ts index 6d931e118..36b10cc10 100644 --- a/packages/shared-storage/src/factory.ts +++ b/packages/shared-storage/src/factory.ts @@ -32,7 +32,7 @@ const PUBLIC_URL_ENV: Partial> = { PICTURE: 'PICTURE_STORAGE_PUBLIC_URL', STORAGE: 'STORAGE_S3_PUBLIC_URL', INVENTORY: 'INVENTORY_S3_PUBLIC_URL', - PLANTA: 'PLANTA_STORAGE_PUBLIC_URL', + PLANTS: 'PLANTS_STORAGE_PUBLIC_URL', }; /** @@ -111,5 +111,5 @@ export const createStorageStorage = (publicUrl?: string) => createStorage('STORA export const createMailStorage = () => createStorage('MAIL'); export const createInventoryStorage = (publicUrl?: string) => createStorage('INVENTORY', publicUrl); export const createMusicStorage = () => createStorage('MUSIC'); -export const createPlantaStorage = (publicUrl?: string) => createStorage('PLANTA', publicUrl); +export const createPlantsStorage = (publicUrl?: string) => createStorage('PLANTS', publicUrl); export const createProjectDocStorage = () => createStorage('PROJECTDOC'); diff --git a/packages/shared-storage/src/index.ts b/packages/shared-storage/src/index.ts index cf6687f88..ea6ec3657 100644 --- a/packages/shared-storage/src/index.ts +++ b/packages/shared-storage/src/index.ts @@ -17,7 +17,7 @@ export { createMailStorage, createInventoryStorage, createMusicStorage, - createPlantaStorage, + createPlantsStorage, createProjectDocStorage, } from './factory'; diff --git a/packages/shared-storage/src/types.ts b/packages/shared-storage/src/types.ts index f5fb0be9e..1778ce0b4 100644 --- a/packages/shared-storage/src/types.ts +++ b/packages/shared-storage/src/types.ts @@ -137,7 +137,7 @@ export const BUCKETS = { MAIL: 'mail-storage', INVENTORY: 'inventory-storage', MUSIC: 'music-storage', - PLANTA: 'planta-storage', + PLANTS: 'plants-storage', PROJECTDOC: 'projectdoc-storage', } as const; diff --git a/packages/shared-stores/src/reminder-scheduler.ts b/packages/shared-stores/src/reminder-scheduler.ts index f993efac8..12a34d474 100644 --- a/packages/shared-stores/src/reminder-scheduler.ts +++ b/packages/shared-stores/src/reminder-scheduler.ts @@ -35,7 +35,7 @@ export interface DueReminder { } export interface ReminderSource { - /** Source identifier (e.g. 'todo', 'calendar', 'planta') */ + /** Source identifier (e.g. 'todo', 'calendar', 'plants') */ id: string; /** Returns reminders that are currently due */ checkDue: () => Promise; diff --git a/packages/shared-types/src/ai-schemas.ts b/packages/shared-types/src/ai-schemas.ts index 3f347717d..56af06148 100644 --- a/packages/shared-types/src/ai-schemas.ts +++ b/packages/shared-types/src/ai-schemas.ts @@ -37,7 +37,7 @@ import { z } from 'zod'; // // History: // 1 — initial schemas (foods/totalNutrition for nutriphi, -// scientificName/commonNames/etc for planta) +// scientificName/commonNames/etc for plants) export const AI_SCHEMA_VERSION = '1' as const; export type AiSchemaVersion = typeof AI_SCHEMA_VERSION; @@ -107,7 +107,7 @@ export type MealAnalysis = z.infer; export type AnalyzedFood = z.infer; export type NutritionData = z.infer; -// ─── Planta: plant photo identification ────────────────────────── +// ─── Plants: plant photo identification ────────────────────────── export const PlantIdentificationSchema = z.object({ scientificName: z.string().optional().describe('Latin binomial, e.g. "Monstera deliciosa"'), diff --git a/packages/shared-utils/src/analytics.ts b/packages/shared-utils/src/analytics.ts index 188c392e3..30eec0938 100644 --- a/packages/shared-utils/src/analytics.ts +++ b/packages/shared-utils/src/analytics.ts @@ -156,7 +156,7 @@ const track = { context: createModuleTracker('context'), skilltree: createModuleTracker('skilltree'), nutriphi: createModuleTracker('nutriphi'), - planta: createModuleTracker('planta'), + plants: createModuleTracker('plants'), questions: createModuleTracker('questions'), photos: createModuleTracker('photos'), storage: createModuleTracker('storage'), @@ -389,13 +389,13 @@ export const NutriPhiEvents = { }; /** - * Planta App Events + * Plants App Events */ -export const PlantaEvents = { - plantAnalyzed: () => track.planta('plant_analyzed'), - plantCreated: () => track.planta('plant_created'), - plantDeleted: () => track.planta('plant_deleted'), - plantWatered: () => track.planta('plant_watered'), +export const PlantsEvents = { + plantAnalyzed: () => track.plants('plant_analyzed'), + plantCreated: () => track.plants('plant_created'), + plantDeleted: () => track.plants('plant_deleted'), + plantWatered: () => track.plants('plant_watered'), }; /** diff --git a/packages/spiral-db/src/schema.ts b/packages/spiral-db/src/schema.ts index fb2fa4872..6a84b679d 100644 --- a/packages/spiral-db/src/schema.ts +++ b/packages/spiral-db/src/schema.ts @@ -193,7 +193,7 @@ export const MANA_APP_INDEX: Record = { inventory: 15, times: 16, nutriphi: 17, - planta: 18, + plants: 18, questions: 19, moodlit: 20, uload: 21, diff --git a/services/mana-auth/src/services/user-data.ts b/services/mana-auth/src/services/user-data.ts index 16f2f9f86..4748342d1 100644 --- a/services/mana-auth/src/services/user-data.ts +++ b/services/mana-auth/src/services/user-data.ts @@ -122,7 +122,7 @@ const PROJECT_META: Record = { presi: { name: 'Presi', icon: '📊' }, inventory: { name: 'Inventar', icon: '📦' }, nutriphi: { name: 'Nutriphi', icon: '🥗' }, - planta: { name: 'Planta', icon: '🌱' }, + plants: { name: 'Plants', icon: '🌱' }, storage: { name: 'Storage', icon: '☁️' }, questions: { name: 'Questions', icon: '❓' }, music: { name: 'Music', icon: '🎵' }, diff --git a/services/mana-credits/package.json b/services/mana-credits/package.json index 35467acf1..61475059a 100644 --- a/services/mana-credits/package.json +++ b/services/mana-credits/package.json @@ -1,5 +1,5 @@ { - "name": "@mana/credits", + "name": "@mana/credits-service", "version": "0.1.0", "private": true, "type": "module", diff --git a/services/mana-subscriptions/package.json b/services/mana-subscriptions/package.json index 70f5fa9f6..aff78d6dc 100644 --- a/services/mana-subscriptions/package.json +++ b/services/mana-subscriptions/package.json @@ -1,5 +1,5 @@ { - "name": "@mana/subscriptions", + "name": "@mana/subscriptions-service", "version": "0.1.0", "private": true, "type": "module",