mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 21:16:41 +02:00
refactor: rename planta → plants, clean up codebase
- 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) <noreply@anthropic.com>
This commit is contained in:
parent
c6c19dbc77
commit
a91a6076cc
110 changed files with 831 additions and 707 deletions
|
|
@ -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<CreditOperationType, OperationMetadata>
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
? '<p>Mana wird für Premium-Funktionen innerhalb der Mana-Apps verwendet:</p><ul><li><strong>Chat</strong>: Cloud-KI-Modelle (Claude, GPT, DeepSeek) — lokale Modelle sind kostenlos</li><li><strong>Picture</strong>: KI-Bildgenerierungen (nach 3 kostenlosen Generierungen)</li><li><strong>Context</strong>: KI-Textgenerierung und -analyse</li><li><strong>Weitere Apps</strong>: KI-gestützte Features in Planta, Questions, etc.</li></ul><p>Basis-Funktionen wie Aufgaben, Kalender, Kontakte, Dateien und Chats mit lokalen Modellen sind <strong>immer kostenlos</strong>.</p>'
|
||||
: '<p>Mana is used for premium features within Mana apps:</p><ul><li><strong>Chat</strong>: Cloud AI models (Claude, GPT, DeepSeek) — local models are free</li><li><strong>Picture</strong>: AI image generations (after 3 free generations)</li><li><strong>Context</strong>: AI text generation and analysis</li><li><strong>More apps</strong>: AI-powered features in Planta, Questions, etc.</li></ul><p>Core features like tasks, calendar, contacts, files, and chats with local models are <strong>always free</strong>.</p>',
|
||||
? '<p>Mana wird für Premium-Funktionen innerhalb der Mana-Apps verwendet:</p><ul><li><strong>Chat</strong>: Cloud-KI-Modelle (Claude, GPT, DeepSeek) — lokale Modelle sind kostenlos</li><li><strong>Picture</strong>: KI-Bildgenerierungen (nach 3 kostenlosen Generierungen)</li><li><strong>Context</strong>: KI-Textgenerierung und -analyse</li><li><strong>Weitere Apps</strong>: KI-gestützte Features in Plants, Questions, etc.</li></ul><p>Basis-Funktionen wie Aufgaben, Kalender, Kontakte, Dateien und Chats mit lokalen Modellen sind <strong>immer kostenlos</strong>.</p>'
|
||||
: '<p>Mana is used for premium features within Mana apps:</p><ul><li><strong>Chat</strong>: Cloud AI models (Claude, GPT, DeepSeek) — local models are free</li><li><strong>Picture</strong>: AI image generations (after 3 free generations)</li><li><strong>Context</strong>: AI text generation and analysis</li><li><strong>More apps</strong>: AI-powered features in Plants, Questions, etc.</li></ul><p>Core features like tasks, calendar, contacts, files, and chats with local models are <strong>always free</strong>.</p>',
|
||||
category: 'billing',
|
||||
order: 92,
|
||||
language: isDE ? 'de' : 'en',
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export const APP_ICONS = {
|
|||
photos: svgToDataUrl(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="ph" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#8b5cf6"/><stop offset="100%" style="stop-color:#7c3aed"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#ph)"/><rect x="20" y="28" width="60" height="44" rx="5" stroke="white" stroke-width="4" fill="none"/><circle cx="40" cy="44" r="6" stroke="white" stroke-width="3" fill="none"/><path d="M20 60l16-14 12 10 14-12 18 16" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>`
|
||||
),
|
||||
planta: svgToDataUrl(
|
||||
plants: svgToDataUrl(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="pl" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#22c55e"/><stop offset="100%" style="stop-color:#16a34a"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#pl)"/><path d="M50 72V42" stroke="white" stroke-width="4" stroke-linecap="round"/><path d="M50 52c-12-2-20-14-18-24 10 0 20 8 18 24z" fill="white" fill-opacity="0.9"/><path d="M50 42c10-4 22-2 24 10-10 2-22-2-24-10z" fill="white" fill-opacity="0.7"/><path d="M38 72h24" stroke="white" stroke-width="4" stroke-linecap="round"/></svg>`
|
||||
),
|
||||
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.
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="bd" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ef4444"/><stop offset="100%" style="stop-color:#f97316"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#bd)"/><rect x="18" y="42" width="6" height="16" rx="2" fill="white"/><rect x="76" y="42" width="6" height="16" rx="2" fill="white"/><rect x="24" y="46" width="4" height="8" rx="1" fill="white" fill-opacity="0.85"/><rect x="72" y="46" width="4" height="8" rx="1" fill="white" fill-opacity="0.85"/><rect x="28" y="48" width="44" height="4" rx="2" fill="white"/><path d="M30 70h12l4-8 6 16 4-10 6 6h12" stroke="white" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>`
|
||||
),
|
||||
firsts: svgToDataUrl(
|
||||
|
|
|
|||
|
|
@ -246,9 +246,9 @@ export const APP_BRANDING: Record<AppId, AppBranding> = {
|
|||
logoStroke: true,
|
||||
logoStrokeWidth: 1.5,
|
||||
},
|
||||
planta: {
|
||||
id: 'planta',
|
||||
name: 'Planta',
|
||||
plants: {
|
||||
id: 'plants',
|
||||
name: 'Plants',
|
||||
tagline: 'Plant Care Assistant',
|
||||
primaryColor: '#22c55e',
|
||||
secondaryColor: '#4ade80',
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export {
|
|||
ClockLogo,
|
||||
QuestionsLogo,
|
||||
SkillTreeLogo,
|
||||
PlantaLogo,
|
||||
PlantsLogo,
|
||||
LightWriteLogo,
|
||||
MusicLogo,
|
||||
ContextLogo,
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@
|
|||
let { size = 55, color, class: className = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<AppLogo app="planta" {size} {color} class={className} />
|
||||
<AppLogo app="plants" {size} {color} class={className} />
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export type AppId =
|
|||
| 'inventory'
|
||||
| 'questions'
|
||||
| 'skilltree'
|
||||
| 'planta'
|
||||
| 'plants'
|
||||
| 'lightwrite'
|
||||
| 'context'
|
||||
| 'music'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const PUBLIC_URL_ENV: Partial<Record<keyof typeof BUCKETS, string>> = {
|
|||
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');
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export {
|
|||
createMailStorage,
|
||||
createInventoryStorage,
|
||||
createMusicStorage,
|
||||
createPlantaStorage,
|
||||
createPlantsStorage,
|
||||
createProjectDocStorage,
|
||||
} from './factory';
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<DueReminder[]>;
|
||||
|
|
|
|||
|
|
@ -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<typeof MealAnalysisSchema>;
|
|||
export type AnalyzedFood = z.infer<typeof AnalyzedFoodSchema>;
|
||||
export type NutritionData = z.infer<typeof NutritionDataSchema>;
|
||||
|
||||
// ─── Planta: plant photo identification ──────────────────────────
|
||||
// ─── Plants: plant photo identification ──────────────────────────
|
||||
|
||||
export const PlantIdentificationSchema = z.object({
|
||||
scientificName: z.string().optional().describe('Latin binomial, e.g. "Monstera deliciosa"'),
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ export const MANA_APP_INDEX: Record<string, number> = {
|
|||
inventory: 15,
|
||||
times: 16,
|
||||
nutriphi: 17,
|
||||
planta: 18,
|
||||
plants: 18,
|
||||
questions: 19,
|
||||
moodlit: 20,
|
||||
uload: 21,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue