Enhance person editing functionality and birthday reminders
- Added a modal for editing person details, including name, role, favorite color, birthday, and description. - Implemented visibility toggling for PIN input based on role selection, ensuring security for Head of Household. - Introduced birthday reminder functionality in the calendar, generating events for upcoming birthdays with appropriate notifications. - Updated calendar and settings UI to accommodate new features, improving user experience and data management.
This commit is contained in:
@@ -75,6 +75,30 @@ function calendarBillReminderDatesInRange(
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calendar date for month/day in a given year (Feb 29 → Feb 28 on non-leap years).
|
||||
*/
|
||||
function hubCalendarBirthdayInYear(int $year, int $month, int $day, DateTimeZone $tzLocal): ?DateTimeImmutable {
|
||||
if ($month < 1 || $month > 12 || $day < 1 || $day > 31) {
|
||||
return null;
|
||||
}
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
if ($month === 2 && $day === 29 && checkdate(2, 28, $year)) {
|
||||
try {
|
||||
return new DateTimeImmutable(sprintf('%04d-02-28', $year), $tzLocal);
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new DateTimeImmutable(sprintf('%04d-%02d-%02d', $year, $month, $day), $tzLocal);
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int negative if $a sorts before $b
|
||||
*/
|
||||
@@ -84,7 +108,7 @@ function hubCalendarEventSortCompare(array $a, array $b): int {
|
||||
if ($da !== $db) {
|
||||
return $da <=> $db;
|
||||
}
|
||||
$rank = ['chore' => 0, 'meal' => 1, 'grocery_due' => 2, 'expense' => 3, 'bill_day' => 4];
|
||||
$rank = ['chore' => 0, 'meal' => 1, 'grocery_due' => 2, 'expense' => 3, 'bill_day' => 4, 'birthday_reminder' => 5];
|
||||
$ta = $rank[(string) ($a['type'] ?? '')] ?? 99;
|
||||
$tb = $rank[(string) ($b['type'] ?? '')] ?? 99;
|
||||
if ($ta !== $tb) {
|
||||
@@ -268,6 +292,60 @@ function hubCalendarAgendaEvents(
|
||||
}
|
||||
}
|
||||
|
||||
$startYear = (int) substr($rangeStart, 0, 4);
|
||||
$endYear = (int) substr($rangeEnd, 0, 4);
|
||||
foreach ($people as $p) {
|
||||
$nm = trim((string) ($p['name'] ?? ''));
|
||||
$bdRaw = trim((string) ($p['birthday'] ?? ''));
|
||||
if ($nm === '' || !preg_match('/^\d{4}-(\d{2})-(\d{2})$/', $bdRaw, $bm)) {
|
||||
continue;
|
||||
}
|
||||
$mm = (int) $bm[1];
|
||||
$dd = (int) $bm[2];
|
||||
for ($y = $startYear; $y <= $endYear; $y++) {
|
||||
$bday = hubCalendarBirthdayInYear($y, $mm, $dd, $tzLocal);
|
||||
if ($bday === null) {
|
||||
continue;
|
||||
}
|
||||
$bYmd = $bday->format('Y-m-d');
|
||||
if ($bYmd < $rangeStart || $bYmd > $rangeEnd) {
|
||||
continue;
|
||||
}
|
||||
$idealReminderYmd = $bday->modify('-7 days')->format('Y-m-d');
|
||||
$eventYmd = max($rangeStart, $idealReminderYmd);
|
||||
if ($eventYmd > $rangeEnd) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$evDt = new DateTimeImmutable($eventYmd . ' 00:00:00', $tzLocal);
|
||||
} catch (Exception $e) {
|
||||
continue;
|
||||
}
|
||||
$interval = $evDt->diff($bday);
|
||||
if ($interval->invert === 1) {
|
||||
continue;
|
||||
}
|
||||
$daysUntil = (int) $interval->days;
|
||||
$bdayLabel = $bday->format('F j');
|
||||
if ($daysUntil === 0) {
|
||||
$title = $nm . '\'s birthday today';
|
||||
} elseif ($daysUntil === 1) {
|
||||
$title = $nm . '\'s birthday tomorrow';
|
||||
} elseif ($daysUntil === 7) {
|
||||
$title = $nm . '\'s birthday in one week';
|
||||
} else {
|
||||
$title = $nm . '\'s birthday in ' . $daysUntil . ' days';
|
||||
}
|
||||
$events[] = [
|
||||
'type' => 'birthday_reminder',
|
||||
'date' => $eventYmd,
|
||||
'title' => $title,
|
||||
'detail' => 'Birthday on ' . $bdayLabel,
|
||||
'hrefQuery' => 'tab=settings',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
usort($events, 'hubCalendarEventSortCompare');
|
||||
return $events;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user