studentTenantRepo = new StudentTenantRepository(); $this->requestRepo = new AppointmentRequestRepository(); $this->availRepo = new InstructorAvailabilityRepository(); $this->instructorRepo = new InstructorRepository(); $this->studentRepo = new StudentRepository(); } /** * GET /api/v1/student/me */ public function me(Request $request): void { $user = Auth::requireRole(['student']); $tenants = $this->studentTenantRepo->getActiveForUser((int) $user['id']); Response::json([ 'user' => [ 'id' => (int) $user['id'], 'email' => $user['email'], 'first_name' => $user['first_name'], 'last_name' => $user['last_name'], 'role' => $user['role'], ], 'tenants' => $tenants, ]); } /** * GET /api/v1/student/instructors?tenant_id=X * Liste aller Fahrlehrer einer Fahrschule (mit Kalender-Sichtbarkeit) */ public function instructors(Request $request): void { $user = Auth::requireRole(['student']); $tenantId = (int) ($request->query('tenant_id') ?? $user['tenant_id']); // Prüfe ob Student Zugriff auf diesen Tenant hat $st = $this->studentTenantRepo->findByUserAndTenant((int) $user['id'], $tenantId); if ($st === null || $st['status'] !== 'active') { Response::json(['message' => 'Kein Zugriff auf diese Fahrschule'], 403); } $instructors = $this->instructorRepo->listForTenant($tenantId); $result = []; foreach ($instructors as $i) { if ($i['is_active'] !== 1 || $i['booking_enabled'] !== 1) { continue; } $result[] = [ 'id' => $i['id'], 'first_name' => $i['first_name'], 'last_name' => $i['last_name'], 'color' => $i['color'] ?? '#3b82f6', ]; } Response::json(['data' => $result]); } /** * GET /api/v1/student/instructors/{id}/slots?from=&to=&tenant_id=X * Belegte Slots für einen Fahrlehrer (was der Fahrschüler sehen darf) */ public function instructorSlots(Request $request, array $params): void { $user = Auth::requireRole(['student']); $instructorId = (int) $params['id']; $from = (string) ($request->query('from') ?? date('Y-m-d')); $to = (string) ($request->query('to') ?? date('Y-m-d', strtotime('+30 days'))); $tenantId = (int) ($request->query('tenant_id') ?? $user['tenant_id']); // Zugriff prüfen $st = $this->studentTenantRepo->findByUserAndTenant((int) $user['id'], $tenantId); if ($st === null || $st['status'] !== 'active') { Response::json(['message' => 'Kein Zugriff'], 403); } // Instructor existiert? $instructor = $this->instructorRepo->findById($instructorId); if ($instructor === null) { Response::json(['message' => 'Fahrlehrer nicht gefunden'], 404); } // Availability holen $availability = $this->availRepo->getActiveForInstructor($instructorId); Response::json([ 'instructor' => [ 'id' => $instructor['id'], 'first_name' => $instructor['first_name'], 'last_name' => $instructor['last_name'], ], 'availability' => $availability, 'from' => $from, 'to' => $to, ]); } /** * POST /api/v1/student/appointment-requests * Neue Terminanfrage eines Fahrschülers */ public function createAppointmentRequest(Request $request): void { $user = Auth::requireRole(['student']); $payload = $request->input(); $instructorId = (int) ($payload['instructor_id'] ?? 0); $lessonTypeId = (int) ($payload['lesson_type_id'] ?? 0); $start = trim((string) ($payload['start'] ?? '')); $end = trim((string) ($payload['end'] ?? '')); $notes = trim((string) ($payload['notes'] ?? '')); $tenantId = (int) ($payload['tenant_id'] ?? $user['tenant_id']); if ($instructorId <= 0 || $lessonTypeId <= 0 || $start === '' || $end === '') { Response::json(['message' => 'instructor_id, lesson_type_id, start und end sind erforderlich'], 422); } // Tenant-Zugriff prüfen $st = $this->studentTenantRepo->findByUserAndTenant((int) $user['id'], $tenantId); if ($st === null || $st['status'] !== 'active') { Response::json(['message' => 'Kein Zugriff auf diese Fahrschule'], 403); } // Instructor prüfen $instructor = $this->instructorRepo->findById($instructorId); if ($instructor === null) { Response::json(['message' => 'Fahrlehrer nicht gefunden'], 404); } if ($instructor['booking_enabled'] !== 1) { Response::json(['message' => 'Dieser Fahrlehrer akzeptiert keine Anfragen'], 400); } // Anfrage erstellen $record = $this->requestRepo->create( (int) $user['id'], $instructorId, $lessonTypeId, $start, $end, $notes ); Response::json(['data' => $record], 201); } /** * GET /api/v1/student/appointment-requests */ public function appointmentRequests(Request $request): void { $user = Auth::requireRole(['student']); $requests = $this->requestRepo->getForStudent((int) $user['id']); Response::json(['data' => $requests]); } /** * POST /api/v1/student/add-tenant * Fahrschüler gibt neuen Code ein → neuer pending tenant */ public function addTenant(Request $request): void { $user = Auth::requireRole(['student']); $payload = $request->input(); $code = trim((string) ($payload['code'] ?? '')); if ($code === '') { Response::json(['message' => 'code ist erforderlich'], 422); } // Code → Tenant finden $tenantRepo = new \App\Repositories\TenantRegistrationCodeRepository(); $tenantCode = $tenantRepo->findByCode($code); if ($tenantCode === null) { Response::json(['message' => 'Ungültiger Code'], 400); } $tenantId = (int) $tenantCode['tenant_id']; // Already connected? $existing = $this->studentTenantRepo->findByUserAndTenant((int) $user['id'], $tenantId); if ($existing !== null) { Response::json(['message' => 'Du bist bereits mit dieser Fahrschule verbunden'], 400); } $record = $this->studentTenantRepo->create((int) $user['id'], $tenantId, 'pending'); Response::json([ 'data' => $record, 'message' => 'Anfrage gesendet, warte auf Bestätigung durch die Fahrschule', ], 201); } }