service->list(); $selectedId = (int) ($request->query('year') ?? 0); $activeYear = collect($years)->firstWhere('is_current', true); $defaultNext = $activeYear ? ($this->resolver->suggestNextName((string) $activeYear['name']) ?? '') : ''; [$defaultStart, $defaultEnd] = $defaultNext !== '' ? $this->resolver->inferDatesFromName($defaultNext) : ['', '']; return view('admin.school-years', [ 'pageConfig' => [ 'jwt' => JWTAuth::fromUser($request->user()), 'selected_year_id' => $selectedId, 'docs_url' => url('/docs'), 'default_draft' => [ 'name' => $defaultNext, 'start_date' => $defaultStart, 'end_date' => $defaultEnd, ], 'api' => [ 'index' => url('/api/v1/school-years'), 'store' => url('/api/v1/school-years'), 'update' => url('/api/v1/school-years/__ID__'), 'summary' => url('/api/v1/school-years/__ID__/summary'), 'validate_close' => url('/api/v1/school-years/__ID__/validate-close'), 'preview_close' => url('/api/v1/school-years/__ID__/preview-close'), 'close' => url('/api/v1/school-years/__ID__/close'), 'reopen' => url('/api/v1/school-years/__ID__/reopen'), 'parent_balances' => url('/api/v1/school-years/__ID__/parent-balances'), 'promotion_preview' => url('/api/v1/school-years/__ID__/promotion-preview'), ], ], ]); } public function store(Request $request): RedirectResponse { $payload = $request->validate([ 'name' => ['required', 'string', 'max:50'], 'start_date' => ['required', 'date'], 'end_date' => ['required', 'date', 'after:start_date'], ]); try { $year = $this->service->createYear($payload, optional($request->user())->id); } catch (ValidationException $e) { throw $e; } return redirect() ->route('admin.school-years', ['year' => $year['id']]) ->with('status', sprintf('Draft school year %s created.', $year['name'])); } public function update(Request $request, int $schoolYear): RedirectResponse { $payload = $request->validate([ 'name' => ['required', 'string', 'max:50'], 'start_date' => ['required', 'date'], 'end_date' => ['required', 'date', 'after:start_date'], ]); $updated = $this->service->updateYear($schoolYear, $payload, optional($request->user())->id); return redirect() ->route('admin.school-years', ['year' => $updated['id']]) ->with('status', sprintf('School year %s updated.', $updated['name'])); } public function validateClose(Request $request, int $schoolYear): JsonResponse { return response()->json([ 'ok' => true, 'validation' => $this->service->validateClose($schoolYear, $this->validatedClosePayload($request)), ]); } public function previewClose(Request $request, int $schoolYear): JsonResponse { return response()->json([ 'ok' => true, 'data' => $this->service->previewClose($schoolYear, $this->validatedClosePayload($request)), ]); } public function close(Request $request, int $schoolYear): RedirectResponse { $payload = $this->validatedClosePayload($request); $payload['confirmation'] = (string) $request->validate([ 'confirmation' => ['required', 'string', 'max:80'], ])['confirmation']; $result = $this->service->close($schoolYear, $payload, optional($request->user())->id); return redirect() ->route('admin.school-years', ['year' => $result['new_school_year']['id']]) ->with('status', sprintf( 'Closed %s and opened %s.', $result['closed_school_year']['name'], $result['new_school_year']['name'] )); } public function reopen(Request $request, int $schoolYear): RedirectResponse { $year = $this->service->reopen($schoolYear, optional($request->user())->id); return redirect() ->route('admin.school-years', ['year' => $year['id']]) ->with('status', sprintf('School year %s is now active.', $year['name'])); } private function validatedClosePayload(Request $request): array { return $request->validate([ 'new_school_year' => ['required', 'array'], 'new_school_year.name' => ['required', 'string', 'max:50'], 'new_school_year.start_date' => ['required', 'date'], 'new_school_year.end_date' => ['required', 'date', 'after:new_school_year.start_date'], 'transfer_unpaid_balances' => ['nullable', 'boolean'], ]); } }