diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000..104066d
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,109 @@
+# Copilot Instructions for "That's Me" Project
+
+## Project Architecture
+
+This is a dual-stack application with separate **Laravel backend** and **Quasar frontend**:
+
+- **Backend** (`/backend/`): Laravel 12 with Livewire/Volt for server-side rendered admin panels
+- **Frontend** (`/frontend/`): Quasar/Vue 3 SPA with anime.js for "LifeWave" visualizations
+- **Core Feature**: Interactive wave visualization showing life events as animated points
+
+## Key Technology Patterns
+
+### Backend (Laravel/Livewire/Volt)
+- **Livewire Volt**: Single-file components in `resources/views/livewire/` with PHP class logic at top
+- **Flux UI**: Use `flux:` prefixed components (`flux:input`, `flux:button`, `flux:modal`) instead of raw HTML
+- **Layout Pattern**: Nested layouts via `x-layouts.app` → `x-layouts.app.sidebar` for main app UI
+- **Auth**: Uses Laravel Breeze with Volt components (`auth.login`, `auth.register`, etc.)
+- **Routing**: Volt routes defined in `routes/web.php` as `Volt::route('path', 'component.name')`
+
+### Frontend (Quasar/Vue)
+- **Animation Core**: Uses anime.js for wave animations (see `/test/` prototypes)
+- **State Management**: Pinia for Vue state
+- **Build Tool**: Vite with Quasar CLI
+- **Components**: Lives in `frontend/src/pages/` and `frontend/src/components/`
+
+### Development Setup
+- **Backend Dev**: `cd backend && php artisan serve` + `npm run dev` (Vite for assets)
+- **Frontend Dev**: `cd frontend && quasar dev`
+- **HTTPS Config**: Backend Vite configured for MAMP SSL certificates
+- **Testing**: Pest for backend tests (`php artisan test`)
+
+## File Structure Conventions
+
+### Backend Key Files
+- `routes/web.php`: Main routing with Volt component mappings
+- `resources/views/livewire/`: Volt single-file components
+- `resources/views/components/layouts/`: Layout components
+- `app/Providers/VoltServiceProvider.php`: Volt mount configuration
+- `resources/css/app.css`: Imports Tailwind + Flux UI
+
+### Frontend Key Files
+- `src/pages/WavePage.vue`: Main life visualization component
+- `src/utils/ConnectedDotsVisualization.js`: Wave animation utilities
+- `quasar.config.js`: Build configuration
+- `/test/anime-*.html`: Animation prototypes and examples
+
+## Specific Coding Patterns
+
+### Livewire/Volt Components
+```php
+
+
+
+
+
+
+```
+
+### Wave Animation Pattern
+The project centers around animated life event visualizations:
+- Life events have `value` (emotional weight), `x` (timeline position), `imageUrl`, `title`
+- Uses anime.js for smooth wave animations with multiple sine wave layers
+- See `test/anime-points-animation.html` for complete implementation patterns
+
+### Flux UI Usage
+- Always use `flux:` components: `flux:button`, `flux:input`, `flux:modal`, `flux:navlist`
+- Layout components: `flux:header`, `flux:sidebar`, `flux:main`
+- Include `@fluxScripts` in layout files
+- Wire navigation: `wire:navigate` for SPA-like navigation
+
+## Development Workflows
+
+- **Backend Changes**: Run `npm run dev` in `/backend/` for asset hot reload
+- **Component Testing**: Use Pest with Volt test helpers: `Volt::test('component.name')`
+- **Styling**: Tailwind + Flux UI (no custom CSS files needed)
+- **Animation Prototyping**: Create in `/test/` directory first, then integrate
+
+## Project-Specific Guidelines
+
+1. **Animation First**: Wave visualizations are core - always consider animation performance
+2. **Offline Support**: Frontend designed for PWA with offline capabilities
+3. **Dual Development**: Backend and frontend are separate apps - coordinate API contracts
+4. **Component Isolation**: Livewire components should be self-contained with embedded logic
+5. **German Language**: UI text often in German (`__('German text')` for translations)
+
+## Common Commands
+
+```bash
+# Backend
+cd backend && composer install && php artisan serve
+cd backend && npm run dev # Asset compilation
+
+# Frontend
+cd frontend && npm install && quasar dev
+cd frontend && quasar build # Production build
+
+# Testing
+cd backend && php artisan test
+```
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index cd4e8c4..edf944f 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -14,7 +14,8 @@
"pinia": "^3.0.1",
"quasar": "^2.16.0",
"vue": "^3.4.18",
- "vue-router": "^4.0.0"
+ "vue-router": "^4.0.0",
+ "vue-select": "^4.0.0-beta.6"
},
"devDependencies": {
"@eslint/js": "^9.14.0",
@@ -7148,6 +7149,14 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
+ "node_modules/vue-select": {
+ "version": "4.0.0-beta.6",
+ "resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.6.tgz",
+ "integrity": "sha512-K+zrNBSpwMPhAxYLTCl56gaMrWZGgayoWCLqe5rWwkB8aUbAUh7u6sXjIR7v4ckp2WKC7zEEUY27g6h1MRsIHw==",
+ "peerDependencies": {
+ "vue": "3.x"
+ }
+ },
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index c8d94ac..bac294a 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -20,7 +20,8 @@
"pinia": "^3.0.1",
"quasar": "^2.16.0",
"vue": "^3.4.18",
- "vue-router": "^4.0.0"
+ "vue-router": "^4.0.0",
+ "vue-select": "^4.0.0-beta.6"
},
"devDependencies": {
"@eslint/js": "^9.14.0",
diff --git a/frontend/src/css/app.css b/frontend/src/css/app.css
new file mode 100644
index 0000000..5cf80e8
--- /dev/null
+++ b/frontend/src/css/app.css
@@ -0,0 +1 @@
+.controls{display:flex;justify-content:space-between;width:100%;max-width:500px;margin-bottom:10px}.button{padding:6px 12px;background-color:#4f46e5;color:#fff;border:none;border-radius:4px;cursor:pointer}.visualization-container{position:relative;width:100%;height:calc(100vh - 86px);overflow:hidden}.gradient-bg{position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;background:linear-gradient(45deg, #8634f9, #ffab1a, #ff2fa2);background-size:200% 200%;animation:gradientAnimation 20s ease infinite}@keyframes gradientAnimation{0%{background-position:0% 0%}25%{background-position:100% 0%}50%{background-position:100% 100%}75%{background-position:0% 100%}100%{background-position:0% 0%}}.median{position:absolute;top:51%;left:0;right:0;height:2px;background-color:rgba(255,255,255,.3);z-index:10}.scroll-container{position:relative;width:100%;height:100%;overflow-x:auto;overflow-y:hidden;min-height:400px;-ms-overflow-style:none;scrollbar-width:none}.scroll-container::-webkit-scrollbar{display:none}.smooth-scroll{scroll-behavior:smooth}.active{cursor:grabbing}.spacer{height:100vh}.dot-tooltip{pointer-events:none;opacity:1}.dot-tooltip .tooltip-background{fill:rgba(0,0,0,0)}.dot-tooltip .tooltip-content{display:flex;justify-content:center;align-items:center;flex-direction:column;width:100%;height:100%;color:#fff}.dot-tooltip .image_container{margin-top:8px;box-shadow:0 0 20px 0 rgba(255,255,255,.25);transition:box-shadow .25s ease-in-out;width:80px;height:80px;overflow:hidden;border-radius:50%;border:2px solid #fff;display:flex;justify-content:center}.dot-tooltip .image_container:hover{box-shadow:0 0 30px 0 rgba(255,255,255,.8)}.dot-tooltip .tooltip-image{width:100%;height:auto;display:block;pointer-events:auto}.dot-tooltip .tooltip-title{font-size:14px;font-weight:400;margin-bottom:2px;text-align:center;text-wrap:balance;-webkit-hyphens:auto;hyphens:auto;line-height:1.1}.dot-tooltip .tooltip-description{font-size:12px;font-weight:300}.dot-tooltip .tooltip-arrow{width:1px;height:30px;background:linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.5), transparent)}.dot{transition:r .2s ease,fill .2s ease;cursor:pointer}.dot:hover{fill:rgba(255,255,255,.9);filter:drop-shadow(0 0 5px rgba(255, 255, 255, 0.8))}.tooltip-img{width:100%;height:100%;-o-object-fit:cover;object-fit:cover;border-radius:4px}
\ No newline at end of file
diff --git a/frontend/src/css/app.scss b/frontend/src/css/app.scss
index 814b596..4ddb09f 100644
--- a/frontend/src/css/app.scss
+++ b/frontend/src/css/app.scss
@@ -62,7 +62,7 @@ $image-size: 80px;
.median {
position: absolute;
- top: 54.75%;
+ top: 51%;
left: 0;
right: 0;
height: 2px;
@@ -76,6 +76,7 @@ $image-size: 80px;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
+ min-height:400px;
&::-webkit-scrollbar {
display: none;
}
diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue
index a715f66..07cbb76 100644
--- a/frontend/src/layouts/MainLayout.vue
+++ b/frontend/src/layouts/MainLayout.vue
@@ -51,7 +51,7 @@
-
+
diff --git a/frontend/src/pages/EditPage.vue b/frontend/src/pages/EditPage.vue
new file mode 100644
index 0000000..3c7703a
--- /dev/null
+++ b/frontend/src/pages/EditPage.vue
@@ -0,0 +1,931 @@
+
+
+
+
+
+
+ Edit Entry
+ Modify your life event details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Emotional Level
+
+
+ Very Negative (-3)
+ Neutral (0)
+ Very Positive (+3)
+
+
+
+
+
+
Key Image *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Additional Images
+
+
+
+
+
+
+
+
+
+
+
+
+
Audio Recordings
+
+
+
+
+
+
+
+
+
+
+
+
+
Video Recordings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Category
+
+
+
+
+
+
+
+
+
+
+
+ This category has no subcategories
+
+
+
+
+
+ Tags
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Related Persons
+
+
+
+
+
+
{{ getInitials(person) }}
+
+ ×
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to delete this entry? This action cannot be undone.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/WavePage.vue b/frontend/src/pages/WavePage.vue
index 1e23d1a..8811faf 100644
--- a/frontend/src/pages/WavePage.vue
+++ b/frontend/src/pages/WavePage.vue
@@ -112,7 +112,7 @@ export default defineComponent({
imageUrl: "/images/0_3.png",
title: "Beginn des neuen Abenteuers",
description: "01.10.2024",
- link: "/page1",
+ link: "/edit/1",
},
{
id: 2,
@@ -121,7 +121,7 @@ export default defineComponent({
imageUrl: "/images/0_2.png",
title: "Omas Annis Geburtstag",
description: "02.10.2024",
- link: "/page2",
+ link: "/edit/2",
},
{
id: 3,
@@ -130,7 +130,7 @@ export default defineComponent({
imageUrl: "/images/disco.png",
title: "Konzertbesuch mit Freunden",
description: "03.10.2024",
- link: "/page3",
+ link: "/edit/3",
},
{
id: 4,
@@ -139,7 +139,7 @@ export default defineComponent({
imageUrl: "/images/pferd.png",
title: "Wanderreiten in den Bergen",
description: "04.10.2024",
- link: "/page4",
+ link: "/edit/4",
},
{
id: 5,
@@ -148,7 +148,7 @@ export default defineComponent({
imageUrl: "/images/gpt.png",
title: "Ruhiger Tag zu Hause",
description: "05.10.2024",
- link: "/page5",
+ link: "/edit/5",
},
{
id: 6,
@@ -157,7 +157,7 @@ export default defineComponent({
imageUrl: "/images/oma.png",
title: "Oma Erna verstorben",
description: "06.10.2024",
- link: "/page6",
+ link: "/edit/6",
},
{
id: 7,
@@ -166,7 +166,7 @@ export default defineComponent({
imageUrl: "/images/see.png",
title: "Erholungsausflug zum See",
description: "07.10.2024",
- link: "/page7",
+ link: "/edit/7",
},
{
id: 8,
@@ -175,7 +175,7 @@ export default defineComponent({
imageUrl: "/images/feier.png",
title: "Kleine Wochenendsfeier",
description: "08.10.2024",
- link: "/page8",
+ link: "/edit/8",
},
{
id: 9,
@@ -184,7 +184,7 @@ export default defineComponent({
imageUrl: "/images/hochzeit.png",
title: "Hochzeit von Cousine Tatjana",
description: "09.10.2024",
- link: "/page9",
+ link: "/edit/9",
},
{
id: 10,
@@ -193,7 +193,7 @@ export default defineComponent({
imageUrl: "/images/work.png",
title: "Erster Tag im neuen Job",
description: "10.10.2024",
- link: "/page10",
+ link: "/edit/10",
},
{
id: 11,
@@ -202,7 +202,7 @@ export default defineComponent({
imageUrl: "/images/klasse.png",
title: "Klassentreffen nach vielen Jahren",
description: "11.10.2024",
- link: "/page11",
+ link: "/edit/11",
},
{
id: 12,
@@ -211,7 +211,7 @@ export default defineComponent({
imageUrl: "/images/familie.png",
title: "Familienabendessen",
description: "12.10.2024",
- link: "/page12",
+ link: "/edit/12",
},
{
id: 13,
@@ -221,7 +221,7 @@ export default defineComponent({
"/images/kinobesuch.png",
title: "Kinobesuch mit der ganzen Familie",
description: "13.10.2024",
- link: "/page13",
+ link: "/edit/13",
},
{
id: 14,
@@ -231,7 +231,7 @@ export default defineComponent({
"/images/entspannung.png",
title: "Entspannung",
description: "14.10.2024",
- link: "/page14",
+ link: "/edit/14",
},
{
id: 15,
@@ -240,7 +240,7 @@ export default defineComponent({
imageUrl: "/images/sonntag.png",
title: "Geruhsamer Sonntag",
description: "15.10.2024",
- link: "/page15",
+ link: "/edit/15",
},
{
id: 16,
@@ -250,7 +250,7 @@ export default defineComponent({
"/images/kindergeburtstag.png",
title: "Kindergeburtstag",
description: "16.10.2024",
- link: "/page16",
+ link: "/edit/16",
},
{
id: 17,
@@ -260,7 +260,7 @@ export default defineComponent({
"/images/familie2.png",
title: "Spaziergang mit der Familie",
description: "17.10.2024",
- link: "/page17",
+ link: "/edit/17",
},
{
id: 18,
@@ -270,7 +270,7 @@ export default defineComponent({
"/images/grosseltern.png",
title: "Familienfeier bei den Großeltern",
description: "18.10.2024",
- link: "/page18",
+ link: "/edit/18",
},
];
diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js
index 8abad25..374db6a 100644
--- a/frontend/src/router/routes.js
+++ b/frontend/src/router/routes.js
@@ -24,6 +24,11 @@ const routes = [
name: 'wave',
component: () => import('pages/WavePage.vue')
},
+ {
+ path: 'edit/:id?',
+ name: 'edit',
+ component: () => import('pages/EditPage.vue')
+ },
],
},
diff --git a/frontend/src/utils/ConnectedDotsVisualization.ts b/frontend/src/utils/ConnectedDotsVisualization.ts
index fb67049..b62a592 100644
--- a/frontend/src/utils/ConnectedDotsVisualization.ts
+++ b/frontend/src/utils/ConnectedDotsVisualization.ts
@@ -75,6 +75,26 @@ export class ConnectedDotsVisualization {
};
// Initialize DOM elements
this.scrollContainer = document.getElementById(containerId) as HTMLElement;
+
+ // Calculate the container height dynamically
+ const containerHeight =
+ this.scrollContainer.clientHeight ||
+ this.scrollContainer.offsetHeight ||
+ window.innerHeight;
+
+ // Default configuration
+ this.config = {
+ totalWidth: calculatedWidth,
+ height: containerHeight, // Use the calculated container height
+ dotRadius: 6,
+ xUnitSize: xUnitSize,
+ tension: 0.5,
+ showGrid: false,
+ tooltipWidth: 128,
+ tooltipHeight: 128,
+ ...config,
+ };
+
// Create SVG elements
this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.gridGroup = document.createElementNS(
@@ -494,25 +514,26 @@ export class ConnectedDotsVisualization {
}
// Public API methods for external use
public updateDots(newDots: DotConfig[]): void {
- this.dots = newDots;
- // Initial width calculation based on dot positions (for grid)
- if (this.dots.length > 0) {
- // Find the minimum and maximum x values
- const minX = Math.min(...this.dots.map((dot) => dot.x));
- const maxX = Math.max(...this.dots.map((dot) => dot.x));
- // Calculate width based on the range of x values
- // Add padding on both sides (3 units on each side)
- this.config.totalWidth = (maxX - minX + 6) * this.config.xUnitSize;
- }
- // Render will calculate the tooltip edges and update the SVG width
- this.render();
- }
+ this.dots = newDots;
+ // Initial width calculation based on dot positions (for grid)
+ if (this.dots.length > 0) {
+ // Find the minimum and maximum x values
+ const minX = Math.min(...this.dots.map((dot) => dot.x));
+ const maxX = Math.max(...this.dots.map((dot) => dot.x));
+ // Calculate width based on the range of x values
+ // Add padding on both sides (3 units on each side)
+ this.config.totalWidth = (maxX - minX + 6) * this.config.xUnitSize;
+ }
+ // Render will calculate the tooltip edges and update the SVG width
+ this.render();
+}
public updateConfig(newConfig: Partial): void {
this.config = { ...this.config, ...newConfig };
this.render();
}
public resize(): void {
- this.config.height = window.innerHeight;
+ const containerHeight = this.scrollContainer.clientHeight || this.scrollContainer.offsetHeight || window.innerHeight;
+ this.config.height = containerHeight;
this.svg.setAttribute("height", `${this.config.height}`);
this.render();
}
diff --git a/frontend/src/utils/editFormOptions.js b/frontend/src/utils/editFormOptions.js
new file mode 100644
index 0000000..825850f
--- /dev/null
+++ b/frontend/src/utils/editFormOptions.js
@@ -0,0 +1,238 @@
+// Form options and data for EditPage component
+
+// Main categories with their subcategories
+export const categoryStructure = [
+ {
+ label: 'Career',
+ value: 'career',
+ subcategories: [
+ { label: 'Promotion', value: 'career-promotion' },
+ { label: 'Retirement', value: 'career-retirement' },
+ { label: 'Career Changes', value: 'career-changes' }
+ ]
+ },
+ {
+ label: 'Education',
+ value: 'education',
+ subcategories: [
+ { label: 'Graduation', value: 'education-graduation' },
+ { label: 'Schooling', value: 'education-schooling' }
+ ]
+ },
+ {
+ label: 'Awards',
+ value: 'awards',
+ subcategories: []
+ },
+ {
+ label: 'Personal Celebrations',
+ value: 'personal-celebrations',
+ subcategories: [
+ { label: 'Birthday', value: 'birthday' },
+ { label: 'Anniversary', value: 'anniversary' }
+ ]
+ },
+ {
+ label: 'Relationships',
+ value: 'relationships',
+ subcategories: [
+ { label: 'Engagement', value: 'relationships-engagement' },
+ { label: 'Marriage', value: 'relationships-marriage' },
+ { label: 'Divorce', value: 'relationships-divorce' }
+ ]
+ },
+ {
+ label: 'Parenthood',
+ value: 'parenthood',
+ subcategories: [
+ { label: 'Pregnancy', value: 'parenthood-pregnancy' },
+ { label: 'Birth', value: 'parenthood-birth' },
+ { label: 'Adoption', value: 'parenthood-adoption' }
+ ]
+ },
+ {
+ label: 'Loss & Passing',
+ value: 'passing',
+ subcategories: [
+ { label: 'Funeral', value: 'passing-funeral' }
+ ]
+ },
+ {
+ label: 'Festivities',
+ value: 'festivities',
+ subcategories: [
+ { label: 'Christmas', value: 'festivities-christmas' },
+ { label: 'Thanksgiving', value: 'festivities-thanksgiving' },
+ { label: 'New Year', value: 'festivities-new-year' },
+ { label: 'Easter', value: 'festivities-easter' },
+ { label: 'Holidays', value: 'festivities-holidays' }
+ ]
+ },
+ {
+ label: 'Social Events',
+ value: 'social-events',
+ subcategories: [
+ { label: 'Reunions', value: 'reunions' },
+ { label: 'Concerts', value: 'concerts' },
+ { label: 'Sports', value: 'sports' },
+ { label: 'Festivals', value: 'festivals' }
+ ]
+ },
+ {
+ label: 'Community',
+ value: 'community',
+ subcategories: [
+ { label: 'Charity', value: 'charity' },
+ { label: 'Community Service', value: 'community-service' }
+ ]
+ },
+ {
+ label: 'Health',
+ value: 'health',
+ subcategories: [
+ { label: 'Surgery', value: 'health-surgery' },
+ { label: 'Illness', value: 'health-illness' },
+ { label: 'Recovery', value: 'health-recovery' },
+ { label: 'Transplants', value: 'health-transplants' },
+ { label: 'Mental Health', value: 'health-mental-health' }
+ ]
+ },
+ {
+ label: 'Religious & Spiritual',
+ value: 'religious',
+ subcategories: [
+ { label: 'Baptism', value: 'religious-baptism' },
+ { label: 'Bar/Bat Mitzvah', value: 'religious-bar-bat-mitzvah' },
+ { label: 'Communion', value: 'religious-communion' },
+ { label: 'Confirmation', value: 'religious-confirmation' },
+ { label: 'Pilgrimage', value: 'religious-pilgrimage' }
+ ]
+ },
+ {
+ label: 'Travel & Adventure',
+ value: 'travel',
+ subcategories: [
+ { label: 'Travel', value: 'travel-general' },
+ { label: 'Vacation', value: 'vacation' },
+ { label: 'Adventure', value: 'adventure' }
+ ]
+ },
+ {
+ label: 'Life Changes',
+ value: 'life-changes',
+ subcategories: [
+ { label: 'Moving', value: 'moving' },
+ { label: 'License', value: 'license' },
+ { label: 'Voting', value: 'voting' },
+ { label: 'Citizenship', value: 'citizenship' }
+ ]
+ },
+ {
+ label: 'Milestones',
+ value: 'milestones',
+ subcategories: []
+ }
+]
+
+// Flattened category options for backward compatibility and simple select usage
+export const categoryOptions = categoryStructure.reduce((acc, category) => {
+ // Add main category if it has no subcategories
+ if (category.subcategories.length === 0) {
+ acc.push({ label: category.label, value: category.value })
+ } else {
+ // Add subcategories with main category prefix
+ category.subcategories.forEach(sub => {
+ acc.push({
+ label: `${category.label} - ${sub.label}`,
+ value: sub.value,
+ mainCategory: category.value,
+ subcategory: sub.value
+ })
+ })
+ }
+ return acc
+}, [])
+
+// Helper functions for category management
+export const getCategoryStructure = () => categoryStructure
+
+export const getMainCategories = () => {
+ return categoryStructure.map(cat => ({
+ label: cat.label,
+ value: cat.value
+ }))
+}
+
+export const getSubcategories = (mainCategoryValue) => {
+ const mainCategory = categoryStructure.find(cat => cat.value === mainCategoryValue)
+ return mainCategory ? mainCategory.subcategories : []
+}
+
+export const getCategoryByValue = (value) => {
+ return categoryOptions.find(cat => cat.value === value)
+}
+
+export const getMainCategoryFromValue = (value) => {
+ const category = getCategoryByValue(value)
+ return category ? category.mainCategory : null
+}
+
+export const tagOptions = [
+ // Emotions
+ 'happy', 'sad', 'exciting', 'stressful', 'memorable', 'important',
+ 'fun', 'challenging', 'rewarding', 'disappointing', 'surprising',
+ 'life-changing', 'routine', 'special', 'difficult', 'joyful',
+ 'overwhelming', 'peaceful', 'anxious', 'proud', 'grateful',
+ 'emotional', 'touching', 'inspiring', 'motivating', 'healing',
+
+ // Significance
+ 'milestone', 'achievement', 'breakthrough', 'turning-point',
+ 'first-time', 'last-time', 'once-in-a-lifetime', 'unexpected',
+ 'planned', 'spontaneous', 'tradition', 'new-experience',
+
+ // Social
+ 'family', 'friends', 'colleagues', 'community', 'solo',
+ 'group', 'intimate', 'public', 'private', 'celebration',
+
+ // Intensity
+ 'intense', 'mild', 'dramatic', 'subtle', 'overwhelming',
+ 'gradual', 'sudden', 'anticipated', 'shocking', 'gentle',
+
+ // Time-related
+ 'brief', 'extended', 'momentary', 'lasting', 'temporary',
+ 'permanent', 'seasonal', 'annual', 'weekly', 'daily'
+]
+
+export const personOptions = [
+ 'Anna Mueller',
+ 'Max Schmidt',
+ 'Sarah Johnson',
+ 'Michael Weber',
+ 'Lisa Anderson',
+ 'Thomas Brown',
+ 'Julia Martinez',
+ 'David Wilson',
+ 'Emma Garcia',
+ 'Robert Davis'
+]
+
+export const defaultFormData = {
+ keyImage: null,
+ keyImageUrl: '',
+ additionalImages: [],
+ additionalImageUrls: [],
+ level: 0,
+ category: '',
+ headline: '',
+ subheadline: '',
+ text: '',
+ tags: [],
+ location: '',
+ date: '',
+ time: '',
+ audioFiles: [],
+ audioRecordings: [],
+ videoFiles: [],
+ videoRecordings: [],
+ relatedPersons: []
+}
\ No newline at end of file