added in course creation timeFrom till timeTo

This commit is contained in:
Rajpreet0 2026-01-06 16:52:01 +01:00
parent 65cc91b3c0
commit dd433be2f8
10 changed files with 95 additions and 282 deletions

246
package-lock.json generated
View file

@ -3621,61 +3621,6 @@
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"license": "MIT"
},
"node_modules/@supabase/auth-js": {
"version": "2.89.0",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.89.0.tgz",
"integrity": "sha512-wiWZdz8WMad8LQdJMWYDZ2SJtZP5MwMqzQq3ehtW2ngiI3UTgbKiFrvMUUS3KADiVlk4LiGfODB2mrYx7w2f8w==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.89.0",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.89.0.tgz",
"integrity": "sha512-XEueaC5gMe5NufNYfBh9kPwJlP5M2f+Ogr8rvhmRDAZNHgY6mI35RCkYDijd92pMcNM7g8pUUJov93UGUnqfyw==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/postgrest-js": {
"version": "2.89.0",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.89.0.tgz",
"integrity": "sha512-/b0fKrxV9i7RNOEXMno/I1862RsYhuUo+Q6m6z3ar1f4ulTMXnDfv0y4YYxK2POcgrOXQOgKYQx1eArybyNvtg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.89.0",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.89.0.tgz",
"integrity": "sha512-aMOvfDb2a52u6PX6jrrjvACHXGV3zsOlWRzZsTIOAJa0hOVvRp01AwC1+nLTGUzxzezejrYeCX+KnnM1xHdl+w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/phoenix": "^1.6.6",
"@types/ws": "^8.18.1",
"tslib": "2.8.1",
"ws": "^8.18.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/ssr": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.7.0.tgz",
@ -3688,37 +3633,6 @@
"@supabase/supabase-js": "^2.43.4"
}
},
"node_modules/@supabase/storage-js": {
"version": "2.89.0",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.89.0.tgz",
"integrity": "sha512-6zKcXofk/M/4Eato7iqpRh+B+vnxeiTumCIP+Tz26xEqIiywzD9JxHq+udRrDuv6hXE+pmetvJd8n5wcf4MFRQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"iceberg-js": "^0.8.1",
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/supabase-js": {
"version": "2.89.0",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.89.0.tgz",
"integrity": "sha512-KlaRwSfFA0fD73PYVMHj5/iXFtQGCcX7PSx0FdQwYEEw9b2wqM7GxadY+5YwcmuEhalmjFB/YvqaoNVF+sWUlg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@supabase/auth-js": "2.89.0",
"@supabase/functions-js": "2.89.0",
"@supabase/postgrest-js": "2.89.0",
"@supabase/realtime-js": "2.89.0",
"@supabase/storage-js": "2.89.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@ -3999,46 +3913,6 @@
"tailwindcss": "4.1.16"
}
},
"node_modules/@testing-library/dom": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
"@types/aria-query": "^5.0.1",
"aria-query": "5.3.0",
"dom-accessibility-api": "^0.5.9",
"lz-string": "^1.5.0",
"picocolors": "1.1.1",
"pretty-format": "^27.0.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@testing-library/dom/node_modules/aria-query": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"dequal": "^2.0.3"
}
},
"node_modules/@testing-library/dom/node_modules/dom-accessibility-api": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/@testing-library/jest-dom": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
@ -4559,14 +4433,6 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -4677,22 +4543,17 @@
"version": "20.19.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
"integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/phoenix": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz",
"integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==",
"license": "MIT",
"peer": true
},
"node_modules/@types/react": {
"version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
@ -4702,6 +4563,7 @@
"version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
@ -4713,16 +4575,6 @@
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
@ -6223,6 +6075,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@ -6383,17 +6236,6 @@
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT"
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/destr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
@ -7854,16 +7696,6 @@
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/iceberg-js": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
"integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -8426,9 +8258,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -8979,17 +8811,6 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@ -9719,55 +9540,6 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
"react-is": "^17.0.1"
},
"engines": {
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
}
},
"node_modules/pretty-format/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
},
"node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/pretty-format/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/prisma": {
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz",
@ -11339,7 +11111,7 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@ -11402,6 +11174,7 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/unrs-resolver": {
@ -11943,6 +11716,7 @@
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"

View file

@ -0,0 +1,12 @@
/*
Warnings:
- You are about to drop the column `time` on the `Course` table. All the data in the column will be lost.
- Added the required column `timeFrom` to the `Course` table without a default value. This is not possible if the table is not empty.
- Added the required column `timeTo` to the `Course` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Course" DROP COLUMN "time",
ADD COLUMN "timeFrom" TEXT NOT NULL,
ADD COLUMN "timeTo" TEXT NOT NULL;

View file

@ -97,7 +97,8 @@ model Course {
name String
sport String
date DateTime
time String
timeFrom String // Anfangszeit
timeTo String // Endzeit
trainers String[] // Array of trainer IDs
room String
description String // Rich text HTML content

View file

@ -37,29 +37,42 @@ export type ManagerData = {
export type AuthUserData = UserData | ManagerData;
// Cache entry type
type CacheEntry = {
data: AuthUserData;
timestamp: number;
};
// Simple in-memory cache with timestamp
const userDataCache = new Map<string, CacheEntry>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
// Note: We don't use in-memory caching because this app runs on Vercel (serverless)
// Serverless functions are stateless - in-memory cache would be:
// 1. Inconsistent across Lambda instances
// 2. Lost on cold starts
// 3. Not shared between requests
// Instead, we rely on React's cache() for per-request deduplication only.
let userDataCallCount = 0;
let supabaseCallCount = 0;
// Cache Supabase user (per request)
// Get authenticated user from Supabase
// Uses React's cache() for per-request deduplication
// No cross-request caching due to serverless architecture
export const getUser = cache(async () => {
supabaseCallCount++;
console.log(`🔐 [AUTH] getUser called (count: ${supabaseCallCount})`);
const supabase = await createClient();
// Validate with Auth server (secure - always authenticates against Supabase)
const {
data: { user },
} = await supabase.auth.getUser();
console.log("🔐 [AUTH] getUser called - User:", user?.email || "none");
if (user) {
console.log("🔐 [AUTH] User authenticated:", user.email);
} else {
console.log("🔐 [AUTH] No authenticated user");
}
return user;
});
// Cache user data from database with time-based cache
// Get user data from database
// Uses React's cache() for per-request deduplication
// No cross-request caching due to serverless architecture
export const getUserData = cache(async (): Promise<AuthUserData | null> => {
userDataCallCount++;
console.log(`📊 [DB] getUserData called (count: ${userDataCallCount})`);
@ -67,18 +80,7 @@ export const getUserData = cache(async (): Promise<AuthUserData | null> => {
const user = await getUser();
if (!user) return null;
// Check in-memory cache
const cached = userDataCache.get(user.id);
const now = Date.now();
if (cached && now - cached.timestamp < CACHE_TTL) {
console.log(
`⚡ [CACHE] Using cached data (${Math.round((now - cached.timestamp) / 1000)}s old)`
);
return cached.data;
}
console.log("🗄️ [DB] Fetching user data from Prisma for:", user.email);
console.log("🗄️ [DB] Fetching user data from database for:", user.email);
// Try to find in User table first
const userData = await prisma.user.findUnique({
@ -101,7 +103,6 @@ export const getUserData = cache(async (): Promise<AuthUserData | null> => {
if (userData) {
console.log("✅ [DB] User data fetched:", userData.name);
const userWithRole: UserData = { ...userData, role: "user" };
userDataCache.set(user.id, { data: userWithRole, timestamp: now });
return userWithRole;
}
@ -133,7 +134,6 @@ export const getUserData = cache(async (): Promise<AuthUserData | null> => {
`${managerData.firstName} ${managerData.lastName}`
);
const managerWithRole: ManagerData = { ...managerData, role: "manager" };
userDataCache.set(user.id, { data: managerWithRole, timestamp: now });
return managerWithRole;
}

View file

@ -64,7 +64,8 @@ export async function createCourse(data: CourseFormData, status: "draft" | "publ
name: validatedData.name,
sport: validatedData.sport,
date: new Date(validatedData.date), // String zu Date konvertieren
time: validatedData.time,
timeFrom: validatedData.timeFrom,
timeTo: validatedData.timeTo,
trainers: validatedData.trainers,
room: validatedData.room,
description: validatedData.description,
@ -334,7 +335,8 @@ export async function updateCourse(
name: validatedData.name,
sport: validatedData.sport,
date: new Date(validatedData.date),
time: validatedData.time,
timeFrom: validatedData.timeFrom,
timeTo: validatedData.timeTo,
trainers: validatedData.trainers,
room: validatedData.room,
description: validatedData.description,

View file

@ -28,8 +28,11 @@ export const courseSchema = z
// Kursdatum als String im Format YYYY-MM-DD
date: z.string().min(1, "Datum ist erforderlich"),
// Kurszeit als String im Format HH:MM
time: z.string().min(1, "Uhrzeit ist erforderlich"),
// Anfangszeit als String im Format HH:MM
timeFrom: z.string().min(1, "Anfangszeit ist erforderlich"),
// Endzeit als String im Format HH:MM
timeTo: z.string().min(1, "Endzeit ist erforderlich"),
// Array von Trainer-IDs (mindestens ein Trainer erforderlich)
trainers: z.array(z.string()).min(1, "Mindestens ein Trainer muss ausgewählt werden"),

View file

@ -3,7 +3,8 @@ export type Course = {
name: string;
sport: string;
date: Date;
time: string;
timeFrom: string;
timeTo: string;
trainers: string[];
room: string;
description: string;

View file

@ -14,7 +14,8 @@ import { Pen, Trash2 } from "lucide-react";
interface CourseListItemProps {
courseName: string; // Name des Kurses
room: string; // Raum, in dem der Kurs stattfindet
time: string; // Uhrzeit des Kurses (z.B. "14:00")
timeFrom: string; // Uhrzeit am Start des Kurses
timeTo: string; // Uhrzeit am Ende des Kurses
currentParticipants: number; // Aktuelle Anzahl der Teilnehmer
maxParticipants: number; // Maximale Anzahl der Teilnehmer
trainerName: string; // Name(n) des/der Trainer
@ -32,7 +33,8 @@ interface CourseListItemProps {
const CourseListItem: React.FC<CourseListItemProps> = ({
courseName,
room,
time,
timeFrom,
timeTo,
currentParticipants,
maxParticipants,
trainerName,
@ -63,7 +65,7 @@ const CourseListItem: React.FC<CourseListItemProps> = ({
<div>
<h2 className="text-xl font-semibold text-gray-900">{courseName}</h2>
<p className="text-gray-500">
{room} • {time}
{room} • {timeFrom} - {timeTo}
</p>
</div>
<div>

View file

@ -62,7 +62,8 @@ const CourseCreateView = ({
const router = useRouter();
const [courseName, setCourseName] = useState("");
const [courseDate, setCourseDate] = useState("");
const [courseTime, setCourseTime] = useState("");
const [timeFrom, setTimeFrom] = useState("");
const [timeTo, setTimeTo] = useState("");
const [selectedSport, setSelectedSport] = useState("");
const [selectedTrainers, setSelectedTrainers] = useState<string[]>([]);
const [selectedRoom, setSelectedRoom] = useState("");
@ -112,7 +113,8 @@ const CourseCreateView = ({
setCourseDate(formattedDate);
}
setCourseTime(initialData.time || "");
setTimeFrom(initialData.timeFrom || "");
setTimeTo(initialData.timeTo || "");
setSelectedTrainers(initialData.trainers || []);
setSelectedRoom(initialData.room || "");
setDescription(initialData.description || "");
@ -129,7 +131,8 @@ const CourseCreateView = ({
if (!courseName.trim()) newErrors.name = "Kursname ist erforderlich";
if (!selectedSport) newErrors.sport = "Sportart ist erforderlich";
if (!courseDate) newErrors.date = "Datum ist erforderlich";
if (!courseTime) newErrors.time = "Uhrzeit ist erforderlich";
if (!timeFrom) newErrors.timeFrom = "Anfangszeit ist erforderlich";
if (!timeTo) newErrors.timeTo = "Endzeit ist erforderlich";
if (selectedTrainers.length === 0)
newErrors.trainers = "Mindestens ein Trainer muss ausgewählt werden";
if (!selectedRoom) newErrors.room = "Raum ist erforderlich";
@ -167,7 +170,8 @@ const CourseCreateView = ({
name: courseName,
sport: selectedSport,
date: courseDate,
time: courseTime,
timeFrom: timeFrom,
timeTo: timeTo,
trainers: selectedTrainers,
room: selectedRoom,
description,
@ -253,13 +257,25 @@ const CourseCreateView = ({
<div>
<InputComponent
isLabel
label="Uhrzeit"
label="Uhrzeit von"
type="time"
id="course-create-time"
value={courseTime}
onChange={(e) => setCourseTime(e.target.value)}
id="course-create-time-from"
value={timeFrom}
onChange={(e) => setTimeFrom(e.target.value)}
/>
{errors.time && <p className="text-red-500 text-sm mt-1">{errors.time}</p>}
{errors.timeFrom && <p className="text-red-500 text-sm mt-1">{errors.timeFrom}</p>}
</div>
<div>
<InputComponent
isLabel
label="Uhrzeit bis"
type="time"
id="course-create-time-to"
value={timeTo}
onChange={(e) => setTimeTo(e.target.value)}
/>
{errors.timeTo && <p className="text-red-500 text-sm mt-1">{errors.timeTo}</p>}
</div>
<MultiSelectDropdown

View file

@ -14,7 +14,8 @@ type Course = {
name: string;
sport: string;
date: Date;
time: string;
timeFrom: string;
timeTo: string;
trainers: string[];
room: string;
description: string;
@ -460,7 +461,8 @@ const CourseListView = () => {
key={course.id}
courseName={course.name}
room={roomsMap[course.room] || course.room}
time={course.time}
timeFrom={course.timeFrom}
timeTo={course.timeTo}
currentParticipants={0} // TODO: Implement participant tracking
maxParticipants={course.maxParticipants}
trainerName={getTrainerName(course.trainers)}