From 7ab80a663535aabfb7f386ff3cf5d96087ffe7e5 Mon Sep 17 00:00:00 2001 From: Bastian Masanek Date: Sat, 1 Nov 2025 19:07:59 +0100 Subject: [PATCH] Add product detail and listing pages with API integration - Created a new product detail page to display individual product information, including images, descriptions, and pricing. - Implemented a product listing page to showcase all available products using the ProductCard and ProductGrid components. - Added API endpoints for fetching product data, ensuring only active products are returned. - Introduced a database seed script to populate the database with initial mock product data for development and testing. - Updated settings to include new database seeding command and adjusted routing for product links. --- .claude/settings.local.json | 3 +- app/components/Product/ProductCard.vue | 2 +- app/pages/products/[id].vue | 195 +++++++++++++++++++++++++ app/pages/products/index.vue | 139 ++++++++++++++++++ package.json | 5 +- pnpm-lock.yaml | 134 +++++++++-------- server/api/products/[id].get.ts | 50 +++++++ server/api/products/index.get.ts | 29 ++++ server/database/seed.ts | 103 +++++++++++++ 9 files changed, 600 insertions(+), 60 deletions(-) create mode 100644 app/pages/products/[id].vue create mode 100644 app/pages/products/index.vue create mode 100644 server/api/products/[id].get.ts create mode 100644 server/api/products/index.get.ts create mode 100644 server/database/seed.ts diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 3975d50..382f312 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -61,7 +61,8 @@ "WebFetch(domain:cidaas.github.io)", "Bash(node check-user.mjs:*)", "Bash(xargs kill:*)", - "mcp__playwright__browser_resize" + "mcp__playwright__browser_resize", + "Bash(pnpm db:seed:*)" ], "deny": [], "ask": [] diff --git a/app/components/Product/ProductCard.vue b/app/components/Product/ProductCard.vue index 7757650..bd64b51 100644 --- a/app/components/Product/ProductCard.vue +++ b/app/components/Product/ProductCard.vue @@ -96,7 +96,7 @@ const formattedPrice = computed(() => { - Details
+/** + * Product Detail Page + * + * Displays full details for a single product with large image and description. + * Includes placeholder "Add to Cart" functionality for future implementation. + */ + +import { ArrowLeft, CheckCircle } from 'lucide-vue-next' + +// Page metadata +definePageMeta({ + layout: 'default', +}) + +// Get product ID from route +const route = useRoute() +const productId = route.params.id as string + +// Type definition for product +interface Product { + id: string + navProductId: string + name: string + description: string + price: string + stockQuantity: number + category: string + active: boolean + createdAt: Date + updatedAt: Date +} + +// Fetch product from API +const { data: product, error, pending } = await useFetch(`/api/products/${productId}`) + +// Format price in EUR +const formattedPrice = computed(() => { + if (!product.value) return '' + return new Intl.NumberFormat('de-DE', { + style: 'currency', + currency: 'EUR', + }).format(Number(product.value.price)) +}) + +// Handle "Add to Cart" action (placeholder for future implementation) +const handleAddToCart = () => { + // TODO: Implement cart functionality in future phase + alert('Add to Cart funktioniert noch nicht. Diese Funktion wird in einer späteren Phase implementiert.') +} + + + diff --git a/app/pages/products/index.vue b/app/pages/products/index.vue new file mode 100644 index 0000000..ef9174b --- /dev/null +++ b/app/pages/products/index.vue @@ -0,0 +1,139 @@ + + + diff --git a/package.json b/package.json index 2c9a0de..74ab6d3 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:studio": "drizzle-kit studio", - "db:push": "drizzle-kit push" + "db:push": "drizzle-kit push", + "db:seed": "tsx server/database/seed.ts" }, "dependencies": { "@nuxtjs/i18n": "^10.1.2", @@ -38,12 +39,14 @@ "@nuxt/eslint": "^1.10.0", "@nuxtjs/tailwindcss": "^6.14.0", "@types/node": "^22.10.0", + "dotenv": "^17.2.3", "drizzle-kit": "^0.31.6", "eslint": "^9.38.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "prettier": "^3.6.2", "shadcn-nuxt": "^2.3.2", + "tsx": "^4.20.6", "typescript": "^5.7.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2130a24..ad1475e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,7 +34,7 @@ importers: version: 0.548.0(vue@3.5.22(typescript@5.9.3)) nuxt: specifier: ^4.2.0 - version: 4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1) + version: 4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1) nuxt-auth-utils: specifier: ^0.5.25 version: 0.5.25(magicast@0.5.0) @@ -59,13 +59,16 @@ importers: devDependencies: '@nuxt/eslint': specifier: ^1.10.0 - version: 1.10.0(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + version: 1.10.0(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@nuxtjs/tailwindcss': specifier: ^6.14.0 - version: 6.14.0(magicast@0.5.0)(yaml@2.8.1) + version: 6.14.0(magicast@0.5.0)(tsx@4.20.6)(yaml@2.8.1) '@types/node': specifier: ^22.10.0 version: 22.18.13 + dotenv: + specifier: ^17.2.3 + version: 17.2.3 drizzle-kit: specifier: ^0.31.6 version: 0.31.6 @@ -84,6 +87,9 @@ importers: shadcn-nuxt: specifier: ^2.3.2 version: 2.3.2(magicast@0.5.0) + tsx: + specifier: ^4.20.6 + version: 4.20.6 typescript: specifier: ^5.7.0 version: 5.9.3 @@ -4699,6 +4705,11 @@ packages: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -5891,19 +5902,19 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@2.7.0(magicast@0.3.5)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))': + '@nuxt/devtools-kit@2.7.0(magicast@0.3.5)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@nuxt/kit': 3.20.0(magicast@0.3.5) execa: 8.0.1 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@3.0.0(magicast@0.5.0)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))': + '@nuxt/devtools-kit@3.0.0(magicast@0.5.0)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@nuxt/kit': 4.2.0(magicast@0.5.0) execa: 8.0.1 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - magicast @@ -5918,12 +5929,12 @@ snapshots: prompts: 2.4.2 semver: 7.7.3 - '@nuxt/devtools@2.7.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@nuxt/devtools@2.7.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 2.7.0(magicast@0.3.5)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + '@nuxt/devtools-kit': 2.7.0(magicast@0.3.5)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@nuxt/devtools-wizard': 2.7.0 '@nuxt/kit': 3.20.0(magicast@0.3.5) - '@vue/devtools-core': 7.7.7(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + '@vue/devtools-core': 7.7.7(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@vue/devtools-kit': 7.7.7 birpc: 2.6.1 consola: 3.4.2 @@ -5948,9 +5959,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 1.0.0 tinyglobby: 0.2.15 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) - vite-plugin-inspect: 11.3.3(@nuxt/kit@3.20.0(magicast@0.3.5))(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) - vite-plugin-vue-tracer: 1.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite-plugin-inspect: 11.3.3(@nuxt/kit@3.20.0(magicast@0.3.5))(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + vite-plugin-vue-tracer: 1.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) which: 5.0.0 ws: 8.18.3 transitivePeerDependencies: @@ -5999,10 +6010,10 @@ snapshots: - supports-color - typescript - '@nuxt/eslint@1.10.0(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))': + '@nuxt/eslint@1.10.0(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@eslint/config-inspector': 1.3.0(eslint@9.38.0(jiti@2.6.1)) - '@nuxt/devtools-kit': 3.0.0(magicast@0.5.0)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + '@nuxt/devtools-kit': 3.0.0(magicast@0.5.0)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@nuxt/eslint-config': 1.10.0(@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@nuxt/eslint-plugin': 1.10.0(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@nuxt/kit': 4.2.0(magicast@0.5.0) @@ -6104,7 +6115,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/nitro-server@4.2.0(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(ioredis@5.8.2)(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1))(typescript@5.9.3)': + '@nuxt/nitro-server@4.2.0(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(ioredis@5.8.2)(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1))(typescript@5.9.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.2.0(magicast@0.5.0) @@ -6122,7 +6133,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.12.9(drizzle-orm@0.44.7(postgres@3.4.7)) - nuxt: 4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1) + nuxt: 4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1) pathe: 2.0.3 pkg-types: 2.3.0 radix3: 1.1.2 @@ -6193,12 +6204,12 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/vite-builder@4.2.0(@types/node@22.18.13)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1))(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1)': + '@nuxt/vite-builder@4.2.0(@types/node@22.18.13)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1))(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1)': dependencies: '@nuxt/kit': 4.2.0(magicast@0.5.0) '@rollup/plugin-replace': 6.0.3(rollup@4.52.5) - '@vitejs/plugin-vue': 6.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': 5.1.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': 5.1.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) autoprefixer: 10.4.21(postcss@8.5.6) consola: 3.4.2 cssnano: 7.1.2(postcss@8.5.6) @@ -6213,7 +6224,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.0 mocked-exports: 0.1.1 - nuxt: 4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1) + nuxt: 4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1) pathe: 2.0.3 pkg-types: 2.3.0 postcss: 8.5.6 @@ -6222,9 +6233,9 @@ snapshots: std-env: 3.10.0 ufo: 1.6.1 unenv: 2.0.0-rc.23 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) - vite-plugin-checker: 0.11.0(eslint@9.38.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite-plugin-checker: 0.11.0(eslint@9.38.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) vue: 3.5.22(typescript@5.9.3) vue-bundle-renderer: 2.2.0 transitivePeerDependencies: @@ -6310,7 +6321,7 @@ snapshots: - uploadthing - vue - '@nuxtjs/tailwindcss@6.14.0(magicast@0.5.0)(yaml@2.8.1)': + '@nuxtjs/tailwindcss@6.14.0(magicast@0.5.0)(tsx@4.20.6)(yaml@2.8.1)': dependencies: '@nuxt/kit': 3.20.0(magicast@0.5.0) autoprefixer: 10.4.21(postcss@8.5.6) @@ -6324,8 +6335,8 @@ snapshots: pkg-types: 2.3.0 postcss: 8.5.6 postcss-nesting: 13.0.2(postcss@8.5.6) - tailwind-config-viewer: 2.0.4(tailwindcss@3.4.18(yaml@2.8.1)) - tailwindcss: 3.4.18(yaml@2.8.1) + tailwind-config-viewer: 2.0.4(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1)) + tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) ufo: 1.6.1 unctx: 2.4.1 transitivePeerDependencies: @@ -7016,22 +7027,22 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) '@rolldown/pluginutils': 1.0.0-beta.45 '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) '@volar/language-core@2.4.23': @@ -7115,14 +7126,14 @@ snapshots: dependencies: '@vue/devtools-kit': 7.7.7 - '@vue/devtools-core@7.7.7(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vue/devtools-core@7.7.7(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.6 pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + vite-hot-client: 2.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) vue: 3.5.22(typescript@5.9.3) transitivePeerDependencies: - vite @@ -9041,16 +9052,16 @@ snapshots: nuxt-define@1.0.0: {} - nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1): + nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1): dependencies: '@dxup/nuxt': 0.2.0(magicast@0.5.0) '@nuxt/cli': 3.29.3(magicast@0.5.0) - '@nuxt/devtools': 2.7.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + '@nuxt/devtools': 2.7.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@nuxt/kit': 4.2.0(magicast@0.5.0) - '@nuxt/nitro-server': 4.2.0(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(ioredis@5.8.2)(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1))(typescript@5.9.3) + '@nuxt/nitro-server': 4.2.0(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(ioredis@5.8.2)(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1))(typescript@5.9.3) '@nuxt/schema': 4.2.0 '@nuxt/telemetry': 2.6.6(magicast@0.5.0) - '@nuxt/vite-builder': 4.2.0(@types/node@22.18.13)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1))(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1) + '@nuxt/vite-builder': 4.2.0(@types/node@22.18.13)(eslint@9.38.0(jiti@2.6.1))(magicast@0.5.0)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@22.18.13)(@vue/compiler-sfc@3.5.22)(db0@0.3.4(drizzle-orm@0.44.7(postgres@3.4.7)))(drizzle-orm@0.44.7(postgres@3.4.7))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.0)(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1))(optionator@0.9.4)(rollup@4.52.5)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1) '@unhead/vue': 2.0.19(vue@3.5.22(typescript@5.9.3)) '@vue/shared': 3.5.22 c12: 3.3.1(magicast@0.5.0) @@ -9468,12 +9479,13 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 postcss: 8.5.6 + tsx: 4.20.6 yaml: 2.8.1 postcss-merge-longhand@7.0.5(postcss@8.5.6): @@ -10047,7 +10059,7 @@ snapshots: tagged-tag@1.0.0: {} - tailwind-config-viewer@2.0.4(tailwindcss@3.4.18(yaml@2.8.1)): + tailwind-config-viewer@2.0.4(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@koa/router': 12.0.2 commander: 6.2.1 @@ -10057,13 +10069,13 @@ snapshots: open: 7.4.2 portfinder: 1.0.38 replace-in-file: 6.3.5 - tailwindcss: 3.4.18(yaml@2.8.1) + tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - supports-color tailwind-merge@3.3.1: {} - tailwindcss@3.4.18(yaml@2.8.1): + tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -10082,7 +10094,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 @@ -10167,6 +10179,13 @@ snapshots: tsscmp@1.0.6: {} + tsx@4.20.6: + dependencies: + esbuild: 0.25.11 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -10360,23 +10379,23 @@ snapshots: type-fest: 4.41.0 vue: 3.5.22(typescript@5.9.3) - vite-dev-rpc@1.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)): + vite-dev-rpc@1.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: birpc: 2.6.1 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) - vite-hot-client: 2.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite-hot-client: 2.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - vite-hot-client@2.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)): + vite-hot-client@2.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - vite-node@3.2.4(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -10391,7 +10410,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.11.0(eslint@9.38.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)): + vite-plugin-checker@0.11.0(eslint@9.38.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@babel/code-frame': 7.27.1 chokidar: 4.0.3 @@ -10400,14 +10419,14 @@ snapshots: picomatch: 4.0.3 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.38.0(jiti@2.6.1) optionator: 0.9.4 typescript: 5.9.3 - vite-plugin-inspect@11.3.3(@nuxt/kit@3.20.0(magicast@0.3.5))(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)): + vite-plugin-inspect@11.3.3(@nuxt/kit@3.20.0(magicast@0.3.5))(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: ansis: 4.2.0 debug: 4.4.3 @@ -10417,24 +10436,24 @@ snapshots: perfect-debounce: 2.0.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) - vite-dev-rpc: 1.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite-dev-rpc: 1.1.0(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optionalDependencies: '@nuxt/kit': 3.20.0(magicast@0.3.5) transitivePeerDependencies: - supports-color - vite-plugin-vue-tracer@1.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)): + vite-plugin-vue-tracer@1.0.1(vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.7 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) - vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1): + vite@7.1.12(@types/node@22.18.13)(jiti@2.6.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -10447,6 +10466,7 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 terser: 5.44.0 + tsx: 4.20.6 yaml: 2.8.1 vscode-uri@3.1.0: {} diff --git a/server/api/products/[id].get.ts b/server/api/products/[id].get.ts new file mode 100644 index 0000000..5e8bbbb --- /dev/null +++ b/server/api/products/[id].get.ts @@ -0,0 +1,50 @@ +/** + * GET /api/products/[id] + * + * Returns a single product by UUID. + * Returns 404 if product is not found or is inactive. + */ + +import { z } from 'zod' +import { and, eq } from 'drizzle-orm' +import { products } from '../../database/schema' + +// UUID validation schema +const paramsSchema = z.object({ + id: z.string().uuid('Invalid product ID format'), +}) + +export default defineEventHandler(async (event) => { + const db = useDatabase() + + // Validate and extract product ID from route params + const params = await getValidatedRouterParams(event, paramsSchema.parse) + + try { + // Fetch product by ID (must be active) + const product = await db.query.products.findFirst({ + where: and(eq(products.id, params.id), eq(products.active, true)), + }) + + // Return 404 if product not found or inactive + if (!product) { + throw createError({ + statusCode: 404, + statusMessage: 'Product not found', + }) + } + + return product + } catch (error) { + // Re-throw createError errors as-is + if (error && typeof error === 'object' && 'statusCode' in error) { + throw error + } + + console.error('Error fetching product:', error) + throw createError({ + statusCode: 500, + statusMessage: 'Failed to fetch product', + }) + } +}) diff --git a/server/api/products/index.get.ts b/server/api/products/index.get.ts new file mode 100644 index 0000000..c667c75 --- /dev/null +++ b/server/api/products/index.get.ts @@ -0,0 +1,29 @@ +/** + * GET /api/products + * + * Returns a list of all active products available for purchase. + * Products are sorted by category and name. + */ + +import { eq } from 'drizzle-orm' +import { products } from '../../database/schema' + +export default defineEventHandler(async (event) => { + const db = useDatabase() + + try { + // Fetch all active products + const allProducts = await db.query.products.findMany({ + where: eq(products.active, true), + orderBy: (products, { asc }) => [asc(products.category), asc(products.name)], + }) + + return allProducts + } catch (error) { + console.error('Error fetching products:', error) + throw createError({ + statusCode: 500, + statusMessage: 'Failed to fetch products', + }) + } +}) diff --git a/server/database/seed.ts b/server/database/seed.ts new file mode 100644 index 0000000..ba8ee83 --- /dev/null +++ b/server/database/seed.ts @@ -0,0 +1,103 @@ +/** + * Database Seed Script + * + * Seeds the database with initial mock product data for development and testing. + * Run with: pnpm db:seed + */ + +import 'dotenv/config' +import { drizzle } from 'drizzle-orm/postgres-js' +import postgres from 'postgres' +import { products } from './schema' + +/** + * Sample annual pass products for experimenta + */ +const mockProducts = [ + { + navProductId: 'MSPACE-JK-2025', + name: 'Makerspace Jahreskarte', + description: + 'Unbegrenzter Zugang zum Makerspace für 365 Tage. Nutze modernste Werkzeuge, 3D-Drucker, Lasercutter und vieles mehr. Perfekt für Maker, Tüftler und kreative Köpfe.', + price: '120.00', + stockQuantity: 100, + category: 'makerspace-annual-pass', + active: true, + }, + { + navProductId: 'EXPERIMENTA-JK-2025', + name: 'experimenta Jahreskarte', + description: + 'Erlebe die Ausstellungswelt der experimenta ein ganzes Jahr lang. Mit freiem Eintritt zu allen Ausstellungen, Science Dome Shows und Sonderausstellungen.', + price: '85.00', + stockQuantity: 200, + category: 'annual-pass', + active: true, + }, + { + navProductId: 'PAEDAGOGEN-JK-2025', + name: 'Pädagogen Jahreskarte', + description: + 'Speziell für Lehrkräfte und Pädagogen. Mit exklusiven Fortbildungsangeboten, didaktischen Materialien und freiem Zugang zu allen Ausstellungen.', + price: '60.00', + stockQuantity: 50, + category: 'educator-annual-pass', + active: true, + }, +] + +async function seed() { + // Get database connection from environment + const connectionString = process.env.DATABASE_URL + if (!connectionString) { + throw new Error('DATABASE_URL environment variable is not set') + } + + console.log('🌱 Starting database seed...') + + // Create database connection + const client = postgres(connectionString) + const db = drizzle(client) + + try { + // Insert products + console.log(`📦 Inserting ${mockProducts.length} products...`) + const insertedProducts = await db + .insert(products) + .values(mockProducts) + .onConflictDoUpdate({ + target: products.navProductId, + set: { + name: mockProducts[0].name, // Drizzle requires a set object, using sql.excluded in real impl + description: mockProducts[0].description, + price: mockProducts[0].price, + stockQuantity: mockProducts[0].stockQuantity, + updatedAt: new Date(), + }, + }) + .returning() + + console.log(`✅ Successfully inserted/updated ${insertedProducts.length} products:`) + insertedProducts.forEach((product) => { + console.log(` - ${product.name} (${product.navProductId}) - €${product.price}`) + }) + + console.log('\n✨ Database seed completed successfully!') + } catch (error) { + console.error('❌ Error seeding database:', error) + throw error + } finally { + // Close database connection + await client.end() + } +} + +// Run seed function +seed() + .then(() => { + process.exit(0) + }) + .catch((error) => { + console.error('Fatal error:', error) + process.exit(1) + })