whereNull('parent_id') ->orderBy('sort_order') ->get(); } public function index(): View { $events = Event::with('client')->orderBy('start_time')->get(); $clients = Client::where('status', 'active')->orderBy('name')->get(); $categoryTree = $this->categoryTree(); // Format for FullCalendar $calendarEvents = $events->map(fn (Event $e) => [ 'id' => $e->id, 'title' => ($e->is_priority ? '⭐ ' : '') . $e->title, 'start' => $e->start_time->toIso8601String(), 'end' => $e->end_time?->toIso8601String(), 'backgroundColor' => $e->color, 'borderColor' => $e->color, 'extendedProps' => [ 'status' => $e->status->value, 'client' => $e->client?->name, 'type' => $e->event_type, 'priority' => $e->is_priority, ], ]); return view('schedule.index', compact('events', 'clients', 'calendarEvents', 'categoryTree')); } public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'client_id' => 'nullable|exists:clients,id', 'category_id' => 'nullable|exists:client_categories,id', 'event_type' => 'required|in:task,meeting,deadline,posting', 'start_time' => 'required|date', 'end_time' => 'nullable|date|after:start_time', 'location' => 'nullable|string|max:255', 'is_priority' => 'boolean', 'is_all_day' => 'boolean', 'color' => 'nullable|string|max:20', 'notes' => 'nullable|string', 'create_brand_contract' => 'nullable|boolean', 'contract_title' => 'nullable|string|max:255', 'contract_start_date' => 'nullable|date', 'contract_end_date' => 'nullable|date|after_or_equal:contract_start_date', ]); $validated['is_priority'] = $request->boolean('is_priority'); $validated['is_all_day'] = $request->boolean('is_all_day'); $event = Event::create(Arr::except($validated, [ 'create_brand_contract', 'contract_title', 'contract_start_date', 'contract_end_date', ])); if ($request->boolean('create_brand_contract') && $event->client_id) { BrandContract::create([ 'title' => $request->input('contract_title') ?: $event->title, 'client_id' => $event->client_id, 'category_id' => $event->category_id, 'start_date' => $request->input('contract_start_date') ?? $event->start_time->toDateString(), 'end_date' => $request->input('contract_end_date') ?? ($event->end_time?->toDateString() ?? $event->start_time->toDateString()), 'is_active' => true, ]); } return redirect()->route('schedule.index') ->with('success', '🐾 Event scheduled!'); } public function edit(Event $event): View { $clients = Client::where('status', 'active')->orderBy('name')->get(); $statuses = EventStatus::cases(); $categoryTree = $this->categoryTree(); return view('schedule.edit', compact('event', 'clients', 'statuses', 'categoryTree')); } public function update(Request $request, Event $event): RedirectResponse { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'client_id' => 'nullable|exists:clients,id', 'category_id' => 'nullable|exists:client_categories,id', 'event_type' => 'required|in:task,meeting,deadline,posting', 'start_time' => 'required|date', 'end_time' => 'nullable|date', 'location' => 'nullable|string|max:255', 'is_priority' => 'boolean', 'status' => 'required|in:pending,in_progress,completed,cancelled', 'color' => 'nullable|string|max:20', 'notes' => 'nullable|string', 'create_brand_contract' => 'nullable|boolean', 'contract_title' => 'nullable|string|max:255', 'contract_start_date' => 'nullable|date', 'contract_end_date' => 'nullable|date|after_or_equal:contract_start_date', ]); $validated['is_priority'] = $request->boolean('is_priority'); $event->update(Arr::except($validated, [ 'create_brand_contract', 'contract_title', 'contract_start_date', 'contract_end_date', ])); if ($request->boolean('create_brand_contract') && $event->client_id) { BrandContract::create([ 'title' => $request->input('contract_title') ?: $event->title, 'client_id' => $event->client_id, 'category_id' => $event->category_id, 'start_date' => $request->input('contract_start_date') ?? $event->start_time->toDateString(), 'end_date' => $request->input('contract_end_date') ?? ($event->end_time?->toDateString() ?? $event->start_time->toDateString()), 'is_active' => true, ]); } return redirect()->route('schedule.index') ->with('success', '🐾 Event updated!'); } public function updateDates(Request $request, Event $event): JsonResponse { $validated = $request->validate([ 'start_time' => 'required|date', 'end_time' => 'nullable|date', ]); $event->update($validated); return response()->json(['ok' => true]); } public function destroy(Event $event): RedirectResponse { $event->delete(); return redirect()->route('schedule.index') ->with('success', '🐾 Event removed.'); } public function apiList(): JsonResponse { $events = Event::with('client')->get()->map(fn (Event $e) => [ 'id' => $e->id, 'title' => ($e->is_priority ? '⭐ ' : '') . $e->title, 'start' => $e->start_time->toIso8601String(), 'end' => $e->end_time?->toIso8601String(), 'backgroundColor' => $e->color, 'borderColor' => $e->color, 'allDay' => $e->is_all_day, 'extendedProps' => [ 'status' => $e->status->value, 'client' => $e->client?->name, 'type' => $e->event_type, 'priority' => $e->is_priority, ], ]); return response()->json($events); } }