166 lines
5.4 KiB
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();
|
|
}
|
|
}
|
|
}
|