kithkin/app/Services/Calendar/CalendarRangeResolver.php

166 lines
5.4 KiB
PHP

<?php
namespace App\Services\Calendar;
use Carbon\Carbon;
use Illuminate\Http\Request;
class CalendarRangeResolver
{
public const VIEWS = ['day', 'week', 'month', 'four'];
/**
* Normalize view + date inputs into a resolved view and date range.
*
* @return array [$view, ['start' => Carbon, 'end' => Carbon]]
*/
public function resolveRange(
Request $request,
string $tz,
int $weekStart,
int $weekEnd,
string $defaultView,
string $defaultDate
): array {
$requestView = $request->query('view', $defaultView);
$view = in_array($requestView, self::VIEWS, true)
? $requestView
: 'month';
$date = $request->query('date', $defaultDate);
$anchor = $this->safeDate($date, $tz, $defaultDate);
switch ($view) {
case 'day':
$start = $anchor->copy()->startOfDay();
$end = $anchor->copy()->endOfDay();
break;
case 'week':
$start = $anchor->copy()->startOfWeek($weekStart);
$end = $anchor->copy()->endOfWeek($weekEnd);
break;
case 'four':
$start = $anchor->copy()->startOfDay();
$end = $anchor->copy()->addDays(3)->endOfDay();
break;
default: // month
$start = $anchor->copy()->startOfMonth();
$end = $anchor->copy()->endOfMonth();
}
return [$view, ['start' => $start, 'end' => $end]];
}
/**
* Calendar grid span differs from logical range (e.g. month padding).
*/
public function gridSpan(string $view, array $range, int $weekStart, int $weekEnd): array
{
switch ($view) {
case 'day':
$start = $range['start']->copy()->startOfDay();
$end = $range['start']->copy()->endOfDay();
break;
case 'week':
$start = $range['start']->copy()->startOfWeek($weekStart);
$end = $range['start']->copy()->endOfWeek($weekEnd);
break;
case 'four':
$start = $range['start']->copy()->startOfDay();
$end = $range['start']->copy()->addDays(3);
break;
default: // month
$start = $range['start']->copy()->startOfMonth()->startOfWeek($weekStart);
$end = $range['end']->copy()->endOfMonth()->endOfWeek($weekEnd);
}
return ['start' => $start, 'end' => $end];
}
/**
* Navigation dates for header controls.
*/
public function navDates(string $view, Carbon $start, string $tz): array
{
$start = $start->copy()->tz($tz);
return match ($view) {
'day' => [
'prev' => $start->copy()->subDay()->toDateString(),
'next' => $start->copy()->addDay()->toDateString(),
'today' => Carbon::today($tz)->toDateString(),
],
'week' => [
'prev' => $start->copy()->subWeek()->toDateString(),
'next' => $start->copy()->addWeek()->toDateString(),
'today' => Carbon::today($tz)->toDateString(),
],
'four' => [
'prev' => $start->copy()->subDays(4)->toDateString(),
'next' => $start->copy()->addDays(4)->toDateString(),
'today' => Carbon::today($tz)->toDateString(),
],
default => [
'prev' => $start->copy()->subMonth()->startOfMonth()->toDateString(),
'next' => $start->copy()->addMonth()->startOfMonth()->toDateString(),
'today' => Carbon::today($tz)->toDateString(),
],
};
}
/**
* Title text for the calendar header.
*/
public function headerTitle(string $view, Carbon $start, Carbon $end): array
{
$sameDay = $start->isSameDay($end);
$sameMonth = $start->isSameMonth($end);
$sameYear = $start->isSameYear($end);
$strong = $start->format('F');
$span = $start->format('Y');
if ($view === 'day' || $sameDay) {
return [
'strong' => $start->format('F j'),
'span' => $start->format('Y'),
];
}
if (in_array($view, ['week', 'four'], true)) {
if ($sameMonth && $sameYear) {
return [
'strong' => $start->format('F j') . ' to ' . $end->format('j'),
'span' => $start->format('Y'),
];
}
if ($sameYear) {
return [
'strong' => $start->format('F') . ' to ' . $end->format('F'),
'span' => $start->format('Y'),
];
}
return [
'strong' => $start->format('F Y') . ' to ' . $end->format('F Y'),
'span' => null,
];
}
return ['strong' => $strong, 'span' => $span];
}
/**
* Safe date parsing with fallback to a default date string.
*/
public function safeDate(string $date, string $tz, string $fallbackDate): Carbon
{
try {
return Carbon::createFromFormat('Y-m-d', $date, $tz)->startOfDay();
} catch (\Throwable $e) {
return Carbon::createFromFormat('Y-m-d', $fallbackDate, $tz)->startOfDay();
}
}
}