Add visual worker calendar and floating lunch break system
- Monthly calendar grid per worker with colour-coded day status - Override types: vacation, sick, custom hours, other, day-off - Date-range override support via modal - Floating 30-min lunch break: finds slot closest to preferred time within configurable window, adapts to existing bookings - Lunch break only applies for shifts >= 6 hours - Lunch slot shown in admin calendar; blocked in booking availability - DB migrations: absence_type/note on worker_schedule_overrides, lunch_window_start/end/preferred_time on workers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,10 @@ $route['workers'] = 'admin/workers';
|
||||
$route['workers/add-worker'] = 'admin/add_worker';
|
||||
$route['workers/update-worker/(:any)'] = 'admin/update_worker/$1';
|
||||
$route['workers/worker-process'] = 'admin/worker_process';
|
||||
$route['workers/worker-calendar'] = 'admin/worker_calendar';
|
||||
$route['workers/worker-calendar/(:any)'] = 'admin/worker_calendar/$1';
|
||||
$route['workers/store-calendar-override'] = 'admin/store_calendar_override';
|
||||
$route['workers/delete-calendar-override'] = 'admin/delete_calendar_override';
|
||||
|
||||
$route['bookings'] = 'admin/bookings';
|
||||
$route['bookings/add-booking'] = 'admin/add_booking';
|
||||
|
||||
@@ -651,7 +651,10 @@ class Admin extends CI_Controller {
|
||||
'service_category_id' => $_POST['service_category_id'],
|
||||
'is_active' => $_POST['is_active'],
|
||||
'google_calendar_id' => isset($_POST['google_calendar_id']) ? $_POST['google_calendar_id'] : '',
|
||||
'ntfy_topic' => isset($_POST['ntfy_topic']) ? $_POST['ntfy_topic'] : ''
|
||||
'ntfy_topic' => isset($_POST['ntfy_topic']) ? $_POST['ntfy_topic'] : '',
|
||||
'lunch_window_start' => !empty($_POST['lunch_window_start']) ? $_POST['lunch_window_start'] : null,
|
||||
'lunch_window_end' => !empty($_POST['lunch_window_end']) ? $_POST['lunch_window_end'] : null,
|
||||
'lunch_preferred_time' => !empty($_POST['lunch_preferred_time']) ? $_POST['lunch_preferred_time'] : null,
|
||||
);
|
||||
$this->Service_model->updateWorker($_POST['worker_id'], $workerArray);
|
||||
$this->Log_model->addLog('workers', $data['currentUser']->username.' módosította a(z) '.$_POST['worker_name'].' nevű dolgozót ('.implode(',',$workerArray).')', $data['currentUser']->username);
|
||||
@@ -798,6 +801,24 @@ class Admin extends CI_Controller {
|
||||
|
||||
if(isset($_GET['delete-booking'])){
|
||||
$selectedBooking = $this->Service_model->getBookingById($_GET['delete-booking']);
|
||||
// Google Calendar: delete events before removing booking
|
||||
$this->load->library('GoogleCalendar');
|
||||
$this->config->load('google_calendar');
|
||||
$delWorker = $this->Service_model->getWorkerById($selectedBooking->worker_id);
|
||||
if(!empty($selectedBooking->gcal_event_id_worker) && $delWorker && !empty($delWorker->google_calendar_id)){
|
||||
$this->googlecalendar->deleteEvent($delWorker->google_calendar_id, $selectedBooking->gcal_event_id_worker);
|
||||
}
|
||||
$evelinCalIdDel = $this->config->item('gcal_evelin_calendar_id');
|
||||
if(!empty($selectedBooking->gcal_event_id_owner) && !empty($evelinCalIdDel)){
|
||||
$this->googlecalendar->deleteEvent($evelinCalIdDel, $selectedBooking->gcal_event_id_owner);
|
||||
}
|
||||
// ntfy push notification
|
||||
$this->_ntfy(
|
||||
'Admin deleted booking: ' . $selectedBooking->guest_name,
|
||||
'Worker: ' . ($delWorker ? $delWorker->worker_name : '?') . "\n" .
|
||||
'Date: ' . $selectedBooking->booking_date . ' ' . $selectedBooking->booking_start_time,
|
||||
($delWorker && isset($delWorker->ntfy_topic)) ? $delWorker->ntfy_topic : null
|
||||
);
|
||||
$this->Service_model->deleteBooking($_GET['delete-booking']);
|
||||
$this->Log_model->addLog('bookings', $data['currentUser']->username.' törölte a(z) '.$_GET['delete-booking'].' számú foglalást ('.$selectedBooking->guest_name.', '.$selectedBooking->guest_email.', '.$selectedBooking->guest_phone.', '.$selectedBooking->booking_date.', '.$selectedBooking->booking_start_time.', '.$selectedBooking->booking_finish_time.', '.$selectedBooking->service_ids.')', $data['currentUser']->username);
|
||||
header('Location:'.SITEURL.'bookings');
|
||||
@@ -961,10 +982,6 @@ class Admin extends CI_Controller {
|
||||
}
|
||||
|
||||
if(isset($_POST['updateBookingManual'])){
|
||||
$booking_date = new DateTime(date('Y-m-d', strtotime($_POST['booking_date'])));
|
||||
$end_booking_date = new DateTime(date('Y-m-d', strtotime($_POST['end_booking_date'])));
|
||||
$diff = $booking_date->diff($end_booking_date);
|
||||
|
||||
$selectedServiceArray = array();
|
||||
|
||||
if($_POST['service_ids'] != ''){
|
||||
@@ -972,28 +989,6 @@ class Admin extends CI_Controller {
|
||||
|
||||
}
|
||||
|
||||
if($_POST['booking_date'] != $_POST['end_booking_date'] && $booking_date <= $end_booking_date){
|
||||
for($index = 0; $index <= $diff->days; $index++){
|
||||
$selectedDate = date('Y-m-d', strtotime($_POST['booking_date']. ' + '.$index.' days'));
|
||||
|
||||
|
||||
$bookingArray = array(
|
||||
'guest_name' => $_POST['guest_name'],
|
||||
'guest_email' => $_POST['guest_email'],
|
||||
'guest_phone' => $_POST['guest_phone'],
|
||||
'worker_id' => $_POST['worker_id'],
|
||||
'booking_date' => $selectedDate,
|
||||
'booking_start_time' => $_POST['booking_start_time'],
|
||||
'booking_finish_time' => $_POST['booking_finish_time'],
|
||||
'service_ids' => serialize($selectedServiceArray),
|
||||
'guest_confirmed' => '0',
|
||||
'guest_confirm_code' => '1234'
|
||||
);
|
||||
|
||||
$this->Service_model->createBooking($bookingArray);
|
||||
}
|
||||
}
|
||||
else{
|
||||
$bookingArray = array(
|
||||
'guest_name' => $_POST['guest_name'],
|
||||
'guest_email' => $_POST['guest_email'],
|
||||
@@ -1006,10 +1001,53 @@ class Admin extends CI_Controller {
|
||||
'guest_confirmed' => '0',
|
||||
'guest_confirm_code' => '1234'
|
||||
);
|
||||
|
||||
$oldBooking = $this->Service_model->getBookingById($_POST['booking_id']);
|
||||
$this->Service_model->updateBooking($_POST['booking_id'], $bookingArray);
|
||||
$this->Log_model->addLog('bookings', $data['currentUser']->username.' módosította a foglalását a(z) '.$bookingArray['guest_name'].' nevű vendégnek ('.implode(', ', $bookingArray).')', $data['currentUser']->username);
|
||||
// Google Calendar: delete old events, create new ones
|
||||
$this->load->library('GoogleCalendar');
|
||||
$this->config->load('google_calendar');
|
||||
$evelinCalId = $this->config->item('gcal_evelin_calendar_id');
|
||||
if($oldBooking){
|
||||
$oldWorker = $this->Service_model->getWorkerById($oldBooking->worker_id);
|
||||
if(!empty($oldBooking->gcal_event_id_worker) && $oldWorker && !empty($oldWorker->google_calendar_id)){
|
||||
$this->googlecalendar->deleteEvent($oldWorker->google_calendar_id, $oldBooking->gcal_event_id_worker);
|
||||
}
|
||||
if(!empty($oldBooking->gcal_event_id_owner) && !empty($evelinCalId)){
|
||||
$this->googlecalendar->deleteEvent($evelinCalId, $oldBooking->gcal_event_id_owner);
|
||||
}
|
||||
}
|
||||
$gcalWorker = $this->Service_model->getWorkerById($bookingArray['worker_id']);
|
||||
$gcalServices = unserialize($bookingArray['service_ids']);
|
||||
$gcalServiceArray = array();
|
||||
if(is_array($gcalServices)){
|
||||
foreach($gcalServices as $sid){
|
||||
$svc = $this->Service_model->getServiceById($sid);
|
||||
if(is_object($svc)) $gcalServiceArray[] = $svc;
|
||||
}
|
||||
}
|
||||
$gcalServiceNames = implode(', ', array_map(function($s){ return $s->service_name_no; }, $gcalServiceArray));
|
||||
$gcalTitle = $gcalWorker->worker_name . ' — ' . $gcalServiceNames . ' — ' . $bookingArray['guest_name'];
|
||||
$gcalDescription = 'Guest: ' . $bookingArray['guest_name'] . "\nPhone: " . $bookingArray['guest_phone'] . "\nEmail: " . $bookingArray['guest_email'];
|
||||
$gcalStart = $bookingArray['booking_date'] . 'T' . $bookingArray['booking_start_time'];
|
||||
$gcalEnd = $bookingArray['booking_date'] . 'T' . $bookingArray['booking_finish_time'];
|
||||
$newWorkerEventId = null;
|
||||
$newOwnerEventId = null;
|
||||
if(!empty($gcalWorker->google_calendar_id)){
|
||||
$newWorkerEventId = $this->googlecalendar->createEvent($gcalWorker->google_calendar_id, $gcalTitle, $gcalDescription, $gcalStart, $gcalEnd);
|
||||
}
|
||||
if(!empty($evelinCalId)){
|
||||
$newOwnerEventId = $this->googlecalendar->createEvent($evelinCalId, $gcalTitle, $gcalDescription, $gcalStart, $gcalEnd);
|
||||
}
|
||||
$this->Service_model->updateBookingCalEvents($_POST['booking_id'], $newWorkerEventId, $newOwnerEventId);
|
||||
// ntfy push notification
|
||||
$this->_ntfy(
|
||||
'Admin modified booking: ' . $bookingArray['guest_name'],
|
||||
'Worker: ' . $gcalWorker->worker_name . "\n" .
|
||||
'Date: ' . $bookingArray['booking_date'] . ' ' . $bookingArray['booking_start_time'] . ' - ' . $bookingArray['booking_finish_time'] . "\n" .
|
||||
'Services: ' . $gcalServiceNames,
|
||||
isset($gcalWorker->ntfy_topic) ? $gcalWorker->ntfy_topic : null
|
||||
);
|
||||
|
||||
if($_POST['send_notification']){
|
||||
$worker = $this->Service_model->getWorkerById($bookingArray['worker_id']);
|
||||
@@ -1126,6 +1164,26 @@ class Admin extends CI_Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private function _ntfy($title, $message, $workerTopic = null){
|
||||
$this->config->load('google_calendar');
|
||||
$evelinTopic = $this->config->item('ntfy_topic');
|
||||
$topics = [];
|
||||
if(!empty($evelinTopic)) $topics[] = $evelinTopic;
|
||||
if(!empty($workerTopic) && $workerTopic !== $evelinTopic) $topics[] = $workerTopic;
|
||||
if(empty($topics)) return;
|
||||
foreach($topics as $topic){
|
||||
$ch = curl_init('https://ntfy.sh/' . $topic);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $message,
|
||||
CURLOPT_HTTPHEADER => ['Title: ' . $title, 'Priority: high', 'Tags: calendar'],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
]);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1143,9 +1201,157 @@ class Admin extends CI_Controller {
|
||||
|
||||
|
||||
|
||||
public function worker_calendar($worker_id = null) {
|
||||
$this->load->helper('url');
|
||||
$this->load->model('Service_model');
|
||||
$this->load->model('User_model');
|
||||
$this->load->model('Module_model');
|
||||
|
||||
if (!isset($_SESSION['username'])) {
|
||||
header("Location:".SITEURL."login");
|
||||
exit;
|
||||
}
|
||||
|
||||
$data['currentUser'] = $this->User_model->getUserByUsername($_SESSION['username']);
|
||||
|
||||
if ($this->User_model->is_user_admin($_SESSION['username'])) {
|
||||
$data['activeModules'] = $this->Module_model->getAllModule();
|
||||
} else {
|
||||
$data['activeModules'] = $this->Module_model->getUserModules($data['currentUser']->user_id);
|
||||
}
|
||||
|
||||
$data['workers'] = $this->Service_model->getAllWorkers();
|
||||
$data['selectedWorker'] = null;
|
||||
$data['overridesByDate'] = [];
|
||||
$data['scheduleRows'] = [];
|
||||
|
||||
$monthParam = $this->input->get('month');
|
||||
if ($monthParam && preg_match('/^\d{4}-\d{2}$/', $monthParam)) {
|
||||
list($year, $month) = explode('-', $monthParam);
|
||||
$year = (int)$year;
|
||||
$month = (int)$month;
|
||||
} else {
|
||||
$year = (int)date('Y');
|
||||
$month = (int)date('m');
|
||||
}
|
||||
$data['year'] = $year;
|
||||
$data['month'] = $month;
|
||||
|
||||
if ($worker_id) {
|
||||
$data['selectedWorker'] = $this->Service_model->getWorkerById($worker_id);
|
||||
$data['overridesByDate'] = $this->Service_model->getWorkerOverridesByMonth($worker_id, $year, $month);
|
||||
$data['scheduleRows'] = $this->Service_model->getWorkerSchedule($worker_id);
|
||||
}
|
||||
|
||||
// Compute lunch break slot for every working day of the month
|
||||
$data['lunchByDate'] = [];
|
||||
$sw = $data['selectedWorker'];
|
||||
if ($sw && !empty($sw->lunch_window_start) && !empty($sw->lunch_window_end)) {
|
||||
$monthBookings = $this->Service_model->getBookingsForWorkerMonth($worker_id, $year, $month);
|
||||
$scheduleMap = [];
|
||||
foreach ($data['scheduleRows'] as $row) {
|
||||
$scheduleMap[$row->weekday] = $row;
|
||||
}
|
||||
$daysInMonth = (int)date('t', mktime(0, 0, 0, $month, 1, $year));
|
||||
for ($d = 1; $d <= $daysInMonth; $d++) {
|
||||
$dateStr = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-' . str_pad($d, 2, '0', STR_PAD_LEFT);
|
||||
$override = isset($data['overridesByDate'][$dateStr]) ? $data['overridesByDate'][$dateStr] : null;
|
||||
if ($override && $override->is_day_off) continue;
|
||||
if ($override && !$override->is_day_off && $override->end_time) {
|
||||
$shiftStart = new DateTime($dateStr . ' ' . $override->start_time);
|
||||
$shiftEnd = new DateTime($dateStr . ' ' . $override->end_time);
|
||||
} else {
|
||||
$phpWeekday = (int)date('w', strtotime($dateStr));
|
||||
if (!isset($scheduleMap[$phpWeekday])) continue;
|
||||
$shiftStart = new DateTime($dateStr . ' ' . $scheduleMap[$phpWeekday]->start_time);
|
||||
$shiftEnd = new DateTime($dateStr . ' ' . $scheduleMap[$phpWeekday]->end_time);
|
||||
}
|
||||
$dayBookings = isset($monthBookings[$dateStr]) ? $monthBookings[$dateStr] : [];
|
||||
$lunch = $this->Service_model->computeLunchBreak(
|
||||
$sw->lunch_window_start, $sw->lunch_window_end,
|
||||
$dateStr, $dayBookings, $shiftEnd, $shiftStart,
|
||||
isset($sw->lunch_preferred_time) ? $sw->lunch_preferred_time : null
|
||||
);
|
||||
if ($lunch) $data['lunchByDate'][$dateStr] = $lunch;
|
||||
}
|
||||
}
|
||||
|
||||
$data['pageTitle'] = 'Munkaidő naptár';
|
||||
$this->load->view('admin/worker_calendar', $data);
|
||||
}
|
||||
|
||||
public function store_calendar_override() {
|
||||
$this->load->model('Service_model');
|
||||
$this->load->helper('url');
|
||||
|
||||
if (!isset($_SESSION['username'])) {
|
||||
header("Location:".SITEURL."login");
|
||||
exit;
|
||||
}
|
||||
|
||||
$worker_id = $this->input->post('worker_id');
|
||||
$start_date = $this->input->post('start_date');
|
||||
$end_date = $this->input->post('end_date') ?: $start_date;
|
||||
$type = $this->input->post('override_type');
|
||||
$start_time = $this->input->post('start_time');
|
||||
$end_time = $this->input->post('end_time');
|
||||
$note = $this->input->post('note');
|
||||
|
||||
$current = strtotime($start_date);
|
||||
$last = strtotime($end_date);
|
||||
$guard = 0;
|
||||
|
||||
while ($current <= $last && $guard < 366) {
|
||||
$dateStr = date('Y-m-d', $current);
|
||||
if ($type === 'remove') {
|
||||
$this->Service_model->deleteWorkerScheduleOverrideByDate($worker_id, $dateStr);
|
||||
} else {
|
||||
$data = [];
|
||||
if ($type === 'day_off') {
|
||||
$data['is_day_off'] = 1;
|
||||
$data['absence_type'] = null;
|
||||
$data['start_time'] = null;
|
||||
$data['end_time'] = null;
|
||||
$data['note'] = null;
|
||||
} elseif ($type === 'custom') {
|
||||
$data['is_day_off'] = 0;
|
||||
$data['absence_type'] = 'custom';
|
||||
$data['start_time'] = $start_time;
|
||||
$data['end_time'] = $end_time;
|
||||
$data['note'] = $note;
|
||||
} else {
|
||||
// vacation, sick, other
|
||||
$data['is_day_off'] = 1;
|
||||
$data['absence_type'] = $type;
|
||||
$data['start_time'] = null;
|
||||
$data['end_time'] = null;
|
||||
$data['note'] = $note;
|
||||
}
|
||||
$this->Service_model->upsertWorkerScheduleOverride($worker_id, $dateStr, $data);
|
||||
}
|
||||
$current = strtotime('+1 day', $current);
|
||||
$guard++;
|
||||
}
|
||||
|
||||
$monthParam = date('Y-m', strtotime($start_date));
|
||||
redirect('workers/worker-calendar/' . $worker_id . '?month=' . $monthParam);
|
||||
}
|
||||
|
||||
public function delete_calendar_override() {
|
||||
$this->load->model('Service_model');
|
||||
$this->load->helper('url');
|
||||
|
||||
if (!isset($_SESSION['username'])) {
|
||||
header("Location:".SITEURL."login");
|
||||
exit;
|
||||
}
|
||||
|
||||
$worker_id = $this->input->post('worker_id');
|
||||
$date = $this->input->post('date');
|
||||
|
||||
$this->Service_model->deleteWorkerScheduleOverrideByDate($worker_id, $date);
|
||||
$monthParam = date('Y-m', strtotime($date));
|
||||
redirect('workers/worker-calendar/' . $worker_id . '?month=' . $monthParam);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,10 +96,8 @@ class Service_model extends CI_Model {
|
||||
|
||||
public function updateBooking($booking_id, $bookingArray){
|
||||
$this->load->database();
|
||||
|
||||
foreach($bookingArray as $propertyKey => $propertyValue){
|
||||
$query = $this->db->query("UPDATE bookings SET ".$propertyKey." = '".$propertyValue."' WHERE booking_id = '".$booking_id."';");
|
||||
}
|
||||
$this->db->where('booking_id', $booking_id);
|
||||
$this->db->update('bookings', $bookingArray);
|
||||
}
|
||||
|
||||
public function getAllServiceByServiceType($service_type, $lang){
|
||||
@@ -490,6 +488,67 @@ class Service_model extends CI_Model {
|
||||
log_message('error', '⏳ Min start (next 15min +1h, Europe/Oslo): ' . $dayStartTime->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
// --- Lunch break: find free 30-min slot closest to preferred time within worker's lunch window ---
|
||||
$lunchBlockStart = null;
|
||||
$lunchBlockEnd = null;
|
||||
|
||||
$workerRow = $this->db->query("SELECT lunch_window_start, lunch_window_end, lunch_preferred_time FROM workers WHERE worker_id = '$worker_id' LIMIT 1")->row();
|
||||
$shiftSeconds = $finishTime->getTimestamp() - $dayStartTime->getTimestamp();
|
||||
if ($workerRow && $workerRow->lunch_window_start && $workerRow->lunch_window_end && $shiftSeconds >= 6 * 3600) {
|
||||
$allBookings = $this->db->query("
|
||||
SELECT booking_start_time, booking_finish_time FROM bookings
|
||||
WHERE booking_date = '$selectedDate' AND worker_id = '$worker_id'
|
||||
")->result();
|
||||
|
||||
$lunchWinStart = new DateTime($selectedDate . ' ' . $workerRow->lunch_window_start);
|
||||
$lunchWinEnd = new DateTime($selectedDate . ' ' . $workerRow->lunch_window_end);
|
||||
$lunchDuration = new DateInterval('PT30M');
|
||||
$lunchStep = new DateInterval('PT15M');
|
||||
|
||||
// Clamp window to actual shift hours
|
||||
if ($lunchWinStart < $dayStartTime) $lunchWinStart = clone $dayStartTime;
|
||||
if ($lunchWinEnd > $finishTime) $lunchWinEnd = clone $finishTime;
|
||||
|
||||
$scanEnd = (clone $lunchWinEnd)->sub($lunchDuration);
|
||||
|
||||
$isFree = function($c) use ($lunchDuration, $allBookings, $selectedDate) {
|
||||
$cEnd = (clone $c)->add($lunchDuration);
|
||||
foreach ($allBookings as $bk) {
|
||||
$bkS = new DateTime($selectedDate . ' ' . $bk->booking_start_time);
|
||||
$bkE = new DateTime($selectedDate . ' ' . $bk->booking_finish_time);
|
||||
if ($bkS < $cEnd && $bkE > $c) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Collect free slots inside window, pick closest to preferred time
|
||||
$freeSlots = [];
|
||||
for ($c = clone $lunchWinStart; $c <= $scanEnd; $c->add($lunchStep)) {
|
||||
if ($isFree($c)) $freeSlots[] = clone $c;
|
||||
}
|
||||
|
||||
if (!empty($freeSlots)) {
|
||||
$prefDt = $workerRow->lunch_preferred_time
|
||||
? new DateTime($selectedDate . ' ' . $workerRow->lunch_preferred_time)
|
||||
: clone $lunchWinStart;
|
||||
$best = null;
|
||||
$bestDiff = PHP_INT_MAX;
|
||||
foreach ($freeSlots as $slot) {
|
||||
$diff = abs($slot->getTimestamp() - $prefDt->getTimestamp());
|
||||
if ($diff < $bestDiff) { $bestDiff = $diff; $best = $slot; }
|
||||
}
|
||||
$lunchBlockStart = $best;
|
||||
$lunchBlockEnd = (clone $best)->add($lunchDuration);
|
||||
} else {
|
||||
// Fallback: search after the window
|
||||
for ($c = clone $lunchWinEnd; ; $c->add($lunchStep)) {
|
||||
$candEnd = (clone $c)->add($lunchDuration);
|
||||
if ($candEnd > $finishTime) break;
|
||||
if ($isFree($c)) { $lunchBlockStart = clone $c; $lunchBlockEnd = $candEnd; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$interval = new DateInterval('PT15M');
|
||||
list($h, $m) = explode(':', $servicelength);
|
||||
$serviceDuration = new DateInterval('PT' . (int)$h . 'H' . (int)$m . 'M');
|
||||
@@ -511,7 +570,8 @@ class Service_model extends CI_Model {
|
||||
)
|
||||
");
|
||||
|
||||
if ($bookingQuery->num_rows() == 0) {
|
||||
$overlapsLunch = $lunchBlockStart && ($current < $lunchBlockEnd) && ($slotEnd > $lunchBlockStart);
|
||||
if ($bookingQuery->num_rows() == 0 && !$overlapsLunch) {
|
||||
$availableTimeArray[] = $startTimeStr;
|
||||
}
|
||||
|
||||
@@ -609,6 +669,120 @@ class Service_model extends CI_Model {
|
||||
$this->db->insert('worker_schedule_overrides', $override);
|
||||
}
|
||||
|
||||
public function getBookingsForWorkerMonth($worker_id, $year, $month) {
|
||||
$this->load->database();
|
||||
$yearMonth = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT);
|
||||
$query = $this->db->query("
|
||||
SELECT booking_date, booking_start_time, booking_finish_time FROM bookings
|
||||
WHERE worker_id = ? AND DATE_FORMAT(booking_date,'%Y-%m') = ?
|
||||
ORDER BY booking_date, booking_start_time
|
||||
", [$worker_id, $yearMonth]);
|
||||
$result = [];
|
||||
foreach ($query->result() as $row) {
|
||||
$result[$row->booking_date][] = $row;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function computeLunchBreak($winStart, $winEnd, $date, $dayBookings, $shiftEnd, $shiftStart = null, $preferredTime = null) {
|
||||
// Lunch break only applies for shifts of 6+ hours
|
||||
if ($shiftStart && ($shiftEnd->getTimestamp() - $shiftStart->getTimestamp()) < 6 * 3600) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lunchDuration = new DateInterval('PT30M');
|
||||
$lunchStep = new DateInterval('PT15M');
|
||||
$winStartDt = new DateTime($date . ' ' . $winStart);
|
||||
$winEndDt = new DateTime($date . ' ' . $winEnd);
|
||||
|
||||
// Clamp window to actual shift hours
|
||||
if ($shiftStart && $winStartDt < $shiftStart) $winStartDt = clone $shiftStart;
|
||||
if ($winEndDt > $shiftEnd) $winEndDt = clone $shiftEnd;
|
||||
|
||||
$scanEnd = (clone $winEndDt)->sub($lunchDuration);
|
||||
|
||||
$isFree = function($candidate) use ($lunchDuration, $dayBookings, $date) {
|
||||
$candEnd = (clone $candidate)->add($lunchDuration);
|
||||
foreach ($dayBookings as $bk) {
|
||||
$bkS = new DateTime($date . ' ' . $bk->booking_start_time);
|
||||
$bkE = new DateTime($date . ' ' . $bk->booking_finish_time);
|
||||
if ($bkS < $candEnd && $bkE > $candidate) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Collect all free candidate slots within window
|
||||
$freeSlots = [];
|
||||
for ($c = clone $winStartDt; $c <= $scanEnd; $c->add($lunchStep)) {
|
||||
if ($isFree($c)) $freeSlots[] = clone $c;
|
||||
}
|
||||
|
||||
if (!empty($freeSlots)) {
|
||||
// Pick the slot closest to preferred time (or start of window if no preference)
|
||||
$prefDt = $preferredTime
|
||||
? new DateTime($date . ' ' . $preferredTime)
|
||||
: clone $winStartDt;
|
||||
$best = null;
|
||||
$bestDiff = PHP_INT_MAX;
|
||||
foreach ($freeSlots as $slot) {
|
||||
$diff = abs($slot->getTimestamp() - $prefDt->getTimestamp());
|
||||
if ($diff < $bestDiff) { $bestDiff = $diff; $best = $slot; }
|
||||
}
|
||||
$bestEnd = (clone $best)->add($lunchDuration);
|
||||
return ['start' => $best->format('H:i'), 'end' => $bestEnd->format('H:i')];
|
||||
}
|
||||
|
||||
// Fallback: search after the window
|
||||
for ($c = clone $winEndDt; ; $c->add($lunchStep)) {
|
||||
$candEnd = (clone $c)->add($lunchDuration);
|
||||
if ($candEnd > $shiftEnd) break;
|
||||
if ($isFree($c)) return ['start' => $c->format('H:i'), 'end' => $candEnd->format('H:i')];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getWorkerOverridesByMonth($worker_id, $year, $month) {
|
||||
$this->load->database();
|
||||
$yearMonth = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT);
|
||||
$query = $this->db->query(
|
||||
"SELECT * FROM worker_schedule_overrides WHERE worker_id = ? AND DATE_FORMAT(date,'%Y-%m') = ?",
|
||||
[$worker_id, $yearMonth]
|
||||
);
|
||||
$result = [];
|
||||
foreach ($query->result() as $row) {
|
||||
$result[$row->date] = $row;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getWorkerScheduleOverrideByDate($worker_id, $date) {
|
||||
$this->load->database();
|
||||
$this->db->where('worker_id', $worker_id);
|
||||
$this->db->where('date', $date);
|
||||
$this->db->limit(1);
|
||||
return $this->db->get('worker_schedule_overrides')->row();
|
||||
}
|
||||
|
||||
public function upsertWorkerScheduleOverride($worker_id, $date, $data) {
|
||||
$existing = $this->getWorkerScheduleOverrideByDate($worker_id, $date);
|
||||
if ($existing) {
|
||||
$this->db->where('worker_id', $worker_id);
|
||||
$this->db->where('date', $date);
|
||||
$this->db->update('worker_schedule_overrides', $data);
|
||||
} else {
|
||||
$data['worker_id'] = $worker_id;
|
||||
$data['date'] = $date;
|
||||
$this->db->insert('worker_schedule_overrides', $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteWorkerScheduleOverrideByDate($worker_id, $date) {
|
||||
$this->load->database();
|
||||
$this->db->where('worker_id', $worker_id);
|
||||
$this->db->where('date', $date);
|
||||
$this->db->delete('worker_schedule_overrides');
|
||||
}
|
||||
|
||||
public function getWorkerScheduleByDay($worker_id, $weekday) {
|
||||
$this->db->where('worker_id', $worker_id);
|
||||
$this->db->where('weekday', $weekday);
|
||||
|
||||
@@ -52,6 +52,18 @@
|
||||
<option value="0" <?php echo !$selectedItem->is_active?'selected':'';?>>nem</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="formRow">
|
||||
<label class="formTitle">Ebédszünet ablak kezdete</label>
|
||||
<input type="text" class="formInputBox" name="lunch_window_start" value="<?php echo isset($selectedItem->lunch_window_start) ? substr($selectedItem->lunch_window_start, 0, 5) : ''; ?>" placeholder="HH:MM (pl. 11:00)" pattern="[0-2][0-9]:[0-5][0-9]" inputmode="numeric">
|
||||
</div>
|
||||
<div class="formRow">
|
||||
<label class="formTitle">Ebédszünet ablak vége</label>
|
||||
<input type="text" class="formInputBox" name="lunch_window_end" value="<?php echo isset($selectedItem->lunch_window_end) ? substr($selectedItem->lunch_window_end, 0, 5) : ''; ?>" placeholder="HH:MM (pl. 14:00)" pattern="[0-2][0-9]:[0-5][0-9]" inputmode="numeric">
|
||||
</div>
|
||||
<div class="formRow">
|
||||
<label class="formTitle">Ebédszünet preferált időpont</label>
|
||||
<input type="text" class="formInputBox" name="lunch_preferred_time" value="<?php echo isset($selectedItem->lunch_preferred_time) ? substr($selectedItem->lunch_preferred_time, 0, 5) : ''; ?>" placeholder="HH:MM (pl. 12:00)" pattern="[0-2][0-9]:[0-5][0-9]" inputmode="numeric">
|
||||
</div>
|
||||
<div class="formRow">
|
||||
<label class="formTitle">Google Calendar ID</label>
|
||||
<input type="text" class="formInputBox" name="google_calendar_id" value="<?php echo isset($selectedItem->google_calendar_id) ? $selectedItem->google_calendar_id : '';?>">
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
<style>
|
||||
.cal-wrap { color: #fff; max-width: 820px; margin: 0 auto; padding: 10px; }
|
||||
.cal-header { display: flex; align-items: center; justify-content: space-between; margin: 14px 0 10px; }
|
||||
.cal-header a { color: #ccc; text-decoration: none; font-size: 18px; padding: 4px 10px; border: 1px solid #555; border-radius: 3px; }
|
||||
.cal-header a:hover { background: #333; }
|
||||
.cal-title { font-size: 20px; font-weight: bold; }
|
||||
.cal-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
||||
.cal-table th { background: #222; text-align: center; padding: 6px 2px; font-size: 13px; border: 1px solid #444; }
|
||||
.cal-table td { border: 1px solid #333; vertical-align: top; height: 72px; padding: 4px; cursor: pointer; position: relative; font-size: 12px; }
|
||||
.cal-table td:hover { background: #1a1a1a; }
|
||||
.cal-table td.empty { cursor: default; background: #111; }
|
||||
.cal-day-num { font-weight: bold; font-size: 13px; margin-bottom: 3px; }
|
||||
.cal-badge { display: inline-block; padding: 1px 5px; border-radius: 3px; font-size: 11px; color: #fff; margin-top: 2px; }
|
||||
.badge-vacation { background: #d9534f; }
|
||||
.badge-sick { background: #f0ad4e; color: #222; }
|
||||
.badge-custom { background: #5bc0de; color: #222; }
|
||||
.badge-other { background: #9b59b6; }
|
||||
.badge-dayoff { background: #555; }
|
||||
.badge-working { background: #5cb85c; }
|
||||
.badge-noshift { background: #333; color: #888; }
|
||||
.cal-today { outline: 2px solid #fff; outline-offset: -2px; }
|
||||
|
||||
/* Legend */
|
||||
.cal-legend { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0; font-size: 12px; }
|
||||
.cal-legend-item { display: flex; align-items: center; gap: 5px; }
|
||||
.cal-legend-dot { width: 14px; height: 14px; border-radius: 2px; display: inline-block; }
|
||||
|
||||
/* Modal */
|
||||
#calModal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.65); z-index: 9999; }
|
||||
#calModalBox { background: #1e1e1e; border: 1px solid #555; border-radius: 6px; max-width: 400px; margin: 80px auto; padding: 24px; color: #fff; position: relative; }
|
||||
#calModalBox h3 { margin: 0 0 14px; font-size: 17px; }
|
||||
#calModalBox label { display: block; margin: 10px 0 3px; font-size: 13px; color: #ccc; }
|
||||
#calModalBox select, #calModalBox input[type="text"], #calModalBox input[type="date"], #calModalBox textarea {
|
||||
width: 100%; background: #2a2a2a; color: #fff; border: 1px solid #555; padding: 6px 8px;
|
||||
border-radius: 3px; font-size: 13px; box-sizing: border-box;
|
||||
}
|
||||
#calModalBox textarea { height: 60px; resize: vertical; }
|
||||
.cal-modal-btns { display: flex; gap: 10px; margin-top: 16px; }
|
||||
.cal-btn-save { background: #5cb85c; color: #fff; border: none; padding: 8px 18px; border-radius: 3px; cursor: pointer; font-size: 14px; }
|
||||
.cal-btn-cancel { background: #555; color: #fff; border: none; padding: 8px 18px; border-radius: 3px; cursor: pointer; font-size: 14px; }
|
||||
</style>
|
||||
|
||||
<div class="cal-wrap">
|
||||
<div class="pageTitle"><?php echo $pageTitle; ?></div>
|
||||
|
||||
<!-- Worker selector -->
|
||||
<?php $monthParam = $year.'-'.str_pad($month, 2, '0', STR_PAD_LEFT); ?>
|
||||
<div style="margin: 14px 0;">
|
||||
<label style="color:#ccc; font-size:13px;">Dolgozó:</label>
|
||||
<select onchange="if(this.value) window.location='<?php echo site_url('workers/worker-calendar'); ?>/' + this.value + '?month=<?php echo $monthParam; ?>'" style="background:#2a2a2a;color:#fff;border:1px solid #555;padding:5px 8px;border-radius:3px;font-size:13px;">
|
||||
<option value="">-- Válassz dolgozót --</option>
|
||||
<?php foreach ($workers as $w): ?>
|
||||
<option value="<?php echo $w->worker_id; ?>"
|
||||
<?php echo ($selectedWorker && $selectedWorker->worker_id == $w->worker_id) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($w->worker_name); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<?php if ($selectedWorker):
|
||||
// Build schedule map: weekday (0=Sun..6=Sat) => row
|
||||
$scheduleMap = [];
|
||||
foreach ($scheduleRows as $row) {
|
||||
$scheduleMap[$row->weekday] = $row;
|
||||
}
|
||||
|
||||
// Calendar math
|
||||
$firstDay = mktime(0, 0, 0, $month, 1, $year);
|
||||
$daysInMonth = (int)date('t', $firstDay);
|
||||
// day-of-week of 1st (0=Sun, so Mon=1)
|
||||
$startDow = (int)date('w', $firstDay); // 0=Sun..6=Sat
|
||||
// Convert to Mon-based: Mon=0 .. Sun=6
|
||||
$startOffset = ($startDow + 6) % 7;
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$monthLabel = strftime('%Y %B', $firstDay); // Hungarian month names via locale
|
||||
// Fallback: PHP intl not guaranteed; build own table
|
||||
$monthNames = ['január','február','március','április','május','június','július','augusztus','szeptember','október','november','december'];
|
||||
$monthLabel = $year . ' ' . $monthNames[$month - 1];
|
||||
|
||||
$prevMonth = $month == 1 ? 12 : $month - 1;
|
||||
$prevYear = $month == 1 ? $year - 1 : $year;
|
||||
$nextMonth = $month == 12 ? 1 : $month + 1;
|
||||
$nextYear = $month == 12 ? $year + 1 : $year;
|
||||
$prevParam = $prevYear . '-' . str_pad($prevMonth, 2, '0', STR_PAD_LEFT);
|
||||
$nextParam = $nextYear . '-' . str_pad($nextMonth, 2, '0', STR_PAD_LEFT);
|
||||
$workerId = $selectedWorker->worker_id;
|
||||
?>
|
||||
|
||||
<!-- Month navigation -->
|
||||
<div class="cal-header">
|
||||
<a href="<?php echo site_url('workers/worker-calendar/'.$workerId).'?month='.$prevParam; ?>">← Előző</a>
|
||||
<span class="cal-title"><?php echo htmlspecialchars($monthLabel); ?></span>
|
||||
<a href="<?php echo site_url('workers/worker-calendar/'.$workerId).'?month='.$nextParam; ?>">Következő →</a>
|
||||
</div>
|
||||
|
||||
<!-- Legend -->
|
||||
<div class="cal-legend">
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#5cb85c;"></span> Munkanap</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#333;border:1px solid #555;"></span> Szabad nap</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#555;"></span> Napiszabadság</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#d9534f;"></span> Szabadság</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#f0ad4e;"></span> Betegszabadság</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#5bc0de;"></span> Egyedi munkaidő</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#9b59b6;"></span> Egyéb</div>
|
||||
<div class="cal-legend-item"><span class="cal-legend-dot" style="background:#888;"></span> Ebédszünet (auto)</div>
|
||||
</div>
|
||||
|
||||
<!-- Calendar grid -->
|
||||
<table class="cal-table">
|
||||
<tr>
|
||||
<th>H</th><th>K</th><th>Sz</th><th>Cs</th><th>P</th><th>Sz</th><th>V</th>
|
||||
</tr>
|
||||
<?php
|
||||
$cell = 0;
|
||||
$day = 1;
|
||||
$phpDow = [1,2,3,4,5,6,0]; // Mon=0 -> PHP weekday=1, ... Sun=6 -> PHP=0
|
||||
echo '<tr>';
|
||||
// Empty cells before first day
|
||||
for ($i = 0; $i < $startOffset; $i++) {
|
||||
echo '<td class="empty"></td>';
|
||||
$cell++;
|
||||
}
|
||||
while ($day <= $daysInMonth) {
|
||||
if ($cell % 7 === 0 && $cell > 0) {
|
||||
echo '</tr><tr>';
|
||||
}
|
||||
$colIndex = $cell % 7; // 0=Mon .. 6=Sun
|
||||
$phpWeekday = $phpDow[$colIndex]; // PHP weekday number
|
||||
|
||||
$dateStr = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-' . str_pad($day, 2, '0', STR_PAD_LEFT);
|
||||
$isToday = ($dateStr === $today);
|
||||
|
||||
// Determine status
|
||||
$override = isset($overridesByDate[$dateStr]) ? $overridesByDate[$dateStr] : null;
|
||||
$hasSchedule = isset($scheduleMap[$phpWeekday]);
|
||||
|
||||
$badgeHtml = '';
|
||||
$cellTitle = '';
|
||||
$overrideJson = 'null';
|
||||
|
||||
if ($override) {
|
||||
$absType = $override->absence_type ?? null;
|
||||
if ($absType === 'vacation') {
|
||||
$badgeHtml = '<span class="cal-badge badge-vacation">Szabadság</span>';
|
||||
$cellTitle = 'Szabadság';
|
||||
} elseif ($absType === 'sick') {
|
||||
$badgeHtml = '<span class="cal-badge badge-sick">Beteg</span>';
|
||||
$cellTitle = 'Betegszabadság';
|
||||
} elseif ($absType === 'custom') {
|
||||
$st = substr($override->start_time, 0, 5);
|
||||
$et = substr($override->end_time, 0, 5);
|
||||
$badgeHtml = '<span class="cal-badge badge-custom">'.$st.'–'.$et.'</span>';
|
||||
$cellTitle = 'Egyedi: '.$st.'–'.$et;
|
||||
} elseif ($absType === 'other') {
|
||||
$badgeHtml = '<span class="cal-badge badge-other">Egyéb</span>';
|
||||
$cellTitle = 'Egyéb';
|
||||
} else {
|
||||
// is_day_off=1, no absence_type = plain day off
|
||||
$badgeHtml = '<span class="cal-badge badge-dayoff">Szabad</span>';
|
||||
$cellTitle = 'Napi szabadság';
|
||||
}
|
||||
$overrideJson = json_encode([
|
||||
'is_day_off' => (int)$override->is_day_off,
|
||||
'absence_type' => $override->absence_type,
|
||||
'start_time' => $override->start_time ? substr($override->start_time, 0, 5) : '',
|
||||
'end_time' => $override->end_time ? substr($override->end_time, 0, 5) : '',
|
||||
'note' => $override->note ?? '',
|
||||
]);
|
||||
} elseif ($hasSchedule) {
|
||||
$sch = $scheduleMap[$phpWeekday];
|
||||
$st = substr($sch->start_time, 0, 5);
|
||||
$et = substr($sch->end_time, 0, 5);
|
||||
$badgeHtml = '<span class="cal-badge badge-working">'.$st.'–'.$et.'</span>';
|
||||
$cellTitle = 'Munkanap: '.$st.'–'.$et;
|
||||
} else {
|
||||
$badgeHtml = '<span class="cal-badge badge-noshift">Szabad</span>';
|
||||
$cellTitle = 'Nem dolgozik';
|
||||
}
|
||||
|
||||
$todayClass = $isToday ? ' cal-today' : '';
|
||||
$encodedDate = htmlspecialchars(json_encode($dateStr), ENT_QUOTES);
|
||||
$encodedWid = htmlspecialchars(json_encode($workerId), ENT_QUOTES);
|
||||
$encodedOver = htmlspecialchars($overrideJson, ENT_QUOTES);
|
||||
echo '<td class="'.$todayClass.'" title="'.htmlspecialchars($cellTitle).'" onclick="openModal('.$encodedDate.', '.$encodedWid.', '.$encodedOver.')">';
|
||||
echo '<div class="cal-day-num">'.$day.'</div>';
|
||||
echo $badgeHtml;
|
||||
if (isset($lunchByDate[$dateStr])) {
|
||||
$lb = $lunchByDate[$dateStr];
|
||||
echo '<span class="cal-badge" style="background:#888;display:block;margin-top:2px;">🍴 '.$lb['start'].'–'.$lb['end'].'</span>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
$day++;
|
||||
$cell++;
|
||||
}
|
||||
// Pad remaining cells
|
||||
while ($cell % 7 !== 0) {
|
||||
echo '<td class="empty"></td>';
|
||||
$cell++;
|
||||
}
|
||||
echo '</tr>';
|
||||
?>
|
||||
</table>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div id="calModal" onclick="closeModalBackdrop(event)">
|
||||
<div id="calModalBox">
|
||||
<h3 id="calModalTitle">Nap szerkesztése</h3>
|
||||
<form method="POST" action="<?php echo site_url('workers/store-calendar-override'); ?>" id="calModalForm">
|
||||
<input type="hidden" name="worker_id" id="modalWorkerId" value="">
|
||||
<input type="hidden" name="start_date" id="modalStartDate" value="">
|
||||
|
||||
<label>Típus:</label>
|
||||
<select name="override_type" id="modalType" onchange="onTypeChange()">
|
||||
<option value="remove">Felülírás törlése (alap munkaidő visszaállítása)</option>
|
||||
<option value="day_off">Szabadnap</option>
|
||||
<option value="vacation">Szabadság</option>
|
||||
<option value="sick">Betegszabadság</option>
|
||||
<option value="custom">Egyedi munkaidő</option>
|
||||
<option value="other">Egyéb</option>
|
||||
</select>
|
||||
|
||||
<div id="customTimeRow" style="display:none;">
|
||||
<label>Kezdés:</label>
|
||||
<input type="text" name="start_time" id="modalStartTime" placeholder="HH:MM" pattern="[0-2][0-9]:[0-5][0-9]" inputmode="numeric">
|
||||
<label>Vége:</label>
|
||||
<input type="text" name="end_time" id="modalEndTime" placeholder="HH:MM" pattern="[0-2][0-9]:[0-5][0-9]" inputmode="numeric">
|
||||
</div>
|
||||
|
||||
<div id="noteRow" style="display:none;">
|
||||
<label>Megjegyzés:</label>
|
||||
<textarea name="note" id="modalNote"></textarea>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:12px;">
|
||||
<label>
|
||||
<input type="checkbox" id="rangeCheck" onchange="toggleRange()" style="margin-right:5px;">
|
||||
Dátumtartományra alkalmazás
|
||||
</label>
|
||||
</div>
|
||||
<div id="rangeRow" style="display:none;">
|
||||
<label>Záró dátum:</label>
|
||||
<input type="date" name="end_date" id="modalEndDate">
|
||||
</div>
|
||||
|
||||
<div class="cal-modal-btns">
|
||||
<button type="submit" class="cal-btn-save">Mentés</button>
|
||||
<button type="button" class="cal-btn-cancel" onclick="closeModal()">Mégse</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openModal(date, workerId, existing) {
|
||||
document.getElementById('modalWorkerId').value = workerId;
|
||||
document.getElementById('modalStartDate').value = date;
|
||||
document.getElementById('calModalTitle').textContent = 'Nap: ' + date;
|
||||
document.getElementById('rangeCheck').checked = false;
|
||||
document.getElementById('rangeRow').style.display = 'none';
|
||||
document.getElementById('modalEndDate').value = '';
|
||||
|
||||
var sel = document.getElementById('modalType');
|
||||
var noteEl = document.getElementById('modalNote');
|
||||
noteEl.value = '';
|
||||
|
||||
if (existing) {
|
||||
var absType = existing.absence_type;
|
||||
if (absType === 'vacation') {
|
||||
sel.value = 'vacation';
|
||||
} else if (absType === 'sick') {
|
||||
sel.value = 'sick';
|
||||
} else if (absType === 'custom') {
|
||||
sel.value = 'custom';
|
||||
} else if (absType === 'other') {
|
||||
sel.value = 'other';
|
||||
} else if (existing.is_day_off == 1) {
|
||||
sel.value = 'day_off';
|
||||
} else {
|
||||
sel.value = 'remove';
|
||||
}
|
||||
document.getElementById('modalStartTime').value = existing.start_time || '';
|
||||
document.getElementById('modalEndTime').value = existing.end_time || '';
|
||||
noteEl.value = existing.note || '';
|
||||
} else {
|
||||
sel.value = 'remove';
|
||||
document.getElementById('modalStartTime').value = '';
|
||||
document.getElementById('modalEndTime').value = '';
|
||||
}
|
||||
|
||||
onTypeChange();
|
||||
document.getElementById('calModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('calModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function closeModalBackdrop(e) {
|
||||
if (e.target === document.getElementById('calModal')) {
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
|
||||
function onTypeChange() {
|
||||
var type = document.getElementById('modalType').value;
|
||||
document.getElementById('customTimeRow').style.display = (type === 'custom') ? 'block' : 'none';
|
||||
var showNote = (type === 'vacation' || type === 'sick' || type === 'other' || type === 'custom');
|
||||
document.getElementById('noteRow').style.display = showNote ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function toggleRange() {
|
||||
var show = document.getElementById('rangeCheck').checked;
|
||||
document.getElementById('rangeRow').style.display = show ? 'block' : 'none';
|
||||
if (!show) document.getElementById('modalEndDate').value = '';
|
||||
}
|
||||
</script>
|
||||
@@ -23,6 +23,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<a href="<?php echo SITEURL;?>workers/update-worker/<?php echo $resultItem->worker_id;?>"><i class="fas fa-edit"></i></a>
|
||||
| <a href="<?php echo SITEURL;?>workers/worker-calendar/<?php echo $resultItem->worker_id;?>"><i class="fas fa-calendar-alt"></i></a>
|
||||
| <a href="<?php echo site_url().'workers/worker-process?delete-worker='.$resultItem->worker_id;?>" onclick="return confirm('Valóban törölni szeretnéd?');"><i class="fas fa-trash-alt" style="font-size: 14px;color:#585858;"></i></a>
|
||||
</td>
|
||||
<td>#<?php echo $resultItem->worker_id;?></td>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
include(getcwd().'/application/views/includes/skeleton-top.php');
|
||||
include(getcwd().'/application/views/admin/includes/worker-calendar-form.php');
|
||||
include(getcwd().'/application/views/includes/skeleton-bottom.php');
|
||||
?>
|
||||
Reference in New Issue
Block a user