155 lines
5 KiB
PHP
155 lines
5 KiB
PHP
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Illuminate\Support\Carbon;
|
||
|
||
class CabinetTabletSetting extends Model
|
||
{
|
||
/** @use HasFactory<\Database\Factories\CabinetTabletSettingFactory> */
|
||
use HasFactory;
|
||
|
||
protected $fillable = [
|
||
'store_status',
|
||
'notice_headline',
|
||
'notice_subtext',
|
||
'override_open_today',
|
||
'override_close_today',
|
||
'next_appointment_date',
|
||
'next_appointment_time',
|
||
'hours_monday_open', 'hours_monday_close',
|
||
'hours_tuesday_open', 'hours_tuesday_close',
|
||
'hours_wednesday_open', 'hours_wednesday_close',
|
||
'hours_thursday_open', 'hours_thursday_close',
|
||
'hours_friday_open', 'hours_friday_close',
|
||
'hours_saturday_open', 'hours_saturday_close',
|
||
'hours_sunday_open', 'hours_sunday_close',
|
||
'contact_phone',
|
||
'contact_email',
|
||
];
|
||
|
||
protected function casts(): array
|
||
{
|
||
return [
|
||
'next_appointment_date' => 'date',
|
||
];
|
||
}
|
||
|
||
private const DAYS = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
||
|
||
private const GERMAN_DAY_LABELS = [
|
||
'monday' => 'Montag',
|
||
'tuesday' => 'Dienstag',
|
||
'wednesday' => 'Mittwoch',
|
||
'thursday' => 'Donnerstag',
|
||
'friday' => 'Freitag',
|
||
'saturday' => 'Samstag',
|
||
'sunday' => 'Sonntag',
|
||
];
|
||
|
||
/**
|
||
* Get or create the singleton settings row.
|
||
*/
|
||
public static function current(): self
|
||
{
|
||
return static::firstOrCreate(['id' => 1]);
|
||
}
|
||
|
||
/**
|
||
* Get opening hours as display strings for the frontend (e.g. "10:00 – 18:00" or "Geschlossen").
|
||
*
|
||
* @return array<string, string>
|
||
*/
|
||
public function getHoursArray(): array
|
||
{
|
||
$result = [];
|
||
foreach (self::DAYS as $day) {
|
||
$open = $this->{"hours_{$day}_open"};
|
||
$close = $this->{"hours_{$day}_close"};
|
||
$result[$day] = ($open && $close) ? "{$open} – {$close}" : 'Geschlossen';
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* Compute the effective store status based on opening hours and current Berlin time.
|
||
*
|
||
* Returns the display status ('open', 'closed', 'notice'), the next opening time when
|
||
* closed, and today's closing time when open.
|
||
*
|
||
* @return array{status: string, today_close: string|null, next_open: array{label: string, time: string}|null}
|
||
*/
|
||
public function computeStatus(): array
|
||
{
|
||
if ($this->store_status === 'notice' || $this->store_status === 'warning') {
|
||
return ['status' => $this->store_status, 'today_close' => null, 'next_open' => null];
|
||
}
|
||
|
||
if ($this->store_status === 'closed') {
|
||
$now = Carbon::now('Europe/Berlin');
|
||
|
||
return ['status' => 'closed', 'today_close' => null, 'next_open' => $this->findNextOpenTime($now, true)];
|
||
}
|
||
|
||
// Auto mode: compute from opening hours
|
||
$now = Carbon::now('Europe/Berlin');
|
||
$dayKey = strtolower($now->englishDayOfWeek);
|
||
|
||
$openTime = $this->override_open_today ?: $this->{"hours_{$dayKey}_open"};
|
||
$closeTime = $this->override_close_today ?: $this->{"hours_{$dayKey}_close"};
|
||
$currentHHMM = $now->format('H:i');
|
||
|
||
if ($openTime && $closeTime && $currentHHMM >= $openTime && $currentHHMM < $closeTime) {
|
||
return ['status' => 'open', 'today_close' => $closeTime, 'next_open' => null];
|
||
}
|
||
|
||
return ['status' => 'closed', 'today_close' => null, 'next_open' => $this->findNextOpenTime($now, false)];
|
||
}
|
||
|
||
/**
|
||
* Find the next upcoming opening time from the given Berlin datetime.
|
||
*
|
||
* @return array{label: string, time: string}|null
|
||
*/
|
||
private function findNextOpenTime(Carbon $now, bool $skipToday): ?array
|
||
{
|
||
// Check if today still has an upcoming opening (we might be before opening time)
|
||
if (! $skipToday) {
|
||
$dayKey = strtolower($now->englishDayOfWeek);
|
||
$todayOpen = $this->override_open_today ?: $this->{"hours_{$dayKey}_open"};
|
||
|
||
if ($todayOpen && $now->format('H:i') < $todayOpen) {
|
||
return ['label' => 'Heute', 'time' => $todayOpen];
|
||
}
|
||
}
|
||
|
||
// Look ahead up to 7 days
|
||
for ($i = 1; $i <= 7; $i++) {
|
||
$checkDate = $now->copy()->addDays($i);
|
||
$dayKey = strtolower($checkDate->englishDayOfWeek);
|
||
$openTime = $this->{"hours_{$dayKey}_open"};
|
||
|
||
if ($openTime) {
|
||
$label = $i === 1 ? 'Morgen' : self::GERMAN_DAY_LABELS[$dayKey];
|
||
|
||
return ['label' => $label, 'time' => $openTime];
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Clear override times (called by midnight scheduler).
|
||
*/
|
||
public function clearOverrides(): void
|
||
{
|
||
$this->update([
|
||
'override_open_today' => null,
|
||
'override_close_today' => null,
|
||
]);
|
||
}
|
||
}
|