368 lines
19 KiB
PHP
368 lines
19 KiB
PHP
<?php
|
|
if (isset($_GET['action']) && $_GET['action'] == 'offline_report') {
|
|
include_once 'koneksi.php';
|
|
date_default_timezone_set('Asia/Jakarta');
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
if ($data && isset($data['activity'])) {
|
|
$activity = trim($data['activity']);
|
|
if (!empty($activity)) {
|
|
$stmt = $pdo->prepare("INSERT INTO schedule (date, kegiatan) VALUES (NOW(), ?)");
|
|
$stmt->execute([$activity]);
|
|
}
|
|
}
|
|
echo json_encode(["status" => "success"]);
|
|
exit;
|
|
}
|
|
|
|
include_once 'koneksi.php';
|
|
date_default_timezone_set('Asia/Jakarta');
|
|
// Create table if not exists
|
|
$pdo->exec("CREATE TABLE IF NOT EXISTS schedule (
|
|
id_schedule INT AUTO_INCREMENT PRIMARY KEY,
|
|
date TIMESTAMP NOT NULL,
|
|
kegiatan VARCHAR(255) NOT NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
|
// Current month/year
|
|
$month = isset($_GET['m']) ? (int)$_GET['m'] : (int)date('m');
|
|
$year = isset($_GET['y']) ? (int)$_GET['y'] : (int)date('Y');
|
|
if($month < 1){ $month = 12; $year--; }
|
|
if($month > 12){ $month = 1; $year++; }
|
|
|
|
$firstDay = mktime(0,0,0,$month,1,$year);
|
|
$daysInMonth = (int)date('t', $firstDay);
|
|
$startWeekday = (int)date('w', $firstDay);
|
|
$today = date('Y-m-d');
|
|
|
|
$pm = $month-1; $py = $year; if($pm<1){$pm=12;$py--;}
|
|
$nm = $month+1; $ny = $year; if($nm>12){$nm=1;$ny++;}
|
|
|
|
// Fetch events for this month
|
|
$stmt = $pdo->prepare("SELECT * FROM schedule WHERE MONTH(date)=? AND YEAR(date)=? ORDER BY date ASC");
|
|
$stmt->execute([$month, $year]);
|
|
$rows = $stmt->fetchAll();
|
|
|
|
$eventsByDate = [];
|
|
foreach($rows as $r){
|
|
$d = substr($r['date'], 0, 10);
|
|
if(!isset($eventsByDate[$d])) $eventsByDate[$d] = [];
|
|
$eventsByDate[$d][] = $r;
|
|
}
|
|
|
|
// Activities for selected month
|
|
$activities = $rows;
|
|
|
|
$monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December'];
|
|
|
|
// Unique color per date — no repeats within the month
|
|
$allColors = ['#4B49AC','#10b981','#f59e0b','#ec4899','#0891b2','#8b5cf6','#ef4444','#4b7bec',
|
|
'#059669','#d946ef','#0d9488','#e11d48','#7c3aed','#ca8a04','#2563eb','#dc2626',
|
|
'#16a34a','#9333ea','#0284c7','#c026d3','#ea580c','#4f46e5','#15803d','#be123c',
|
|
'#6d28d9','#b45309','#0369a1','#a21caf','#c2410c','#4338ca','#047857','#9f1239'];
|
|
$dateColors = [];
|
|
$ci = 0;
|
|
foreach($eventsByDate as $dateKey => $evts){
|
|
$dateColors[$dateKey] = $allColors[$ci % count($allColors)];
|
|
$ci++;
|
|
}
|
|
|
|
include 'header.php';
|
|
?>
|
|
<div class="main-panel">
|
|
<div class="content-wrapper">
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin">
|
|
<div class="row">
|
|
<div class="col-12 col-xl-8 mb-4 mb-xl-0">
|
|
<h3 class="font-weight-bold">Schedule</h3>
|
|
<h6 class="font-weight-normal mb-0">Activity schedule and upcoming agenda, <span class="text-primary">View all schedules here!</span></h6>
|
|
</div>
|
|
|
|
<div class="col-12 col-xl-4">
|
|
<div class="justify-content-end d-flex flex-wrap" style="gap:15px; align-items:center;">
|
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#modalExportSchedule">
|
|
<i class="mdi mdi-file-export mr-1"></i> Export Data
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin stretch-card">
|
|
<div class="card" style="border:1px solid #e5e9f0;box-shadow:none;border-radius:12px;">
|
|
<div class="card-body" style="padding:24px;">
|
|
<div class="sch-layout">
|
|
<!-- LEFT -->
|
|
<div class="sch-sidebar">
|
|
<div class="mini-cal-hdr">
|
|
<a href="?m=<?=$pm?>&y=<?=$py?>" class="mcn"><i class="mdi mdi-chevron-left"></i></a>
|
|
<span class="mct"><?= $monthNames[$month-1] ?> <?= $year ?></span>
|
|
<a href="?m=<?=$nm?>&y=<?=$ny?>" class="mcn"><i class="mdi mdi-chevron-right"></i></a>
|
|
</div>
|
|
<div class="mcg">
|
|
<?php foreach(['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] as $dn): ?>
|
|
<div class="mcgh"><?=$dn?></div>
|
|
<?php endforeach; ?>
|
|
<?php for($i=0;$i<$startWeekday;$i++): ?><div class="mcd empty"></div><?php endfor; ?>
|
|
<?php for($d=1;$d<=$daysInMonth;$d++):
|
|
$ds = sprintf('%04d-%02d-%02d',$year,$month,$d);
|
|
$isT = ($ds === $today);
|
|
$hasEv = isset($eventsByDate[$ds]);
|
|
$wd = ($startWeekday + $d - 1) % 7;
|
|
?>
|
|
<div class="mcd<?=$isT?' today':''?><?=$hasEv?' hev':''?><?=$wd===0?' sun':''?>"><?=$d?></div>
|
|
<?php endfor; ?>
|
|
</div>
|
|
|
|
<hr style="margin:18px 0;border-color:#f0f0f0;">
|
|
|
|
<p class="card-title mb-3" style="font-size:14px;"><i class="mdi mdi-format-list-checks mr-1 text-primary"></i>Activities for <?= $monthNames[$month-1] ?></p>
|
|
<div class="al">
|
|
<?php if(empty($activities)): ?>
|
|
<div style="text-align:center;color:#999;padding:30px 10px;font-size:13px;">
|
|
<i class="mdi mdi-calendar-blank" style="font-size:32px;display:block;margin-bottom:6px;color:#ddd;"></i>No activities yet
|
|
</div>
|
|
<?php else:
|
|
foreach($activities as $ev):
|
|
$dt = new DateTime($ev['date']);
|
|
$dateKey = substr($ev['date'], 0, 10);
|
|
$isToday = ($dateKey === $today);
|
|
$color = $dateColors[$dateKey] ?? '#4B49AC';
|
|
$jam = $dt->format('H:i');
|
|
?>
|
|
<div class="ali">
|
|
<div class="ald" style="background:<?=$color?>"></div>
|
|
<div style="flex:1;min-width:0;">
|
|
<div class="aldt">
|
|
<?= $isToday?'<span style="color:#4B49AC;font-weight:700;">Today</span> · ':'' ?>
|
|
<?= $dt->format('d M Y') ?> · <span style="color:#bbb;"><?= $dt->format('l') ?></span>
|
|
</div>
|
|
<div class="altx"><?= htmlspecialchars($ev['kegiatan']) ?> <span style="color:#999;font-weight:400;font-size:10px;">(<?=$jam?>)</span></div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sch-divider"></div>
|
|
|
|
<!-- RIGHT -->
|
|
<div class="sch-main">
|
|
<div class="bch">
|
|
<div style="font-size:18px;font-weight:700;color:#1a1a1a;">
|
|
<i class="mdi mdi-calendar-month mr-1 text-primary"></i><?= $monthNames[$month-1] ?> <?= $year ?>
|
|
</div>
|
|
<div class="bcs">
|
|
<a href="?m=<?=$pm?>&y=<?=$py?>" class="bca"><i class="mdi mdi-chevron-left"></i></a>
|
|
<select id="selMonth" class="bsel" onchange="goMonth()">
|
|
<?php for($i=1;$i<=12;$i++): ?>
|
|
<option value="<?=$i?>" <?=$i==$month?'selected':''?>><?=$monthNames[$i-1]?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
<select id="selYear" class="bsel" onchange="goMonth()">
|
|
<?php for($i=$year-5;$i<=$year+5;$i++): ?>
|
|
<option value="<?=$i?>" <?=$i==$year?'selected':''?>><?=$i?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
<a href="?m=<?=$nm?>&y=<?=$ny?>" class="bca"><i class="mdi mdi-chevron-right"></i></a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bcg">
|
|
<?php foreach(['SUN','MON','TUE','WED','THU','FRI','SAT'] as $dh): ?>
|
|
<div class="bcdh"><?=$dh?></div>
|
|
<?php endforeach; ?>
|
|
<?php for($i=0;$i<$startWeekday;$i++): ?><div class="bcc empty"></div><?php endfor; ?>
|
|
<?php
|
|
for($d=1;$d<=$daysInMonth;$d++):
|
|
$ds = sprintf('%04d-%02d-%02d',$year,$month,$d);
|
|
$isT = ($ds === $today);
|
|
$wd = ($startWeekday + $d - 1) % 7;
|
|
$cls = 'bcc';
|
|
if($isT) $cls .= ' today';
|
|
if($wd === 0) $cls .= ' sun';
|
|
$evData = isset($eventsByDate[$ds]) ? $eventsByDate[$ds] : [];
|
|
$dateColor = $dateColors[$ds] ?? '#4B49AC';
|
|
$cr = hexdec(substr($dateColor,1,2));
|
|
$cg = hexdec(substr($dateColor,3,2));
|
|
$cb = hexdec(substr($dateColor,5,2));
|
|
?>
|
|
<div class="<?=$cls?>" onclick="openDateModal('<?=$ds?>')">
|
|
<div class="bcn<?=$isT?' tn':''?>"><?=$d?></div>
|
|
<?php
|
|
$show = array_slice($evData, 0, 3);
|
|
foreach($show as $evt):
|
|
?>
|
|
<div class="bce" style="background:rgba(<?=$cr?>,<?=$cg?>,<?=$cb?>,0.12);color:<?=$dateColor?>;border-left:3px solid <?=$dateColor?>;"><?= htmlspecialchars($evt['kegiatan']) ?></div>
|
|
<?php endforeach;
|
|
if(count($evData) > 3): ?>
|
|
<div class="bcm">+<?= count($evData)-3 ?> more</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endfor; ?>
|
|
<?php
|
|
$total = $startWeekday + $daysInMonth;
|
|
$rem = (7 - ($total % 7)) % 7;
|
|
for($i=0;$i<$rem;$i++): ?><div class="bcc empty"></div><?php endfor; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="modalDateEvents" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content mdl-card">
|
|
<div class="modal-header py-2">
|
|
<h6 class="modal-title font-weight-bold"><i class="mdi mdi-calendar-today mr-1 text-primary"></i> <span id="modalDateTitle">Activities</span></h6>
|
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body" id="modalDateBody" style="padding:16px 20px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="modalExportSchedule" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content mdl-card">
|
|
<div class="modal-header py-2">
|
|
<h6 class="modal-title font-weight-bold"><i class="mdi mdi-file-export mr-1 text-primary"></i> Export Schedule Data</h6>
|
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body" style="padding:16px 20px;">
|
|
<form id="formExportSchedule" method="GET" action="export_schedule.php" target="_blank">
|
|
<div class="form-group">
|
|
<label class="font-weight-bold">Month</label>
|
|
<select class="form-control" name="start_month">
|
|
<?php for($i=1;$i<=12;$i++): ?>
|
|
<option value="<?=$i?>" <?=$i==1?'selected':''?>><?=$monthNames[$i-1]?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="font-weight-bold">Until Month (Optional)</label>
|
|
<select class="form-control" name="end_month">
|
|
<option value="">-- Leave blank / Only 1 Month --</option>
|
|
<?php for($i=1;$i<=12;$i++): ?>
|
|
<option value="<?=$i?>"><?=$monthNames[$i-1]?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="font-weight-bold">Year</label>
|
|
<select class="form-control" name="year">
|
|
<?php for($i=$year-5;$i<=$year+5;$i++): ?>
|
|
<option value="<?=$i?>" <?=$i==$year?'selected':''?>><?=$i?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="font-weight-bold">File Format</label>
|
|
<select class="form-control" name="format">
|
|
<option value="excel">Excel</option>
|
|
<option value="pdf">PDF</option>
|
|
</select>
|
|
</div>
|
|
<div class="text-right mt-4">
|
|
<button type="submit" class="btn btn-primary"> Download</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.sch-layout{display:flex;gap:0;align-items:stretch;min-height:500px}
|
|
.sch-sidebar{width:240px;flex-shrink:0;padding-right:20px}
|
|
.sch-divider{width:1px;background:#e5e9f0;flex-shrink:0}
|
|
.sch-main{flex:1;padding-left:24px;min-width:0;overflow:hidden;display:flex;flex-direction:column}
|
|
@media(max-width:992px){.sch-layout{flex-direction:column}.sch-sidebar{width:100%;padding-right:0;padding-bottom:16px}.sch-divider{width:100%;height:1px;margin:8px 0}.sch-main{padding-left:0}}
|
|
@media(max-width:576px){.bcc{min-height:50px;padding:4px}.bce{font-size:7px;padding:1px 2px}.bcm{font-size:7px}.bcn{font-size:10px}.bcdh{font-size:8px;padding:6px 2px}.bch{flex-direction:column;align-items:flex-start}}
|
|
|
|
.mini-cal-hdr{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
|
|
.mct{font-size:13px;font-weight:700;color:#1a1a1a}
|
|
.mcn{width:26px;height:26px;border-radius:6px;border:1px solid #e5e9f0;display:flex;align-items:center;justify-content:center;color:#666;text-decoration:none;font-size:14px;transition:.2s}
|
|
.mcn:hover{background:#f0f4ff;color:#4B49AC;text-decoration:none}
|
|
.mcg{display:grid;grid-template-columns:repeat(7,1fr);gap:1px;text-align:center}
|
|
.mcgh{font-size:9px;font-weight:700;color:#999;padding:4px 0;text-transform:uppercase}
|
|
.mcd{font-size:11px;color:#333;padding:5px 0;border-radius:6px;font-weight:500;position:relative}
|
|
.mcd.empty{visibility:hidden}.mcd.sun{color:#ef4444}
|
|
.mcd.today{background:#4B49AC;color:#fff;font-weight:700;border-radius:50%}
|
|
.mcd.hev::after{content:'';position:absolute;bottom:1px;left:50%;transform:translateX(-50%);width:4px;height:4px;border-radius:50%;background:#4B49AC}
|
|
.mcd.today.hev::after{background:#fff}
|
|
|
|
.al{max-height:340px;overflow-y:auto}
|
|
.ali{display:flex;gap:10px;padding:10px 0;border-bottom:1px solid #f5f5f5;align-items:flex-start}
|
|
.ali:last-child{border-bottom:none}
|
|
.ald{width:8px;height:8px;border-radius:50%;flex-shrink:0;margin-top:5px}
|
|
.aldt{font-size:10px;color:#666;font-weight:500;margin-bottom:1px}
|
|
.altx{font-size:12px;font-weight:600;color:#1a1a1a;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
|
|
.bch{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;flex-wrap:wrap;gap:8px}
|
|
.bcs{display:flex;align-items:center;gap:6px}
|
|
.bsel{border:1px solid #e5e9f0;border-radius:6px;padding:5px 10px;font-size:11px;font-weight:600;color:#333;background:#fff;cursor:pointer;outline:none}
|
|
.bsel:focus{border-color:#4B49AC}
|
|
.bca{width:30px;height:30px;border-radius:6px;border:1px solid #e5e9f0;display:flex;align-items:center;justify-content:center;color:#666;text-decoration:none;font-size:16px;transition:.2s}
|
|
.bca:hover{background:#f0f4ff;color:#4B49AC;text-decoration:none}
|
|
|
|
.bcg{display:grid;grid-template-columns:repeat(7,1fr);gap:1px;background:#e5e9f0;border:1px solid #e5e9f0;border-radius:10px;overflow:hidden;flex:1}
|
|
.bcdh{background:#fafbfc;padding:10px 4px;text-align:center;font-size:10px;font-weight:700;color:#555;text-transform:uppercase;letter-spacing:.5px}
|
|
.bcc{background:#fff;min-height:100px;padding:6px;transition:background .15s;cursor:pointer;overflow:hidden}
|
|
.bcc:hover{background:#f8faff}
|
|
.bcc.empty{background:#fafbfc;pointer-events:none}
|
|
.bcc.today{background:#eff6ff}
|
|
.bcc.sun .bcn{color:#ef4444}
|
|
.bcn{font-size:12px;font-weight:700;color:#333;margin-bottom:4px}
|
|
.tn{background:#4B49AC;color:#fff!important;width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:11px}
|
|
.bce{font-size:9px;padding:2px 4px;border-radius:3px;margin-bottom:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;max-width:100%}
|
|
.bcm{font-size:9px;color:#999;padding:0 4px;font-weight:500}
|
|
|
|
.mevi{display:flex;align-items:flex-start;gap:12px;padding:12px 14px;background:#f8fafc;border-radius:8px;margin-bottom:8px;border-left:3px solid #4B49AC}
|
|
.mevic{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px}
|
|
.mevt{font-size:14px;font-weight:600;color:#1a1a1a}
|
|
.mevtm{font-size:11px;color:#666;margin-top:2px;display:flex;align-items:center;gap:4px}
|
|
.meve{text-align:center;padding:30px 10px;color:#999;font-size:13px}
|
|
</style>
|
|
|
|
<?php include 'footer.php'; ?>
|
|
|
|
<script>
|
|
function goMonth(){
|
|
var m=document.getElementById('selMonth').value,y=document.getElementById('selYear').value;
|
|
window.location.href='?m='+m+'&y='+y;
|
|
}
|
|
var allEvents=<?= json_encode($eventsByDate) ?>;
|
|
var dateColors=<?= json_encode($dateColors) ?>;
|
|
function openDateModal(ds){
|
|
var p=ds.split('-'),d=new Date(p[0],p[1]-1,p[2]);
|
|
var dn=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
|
|
var mn=['January','February','March','April','May','June','July','August','September','October','November','December'];
|
|
var title=dn[d.getDay()]+', '+parseInt(p[2])+' '+mn[d.getMonth()]+' '+p[0];
|
|
document.getElementById('modalDateTitle').textContent=title;
|
|
var body=document.getElementById('modalDateBody'),evts=allEvents[ds]||[],color=dateColors[ds]||'#4B49AC';
|
|
if(!evts.length){
|
|
body.innerHTML='<div class="meve"><i class="mdi mdi-calendar-blank" style="font-size:32px;display:block;margin-bottom:6px;color:#ddd;"></i>No activities on this date</div>';
|
|
} else {
|
|
var h='';
|
|
evts.forEach(function(ev){
|
|
var ts=ev.date||'';
|
|
var jam=ts.length>=16?ts.substring(11,16):'00:00';
|
|
h+='<div class="mevi" style="border-left-color:'+color+'">'+
|
|
'<div class="mevic" style="background:'+hexToRgba(color,0.1)+';color:'+color+'"><i class="mdi mdi-clock-outline"></i></div>'+
|
|
'<div><div class="mevt">'+esc(ev.kegiatan)+'</div>'+
|
|
'<div class="mevtm"><i class="mdi mdi-clock-outline"></i> '+jam+' · <i class="mdi mdi-calendar"></i> '+title+'</div></div></div>';
|
|
});
|
|
body.innerHTML=h;
|
|
}
|
|
$('#modalDateEvents').modal('show');
|
|
}
|
|
function hexToRgba(hex,a){var r=parseInt(hex.substr(1,2),16),g=parseInt(hex.substr(3,2),16),b=parseInt(hex.substr(5,2),16);return'rgba('+r+','+g+','+b+','+a+')';}
|
|
function esc(t){var d=document.createElement('div');d.textContent=t;return d.innerHTML;}
|
|
</script>
|