Files
my2/server/database/seed.ts
Bastian Masanek e252d68f0c Add educator annual pass functionality and update product listings
- Introduced a new page for educator annual passes, displaying relevant products with a dedicated layout and loading/error states.
- Updated AreaTabs component to include the educator tab and adjusted routing logic.
- Modified useAuth to redirect users to the products page after login.
- Adjusted mock product prices and stock quantities in the database seeding script to reflect new pricing strategy.

These changes enhance the user experience for educators and improve product visibility in the application.
2025-11-03 14:03:29 +01:00

223 lines
6.6 KiB
TypeScript

/**
* 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 { and, eq } from 'drizzle-orm'
import postgres from 'postgres'
import * as schema from './schema'
import { products, roles, productRoleVisibility } from './schema'
/**
* Standard roles for the system
* MVP: All roles are created, but assignment is manual
* Phase 2/3: educator and company roles require approval workflow
*/
const standardRoles = [
{
code: 'private' as const,
displayName: 'Privatperson',
description: 'Für private Besucher und Einzelpersonen',
requiresApproval: false,
sortOrder: 1,
active: true,
},
{
code: 'educator' as const,
displayName: 'Pädagoge',
description: 'Für Lehrer, Erzieher und pädagogische Fachkräfte',
requiresApproval: true,
sortOrder: 2,
active: true,
},
{
code: 'company' as const,
displayName: 'Unternehmen',
description: 'Für Firmenkunden und B2B-Partner',
requiresApproval: true,
sortOrder: 3,
active: true,
},
]
/**
* Sample annual pass products for experimenta
*/
const mockProducts: Array<{
navProductId: string
name: string
description: string
price: string
stockQuantity: number
category: string
active: boolean
roles: Array<'private' | 'educator' | 'company'>
}> = [
{
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: '30.00',
stockQuantity: 100,
category: 'makerspace-annual-pass',
active: true,
roles: ['private', 'educator', 'company'],
},
{
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: '49.00',
stockQuantity: 200,
category: 'annual-pass',
active: true,
roles: ['private', 'educator', 'company'],
},
{
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: '0.00',
stockQuantity: 99999,
category: 'educator-annual-pass',
active: true,
roles: ['private', 'educator'],
},
]
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 with schema
const client = postgres(connectionString)
const db = drizzle(client, { schema })
try {
// 1. Insert/Update Roles
console.log(`👥 Inserting ${standardRoles.length} roles...`)
const insertedRoles = []
for (const roleData of standardRoles) {
const [role] = await db
.insert(roles)
.values(roleData)
.onConflictDoUpdate({
target: roles.code,
set: {
displayName: roleData.displayName,
description: roleData.description,
requiresApproval: roleData.requiresApproval,
sortOrder: roleData.sortOrder,
active: roleData.active,
updatedAt: new Date(),
},
})
.returning()
insertedRoles.push(role)
}
console.log(`✅ Successfully inserted/updated ${insertedRoles.length} roles:`)
insertedRoles.forEach((role) => {
console.log(
` - ${role.displayName} (${role.code}) ${role.requiresApproval ? '[requires approval]' : '[auto-approved]'}`
)
})
// 2. Insert/Update Products
console.log(`\n📦 Inserting ${mockProducts.length} products...`)
const insertedProducts = []
for (const productData of mockProducts) {
const [product] = await db
.insert(products)
.values(productData)
.onConflictDoUpdate({
target: products.navProductId,
set: {
name: productData.name,
description: productData.description,
price: productData.price,
stockQuantity: productData.stockQuantity,
category: productData.category,
active: productData.active,
updatedAt: new Date(),
},
})
.returning()
insertedProducts.push(product)
}
console.log(`✅ Successfully inserted/updated ${insertedProducts.length} products:`)
insertedProducts.forEach((product) => {
console.log(` - ${product.name} (${product.navProductId}) - €${product.price}`)
})
// 3. Assign Roles to Products (using roles array from mock data)
console.log(`\n🔗 Assigning roles to products...`)
let assignmentCount = 0
for (let i = 0; i < insertedProducts.length; i++) {
const product = insertedProducts[i]
const productData = mockProducts[i]
const roleCodes = productData.roles || []
for (const roleCode of roleCodes) {
// Find role display name for logging
const role = insertedRoles.find((r) => r.code === roleCode)
if (!role) {
console.warn(` ⚠️ Role '${roleCode}' not found for product ${product.name}`)
continue
}
// Check if assignment already exists
const existing = await db.query.productRoleVisibility.findFirst({
where: and(
eq(productRoleVisibility.productId, product.id),
eq(productRoleVisibility.roleCode, roleCode)
),
})
if (!existing) {
await db.insert(productRoleVisibility).values({
productId: product.id,
roleCode, // Direct reference to roles.code (PK)
})
assignmentCount++
console.log(` - ${product.name}${role.displayName}`)
}
}
}
console.log(`✅ Created ${assignmentCount} product-role assignments`)
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)
})