From 89bf8ec287076c14dbe60ee905680cf0dfb30e18 Mon Sep 17 00:00:00 2001 From: zadit biasa aja <75159257+everythingonblack@users.noreply.github.com> Date: Tue, 1 Jul 2025 10:21:44 +0000 Subject: [PATCH] ok --- src/Dashboard.js | 64 ++++-- src/Dashboard.module.css | 477 +++++++++++++++++++-------------------- src/KTPScanner.js | 6 +- src/Login.js | 4 +- src/Login.module.css | 7 +- src/PSI.png | Bin 0 -> 5245 bytes 6 files changed, 281 insertions(+), 277 deletions(-) create mode 100644 src/PSI.png diff --git a/src/Dashboard.js b/src/Dashboard.js index ee76d3a..1ee3ad1 100644 --- a/src/Dashboard.js +++ b/src/Dashboard.js @@ -144,10 +144,23 @@ const Dashboard = () => { {isMenuOpen && (
@@ -155,7 +168,7 @@ const Dashboard = () => { onClick={() => { navigate("/profile"); setIsMenuOpen(false); - }} /* Tutup menu setelah klik */ + }} className={styles.dropdownItem} > Profile @@ -164,7 +177,7 @@ const Dashboard = () => { onClick={() => { navigate("/scan"); setIsMenuOpen(false); - }} /* Tutup menu setelah klik */ + }} className={styles.dropdownItem} > Scan @@ -173,7 +186,7 @@ const Dashboard = () => { onClick={() => { handleLogout(); setIsMenuOpen(false); - }} /* Tutup menu setelah klik */ + }} className={styles.dropdownItem} > Logout @@ -187,45 +200,47 @@ const Dashboard = () => { {/* Summary Cards */}
-

Total Hari Ini

-

{totalFilesSentToday}

+

Hari Ini

+

{totalFilesSentToday.toLocaleString()}

-

Total Bulan Ini

-

{totalFilesSentMonth}

+

Bulan Ini

+

{totalFilesSentMonth.toLocaleString()}

Total Keseluruhan

-

{totalFilesSentOverall}

+

{totalFilesSentOverall.toLocaleString()}

{/* Grid for Form (Admin) and Chart (Admin & Officer) */}
- {user.role === "admin" /* Render form hanya jika admin */ && ( + {user.role === "admin" && (

Tambah Officer Baru

@@ -238,7 +253,7 @@ const Dashboard = () => { {/* Chart Section - Visible to both Admin and Officer */}
-

Performa Pengiriman File Petugas

+

Performa Pengiriman File

{officerPerformanceData.length > 0 ? ( // Contoh implementasi Recharts: /* @@ -249,25 +264,28 @@ const Dashboard = () => { */
- Grafik performa petugas akan ditampilkan di sini. (Integrasikan - library grafik seperti Recharts/Chart.js) + 📊 Grafik performa akan ditampilkan di sini +
+ Integrasikan dengan Recharts atau Chart.js
) : ( -

- Tidak ada data performa petugas untuk ditampilkan. -

+
+ 📋 Belum ada data performa untuk ditampilkan +
)}
-
© 2025 Kediri Technopark
+
+ © 2025 Kediri Technopark • Dashboard PSI +
); }; diff --git a/src/Dashboard.module.css b/src/Dashboard.module.css index 6fdc63a..0667e94 100644 --- a/src/Dashboard.module.css +++ b/src/Dashboard.module.css @@ -1,31 +1,48 @@ -/* Variabel Warna */ +/* Modern Color Palette */ :root { - --primary-red: #e53935; - --secondary-red: #ef5350; - --dark-red: #c62828; - --light-gray: #eeeeee; - --dark-gray: #424242; + --primary-blue: #3b82f6; + --secondary-blue: #60a5fa; + --dark-blue: #1e40af; + --neutral-50: #fafafa; + --neutral-100: #f5f5f5; + --neutral-200: #e5e5e5; + --neutral-300: #d4d4d4; + --neutral-500: #737373; + --neutral-700: #404040; + --neutral-800: #262626; + --neutral-900: #171717; --white: #ffffff; - --success-green: #4caf50; - --warning-yellow: #ffc107; - --text-color-light: #f5f5f5; /* Teks terang di latar belakang gelap */ - --text-color-dark: #212121; /* Teks gelap di latar belakang terang */ + --success-green: #10b981; + --warning-amber: #f59e0b; + --error-red: #ef4444; + --text-primary: #0f172a; + --text-secondary: #64748b; + --text-light: #ffffff; + --border-light: #e2e8f0; + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), + 0 4px 6px -4px rgb(0 0 0 / 0.1); } /* Base Styles & Reset */ * { - box-sizing: border-box; /* Pastikan padding dan border tidak menambah lebar elemen */ + box-sizing: border-box; } body { margin: 0; - font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; /* Font lebih modern */ - line-height: 1.6; - color: var(--text-color-dark); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; + line-height: 1.5; + color: var(--text-primary); + background-color: var(--neutral-50); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } .dashboardContainer { - background-color: var(--light-gray); + background-color: var(--neutral-50); min-height: 100vh; display: flex; flex-direction: column; @@ -33,33 +50,40 @@ body { /* --- Header --- */ .dashboardHeader { - background-color: var(--primary-red); - color: var(--text-color-light); - padding: 1rem; /* Menggunakan rem */ + background-color: var(--white); + color: var(--text-primary); + padding: 1rem 1.5rem; display: flex; justify-content: space-between; align-items: center; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + box-shadow: var(--shadow-sm); + border-bottom: 3px solid #ef4444; + position: sticky; + top: 0; + z-index: 50; + backdrop-filter: blur(8px); } .logoAndTitle { display: flex; align-items: center; - flex-shrink: 0; /* Mencegah logo dan judul mengecil terlalu banyak */ + flex-shrink: 0; } .logoAndTitle img { - width: 2.5rem; /* Ukuran rem */ + width: 2.5rem; height: 2.5rem; - border-radius: 50%; - margin-right: 0.8rem; + border-radius: 0.75rem; + margin-right: 0.75rem; + object-fit: cover; } .dashboardHeader .h1 { margin: 0; - font-size: 1.5rem; /* Ukuran rem untuk mobile */ - font-weight: bold; - white-space: nowrap; /* Mencegah judul patah baris */ + font-size: 1.5rem; + font-weight: 700; + color: #ed4344; + letter-spacing: -0.025em; } /* Dropdown Menu */ @@ -67,238 +91,262 @@ body { position: relative; display: flex; align-items: center; - gap: 0.8rem; - flex-shrink: 0; /* Mencegah mengecil */ + gap: 0.75rem; + flex-shrink: 0; } .userDisplayName { - color: var(--text-color-light); - font-weight: bold; - font-size: 0.9rem; - white-space: nowrap; /* Pastikan nama pengguna tidak patah baris */ + color: var(--text-secondary); + font-weight: 500; + font-size: 0.875rem; } .dropdownToggle { - background-color: var(--dark-red); - color: var(--text-color-light); - border: none; - padding: 0.5rem 0.75rem; /* Padding rem */ - border-radius: 0.3rem; + background-color: var(--neutral-100); + color: var(--text-primary); + border: 1px solid var(--border-light); + padding: 0.5rem; + border-radius: 0.5rem; cursor: pointer; font-size: 1rem; - transition: background-color 0.3s ease; - min-width: 3rem; /* Pastikan tombol tidak terlalu kecil */ + transition: all 0.2s ease; + min-width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; } .dropdownToggle:hover { - background-color: var(--secondary-red); + background-color: var(--neutral-200); + border-color: var(--neutral-300); } .dropdownMenu { position: absolute; - top: calc(100% + 0.5rem); /* Posisikan di bawah tombol dengan jarak */ + top: calc(100% + 0.5rem); right: 0; background-color: var(--white); - border-radius: 0.3rem; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + border-radius: 0.75rem; + box-shadow: var(--shadow-lg); + border: 1px solid var(--border-light); z-index: 10; display: flex; flex-direction: column; - min-width: 8rem; /* Lebar minimum */ - overflow: hidden; /* Pastikan item tidak keluar */ + min-width: 10rem; + overflow: hidden; + padding: 0.5rem; } .dropdownItem { background: none; border: none; - padding: 0.6rem 0.8rem; + padding: 0.75rem 1rem; text-align: left; cursor: pointer; - color: var(--text-color-dark); - transition: background-color 0.3s ease; - font-size: 0.9rem; - white-space: nowrap; /* Mencegah item menu patah baris */ + color: var(--text-primary); + transition: background-color 0.2s ease; + font-size: 0.875rem; + font-weight: 500; + border-radius: 0.5rem; + margin-bottom: 0.125rem; } .dropdownItem:hover { - background-color: var(--light-gray); + background-color: var(--neutral-100); +} + +.dropdownItem:last-child { + margin-bottom: 0; } /* --- Main Content --- */ .mainContent { flex-grow: 1; - padding: 1.25rem; /* Padding rem */ + padding: 2rem 1.5rem; display: flex; flex-direction: column; - gap: 1.5rem; /* Jarak antar bagian utama */ + gap: 2rem; + max-width: 1400px; + margin: 0 auto; + width: 100%; } /* Summary Cards Container */ .summaryCardsContainer { - display: flex; - flex-wrap: wrap; /* Mengizinkan kartu melipat */ - gap: 1rem; /* Jarak antar kartu */ - justify-content: center; /* Tengahkan kartu di mobile */ - padding: 1rem; - background-color: var(--white); - border-radius: 0.6rem; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; } .summaryCard { - background-color: var(--light-gray); - color: var(--text-color-dark); - padding: 1rem; - border-radius: 0.5rem; - border: 1px solid var(--secondary-red); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - text-align: center; - flex: 1 1 calc(50% - 0.75rem); /* Di layar mobile, 2 kartu per baris */ - min-width: 120px; /* Batasan agar tidak terlalu kecil */ - max-width: 250px; /* Batasan agar tidak terlalu lebar di mobile */ + background-color: var(--white); + padding: 1.5rem; + border-radius: 1rem; + border: 1px solid var(--border-light); + box-shadow: var(--shadow-sm); + transition: all 0.2s ease; +} + +.summaryCard:hover { + box-shadow: var(--shadow-md); + transform: translateY(-1px); } .summaryCard h3 { - margin-top: 0; - font-size: 0.9rem; - color: var(--dark-red); + margin: 0 0 0.5rem 0; + font-size: 0.875rem; + color: var(--text-secondary); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.05em; } .summaryCard p { - font-size: 1.6rem; - font-weight: bold; - color: var(--dark-gray); - margin-bottom: 0; + font-size: 2rem; + font-weight: 700; + color: #ef4444; + margin: 0; + line-height: 1; } /* Dashboard Grid for Form and Chart */ .dashboardGrid { display: grid; - grid-template-columns: 1fr; /* Default: satu kolom untuk mobile */ - gap: 1.5rem; /* Jarak antar bagian */ - flex-grow: 1; /* Agar grid bisa membesar mengisi sisa ruang */ + grid-template-columns: 1fr; + gap: 2rem; + flex-grow: 1; } .formSection, .chartSection { background-color: var(--white); - padding: 1.5rem; /* Padding rem */ - border-radius: 0.6rem; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + padding: 2rem; + border-radius: 1rem; + border: 1px solid var(--border-light); + box-shadow: var(--shadow-sm); } .formSection h2, .chartSection h2 { - color: var(--dark-red); - margin-top: 0; - margin-bottom: 1.25rem; - text-align: center; - font-size: 1.4rem; + color: var(--text-primary); + margin: 0 0 1.5rem 0; + font-size: 1.25rem; + font-weight: 600; + letter-spacing: -0.025em; } .form label { display: block; - margin-bottom: 0.8rem; - color: var(--text-color-dark); - font-weight: bold; - font-size: 0.95rem; + margin-bottom: 1rem; + color: var(--text-primary); + font-weight: 500; + font-size: 0.875rem; } .form input[type="text"], .form input[type="password"], .form select { width: 100%; - padding: 0.75rem; - margin-top: 0.3rem; - border: 1px solid var(--light-gray); - border-radius: 0.3rem; - font-size: 1rem; - transition: border-color 0.3s ease, box-shadow 0.3s ease; + padding: 0.75rem 1rem; + margin-top: 0.375rem; + border: 1px solid var(--border-light); + border-radius: 0.5rem; + font-size: 0.875rem; + transition: all 0.2s ease; + background-color: var(--white); + color: var(--text-primary); } .form input[type="text"]:focus, .form input[type="password"]:focus, .form select:focus { - border-color: var(--primary-red); - box-shadow: 0 0 0 3px rgba(229, 57, 53, 0.2); /* Ring focus */ + border-color: var(--primary-blue); + box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1); outline: none; } .submitButton { - background-color: var(--primary-red); - color: var(--white); + background-color: var(--primary-blue); + color: var(--text-light); border: none; - padding: 0.8rem 1.5rem; + padding: 0.75rem 1.5rem; border-radius: 0.5rem; cursor: pointer; - font-size: 1rem; - font-weight: bold; - margin-top: 1.25rem; + font-size: 0.875rem; + font-weight: 600; + margin-top: 1rem; width: 100%; - transition: background-color 0.3s ease, transform 0.2s ease; + transition: all 0.2s ease; + letter-spacing: 0.025em; } .submitButton:hover { - background-color: var(--secondary-red); - transform: translateY(-2px); /* Efek sedikit naik */ + background-color: var(--dark-blue); + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.submitButton:active { + transform: translateY(0); } /* Messages */ .success { - background-color: #e6ffe6; + background-color: rgb(16 185 129 / 0.1); color: var(--success-green); - border: 1px solid var(--success-green); - padding: 0.8rem; - border-radius: 0.3rem; + border: 1px solid rgb(16 185 129 / 0.2); + padding: 0.75rem 1rem; + border-radius: 0.5rem; margin-top: 1rem; - text-align: center; - font-size: 0.9rem; + font-size: 0.875rem; + font-weight: 500; } .error { - background-color: #ffe6e6; - color: var(--primary-red); - border: 1px solid var(--primary-red); - padding: 0.8rem; - border-radius: 0.3rem; + background-color: rgb(239 68 68 / 0.1); + color: var(--error-red); + border: 1px solid rgb(239 68 68 / 0.2); + padding: 0.75rem 1rem; + border-radius: 0.5rem; margin-top: 1rem; - text-align: center; - font-size: 0.9rem; + font-size: 0.875rem; + font-weight: 500; } .warning { - background-color: #fffbe6; - color: var(--warning-yellow); - border: 1px solid var(--warning-yellow); + background-color: rgb(245 158 11 / 0.1); + color: var(--warning-amber); + border: 1px solid rgb(245 158 11 / 0.2); padding: 1rem; - border-radius: 0.3rem; - margin-top: 1.25rem; - text-align: center; - font-weight: bold; - font-size: 1rem; + border-radius: 0.5rem; + margin-top: 1rem; + font-weight: 500; + font-size: 0.875rem; } /* Footer */ .footer { - background-color: var(--dark-red); - color: var(--text-color-light); + background-color: var(--white); + color: var(--text-secondary); text-align: center; - padding: 0.75rem; + padding: 1rem; margin-top: auto; - font-size: 0.85rem; + font-size: 0.75rem; + border-top: 1px solid var(--border-light); } .chartPlaceholder { - background-color: var(--light-gray); - height: 20rem; /* Menggunakan rem */ + background-color: var(--neutral-50); + height: 20rem; display: flex; justify-content: center; align-items: center; - color: var(--dark-gray); + color: var(--text-secondary); font-style: italic; - border-radius: 0.6rem; - border: 1px dashed var(--secondary-red); - font-size: 1rem; + border-radius: 0.75rem; + border: 2px dashed var(--border-light); + font-size: 0.875rem; } /* --- Media Queries for Tablets and Desktops --- */ @@ -315,119 +363,24 @@ body { } .dashboardHeader .h1 { - font-size: 1.8rem; + font-size: 1.75rem; } .userDisplayName { - font-size: 1rem; - } - - .dropdownToggle { - padding: 0.6rem 1rem; - font-size: 1rem; - } - - .dropdownMenu { - min-width: 10rem; - } - - .dropdownItem { - font-size: 1rem; + font-size: 0.875rem; } .mainContent { - padding: 1.5rem 2.5rem; /* Padding lebih besar di tablet/desktop */ - gap: 2rem; - } - - .summaryCardsContainer { - justify-content: flex-start; /* Sejajarkan ke kiri di tablet/desktop */ - padding: 1.5rem; - } - - .summaryCard { - flex: 1 1 calc(33.33% - 1rem); /* 3 kartu per baris di tablet */ - max-width: 200px; /* Batasi lebar kartu agar tetap rapi */ - } - - .summaryCard h3 { - font-size: 1rem; - } - - .summaryCard p { - font-size: 2rem; - } - - .dashboardGrid { - grid-template-columns: 1fr 2fr; /* Dua kolom: form (1/3), chart (2/3) */ - gap: 2rem; - } - - .formSection, - .chartSection { - padding: 2rem; - } - - .formSection h2, - .chartSection h2 { - font-size: 1.6rem; - } - - .form label { - font-size: 1rem; - } - - .form input[type="text"], - .form input[type="password"], - .form select { - padding: 0.8rem; - font-size: 1rem; - } - - .submitButton { - padding: 1rem 1.8rem; - font-size: 1.1rem; - } - - .warning { - font-size: 1rem; - } - - .chartPlaceholder { - height: 25rem; /* Tinggi placeholder lebih besar di tablet/desktop */ - } -} - -/* Desktop-sized screens and up */ -@media (min-width: 1024px) { - .dashboardHeader { - padding: 1.5rem 3rem; - } - - .logoAndTitle img { - width: 3.5rem; - height: 3.5rem; - } - - .dashboardHeader .h1 { - font-size: 2.2rem; - } - - .mainContent { - padding: 2rem 4rem; + padding: 2.5rem 2rem; gap: 2.5rem; } .summaryCardsContainer { - padding: 2rem; - } - - .summaryCard { - flex: 0 0 auto; /* Ukuran tetap di desktop */ - min-width: 180px; + grid-template-columns: repeat(3, 1fr); } .dashboardGrid { + grid-template-columns: 1fr 2fr; gap: 2.5rem; } @@ -438,11 +391,41 @@ body { .formSection h2, .chartSection h2 { - font-size: 1.8rem; + font-size: 1.5rem; } - .submitButton { - padding: 1.1rem 2rem; + .chartPlaceholder { + height: 25rem; + } +} + +/* Desktop-sized screens and up */ +@media (min-width: 1024px) { + .dashboardHeader { + padding: 1.25rem 3rem; + } + + .logoAndTitle img { + width: 3.5rem; + height: 3.5rem; + } + + .dashboardHeader .h1 { + font-size: 2rem; + } + + .mainContent { + padding: 3rem 2.5rem; + gap: 3rem; + } + + .dashboardGrid { + gap: 3rem; + } + + .formSection, + .chartSection { + padding: 3rem; } .chartPlaceholder { @@ -450,11 +433,9 @@ body { } } -/* Jika hanya officer, pastikan chart mengambil seluruh lebar kolom */ -/* Ini sebenarnya sudah ditangani oleh grid secara default, tapi bisa diperjelas */ +/* Single column layout when only one section is present */ @media (min-width: 768px) { .dashboardGrid > *:only-child { - /* Jika hanya ada satu anak elemen di dalam dashboardGrid */ - grid-column: 1 / -1; /* Ambil seluruh lebar dari kolom pertama hingga terakhir */ + grid-column: 1 / -1; } } diff --git a/src/KTPScanner.js b/src/KTPScanner.js index 7bd1a38..87fb74b 100644 --- a/src/KTPScanner.js +++ b/src/KTPScanner.js @@ -267,12 +267,16 @@ const CameraCanvas = () => { const ReadImage = async (capturedImage) => { try { setLoading(true); + const token = localStorage.getItem("token"); let res = await fetch( "https://bot.kediritechnopark.com/webhook/mastersnapper/read", { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, body: JSON.stringify({ image: capturedImage }), } ); diff --git a/src/Login.js b/src/Login.js index c9f045c..f30f286 100644 --- a/src/Login.js +++ b/src/Login.js @@ -35,7 +35,7 @@ const Login = () => { if (loginData?.success && loginData?.token) { localStorage.setItem("token", loginData.token); - window.location.href = "/dashboard"; + window.location.href = "/"; } else { setError(loginData?.message || "Username atau password salah"); } @@ -48,7 +48,7 @@ const Login = () => { return (
- Logo + Logo

PSI Admin Login

Silakan masuk untuk melanjutkan ke dashboard diff --git a/src/Login.module.css b/src/Login.module.css index a744827..09cfcf7 100644 --- a/src/Login.module.css +++ b/src/Login.module.css @@ -13,7 +13,8 @@ border-radius: 16px; padding: 40px; box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.05); - width: 300px; + width: 100%; + max-width: 400px; text-align: center; } @@ -27,7 +28,7 @@ font-size: 28px; font-weight: 700; margin-bottom: 10px; - color: #d22129; /* 🔴 Warna merah PSI */ + color: #ef4444; /* 🔴 Warna merah PSI */ } .subtitle { @@ -55,7 +56,7 @@ } .button { - background-color: #d22129; /* 🔴 Warna merah PSI */ + background-color: #ef4444; /* 🔴 Warna merah PSI */ color: #ffffff; padding: 12px 24px; border-radius: 24px; diff --git a/src/PSI.png b/src/PSI.png new file mode 100644 index 0000000000000000000000000000000000000000..f7075fb9de258e05d9f21690030c042773c99b2f GIT binary patch literal 5245 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