2330 lines
74 KiB
PHP
2330 lines
74 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="id">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MONITORING PREDIKSI - UDD PMI KABUPATEN JEMBER</title>
|
|
<link rel="icon" type="image/png" href="{{ asset('images/logo-pmi.png') }}">
|
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
|
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html,
|
|
body,
|
|
input,
|
|
button,
|
|
select,
|
|
textarea {
|
|
font-family: 'Poppins', sans-serif;
|
|
}
|
|
|
|
body {
|
|
background: white;
|
|
color: #1e293b;
|
|
min-height: 100vh;
|
|
padding: 32px;
|
|
overflow-x: clip;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1700px;
|
|
margin: auto;
|
|
background: white;
|
|
backdrop-filter: blur(16px);
|
|
border: 1px solid #d32f2fce;
|
|
border-radius: 10px;
|
|
padding: 30px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.27);
|
|
}
|
|
|
|
.top-banner {
|
|
background:
|
|
linear-gradient(135deg, rgba(211, 47, 47, .95), rgba(127, 0, 0, .96));
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 24px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
color: white;
|
|
box-shadow:
|
|
0 10px 30px rgba(220, 38, 38, 0.25);
|
|
}
|
|
|
|
.banner-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 18px;
|
|
}
|
|
|
|
.banner-subtitle {
|
|
font-size: 12px;
|
|
opacity: .85;
|
|
letter-spacing: 1px;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.banner-title {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.banner-icon {
|
|
position: relative;
|
|
width: 52px;
|
|
height: 52px;
|
|
min-width: 52px;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: rgba(255, 255, 255, .96);
|
|
font-size: 22px;
|
|
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
|
|
border: 1px solid rgba(255, 255, 255, .18);
|
|
backdrop-filter: blur(12px);
|
|
box-shadow:
|
|
inset 0 1px 1px rgba(255, 255, 255, .24),
|
|
inset 0 -1px 1px rgba(255, 255, 255, .05),
|
|
0 10px 18px rgba(0, 0, 0, .16),
|
|
0 4px 8px rgba(0, 0, 0, .10);
|
|
overflow: hidden;
|
|
transition: all .25s ease;
|
|
}
|
|
|
|
.banner-icon::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
background:
|
|
linear-gradient(to bottom right,
|
|
rgba(255, 255, 255, .18),
|
|
transparent 60%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.header {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.header p {
|
|
margin-top: 6px;
|
|
color: #64748b;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.header-nav {
|
|
display: flex;
|
|
gap: 10px;
|
|
width: 800px;
|
|
}
|
|
|
|
.header-btn {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 14px;
|
|
border-radius: 5px;
|
|
text-decoration: none;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
transition: .25s ease;
|
|
border: 1px solid rgba(255, 255, 255, .15);
|
|
color: white;
|
|
}
|
|
|
|
.header-btn:nth-child(1) {
|
|
background: linear-gradient(135deg,
|
|
#5b8def,
|
|
#3c6fd7);
|
|
}
|
|
|
|
.header-btn:nth-child(1):hover {
|
|
background: linear-gradient(135deg,
|
|
#6e9fff,
|
|
#4b84f7);
|
|
}
|
|
|
|
.header-btn:nth-child(1) i {
|
|
color: #eaf2ff;
|
|
}
|
|
|
|
.header-btn:nth-child(2) {
|
|
background: linear-gradient(135deg,
|
|
#d98a3d,
|
|
#c97828);
|
|
}
|
|
|
|
.header-btn:nth-child(2):hover {
|
|
background: linear-gradient(135deg,
|
|
#e69c5a,
|
|
#d98a3d);
|
|
}
|
|
|
|
.header-btn:nth-child(2) i {
|
|
color: #fff4e8;
|
|
}
|
|
|
|
.header-btn:nth-child(3) {
|
|
background: linear-gradient(135deg,
|
|
#22c55e,
|
|
#15803d);
|
|
}
|
|
|
|
.header-btn:nth-child(3):hover {
|
|
background: linear-gradient(135deg,
|
|
#34d399,
|
|
#16a34a);
|
|
}
|
|
|
|
.header-btn:nth-child(3) i {
|
|
color: #fff1f1;
|
|
}
|
|
|
|
.header-btn:nth-child(4) {
|
|
background: linear-gradient(135deg,
|
|
#8b5cf6,
|
|
#7c3aed);
|
|
}
|
|
|
|
.header-btn:nth-child(4) i {
|
|
color: #f3e8ff;
|
|
}
|
|
|
|
.header-btn:hover {
|
|
transform: translateY(-2px);
|
|
filter: brightness(1.05);
|
|
color: white;
|
|
}
|
|
|
|
.header-btn.active {
|
|
pointer-events: none;
|
|
cursor: default;
|
|
}
|
|
|
|
.refresh-btn {
|
|
min-width: 45px;
|
|
align-self: stretch;
|
|
border: none;
|
|
border-radius: 5px;
|
|
background:
|
|
linear-gradient(135deg,
|
|
#ef4444,
|
|
#b91c1c);
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: .25s;
|
|
}
|
|
|
|
.refresh-icon {
|
|
font-size: 20px;
|
|
transition: transform .3s ease;
|
|
}
|
|
|
|
.refresh-btn:hover .refresh-icon {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.header-left i,
|
|
.header-btn i {
|
|
position: relative;
|
|
font-size: 18px;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: rgba(255, 255, 255, .96);
|
|
background: linear-gradient(145deg,
|
|
rgba(255, 255, 255, .22),
|
|
rgba(255, 255, 255, .08));
|
|
border: 1px solid rgba(255, 255, 255, .18);
|
|
backdrop-filter: blur(10px);
|
|
box-shadow:
|
|
inset 0 1px 1px rgba(255, 255, 255, .24),
|
|
inset 0 -1px 1px rgba(255, 255, 255, .05),
|
|
0 10px 18px rgba(0, 0, 0, .16),
|
|
0 4px 8px rgba(0, 0, 0, .10);
|
|
}
|
|
|
|
.dashboard-grid {
|
|
display: grid;
|
|
grid-template-columns: 2fr 0.8fr;
|
|
gap: 20px;
|
|
align-items: stretch;
|
|
padding-top: 15px;
|
|
}
|
|
|
|
.right-side {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
align-self: stretch;
|
|
}
|
|
|
|
.donut-card {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: auto;
|
|
}
|
|
|
|
.left-side {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.summary-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 18px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.summary-card {
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 22px;
|
|
border: 1px solid rgba(0, 0, 0, 0.09);
|
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08);
|
|
transition: .25s;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.summary-card::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 5px;
|
|
background:
|
|
linear-gradient(to right, #dc2626, #ef4444);
|
|
}
|
|
|
|
.summary-icon {
|
|
position: absolute;
|
|
top: 18px;
|
|
right: 18px;
|
|
width: 46px;
|
|
height: 46px;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 20px;
|
|
background: rgba(220, 38, 38, 0.08);
|
|
color: #dc2626;
|
|
}
|
|
|
|
.summary-icon.range {
|
|
background: rgba(124, 58, 237, 0.12);
|
|
color: #6d28d9;
|
|
}
|
|
|
|
.summary-icon.warning {
|
|
background: rgba(245, 158, 11, 0.12);
|
|
color: #d97706;
|
|
}
|
|
|
|
.summary-icon.primary {
|
|
background: rgba(34, 197, 94, 0.12);
|
|
color: #16a34a;
|
|
}
|
|
|
|
.summary-icon.danger {
|
|
|
|
background: rgba(37, 99, 235, 0.1);
|
|
color: #2563eb;
|
|
}
|
|
|
|
.summary-card:hover {
|
|
transform: translateY(-3px);
|
|
}
|
|
|
|
.summary-card .title {
|
|
font-size: 14px;
|
|
color: #64748b;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.summary-card .value {
|
|
font-size: 27px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.summary-card .desc {
|
|
margin-top: 10px;
|
|
font-size: 13px;
|
|
color: #94a3b8;
|
|
}
|
|
|
|
.card {
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 24px;
|
|
margin-bottom: 24px;
|
|
border: 1px solid rgba(0, 0, 0, 0.09);
|
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
margin-bottom: 20px;
|
|
color: #8f0000;
|
|
}
|
|
|
|
#periodeText {
|
|
color: #ef4444;
|
|
margin-left: 6px;
|
|
}
|
|
|
|
.filters {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
grid-template-areas:
|
|
"periode jenis status btnFilter"
|
|
"search search search btnPrint";
|
|
gap: 14px;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.filters input,
|
|
.filters select {
|
|
padding: 12px 14px;
|
|
border-radius: 8px;
|
|
border: 1px solid #dbe2ea;
|
|
background: #f8fafc;
|
|
outline: none;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.filters select {
|
|
padding: 12px 14px;
|
|
border-radius: 8px;
|
|
border: 1px solid #dbe2ea;
|
|
background: white;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
transition: all .25s ease;
|
|
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
background-repeat: no-repeat;
|
|
padding-right: 38px;
|
|
}
|
|
|
|
.filters select:hover {
|
|
border-color: #cbd5e1;
|
|
}
|
|
|
|
.filters button {
|
|
border: none;
|
|
background: #2563eb;
|
|
color: white;
|
|
border-radius: 12px;
|
|
padding: 12px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.filter-action {
|
|
position: relative;
|
|
grid-area: btnFilter;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
}
|
|
|
|
.filter-action button {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.filters input[name="search"] {
|
|
padding: 12px 14px;
|
|
border-radius: 8px;
|
|
border: 1px solid #dbe2ea;
|
|
background: white;
|
|
font-size: 14px;
|
|
outline: none;
|
|
transition: all .25s ease;
|
|
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.11);
|
|
}
|
|
|
|
.filters input[name="search"]::placeholder {
|
|
color: #94a3b8;
|
|
}
|
|
|
|
.filters input[name="search"]:hover {
|
|
border-color: #cbd5e1;
|
|
}
|
|
|
|
#periode {
|
|
grid-area: periode;
|
|
}
|
|
|
|
#jenis {
|
|
grid-area: jenis;
|
|
}
|
|
|
|
#status {
|
|
grid-area: status;
|
|
}
|
|
|
|
.filter-action {
|
|
grid-area: btnFilter;
|
|
}
|
|
|
|
.filters input[name="search"] {
|
|
grid-area: search;
|
|
}
|
|
|
|
#btnPrint {
|
|
grid-area: btnPrint;
|
|
}
|
|
|
|
.note-periode {
|
|
position: absolute;
|
|
top: -22px;
|
|
left: 0;
|
|
font-size: 12px;
|
|
color: #ef4444;
|
|
white-space: nowrap;
|
|
display: none;
|
|
}
|
|
|
|
.table-wrapper {
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
-webkit-overflow-scrolling: touch;
|
|
border-left: 1px solid #e2e8f0;
|
|
border-right: 1px solid #e2e8f0;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.table-footer {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-top: 18px;
|
|
gap: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.table-info {
|
|
font-size: 13px;
|
|
color: #64748b;
|
|
}
|
|
|
|
.pagination {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.pagination button {
|
|
min-width: 38px;
|
|
height: 38px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: #fff5f5;
|
|
color: #b91c1c;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: .2s ease;
|
|
border: 1px solid #fecaca;
|
|
}
|
|
|
|
.pagination button:hover {
|
|
background: #fee2e2;
|
|
}
|
|
|
|
.pagination button.active {
|
|
background: linear-gradient(135deg, #d32f2f, #9a0007);
|
|
color: white;
|
|
border-color: transparent;
|
|
box-shadow: 0 6px 16px rgba(211, 47, 47, 0.25);
|
|
}
|
|
|
|
.pagination button:disabled {
|
|
opacity: .45;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.table-title-flex {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 12px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.table-title-right {
|
|
font-size: 13px;
|
|
color: #64748b;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.footer-red {
|
|
margin-top: 30px;
|
|
background: linear-gradient(135deg, #d32f2f, #7f0000);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 18px 22px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 13px;
|
|
box-shadow: 0 10px 30px rgba(211, 47, 47, 0.22);
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.footer-red strong {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.sticky-col {
|
|
position: sticky;
|
|
background: #fff7f7;
|
|
z-index: 5;
|
|
}
|
|
|
|
.sticky-kode {
|
|
left: 0;
|
|
min-width: 140px;
|
|
max-width: 140px;
|
|
}
|
|
|
|
.sticky-nama {
|
|
left: 140px;
|
|
min-width: 260px;
|
|
max-width: 260px;
|
|
}
|
|
|
|
thead .sticky-col {
|
|
z-index: 10;
|
|
background: linear-gradient(135deg, #ffdcdc, #ffe7e7);
|
|
}
|
|
|
|
tbody tr:nth-child(even) .sticky-col {
|
|
background: #fff1f1;
|
|
}
|
|
|
|
tbody tr:nth-child(odd) .sticky-col {
|
|
background: #fff7f7;
|
|
}
|
|
|
|
.sticky-shadow {
|
|
box-shadow: 4px 0 10px rgba(211, 47, 47, 0.12);
|
|
border-right: 1px solid #f5c2c2;
|
|
}
|
|
|
|
table {
|
|
width: max-content;
|
|
min-width: 1600px;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
th,
|
|
td {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
th {
|
|
background: linear-gradient(135deg, #ffdcdc, #ffe7e7);
|
|
color: #9a0007;
|
|
padding: 16px 14px;
|
|
text-align: left;
|
|
font-size: 11.5px;
|
|
text-transform: uppercase;
|
|
letter-spacing: .5px;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
}
|
|
|
|
th {
|
|
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
th:last-child {
|
|
border-right: none;
|
|
}
|
|
|
|
td {
|
|
padding: 16px 14px;
|
|
border-bottom: 1px solid #f1f5f9;
|
|
font-size: 13px;
|
|
}
|
|
|
|
tr:hover {
|
|
background: #f8fafc;
|
|
}
|
|
|
|
tbody tr:nth-child(odd) {
|
|
background: #ffffff;
|
|
}
|
|
|
|
tbody tr:nth-child(even) {
|
|
background: #fafafa;
|
|
}
|
|
|
|
.badge {
|
|
padding: 6px 12px;
|
|
border-radius: 5px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
display: inline-block;
|
|
}
|
|
|
|
.kode-badge {
|
|
display: inline-block;
|
|
padding: 4px 10px;
|
|
border-radius: 5px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
background: #fee2e2;
|
|
color: #b91c1c;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.green {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
}
|
|
|
|
.yellow {
|
|
background: #fef9c3;
|
|
color: #854d0e;
|
|
}
|
|
|
|
.red {
|
|
background: #fee2e2;
|
|
color: #991b1b;
|
|
}
|
|
|
|
.blue {
|
|
background: #dbeafe;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.gray {
|
|
background: #e2e8f0;
|
|
color: #475569;
|
|
}
|
|
|
|
.chart-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.chart-box {
|
|
background:
|
|
linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
|
border: 1px solid #eef2f7;
|
|
border-radius: 22px;
|
|
padding: 20px;
|
|
height: 260px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fake-chart {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
right: 20px;
|
|
height: 140px;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 10px;
|
|
}
|
|
|
|
.bar {
|
|
flex: 1;
|
|
background:
|
|
linear-gradient(to top, #b91c1c, #ef4444);
|
|
border-radius: 12px 12px 0 0;
|
|
box-shadow:
|
|
0 6px 14px rgba(239, 68, 68, 0.25);
|
|
}
|
|
|
|
.list-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.list-box ul {
|
|
list-style: none;
|
|
}
|
|
|
|
.list-box li {
|
|
padding: 12px 0;
|
|
border-bottom: 1px solid #eef2f7;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.activity-item {
|
|
padding: 14px;
|
|
border-radius: 14px;
|
|
background: #f8fafc;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.activity-item strong {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.modal-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(15, 23, 42, 0.55);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 9999;
|
|
}
|
|
|
|
.modal-box {
|
|
width: 380px;
|
|
background: rgba(255, 255, 255, 0.92);
|
|
backdrop-filter: blur(14px);
|
|
border-radius: 18px;
|
|
padding: 26px;
|
|
text-align: center;
|
|
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.25);
|
|
animation: modalIn .25s ease;
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.filters input.month-input {
|
|
position: relative;
|
|
width: 100%;
|
|
padding: 8px 12px 8px 38px;
|
|
font-size: 13px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(211, 47, 47, 0.48);
|
|
background:
|
|
linear-gradient(145deg, #ffffff, #fff5f5);
|
|
background-size: 18px 18px;
|
|
box-shadow:
|
|
inset 0 2px 6px rgba(0, 0, 0, 0.08),
|
|
0 4px 10px rgba(211, 47, 47, 0.08);
|
|
}
|
|
|
|
.filters input.month-input:hover {
|
|
border-color: #d32f2f;
|
|
box-shadow:
|
|
inset 0 2px 6px rgba(0, 0, 0, 0.08),
|
|
0 6px 16px rgba(211, 47, 47, 0.15);
|
|
}
|
|
|
|
.filters input.month-input:focus {
|
|
outline: none;
|
|
border-color: #d32f2f;
|
|
background: linear-gradient(145deg, #fff, #ffecec);
|
|
box-shadow:
|
|
inset 0 2px 6px rgba(0, 0, 0, 0.08),
|
|
0 0 0 3px rgba(211, 47, 47, 0.15),
|
|
0 10px 25px rgba(211, 47, 47, 0.2);
|
|
}
|
|
|
|
.filters input.month-input::-webkit-calendar-picker-indicator {
|
|
opacity: 0;
|
|
position: absolute;
|
|
right: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
cursor: pointer;
|
|
}
|
|
|
|
@keyframes modalIn {
|
|
from {
|
|
transform: translateY(20px) scale(0.95);
|
|
opacity: 0;
|
|
}
|
|
|
|
to {
|
|
transform: translateY(0) scale(1);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.modal-icon {
|
|
width: 64px;
|
|
height: 64px;
|
|
margin: 0 auto 12px;
|
|
border-radius: 50%;
|
|
background: rgba(239, 68, 68, 0.1);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 28px;
|
|
color: #ef4444;
|
|
}
|
|
|
|
.modal-box h3 {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.modal-box p {
|
|
font-size: 13px;
|
|
color: #64748b;
|
|
}
|
|
|
|
.modal-action {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 22px;
|
|
}
|
|
|
|
.btn {
|
|
border: none;
|
|
padding: 9px 14px;
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.btn-delete {
|
|
background: #d32f2f;
|
|
border-radius: 7px;
|
|
color: #fff;
|
|
cursor: pointer;
|
|
transition: all .25s ease;
|
|
font-size: 14px;
|
|
box-shadow: 0 6px 14px rgba(211, 47, 47, 0.28);
|
|
}
|
|
|
|
.btn-delete:hover {
|
|
transform: translateY(-2px) !important;
|
|
box-shadow: 0 6px 14px rgba(211, 47, 47, 0.35);
|
|
}
|
|
|
|
.btn-delete:active {
|
|
transform: scale(0.9);
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-1px);
|
|
opacity: .95;
|
|
}
|
|
|
|
.btn-filter {
|
|
background: linear-gradient(135deg, #d32f2f, #9a0007) !important;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px !important;
|
|
padding: 12px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all .25s ease;
|
|
box-shadow: 0 10px 25px rgba(211, 47, 47, .3);
|
|
}
|
|
|
|
.btn-filter:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 14px 30px rgba(211, 47, 47, .38);
|
|
}
|
|
|
|
.btn-filter:active {
|
|
transform: translateY(0px);
|
|
box-shadow: 0 6px 15px rgba(211, 47, 47, .25);
|
|
}
|
|
|
|
.btn-filter:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.btn-cancel {
|
|
flex: 1;
|
|
padding: 10px;
|
|
border-radius: 12px;
|
|
border: none;
|
|
background: #e2e8f0;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: .2s;
|
|
}
|
|
|
|
.btn-cancel:hover {
|
|
background: #cbd5e1;
|
|
}
|
|
|
|
.btn-confirm {
|
|
flex: 1;
|
|
padding: 10px;
|
|
border-radius: 12px;
|
|
border: none;
|
|
background: linear-gradient(135deg, #dc2626, #ef4444);
|
|
color: white;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
box-shadow: 0 8px 20px rgba(239, 68, 68, 0.25);
|
|
transition: .2s;
|
|
}
|
|
|
|
.btn-confirm:hover {
|
|
transform: translateY(-1px);
|
|
opacity: 0.95;
|
|
}
|
|
|
|
#btnFilter {
|
|
background: linear-gradient(135deg, #d32f2f, #9a0007) !important;
|
|
}
|
|
|
|
#btnPrint {
|
|
background: linear-gradient(135deg, #059669, #10b981) !important;
|
|
box-shadow: 0 10px 25px rgba(16, 185, 129, .25);
|
|
}
|
|
|
|
#btnPrint:hover {
|
|
box-shadow: 0 14px 30px rgba(16, 185, 129, .35);
|
|
}
|
|
|
|
#btnPrint:active {
|
|
box-shadow: 0 6px 15px rgba(16, 185, 129, .2);
|
|
}
|
|
|
|
.sort-icons {
|
|
display: inline-flex;
|
|
flex-direction: column;
|
|
margin-left: 6px;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.sort-icons i {
|
|
font-size: 9px;
|
|
line-height: 9px;
|
|
color: #9a0007;
|
|
cursor: pointer;
|
|
transition: .2s;
|
|
}
|
|
|
|
.sort-icons i:hover {
|
|
transform: scale(1.2);
|
|
color: #d32f2f;
|
|
}
|
|
|
|
.sort-icons .active {
|
|
color: #d32f2f;
|
|
}
|
|
|
|
.donut-wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
gap: 14px;
|
|
}
|
|
|
|
.donut-svg {
|
|
width: 220px;
|
|
height: 220px;
|
|
transform: rotate(-90deg);
|
|
}
|
|
|
|
.donut-slice {
|
|
fill: none;
|
|
stroke-width: 40;
|
|
cursor: pointer;
|
|
transition: .25s ease;
|
|
}
|
|
|
|
.donut-slice:hover {
|
|
transform: scale(1.05);
|
|
filter: brightness(1.1);
|
|
}
|
|
|
|
.tooltip-donut {
|
|
position: fixed;
|
|
z-index: 9999;
|
|
background: rgba(215, 37, 37, 0.92);
|
|
backdrop-filter: blur(10px);
|
|
color: #fff;
|
|
padding: 10px 14px;
|
|
border-radius: 12px;
|
|
font-size: 13px;
|
|
line-height: 1.4;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transform: translate(-50%, -8px) scale(.95);
|
|
transition:
|
|
opacity .18s ease,
|
|
transform .18s ease;
|
|
box-shadow:
|
|
0 12px 28px rgba(0, 0, 0, .28);
|
|
}
|
|
|
|
.tooltip-donut.show {
|
|
opacity: 1;
|
|
transform: translate(-50%, -14px) scale(1);
|
|
}
|
|
|
|
.tooltip-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.tooltip-color {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.tooltip-title {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.tooltip-value {
|
|
font-size: 12px;
|
|
opacity: .8;
|
|
}
|
|
|
|
.donut {
|
|
width: 220px;
|
|
height: 220px;
|
|
border-radius: 50%;
|
|
background: conic-gradient(#ef4444 0% 35%,
|
|
#22c55e 35% 70%,
|
|
#f59e0b 70% 100%);
|
|
position: relative;
|
|
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.12);
|
|
cursor: pointer;
|
|
transition: .25s ease;
|
|
}
|
|
|
|
.donut:hover {
|
|
transform: scale(1.03);
|
|
}
|
|
|
|
.donut::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 140px;
|
|
height: 140px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.donut-center {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
text-align: center;
|
|
z-index: 2;
|
|
}
|
|
|
|
.donut-value {
|
|
font-size: 22px;
|
|
font-weight: 700;
|
|
color: #1e293b;
|
|
}
|
|
|
|
.donut-label {
|
|
font-size: 12px;
|
|
color: #64748b;
|
|
}
|
|
|
|
.donut-legend {
|
|
display: flex;
|
|
gap: 14px;
|
|
font-size: 12px;
|
|
color: #475569;
|
|
}
|
|
|
|
.dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
.dot.red {
|
|
background: #2563eb;
|
|
}
|
|
|
|
.dot.green {
|
|
background: #22c55e;
|
|
}
|
|
|
|
.dot.yellow {
|
|
background: #f59e0b;
|
|
}
|
|
|
|
.table-length-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.rows-select {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
padding: 10px 42px 10px 14px;
|
|
border-radius: 10px;
|
|
border: 1px solid #fecaca;
|
|
background:
|
|
linear-gradient(135deg, #fff5f5, #ffffff);
|
|
color: #b91c1c;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
box-shadow:
|
|
0 6px 16px rgba(211, 47, 47, 0.08),
|
|
inset 0 2px 5px rgba(255, 255, 255, 0.8);
|
|
transition: all .25s ease;
|
|
background-image:
|
|
linear-gradient(45deg, transparent 50%, #b91c1c 50%),
|
|
linear-gradient(135deg, #b91c1c 50%, transparent 50%);
|
|
background-position:
|
|
calc(100% - 18px) calc(50% - 3px),
|
|
calc(100% - 12px) calc(50% - 3px);
|
|
background-size: 6px 6px;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
.rows-select:focus {
|
|
outline: none;
|
|
border-color: #dc2626;
|
|
box-shadow:
|
|
0 0 0 4px rgba(220, 38, 38, 0.12),
|
|
0 10px 24px rgba(220, 38, 38, 0.12);
|
|
}
|
|
|
|
/* =========================
|
|
RESPONSIVE BREAKPOINTS
|
|
========================= */
|
|
@media (max-width: 992px) {
|
|
.top-banner {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 16px;
|
|
}
|
|
|
|
.header-nav {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.header-nav {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 10px;
|
|
}
|
|
|
|
.header-btn {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.header-nav {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.header-btn {
|
|
justify-content: flex-start;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
.sticky-kode,
|
|
.sticky-nama {
|
|
position: static;
|
|
left: auto;
|
|
box-shadow: none;
|
|
}
|
|
|
|
table {
|
|
min-width: 1200px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1800px) {
|
|
.container {
|
|
max-width: 1900px;
|
|
}
|
|
|
|
.summary-grid {
|
|
grid-template-columns: repeat(4, 1fr);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1600px) {
|
|
.dashboard-grid {
|
|
grid-template-columns: 1.7fr 0.9fr;
|
|
}
|
|
|
|
table {
|
|
min-width: 1500px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1400px) {
|
|
body {
|
|
padding: 20px;
|
|
}
|
|
|
|
.dashboard-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.right-side {
|
|
width: 100%;
|
|
}
|
|
|
|
.donut-card {
|
|
min-height: 380px;
|
|
}
|
|
|
|
table {
|
|
min-width: 1400px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
.summary-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.filters {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
grid-template-areas:
|
|
"periode jenis"
|
|
"status search"
|
|
"btnFilter btnPrint";
|
|
}
|
|
|
|
.filter-action {
|
|
width: 100%;
|
|
}
|
|
|
|
#btnPrint,
|
|
#btnFilter {
|
|
width: 100%;
|
|
}
|
|
|
|
.table-footer {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
body {
|
|
padding: 16px;
|
|
}
|
|
|
|
.container {
|
|
padding: 20px;
|
|
}
|
|
|
|
.top-banner {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 16px;
|
|
}
|
|
|
|
.banner-title {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 17px;
|
|
}
|
|
|
|
.summary-card .value {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.donut-svg {
|
|
width: 190px;
|
|
height: 190px;
|
|
}
|
|
|
|
.table-title-flex {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
body {
|
|
padding: 12px;
|
|
}
|
|
|
|
.container {
|
|
padding: 16px;
|
|
}
|
|
|
|
.summary-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.filters {
|
|
grid-template-columns: 1fr;
|
|
|
|
grid-template-areas:
|
|
"periode"
|
|
"jenis"
|
|
"status"
|
|
"search"
|
|
"btnFilter"
|
|
"btnPrint";
|
|
}
|
|
|
|
.banner-icon {
|
|
width: 46px;
|
|
height: 46px;
|
|
min-width: 46px;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.banner-title {
|
|
font-size: 18px;
|
|
}
|
|
|
|
.banner-subtitle {
|
|
font-size: 11px;
|
|
}
|
|
|
|
.summary-card {
|
|
padding: 18px;
|
|
}
|
|
|
|
.summary-card .value {
|
|
font-size: 22px;
|
|
}
|
|
|
|
.summary-icon {
|
|
width: 42px;
|
|
height: 42px;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.donut-svg {
|
|
width: 170px;
|
|
height: 170px;
|
|
}
|
|
|
|
.donut-legend {
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.modal-box {
|
|
width: 92%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
body {
|
|
padding: 10px;
|
|
}
|
|
|
|
.container {
|
|
padding: 14px;
|
|
}
|
|
|
|
.top-banner {
|
|
padding: 16px;
|
|
}
|
|
|
|
.banner-title {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.banner-subtitle {
|
|
font-size: 10px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 15px;
|
|
}
|
|
|
|
.summary-card .title {
|
|
font-size: 13px;
|
|
}
|
|
|
|
.summary-card .value {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.summary-card .desc {
|
|
font-size: 11px;
|
|
}
|
|
|
|
.table-info {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.pagination {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.rows-select {
|
|
width: 100%;
|
|
}
|
|
|
|
.footer-red {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
text-align: left;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 360px) {
|
|
.container {
|
|
padding: 12px;
|
|
}
|
|
|
|
.banner-title {
|
|
font-size: 15px;
|
|
}
|
|
|
|
.summary-card {
|
|
padding: 14px;
|
|
}
|
|
|
|
.summary-card .value {
|
|
font-size: 18px;
|
|
}
|
|
|
|
.filters input,
|
|
.filters select,
|
|
.filters button {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.donut-svg {
|
|
width: 150px;
|
|
height: 150px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body class="p-8">
|
|
<div class="w-full mx-auto">
|
|
<div class="container">
|
|
<div class="top-banner">
|
|
<div class="banner-left">
|
|
<div class="banner-icon">
|
|
<i class="fa-solid fa-pie-chart"></i>
|
|
</div>
|
|
<div>
|
|
<div class="banner-title">
|
|
MONITORING PREDIKSI
|
|
</div>
|
|
<div class="banner-subtitle">
|
|
Monitoring kondisi prediksi kebutuhan barang habis pakai
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="header-nav">
|
|
<a href="/prediksi-barang" class="header-btn">
|
|
<i class="fa-solid fa-chart-column"></i>
|
|
Prediksi
|
|
</a>
|
|
<a href="/riwayat-prediksi" class="header-btn">
|
|
<i class="fa-solid fa-clock-rotate-left"></i>
|
|
Riwayat
|
|
</a>
|
|
<a href="/rencana-pengadaan" class="header-btn">
|
|
<i class="fa-solid fa-boxes-stacked"></i>
|
|
Pengadaan
|
|
</a>
|
|
<a href="/monitoring-prediksi" class="header-btn active">
|
|
<i class="fa-solid fa-pie-chart"></i>
|
|
Monitoring
|
|
</a>
|
|
<button class="refresh-btn" onclick="location.reload()">
|
|
<i class="fa-solid fa-rotate-right refresh-icon"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-grid">
|
|
<div class="left-side">
|
|
<div class="summary-grid" id="summaryWrapper">
|
|
<div class="summary-card">
|
|
<div class="summary-icon range">
|
|
<i class="bi bi-box-seam"></i>
|
|
</div>
|
|
<div class="title">Total Barang</div>
|
|
<div class="value">{{ $totalBarang }}</div>
|
|
<div class="desc">Barang habis pakai dalam sistem</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<div class="summary-icon warning">
|
|
<i class="bi bi-exclamation-triangle"></i>
|
|
</div>
|
|
<div class="title">Belum Dapat Dimonitor</div>
|
|
<div class="value">{{ $barangBelumDimonitor }}</div>
|
|
<div class="desc">Barang yang belum memiliki data monitoring</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<div class="summary-icon primary">
|
|
<i class="bi bi-graph-up-arrow"></i>
|
|
</div>
|
|
<div class="title">Data Monitoring</div>
|
|
<div class="value">{{ $dataMonitoring }}</div>
|
|
<div class="desc">Barang yang memiliki prediksi dan data aktual</div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="section-title">Filter Monitoring</div>
|
|
<form id="filterForm">
|
|
@csrf
|
|
<div class="filters">
|
|
<input type="month" class="month-input" id="periode" name="periode"
|
|
value="{{ $filters['periode'] ?? date('Y-m') }}" min="{{ $minPeriode }}"
|
|
max="{{ $maxPeriode }}" oninput="cekPeriode()">
|
|
<select name="jenis" id="jenis">
|
|
<option value="">Semua Jenis</option>
|
|
@foreach ($jenisList as $j)
|
|
<option value="{{ $j }}"
|
|
{{ ($filters['jenis'] ?? '') == $j ? 'selected' : '' }}>
|
|
{{ $j }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
<select name="status" id="status">
|
|
<option value="">Semua Status</option>
|
|
<option value="stabil"
|
|
{{ ($filters['status'] ?? '') == 'stabil' ? 'selected' : '' }}>
|
|
Stabil
|
|
</option>
|
|
<option value="naik"
|
|
{{ ($filters['status'] ?? '') == 'naik' ? 'selected' : '' }}>
|
|
Naik
|
|
</option>
|
|
<option value="turun"
|
|
{{ ($filters['status'] ?? '') == 'turun' ? 'selected' : '' }}>
|
|
Turun
|
|
</option>
|
|
</select>
|
|
<input type="text" name="search" value="{{ $filters['search'] ?? '' }}"
|
|
placeholder="Cari kode / nama barang">
|
|
<button type="button" id="btnPrint" class="btn-filter" disabled
|
|
style="opacity:0.5;cursor:not-allowed;">
|
|
Cetak
|
|
</button>
|
|
<div class="filter-action">
|
|
<button id="btnFilter" type="submit" class="btn-filter" disabled>
|
|
Tampilkan
|
|
</button>
|
|
<div id="notePeriode" class="note-periode">
|
|
Tidak ada data
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="right-side">
|
|
<div class="card donut-card">
|
|
<div class="section-title">Distribusi Status Prediksi</div>
|
|
<div class="donut-wrapper">
|
|
<svg viewBox="0 0 200 200" class="donut-svg">
|
|
@php
|
|
$radius = 70;
|
|
$circumference = 2 * 3.1416 * $radius;
|
|
$naikDash = ($persenNaik / 100) * $circumference;
|
|
$stabilDash = ($persenStabil / 100) * $circumference;
|
|
$turunDash = ($persenTurun / 100) * $circumference;
|
|
$naikOffset = 0;
|
|
$stabilOffset = -$naikDash;
|
|
$turunOffset = -($naikDash + $stabilDash);
|
|
@endphp
|
|
<defs>
|
|
<linearGradient id="gradNaik" x1="0" y1="0" x2="1"
|
|
y2="1">
|
|
<stop offset="0%" stop-color="#3b82f6" />
|
|
<stop offset="100%" stop-color="#1d4ed8" />
|
|
</linearGradient>
|
|
<linearGradient id="gradStabil" x1="0" y1="0" x2="1"
|
|
y2="1">
|
|
<stop offset="0%" stop-color="#34d399" />
|
|
<stop offset="100%" stop-color="#059669" />
|
|
</linearGradient>
|
|
<linearGradient id="gradTurun" x1="0" y1="0" x2="1"
|
|
y2="1">
|
|
<stop offset="0%" stop-color="#fbbf24" />
|
|
<stop offset="100%" stop-color="#f59e0b" />
|
|
</linearGradient>
|
|
<filter id="shadowGlow" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feDropShadow dx="0" dy="6" stdDeviation="6"
|
|
flood-color="rgba(0,0,0,0.25)" />
|
|
</filter>
|
|
</defs>
|
|
<circle class="donut-slice" data-label="Naik" data-value="{{ $persenNaik }}"
|
|
stroke="url(#gradNaik)" filter="url(#shadowGlow)" cx="100" cy="100"
|
|
r="70" stroke-dasharray="{{ $naikDash }} {{ $circumference }}"
|
|
stroke-dashoffset="{{ $naikOffset }}" />
|
|
<circle class="donut-slice" data-label="Stabil" data-value="{{ $persenStabil }}"
|
|
stroke="url(#gradStabil)" filter="url(#shadowGlow)" cx="100"
|
|
cy="100" r="70"
|
|
stroke-dasharray="{{ $stabilDash }} {{ $circumference }}"
|
|
stroke-dashoffset="{{ $stabilOffset }}" />
|
|
<circle class="donut-slice" data-label="Turun" data-value="{{ $persenTurun }}"
|
|
stroke="url(#gradTurun)" filter="url(#shadowGlow)" cx="100" cy="100"
|
|
r="70" stroke-dasharray="{{ $turunDash }} {{ $circumference }}"
|
|
stroke-dashoffset="{{ $turunOffset }}" />
|
|
</svg>
|
|
<div class="donut-center"></div>
|
|
<div class="donut-legend">
|
|
<span class="legend-item" data-index="0">
|
|
<span class="dot red"></span> Naik
|
|
</span>
|
|
<span class="legend-item" data-index="1">
|
|
<span class="dot green"></span> Stabil
|
|
</span>
|
|
<span class="legend-item" data-index="2">
|
|
<span class="dot yellow"></span> Turun
|
|
</span>
|
|
</div>
|
|
<div class="tooltip-donut" id="tooltipDonut"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="section-title table-title-flex" id="judulTabel">
|
|
<div>
|
|
Data Monitoring Prediksi
|
|
<span id="periodeText" style="font-weight:600;"></span>
|
|
</div>
|
|
<div class="table-title-right">
|
|
<div class="table-length-wrapper">
|
|
<span>Tampilkan </span>
|
|
<select id="rowsPerPageSelect" class="rows-select">
|
|
<option value="10" selected>10</option>
|
|
<option value="25">25</option>
|
|
<option value="50">50</option>
|
|
<option value="100">100</option>
|
|
</select>
|
|
<span> data</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="table-wrapper">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th class="sticky-col sticky-kode">
|
|
Kode
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th class="sticky-col sticky-nama sticky-shadow">
|
|
Nama Barang
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Jenis
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Satuan
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Pemakaian Terakhir
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Waktu Pemakaian Terakhir
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Prediksi Sekarang
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Selisih
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Status
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>
|
|
Waktu Prediksi
|
|
<span class="sort-icons">
|
|
<i class="fa-solid fa-chevron-up"></i>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</span>
|
|
</th>
|
|
<th>Aksi</th>
|
|
{{-- <th>Fluktuatif</th> --}}
|
|
{{-- <th>Rata-rata 3 Bulan</th> --}}
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tableBody">
|
|
@forelse ($monitoringData as $row)
|
|
<tr>
|
|
<td class="sticky-col sticky-kode">
|
|
<span class="kode-badge">{{ $row->kode }}</span>
|
|
</td>
|
|
<td class="sticky-col sticky-nama sticky-shadow">
|
|
{{ $row->nama_barang }}
|
|
</td>
|
|
<td>{{ $row->jenis }}</td>
|
|
<td>{{ $row->satuan }}</td>
|
|
{{-- <td>
|
|
@if ($row->fluktuatif == 1)
|
|
<span class="badge yellow">Fluktuatif</span>
|
|
@else
|
|
<span class="badge green">Stabil</span>
|
|
@endif
|
|
</td> --}}
|
|
<td>{{ $row->pemakaian_terakhir ?? '-' }}</td>
|
|
<td>
|
|
@if ($row->bulan_terakhir && $row->tahun_terakhir)
|
|
{{ \Carbon\Carbon::create()->month($row->bulan_terakhir)->translatedFormat('F') }},
|
|
{{ $row->tahun_terakhir }}
|
|
@else
|
|
-
|
|
@endif
|
|
</td>
|
|
<td>{{ $row->jumlah_prediksi }}</td>
|
|
<td>
|
|
@if ($row->selisih > 0)
|
|
+{{ $row->selisih }}
|
|
@else
|
|
{{ $row->selisih }}
|
|
@endif
|
|
</td>
|
|
<td>
|
|
@if ($row->status == 'naik')
|
|
<span class="badge blue">Naik</span>
|
|
@elseif ($row->status == 'turun')
|
|
<span class="badge yellow">Turun</span>
|
|
@else
|
|
<span class="badge green">Stabil</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
@if ($row->tanggal_prediksi)
|
|
{{ \Carbon\Carbon::parse($row->tanggal_prediksi)->translatedFormat('d F Y') }}
|
|
@else
|
|
-
|
|
@endif
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-delete"
|
|
onclick="confirmDelete('{{ $row->kode }}')">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="12" style="text-align:center;">
|
|
Tidak ada data monitoring
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="table-footer">
|
|
<div class="table-info" id="tableInfo">
|
|
Menampilkan 1 sampai {{ count($monitoringData) }} dari {{ count($monitoringData) }} data
|
|
</div>
|
|
<div class="pagination" id="pagination"></div>
|
|
</div>
|
|
</div>
|
|
{{-- <div class="card">
|
|
<div class="section-title">Monitoring Sistem & Sinkronisasi</div>
|
|
<div class="summary-grid">
|
|
<div class="summary-card">
|
|
<div class="title">Sinkronisasi Terakhir</div>
|
|
<div class="value" style="font-size:20px;">
|
|
17 Mei 2026
|
|
</div>
|
|
<div class="desc">08:14 WIB</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<div class="title">Data Berhasil Sinkron</div>
|
|
<div class="value">12.540</div>
|
|
<div class="desc">Detail transaksi</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<div class="title">Data Gagal Sinkron</div>
|
|
<div class="value">3</div>
|
|
<div class="desc">Perlu pengecekan</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<div class="title">Status Sistem</div>
|
|
<div class="value" style="font-size:22px;">
|
|
Aktif
|
|
</div>
|
|
<div class="desc">Semua layanan normal</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="section-title">Aktivitas Terakhir</div>
|
|
<div class="activity-item">
|
|
<strong>Prediksi berhasil dibuat</strong>
|
|
146 data prediksi berhasil diproses pada 17 Mei 2026 08:10 WIB
|
|
</div>
|
|
<div class="activity-item">
|
|
<strong>Sinkronisasi berhasil</strong>
|
|
Sistem berhasil mengambil data transaksi terbaru
|
|
</div>
|
|
<div class="activity-item">
|
|
<strong>Deteksi anomali</strong>
|
|
12 barang mengalami perubahan prediksi signifikan
|
|
</div>
|
|
</div> --}}
|
|
<div class="footer-red">
|
|
<div>
|
|
<strong>SIMDONDAR</strong> — Sistem Monitoring Prediksi Barang Habis Pakai
|
|
</div>
|
|
<div>
|
|
UDD PMI Kabupaten Jember
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="deleteModal" class="modal-overlay">
|
|
<div class="modal-box">
|
|
<div class="modal-icon">
|
|
<i class="bi bi-exclamation-triangle"></i>
|
|
</div>
|
|
<h3>Konfirmasi Hapus</h3>
|
|
<p>Data monitoring ini akan dihapus permanen.</p>
|
|
<div class="modal-action">
|
|
<button class="btn-cancel" onclick="closeModal()">Batal</button>
|
|
<button class="btn-confirm" id="btnConfirmDelete">Hapus</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const availablePeriods = @json($availablePeriods);
|
|
|
|
function cekPeriode() {
|
|
const periode = document.getElementById('periode').value;
|
|
const btnFilter = document.getElementById('btnFilter');
|
|
const btnPrint = document.getElementById('btnPrint');
|
|
const note = document.getElementById('notePeriode');
|
|
if (!periode) {
|
|
btnFilter.disabled = true;
|
|
btnFilter.style.opacity = 0.5;
|
|
btnFilter.style.cursor = 'not-allowed';
|
|
btnPrint.disabled = true;
|
|
btnPrint.style.opacity = 0.5;
|
|
btnPrint.style.cursor = 'not-allowed';
|
|
note.style.display = 'none';
|
|
return;
|
|
}
|
|
if (availablePeriods.includes(periode)) {
|
|
btnFilter.disabled = false;
|
|
btnFilter.style.opacity = 1;
|
|
btnFilter.style.cursor = 'pointer';
|
|
btnPrint.disabled = false;
|
|
btnPrint.style.opacity = 1;
|
|
btnPrint.style.cursor = 'pointer';
|
|
note.style.display = 'none';
|
|
} else {
|
|
btnFilter.disabled = true;
|
|
btnFilter.style.opacity = 0.5;
|
|
btnFilter.style.cursor = 'not-allowed';
|
|
btnPrint.disabled = true;
|
|
btnPrint.style.opacity = 0.5;
|
|
btnPrint.style.cursor = 'not-allowed';
|
|
note.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
cekPeriode();
|
|
window.filterApplied = true;
|
|
const periode = document.getElementById('periode').value;
|
|
document.getElementById('periodeText').innerText = formatPeriode(periode);
|
|
attachDonutTooltip();
|
|
const btn = document.getElementById("btnPrint");
|
|
btn.disabled = false;
|
|
btn.style.opacity = 1;
|
|
btn.style.cursor = "pointer";
|
|
});
|
|
document.getElementById('filterForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
submitFilter();
|
|
});
|
|
let searchTimeout = null;
|
|
document.querySelector('input[name="search"]').addEventListener('input', function() {
|
|
clearTimeout(searchTimeout);
|
|
searchTimeout = setTimeout(() => {
|
|
submitFilter();
|
|
}, 400);
|
|
});
|
|
|
|
function formatPeriode(periode) {
|
|
if (!periode) return '';
|
|
const [tahun, bulan] = periode.split('-');
|
|
const namaBulan = [
|
|
'', 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
|
|
'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'
|
|
];
|
|
return namaBulan[parseInt(bulan)] + ' ' + tahun;
|
|
}
|
|
|
|
let currentFetchController = null;
|
|
|
|
function submitFilter() {
|
|
if (currentFetchController) {
|
|
currentFetchController.abort();
|
|
}
|
|
currentFetchController = new AbortController();
|
|
window.filterApplied = true;
|
|
window.filterApplied = true;
|
|
cekPeriode();
|
|
const form = document.getElementById('filterForm');
|
|
const params = new URLSearchParams(new FormData(form)).toString();
|
|
const periode = document.getElementById('periode').value;
|
|
fetch("{{ route('monitoring-prediksi') }}?" + params, {
|
|
signal: currentFetchController.signal,
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
})
|
|
.then(res => res.text())
|
|
.then(html => {
|
|
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
const newTable = doc.querySelector('.table-wrapper');
|
|
document.querySelector('.table-wrapper').innerHTML = newTable.innerHTML;
|
|
const newDonut = doc.querySelector('.donut-wrapper');
|
|
document.querySelector('.donut-wrapper').innerHTML = newDonut.innerHTML;
|
|
const newSummary = doc.querySelector('#summaryWrapper');
|
|
document.querySelector('#summaryWrapper').innerHTML = newSummary.innerHTML;
|
|
doc.querySelectorAll("script").forEach(s => {
|
|
if (s.innerText.includes("window.donutRawData")) {
|
|
eval(s.innerText);
|
|
}
|
|
});
|
|
attachDonutTooltip();
|
|
initTableSorting();
|
|
initPagination();
|
|
document.getElementById("tooltipDonut").classList.remove("show");
|
|
document.getElementById('periodeText').innerText = formatPeriode(periode);
|
|
document.querySelector('.table-wrapper').scrollIntoView({
|
|
behavior: 'smooth'
|
|
});
|
|
})
|
|
.catch(err => {
|
|
if (err.name === 'AbortError') {
|
|
return;
|
|
}
|
|
console.error(err);
|
|
});
|
|
}
|
|
|
|
document.getElementById('btnPrint').addEventListener('click', function() {
|
|
const form = document.getElementById('filterForm');
|
|
const params = new URLSearchParams(new FormData(form)).toString();
|
|
window.location.href = "{{ route('monitoring.print') }}?" + params;
|
|
});
|
|
let deleteKode = null;
|
|
|
|
function confirmDelete(kode) {
|
|
deleteKode = kode;
|
|
document.getElementById('deleteModal').style.display = 'flex';
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('deleteModal').style.display = 'none';
|
|
deleteKode = null;
|
|
}
|
|
|
|
document.getElementById('btnConfirmDelete').addEventListener('click', function() {
|
|
if (!deleteKode) return;
|
|
fetch(`/monitoring/delete/${deleteKode}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(res => res.json())
|
|
.then(() => {
|
|
location.reload();
|
|
});
|
|
});
|
|
|
|
function initTableSorting() {
|
|
const table = document.querySelector("table");
|
|
if (!table) return;
|
|
const tbody = table.querySelector("tbody");
|
|
const headers = table.querySelectorAll("thead th");
|
|
headers.forEach((th, i) => {
|
|
const up = th.querySelector(".fa-chevron-up");
|
|
const down = th.querySelector(".fa-chevron-down");
|
|
if (!up || !down) return;
|
|
up.onclick = null;
|
|
down.onclick = null;
|
|
|
|
function getValue(row, index) {
|
|
return row.children[index].innerText.trim().toLowerCase();
|
|
}
|
|
|
|
function sortTable(asc = true) {
|
|
let rows = Array.from(tbody.querySelectorAll("tr"));
|
|
rows.sort((a, b) => {
|
|
let aVal = getValue(a, i);
|
|
let bVal = getValue(b, i);
|
|
let numA = parseFloat(aVal.replace(/[^0-9.-]/g, ''));
|
|
let numB = parseFloat(bVal.replace(/[^0-9.-]/g, ''));
|
|
if (!isNaN(numA) && !isNaN(numB)) {
|
|
return asc ? numA - numB : numB - numA;
|
|
}
|
|
return asc ?
|
|
aVal.localeCompare(bVal) :
|
|
bVal.localeCompare(aVal);
|
|
});
|
|
tbody.innerHTML = "";
|
|
rows.forEach(r => tbody.appendChild(r));
|
|
}
|
|
up.style.cursor = "pointer";
|
|
down.style.cursor = "pointer";
|
|
up.onclick = () => sortTable(true);
|
|
down.onclick = () => sortTable(false);
|
|
});
|
|
}
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
initTableSorting();
|
|
initPagination();
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
const tooltip = document.getElementById("tooltipDonut");
|
|
let currentPage = 1;
|
|
let rowsPerPage = 10;
|
|
|
|
function initPagination() {
|
|
const tbody = document.getElementById("tableBody");
|
|
if (!tbody) return;
|
|
const rows = Array.from(tbody.querySelectorAll("tr"));
|
|
if (!rows.length) return;
|
|
const pagination = document.getElementById("pagination");
|
|
const tableInfo = document.getElementById("tableInfo");
|
|
const totalRows = rows.length;
|
|
const totalPages = Math.ceil(totalRows / rowsPerPage);
|
|
const rowsSelect = document.getElementById("rowsPerPageSelect");
|
|
if (rowsSelect) {
|
|
rowsSelect.value = rowsPerPage;
|
|
rowsSelect.onchange = function() {
|
|
rowsPerPage = parseInt(this.value);
|
|
currentPage = 1;
|
|
initPagination();
|
|
};
|
|
}
|
|
|
|
function renderTable() {
|
|
const start = (currentPage - 1) * rowsPerPage;
|
|
const end = start + rowsPerPage;
|
|
rows.forEach((row, index) => {
|
|
row.style.display =
|
|
index >= start && index < end ?
|
|
"" :
|
|
"none";
|
|
});
|
|
const showingFrom = totalRows === 0 ? 0 : start + 1;
|
|
const showingTo = Math.min(end, totalRows);
|
|
tableInfo.innerText =
|
|
`Menampilkan ${showingFrom} sampai ${showingTo} dari ${totalRows} data`;
|
|
renderPagination();
|
|
}
|
|
|
|
function renderPagination() {
|
|
pagination.innerHTML = "";
|
|
const prevBtn = document.createElement("button");
|
|
prevBtn.innerHTML = `<i class="bi bi-chevron-left"></i>`;
|
|
prevBtn.disabled = currentPage === 1;
|
|
prevBtn.onclick = () => {
|
|
currentPage--;
|
|
renderTable();
|
|
};
|
|
pagination.appendChild(prevBtn);
|
|
|
|
function createPageButton(page) {
|
|
const btn = document.createElement("button");
|
|
btn.innerText = page;
|
|
if (page === currentPage) {
|
|
btn.classList.add("active");
|
|
}
|
|
btn.onclick = () => {
|
|
currentPage = page;
|
|
renderTable();
|
|
};
|
|
pagination.appendChild(btn);
|
|
}
|
|
|
|
function createDots() {
|
|
const dots = document.createElement("button");
|
|
dots.innerText = "...";
|
|
dots.disabled = true;
|
|
pagination.appendChild(dots);
|
|
}
|
|
if (totalPages <= 7) {
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
createPageButton(i);
|
|
}
|
|
} else {
|
|
createPageButton(1);
|
|
if (currentPage > 4) {
|
|
createDots();
|
|
}
|
|
let startPage = Math.max(2, currentPage - 1);
|
|
let endPage = Math.min(totalPages - 1, currentPage + 1);
|
|
if (currentPage <= 4) {
|
|
startPage = 2;
|
|
endPage = 5;
|
|
}
|
|
if (currentPage >= totalPages - 3) {
|
|
startPage = totalPages - 4;
|
|
endPage = totalPages - 1;
|
|
}
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
createPageButton(i);
|
|
}
|
|
if (currentPage < totalPages - 3) {
|
|
createDots();
|
|
}
|
|
createPageButton(totalPages);
|
|
}
|
|
const nextBtn = document.createElement("button");
|
|
nextBtn.innerHTML = `<i class="bi bi-chevron-right"></i>`;
|
|
nextBtn.disabled = currentPage === totalPages;
|
|
nextBtn.onclick = () => {
|
|
currentPage++;
|
|
renderTable();
|
|
};
|
|
pagination.appendChild(nextBtn);
|
|
}
|
|
renderTable();
|
|
}
|
|
|
|
function attachDonutTooltip() {
|
|
const tooltip = document.getElementById("tooltipDonut");
|
|
const donut = document.querySelector(".donut-svg");
|
|
if (!donut) return;
|
|
donut.querySelectorAll(".donut-slice").forEach(slice => {
|
|
slice.onmousemove = null;
|
|
slice.onmouseleave = null;
|
|
slice.addEventListener("mousemove", (e) => {
|
|
const label = slice.dataset.label;
|
|
const percent = parseFloat(slice.dataset.value) || 0;
|
|
const rawData = window.donutRawData || {};
|
|
const realValue = rawData[label] || 0;
|
|
tooltip.innerHTML = `
|
|
<div class="tooltip-row">
|
|
<span class="tooltip-color" style="background:${slice.getAttribute("stroke")}"></span>
|
|
<div>
|
|
<div class="tooltip-title">${label}</div>
|
|
<div class="tooltip-value">
|
|
${Math.round(percent)}% (${realValue} item)
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
tooltip.style.left = e.clientX + "px";
|
|
tooltip.style.top = e.clientY + "px";
|
|
tooltip.classList.add("show");
|
|
});
|
|
slice.addEventListener("mouseleave", () => {
|
|
tooltip.classList.remove("show");
|
|
});
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
window.donutRawData = {
|
|
Naik: {{ $totalNaik }},
|
|
Stabil: {{ $totalStabil }},
|
|
Turun: {{ $totalTurun }}
|
|
};
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|