add projet
This commit is contained in:
+331
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Calendar;
|
||||
use App\Models\Configuration;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SchoolCalendarController extends BaseApiController
|
||||
{
|
||||
protected Calendar $calendar;
|
||||
protected Configuration $config;
|
||||
protected string $schoolYear;
|
||||
protected string $semester;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->calendar = model(Calendar::class);
|
||||
$this->config = model(Configuration::class);
|
||||
$this->schoolYear = (string) ($this->config->getConfig('school_year') ?? '');
|
||||
$this->semester = (string) ($this->config->getConfig('semester') ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/school-calendar
|
||||
* Get calendar events filtered by school_year and optional semester
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$schoolYear = (string) ($this->request->getGet('school_year') ?? $this->schoolYear);
|
||||
$semester = $this->request->getGet('semester');
|
||||
|
||||
try {
|
||||
$query = $this->calendar->newQuery();
|
||||
|
||||
if ($schoolYear !== '') {
|
||||
$query->where('school_year', $schoolYear);
|
||||
}
|
||||
|
||||
if ($semester !== null && $semester !== '') {
|
||||
$events = $query->where('semester', $semester)->orderBy('date', 'ASC')->get()->toArray();
|
||||
} else {
|
||||
$events = $query->orderBy('date', 'ASC')->get()->toArray();
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'events' => $events,
|
||||
'school_year' => $schoolYear,
|
||||
'semester' => $semester,
|
||||
], 'Calendar events retrieved successfully');
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'School calendar error: ' . $e->getMessage());
|
||||
return $this->respondError('Failed to retrieve calendar events', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/school-calendar/{id}
|
||||
* Get a single calendar event
|
||||
*/
|
||||
public function show($id = null)
|
||||
{
|
||||
$event = $this->calendar->find($id);
|
||||
if (!$event) {
|
||||
return $this->error('Event not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
return $this->success($event, 'Event retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/v1/school-calendar
|
||||
* Create a new calendar event
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
$payload = $this->payloadData();
|
||||
if (empty($payload)) {
|
||||
return $this->error('Invalid request data', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$rules = [
|
||||
'title' => 'required|min_length[3]',
|
||||
'start' => 'required|valid_date[Y-m-d]',
|
||||
];
|
||||
|
||||
$errors = $this->validateRequest($payload, $rules);
|
||||
if (!empty($errors)) {
|
||||
return $this->respondValidationError($errors);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'title' => trim((string) ($payload['title'] ?? '')),
|
||||
'description' => trim((string) ($payload['description'] ?? '')),
|
||||
'date' => trim((string) ($payload['start'] ?? '')),
|
||||
'notify_parent' => isset($payload['notify_parent']) && $payload['notify_parent'] ? 1 : 0,
|
||||
'notify_teacher' => isset($payload['notify_teacher']) && $payload['notify_teacher'] ? 1 : 0,
|
||||
'notify_admin' => isset($payload['notify_admin']) && $payload['notify_admin'] ? 1 : 0,
|
||||
'no_school' => isset($payload['no_school']) && $payload['no_school'] ? 1 : 0,
|
||||
'school_year' => trim((string) ($payload['school_year'] ?? $this->schoolYear)),
|
||||
'semester' => trim((string) ($payload['semester'] ?? $this->semester)),
|
||||
];
|
||||
|
||||
try {
|
||||
$event = $this->calendar->create($data);
|
||||
log_message('info', 'Event saved successfully: ' . print_r($data, true));
|
||||
return $this->success($event, 'Event added successfully', Response::HTTP_CREATED);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Failed to save event: ' . $e->getMessage());
|
||||
return $this->respondError('Failed to save the event', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /api/v1/school-calendar/{id}
|
||||
* Update a calendar event
|
||||
*/
|
||||
public function update($id = null)
|
||||
{
|
||||
$event = $this->calendar->find($id);
|
||||
if (!$event) {
|
||||
return $this->error('Event not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$payload = $this->payloadData();
|
||||
if (empty($payload)) {
|
||||
return $this->error('Invalid request data', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$rules = [
|
||||
'title' => 'permit_empty|min_length[3]',
|
||||
'start' => 'permit_empty|valid_date[Y-m-d]',
|
||||
];
|
||||
|
||||
$errors = $this->validateRequest($payload, $rules);
|
||||
if (!empty($errors)) {
|
||||
return $this->respondValidationError($errors);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
if (isset($payload['title'])) {
|
||||
$data['title'] = trim((string) $payload['title']);
|
||||
}
|
||||
if (isset($payload['description'])) {
|
||||
$data['description'] = trim((string) $payload['description']);
|
||||
}
|
||||
if (isset($payload['start'])) {
|
||||
$data['date'] = trim((string) $payload['start']);
|
||||
}
|
||||
if (isset($payload['notify_parent'])) {
|
||||
$data['notify_parent'] = $payload['notify_parent'] ? 1 : 0;
|
||||
}
|
||||
if (isset($payload['notify_teacher'])) {
|
||||
$data['notify_teacher'] = $payload['notify_teacher'] ? 1 : 0;
|
||||
}
|
||||
if (isset($payload['notify_admin'])) {
|
||||
$data['notify_admin'] = $payload['notify_admin'] ? 1 : 0;
|
||||
}
|
||||
if (isset($payload['no_school'])) {
|
||||
$data['no_school'] = $payload['no_school'] ? 1 : 0;
|
||||
}
|
||||
if (isset($payload['school_year'])) {
|
||||
$data['school_year'] = trim((string) $payload['school_year']);
|
||||
}
|
||||
if (isset($payload['semester'])) {
|
||||
$data['semester'] = trim((string) $payload['semester']);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->calendar->update($id, $data);
|
||||
$updatedEvent = $this->calendar->find($id);
|
||||
return $this->success($updatedEvent, 'Event updated successfully');
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Failed to update event: ' . $e->getMessage());
|
||||
return $this->respondError('Failed to update the event', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/v1/school-calendar/{id}
|
||||
* Delete a calendar event
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
$event = $this->calendar->find($id);
|
||||
if (!$event) {
|
||||
return $this->error('Event not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->calendar->delete($id);
|
||||
return $this->success(null, 'Event deleted successfully');
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Failed to delete event: ' . $e->getMessage());
|
||||
return $this->respondError('Failed to delete the event', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/school-calendar/view/parent
|
||||
* Get calendar events formatted for parent view
|
||||
*/
|
||||
public function calendarParentView()
|
||||
{
|
||||
$formattedEvents = $this->calendarView('parent');
|
||||
return $this->success(['events' => $formattedEvents], 'Parent calendar view retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/school-calendar/view/teacher
|
||||
* Get calendar events formatted for teacher view
|
||||
*/
|
||||
public function calendarTeacherView()
|
||||
{
|
||||
$formattedEvents = $this->calendarView('teacher');
|
||||
return $this->success(['events' => $formattedEvents], 'Teacher calendar view retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/school-calendar/view/admin
|
||||
* Get calendar events formatted for admin view
|
||||
*/
|
||||
public function calendarAdminView()
|
||||
{
|
||||
$formattedEvents = $this->calendarView('admin');
|
||||
return $this->success(['events' => $formattedEvents], 'Admin calendar view retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to format calendar events based on audience visibility rules
|
||||
*/
|
||||
private function calendarView(?string $audience = null): array
|
||||
{
|
||||
// Filter events by school year
|
||||
$query = $this->calendar->newQuery();
|
||||
if ($this->schoolYear !== '') {
|
||||
$query->where('school_year', $this->schoolYear);
|
||||
}
|
||||
$events = $query->orderBy('date', 'ASC')->get()->toArray();
|
||||
|
||||
// Apply audience visibility rules:
|
||||
// - If no checkboxes selected (all notify_* = 0) => visible to everyone
|
||||
// - Otherwise visible only to the selected audience
|
||||
if (in_array($audience, ['parent', 'teacher', 'admin'], true)) {
|
||||
$events = array_values(array_filter($events, function ($event) use ($audience) {
|
||||
$p = (int) ($event['notify_parent'] ?? 0);
|
||||
$t = (int) ($event['notify_teacher'] ?? 0);
|
||||
$a = (int) ($event['notify_admin'] ?? 0);
|
||||
$ns = (int) ($event['no_school'] ?? 0);
|
||||
|
||||
$hasAny = ($p + $t + $a) > 0;
|
||||
|
||||
// If marked as no_school, it should be visible to all audiences
|
||||
if ($ns === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$hasAny) {
|
||||
// No audience explicitly selected: show to everyone
|
||||
return true;
|
||||
}
|
||||
|
||||
// Audience explicitly selected: restrict to matching audience
|
||||
if ($audience === 'parent') {
|
||||
return $p === 1;
|
||||
}
|
||||
if ($audience === 'teacher') {
|
||||
return $t === 1;
|
||||
}
|
||||
if ($audience === 'admin') {
|
||||
return $a === 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
// Format for FullCalendar
|
||||
$aud = $audience; // capture for closure
|
||||
$formattedEvents = array_map(function ($event) use ($aud) {
|
||||
$p = (int) ($event['notify_parent'] ?? 0);
|
||||
$t = (int) ($event['notify_teacher'] ?? 0);
|
||||
$a = (int) ($event['notify_admin'] ?? 0);
|
||||
$ns = (int) ($event['no_school'] ?? 0);
|
||||
|
||||
$backgroundColor = null;
|
||||
|
||||
// Color no-school events yellow on teacher and parent calendars
|
||||
if (in_array($aud, ['teacher', 'parent'], true) && $ns === 1) {
|
||||
$backgroundColor = '#ffc107'; // yellow
|
||||
}
|
||||
|
||||
// Highlight events dedicated to staff (teachers/admins) only in green
|
||||
// Definition: visible to staff but not to parents, and not a no-school marker
|
||||
if (in_array($aud, ['teacher', 'admin'], true) && $backgroundColor === null) {
|
||||
$isStaffOnly = ($p === 0 && ($t === 1 || $a === 1) && $ns === 0);
|
||||
if ($isStaffOnly) {
|
||||
// Use a distinct green to differentiate from the default blue
|
||||
$backgroundColor = '#28a745';
|
||||
}
|
||||
}
|
||||
|
||||
// Title-casing for "no school" phrase when marked as no_school
|
||||
$title = $event['title'] ?? 'Untitled';
|
||||
if ($ns === 1 && is_string($title)) {
|
||||
// Replace common variants with Title Case
|
||||
$title = preg_replace('/\bno\s*[- ]\s*school\b/i', 'No School', $title);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $event['id'],
|
||||
'title' => $title,
|
||||
'start' => $event['date'] ?? '', // FullCalendar expects 'start'
|
||||
'description' => $event['description'] ?? '',
|
||||
'extendedProps' => [
|
||||
'semester' => $event['semester'] ?? '',
|
||||
'school_year' => $event['school_year'] ?? '',
|
||||
'notify_parent' => $p,
|
||||
'notify_teacher' => $t,
|
||||
'notify_admin' => $a,
|
||||
'no_school' => $ns,
|
||||
'backgroundColor' => $backgroundColor,
|
||||
]
|
||||
];
|
||||
}, $events);
|
||||
|
||||
return $formattedEvents;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user