Workdays #58

Merged
NiekRabelink merged 1 commits from dev into main 2026-09-12 08:52:13 +00:00
4 changed files with 135 additions and 20 deletions
Showing only changes of commit bcd85f131b - Show all commits
+59 -1
View File
@@ -174,6 +174,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 3. Organisatie Werkdag Standaarden opslaan (Release v1.4)
elseif ($action === 'save_workday_settings') {
$rawWorkdays = isset($_POST['workdays']) && is_array($_POST['workdays'])
? array_values(array_filter(array_map('intval', $_POST['workdays']), fn($d) => $d >= 1 && $d <= 7))
: [1, 2, 3, 4, 5];
if (empty($rawWorkdays)) {
$rawWorkdays = [1, 2, 3, 4, 5];
}
sort($rawWorkdays);
$workdaysStr = implode(',', $rawWorkdays);
$workdayStart = trim($_POST['workday_start'] ?? '08:30');
$workdayEnd = trim($_POST['workday_end'] ?? '17:00');
$lunchStart = trim($_POST['lunch_start'] ?? '12:30');
@@ -183,6 +192,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pomoBreak = max(2, min(30, (int)($_POST['pomodoro_break'] ?? 5)));
$taskDuration = in_array((int)($_POST['default_task_duration'] ?? 30), [15, 30, 45, 60, 90, 120]) ? (int)$_POST['default_task_duration'] : 30;
set_system_setting('workdays', $workdaysStr);
set_system_setting('workday_start', $workdayStart);
set_system_setting('workday_end', $workdayEnd);
set_system_setting('lunch_start', $lunchStart);
@@ -192,7 +202,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
set_system_setting('pomodoro_break', $pomoBreak);
set_system_setting('default_task_duration', $taskDuration);
fociz_log_audit($userId, 'SETTINGS_UPDATED', 'workday_settings', null, ['workday_start' => $workdayStart, 'workday_end' => $workdayEnd], 'SUCCESS', $adminUser['email']);
fociz_log_audit($userId, 'SETTINGS_UPDATED', 'workday_settings', null, ['workdays' => $workdaysStr, 'workday_start' => $workdayStart, 'workday_end' => $workdayEnd], 'SUCCESS', $adminUser['email']);
$message = "Organisatie werkdagstandaarden zijn succesvol bijgewerkt.";
$messageType = 'success';
@@ -929,6 +939,54 @@ require_once __DIR__ . '/includes/header.php';
<input type="hidden" name="action" value="save_workday_settings">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(get_csrf_token()) ?>">
<!-- Actieve Werkdagen & Weekend Configuratie -->
<div class="p-4 sm:p-5 rounded-2xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800 space-y-3">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-1">
<div class="text-xs font-bold text-slate-900 dark:text-white flex items-center space-x-1.5">
<i data-lucide="calendar-days" class="w-4 h-4 text-indigo-500"></i>
<span>Actieve Werkdagen vs. Weekend</span>
</div>
<span class="text-[10px] text-slate-500 dark:text-slate-400">
Ma t/m Vr zijn standaard werkdagen; Za & Zo gelden als weekend
</span>
</div>
<p class="text-xs text-slate-500 dark:text-slate-400">
Vink de dagen aan die voor uw organisatie als reguliere werkdagen gelden. Niet-aangevinkte dagen worden in Fociz visueel duidelijk gemarkeerd als <strong>Weekend / Vrije dag</strong> in de dagplanning, weekoverzichten en tijdlijnen.
</p>
<?php
$dayOptions = [
1 => ['short' => 'Ma', 'full' => 'Maandag', 'type' => 'Werkdag'],
2 => ['short' => 'Di', 'full' => 'Dinsdag', 'type' => 'Werkdag'],
3 => ['short' => 'Wo', 'full' => 'Woensdag', 'type' => 'Werkdag'],
4 => ['short' => 'Do', 'full' => 'Donderdag', 'type' => 'Werkdag'],
5 => ['short' => 'Vr', 'full' => 'Vrijdag', 'type' => 'Werkdag'],
6 => ['short' => 'Za', 'full' => 'Zaterdag', 'type' => 'Weekend'],
7 => ['short' => 'Zo', 'full' => 'Zondag', 'type' => 'Weekend'],
];
?>
<div class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-7 gap-2.5 pt-1">
<?php foreach ($dayOptions as $dNum => $dInfo):
$isChecked = in_array($dNum, $workdaySettings['workdays'], true);
?>
<label class="relative flex flex-col p-3 rounded-xl border cursor-pointer select-none transition-all <?= $isChecked ? 'bg-indigo-50/70 dark:bg-indigo-950/30 border-indigo-400 dark:border-indigo-600 shadow-2xs' : 'bg-white dark:bg-[#111622] border-slate-200/80 dark:border-slate-800 opacity-75 hover:opacity-100' ?>">
<div class="flex items-center justify-between mb-1.5">
<span class="text-xs font-black <?= $isChecked ? 'text-indigo-600 dark:text-indigo-400' : 'text-slate-700 dark:text-slate-300' ?>">
<?= $dInfo['short'] ?>
</span>
<input type="checkbox" name="workdays[]" value="<?= $dNum ?>" <?= $isChecked ? 'checked' : '' ?> class="rounded text-indigo-600 focus:ring-indigo-500 border-slate-300 dark:border-slate-700 w-3.5 h-3.5 cursor-pointer">
</div>
<div class="text-[11px] font-semibold text-slate-800 dark:text-slate-200 leading-tight">
<?= $dInfo['full'] ?>
</div>
<span class="mt-1 text-[9px] font-bold uppercase tracking-wider <?= $dInfo['type'] === 'Weekend' ? 'text-amber-600 dark:text-amber-400' : 'text-slate-400 dark:text-slate-500' ?>">
<?= $dInfo['type'] ?>
</span>
</label>
<?php endforeach; ?>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<!-- Werkdag uren -->
<div class="p-4 rounded-2xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800 space-y-3">
+7 -3
View File
@@ -658,6 +658,10 @@ try {
$workdayEndStr = $workdaySettings['workday_end'] ?? '17:00';
$lunchStartStr = $workdaySettings['lunch_start'] ?? '12:30';
$lunchEndStr = $workdaySettings['lunch_end'] ?? '13:00';
$activeWorkdays = $workdaySettings['workdays'] ?? [1, 2, 3, 4, 5];
if (!is_array($activeWorkdays) || empty($activeWorkdays)) {
$activeWorkdays = [1, 2, 3, 4, 5];
}
$toMins = function(string $timeStr): int {
$parts = explode(':', trim($timeStr));
@@ -696,7 +700,7 @@ try {
$checkDate = clone $now;
if ($startFromNextWorkday) {
$checkDate->modify('+1 day');
while ((int)$checkDate->format('N') >= 6) {
while (!in_array((int)$checkDate->format('N'), $activeWorkdays, true)) {
$checkDate->modify('+1 day');
}
}
@@ -746,8 +750,8 @@ try {
while ($checkedDays < $maxDays) {
$dayOfWeek = (int)$checkDate->format('N'); // 1 = Ma, 7 = Zo
// Weekend overslaan
if ($dayOfWeek === 6 || $dayOfWeek === 7) {
// Niet-werkdag / weekend overslaan op basis van organisatie-instellingen
if (!in_array($dayOfWeek, $activeWorkdays, true)) {
$checkDate->modify('+1 day');
continue;
}
+7
View File
@@ -93,6 +93,11 @@ function set_system_setting(string $key, $value): bool {
* Haalt alle organisatie werkdag standaarden op (Release v1.4)
*/
function get_workday_settings(): array {
$rawDays = get_system_setting('workdays', '1,2,3,4,5');
$workdays = array_values(array_filter(array_map('intval', explode(',', $rawDays)), fn($d) => $d >= 1 && $d <= 7));
if (empty($workdays)) {
$workdays = [1, 2, 3, 4, 5];
}
return [
'workday_start' => get_system_setting('workday_start', '08:30'),
'workday_end' => get_system_setting('workday_end', '17:00'),
@@ -102,6 +107,8 @@ function get_workday_settings(): array {
'pomodoro_deep' => (int)get_system_setting('pomodoro_deep', 50),
'pomodoro_break' => (int)get_system_setting('pomodoro_break', 5),
'default_task_duration' => (int)get_system_setting('default_task_duration', 30),
'workdays' => $workdays,
'workdays_str' => implode(',', $workdays),
];
}
+52 -6
View File
@@ -36,6 +36,13 @@ $selectedTs = strtotime($selectedDate);
// Haal organisatie werkdag- en AI-standaarden op (Release v1.4 & v1.5)
$workdaySettings = get_workday_settings();
$aiSettings = get_ai_settings();
$activeWorkdays = $workdaySettings['workdays'] ?? [1, 2, 3, 4, 5];
if (!is_array($activeWorkdays) || empty($activeWorkdays)) {
$activeWorkdays = [1, 2, 3, 4, 5];
}
$selectedDayN = (int)date('N', $selectedTs);
$isSelectedDateWorkday = in_array($selectedDayN, $activeWorkdays, true);
$isSelectedDateWeekend = !$isSelectedDateWorkday;
// Bereken weekgrenzen (Maandag t/m Zondag) rondom de geselecteerde datum
$dayOfWeek = (int)date('N', $selectedTs); // 1 = Maandag, 7 = Zondag
@@ -215,6 +222,7 @@ for ($i = 0; $i < 7; $i++) {
$dayM = (int)date('n', $dayTs);
$tStat = $weekTaskStats[$dayDate] ?? ['total' => 0, 'completed' => 0];
$mCount = count($outlookEventsByDate[$dayDate] ?? []);
$isWorkday = in_array($dayN, $activeWorkdays, true);
$weekDays[] = [
'date' => $dayDate,
@@ -224,6 +232,8 @@ for ($i = 0; $i < 7; $i++) {
'month_short' => $dutchShortMonths[$dayM],
'is_today' => ($dayDate === $today),
'is_selected' => ($dayDate === $selectedDate),
'is_workday' => $isWorkday,
'is_weekend' => !$isWorkday,
'task_count' => $tStat['total'],
'completed_count' => $tStat['completed'],
'meeting_count' => $mCount,
@@ -425,14 +435,27 @@ for ($h = $startHour; $h <= $endHour; $h++) {
<div class="fociz-card p-6 sm:p-7 relative overflow-hidden">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div>
<div class="flex items-center space-x-2 text-indigo-600 dark:text-indigo-400 text-xs font-bold uppercase tracking-wider mb-1">
<div class="flex flex-wrap items-center gap-2 text-indigo-600 dark:text-indigo-400 text-xs font-bold uppercase tracking-wider mb-1">
<div class="flex items-center space-x-1.5">
<i data-lucide="calendar" class="w-4 h-4"></i>
<span><?= htmlspecialchars($formattedSelectedDate) ?></span>
</div>
<?php if ($isTodaySelected): ?>
<span class="px-2.5 py-0.5 rounded-full text-[10px] font-extrabold bg-indigo-50 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300 ring-1 ring-indigo-500/30">VANDAAG</span>
<?php else: ?>
<span class="px-2.5 py-0.5 rounded-full text-[10px] font-bold bg-slate-100 dark:bg-slate-800/80 text-slate-600 dark:text-slate-400">GEKOZEN DAG</span>
<?php endif; ?>
<?php if ($isSelectedDateWorkday): ?>
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-emerald-50 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300 ring-1 ring-emerald-500/30 flex items-center gap-1" title="Reguliere werkdag conform beheerinstellingen">
<i data-lucide="briefcase" class="w-3 h-3"></i>
<span>Werkdag</span>
</span>
<?php else: ?>
<span class="px-2 py-0.5 rounded-full text-[10px] font-extrabold bg-amber-50 text-amber-700 dark:bg-amber-500/15 dark:text-amber-400 ring-1 ring-amber-500/30 flex items-center gap-1" title="Weekend / Vrije dag conform beheerinstellingen">
<i data-lucide="coffee" class="w-3 h-3"></i>
<span>Weekend / Vrij</span>
</span>
<?php endif; ?>
</div>
<h1 class="text-2xl sm:text-3xl font-black tracking-tight text-slate-900 dark:text-white">
Fociz
@@ -961,18 +984,27 @@ for ($h = $startHour; $h <= $endHour; $h++) {
onclick="switchPlannerDate('<?= $wd['date'] ?>', event); return false;"
class="fociz-card-interactive group relative flex flex-col p-3 rounded-xl border transition-all text-left <?= $wd['is_selected']
? 'bg-gradient-to-b from-indigo-500/15 via-indigo-500/5 to-transparent dark:from-indigo-500/25 dark:via-indigo-500/10 dark:to-transparent border-indigo-500 ring-2 ring-indigo-500/40 shadow-sm'
: 'bg-slate-50/70 dark:bg-[#0c0f17]/50 border-slate-200/80 dark:border-slate-800/80 hover:border-indigo-300 dark:hover:border-indigo-600/50 hover:bg-white dark:hover:bg-[#111624] shadow-2xs' ?>"
: ($wd['is_workday']
? 'bg-slate-50/70 dark:bg-[#0c0f17]/50 border-slate-200/80 dark:border-slate-800/80 hover:border-indigo-300 dark:hover:border-indigo-600/50 hover:bg-white dark:hover:bg-[#111624] shadow-2xs'
: 'bg-slate-100/60 dark:bg-[#080b11]/60 border-dashed border-amber-300/60 dark:border-amber-900/40 hover:border-amber-400 dark:hover:border-amber-700 hover:bg-white dark:hover:bg-[#0f141f] shadow-2xs') ?>"
>
<div class="flex items-center justify-between mb-1">
<span class="text-[10px] font-bold uppercase tracking-wider <?= $wd['is_selected'] ? 'text-indigo-600 dark:text-indigo-400' : 'text-slate-500 dark:text-slate-400' ?>">
<span class="text-[10px] font-bold uppercase tracking-wider <?= $wd['is_selected'] ? 'text-indigo-600 dark:text-indigo-400' : ($wd['is_workday'] ? 'text-slate-500 dark:text-slate-400' : 'text-slate-400 dark:text-slate-500') ?>">
<?= $wd['short_name'] ?>
</span>
<div class="flex items-center gap-1">
<?php if ($wd['is_weekend']): ?>
<span class="px-1.5 py-0.5 rounded text-[8px] font-extrabold uppercase bg-amber-50 dark:bg-amber-950/60 text-amber-700 dark:text-amber-400 border border-amber-200/80 dark:border-amber-800/60" title="Weekend / Niet-werkdag conform beheerinstellingen">
Weekend
</span>
<?php endif; ?>
<?php if ($wd['is_today']): ?>
<span class="px-1.5 py-0.5 rounded-full text-[8px] font-extrabold uppercase bg-indigo-600 text-white shadow-xs">
Nu
</span>
<?php endif; ?>
</div>
</div>
<div class="flex items-baseline space-x-1 mb-2">
<span class="text-xl font-black <?= $wd['is_selected'] ? 'text-indigo-600 dark:text-indigo-400' : 'text-slate-900 dark:text-white' ?>">
@@ -1012,14 +1044,25 @@ for ($h = $startHour; $h <= $endHour; $h++) {
<!-- 3. Sub-header van de Tijdlijn voor de geselecteerde dag -->
<div class="flex items-center justify-between pt-3 border-t border-slate-100 dark:border-slate-800/80">
<div class="flex items-center space-x-2">
<div class="w-7 h-7 rounded-lg bg-indigo-50 dark:bg-indigo-950/50 text-indigo-600 dark:text-indigo-400 flex items-center justify-center font-bold">
<i data-lucide="clock" class="w-3.5 h-3.5"></i>
<div class="w-7 h-7 rounded-lg <?= $isSelectedDateWeekend ? 'bg-amber-50 dark:bg-amber-950/50 text-amber-600 dark:text-amber-400' : 'bg-indigo-50 dark:bg-indigo-950/50 text-indigo-600 dark:text-indigo-400' ?> flex items-center justify-center font-bold">
<i data-lucide="<?= $isSelectedDateWeekend ? 'coffee' : 'clock' ?>" class="w-3.5 h-3.5"></i>
</div>
<div>
<div class="flex items-center space-x-2">
<h4 class="text-xs font-bold text-slate-900 dark:text-white uppercase tracking-wider">
Tijdlijn & Time-blocking voor <?= htmlspecialchars($formattedSelectedDate) ?>
</h4>
<p class="text-[10px] text-slate-400">Urenindeling & synchronisatie met Outlook</p>
<?php if ($isSelectedDateWeekend): ?>
<span class="px-2 py-0.5 rounded-full text-[9px] font-extrabold uppercase bg-amber-50 text-amber-700 dark:bg-amber-950/60 dark:text-amber-300 border border-amber-200/80 dark:border-amber-800/60">
Weekend / Vrije Dag
</span>
<?php endif; ?>
</div>
<p class="text-[10px] text-slate-400">
<?= $isSelectedDateWeekend
? 'Geen reguliere kantooruren gepland conform beheerinstellingen &bull; Vrije dag'
: 'Urenindeling & synchronisatie met Outlook' ?>
</p>
</div>
</div>
<span class="text-xs text-slate-500 dark:text-slate-400 font-mono font-semibold bg-slate-100 dark:bg-slate-800/70 px-2.5 py-1 rounded-lg">
@@ -1059,6 +1102,9 @@ for ($h = $startHour; $h <= $endHour; $h++) {
<div class="flex items-center justify-between text-[10px]">
<div class="flex items-center space-x-2">
<span class="font-bold text-slate-600 dark:text-slate-400 uppercase tracking-wider">Dagcapaciteit</span>
<?php if ($isSelectedDateWeekend): ?>
<span class="px-1.5 py-0.5 rounded text-[9px] font-extrabold uppercase bg-amber-100 text-amber-700 dark:bg-amber-950/70 dark:text-amber-400">Weekend</span>
<?php endif; ?>
<span class="font-mono font-black <?= $capLabelColor ?>"><?= $capPercent ?>% · <?= $capLabel ?></span>
</div>
<div class="flex items-center space-x-3 font-mono text-slate-500 dark:text-slate-400">