From ab2afbb27fa42e7897afc2760a08912965aea303 Mon Sep 17 00:00:00 2001 From: nico Date: Fri, 13 Feb 2026 11:34:43 +0800 Subject: [PATCH] feat: implement help page with equal height cards Co-authored-by: Qwen-Coder --- Dashboard-MD/BANTUAN.md | 453 ++++++++++++++++++++++++++++++++ src/components/help-page.tsx | 246 +++++++++++++++++ src/components/ui/help-card.tsx | 90 +++++++ src/routeTree.gen.ts | 21 ++ src/routes/dashboard/bantuan.ts | 7 + 5 files changed, 817 insertions(+) create mode 100644 Dashboard-MD/BANTUAN.md create mode 100644 src/components/help-page.tsx create mode 100644 src/components/ui/help-card.tsx create mode 100644 src/routes/dashboard/bantuan.ts diff --git a/Dashboard-MD/BANTUAN.md b/Dashboard-MD/BANTUAN.md new file mode 100644 index 0000000..326ff08 --- /dev/null +++ b/Dashboard-MD/BANTUAN.md @@ -0,0 +1,453 @@ +Design Contract – Halaman Bantuan / Support Center + +Dokumen ini adalah kontrak desain UI/UX untuk halaman Bantuan / Support Center. +Implementasi tidak dapat dilanjutkan tanpa mengikuti spesifikasi ini secara konsisten. + +1. Tujuan Halaman + +Halaman Bantuan bertujuan untuk: + +Memberikan akses cepat ke panduan & dokumentasi + +Menyediakan video tutorial + +Menjawab pertanyaan umum (FAQ) + +Menyediakan kontak support + +Menyediakan virtual assistant (chat) + +Menampilkan statistik kredibilitas platform + +2. Struktur Layout Keseluruhan +Layout Grid + +Menggunakan 3-column responsive grid. + +Desktop (≥ 1200px) + +Grid: 3 columns + +Gap: 32px + +Container max-width: 1200px – 1320px + +Center aligned + +Tablet (768px – 1199px) + +Grid: 2 columns + +Gap: 24px + +Mobile (< 768px) + +Grid: 1 column + +Gap: 16px + +Card full width + +3. Struktur Komponen UI +3.1 Card Utama (Reusable Component) + +Semua section menggunakan komponen dasar: + +Card + ├── Icon Container + ├── Title + ├── List / Content + +Spesifikasi Card + +Border radius: 16px + +Padding: 24px + +Shadow: + +Light: soft shadow + +Dark: subtle glow / low-opacity shadow + +Transition hover: 0.2s ease + +Hover effect: + +Slight lift (translateY(-2px)) + +Elevation shadow increase + +4. Daftar Komponen +4.1 Panduan Memulai + +Isi: + +Cara Login + +Navigasi Dashboard + +Fitur Dasar + +Tips & Trik + +Interaksi: + +Klik item → navigasi ke halaman detail + +Cursor pointer + +Hover underline atau highlight + +4.2 Video Tutorial + +Isi: + +Dashboard Overview + +Analisis Data + +Membuat Laporan + +Export Data + +Interaksi: + +Klik → buka modal video atau halaman video + +Bisa tambahkan icon play kecil di tiap item + +4.3 FAQ + +Isi: + +Masalah Login + +Reset Password + +Akses Data + +Laporan Error + +Interaksi: + +Accordion expand/collapse + +Animasi height smooth (200–300ms) + +4.4 Hubungi Support + +Isi: + +Email + +WhatsApp + +Jam Kerja + +Waktu Respon + +Interaksi: + +Email → mailto link + +WhatsApp → wa.me link + +Jam kerja non-clickable + +4.5 Dokumentasi + +Isi: + +API Reference + +Integrasi Sistem + +Format Data + +Best Practices + +Interaksi: + +Klik → navigasi dokumentasi + +4.6 Jenna – Virtual Assistant + +Komponen: + +Chat Container + ├── Header Title + ├── Chat Message Area + ├── Input Field + └── Send Button + + +Spesifikasi: + +Tinggi tetap (±300–350px) + +Scrollable message container + +Input rounded + +Send button icon (arrow) + +Interaksi: + +Enter → kirim pesan + +Klik send → kirim pesan + +Auto-scroll ke pesan terakhir + +Disabled state saat loading + +4.7 Statistik Section + +3 Card horizontal: + +150+ Artikel Panduan + +50+ Video Tutorial + +24/7 Support Aktif + +Spesifikasi: + +Center aligned text + +Angka besar (font-size 32–40px) + +Subtext kecil opacity 70% + +5. Design System +5.1 Light Mode +Background + +Page background: #F5F7FA + +Card background: #FFFFFF + +Primary Color + +Blue: #3B82F6 + +Hover: #2563EB + +Text + +Primary: #111827 + +Secondary: #6B7280 + +Border + +#E5E7EB + +Shadow +0 4px 12px rgba(0,0,0,0.05) + +Icon Container + +Background: #EFF6FF + +Icon color: #2563EB + +5.2 Dark Mode +Background + +Page background: #0F172A + +Card background: #1E293B + +Primary Color + +Blue: #3B82F6 + +Hover: #60A5FA + +Text + +Primary: #F8FAFC + +Secondary: #94A3B8 + +Border + +#334155 + +Shadow +0 4px 20px rgba(0,0,0,0.4) + +Icon Container + +Background: #1D4ED8 + +Icon color: #FFFFFF + +6. Typography + +Font Family: + +Inter / Poppins / System UI + +Hierarchy: + +Element Size Weight +Section Title 18–20px 600 +Card Title 16–18px 600 +List Item 14–16px 400 +Statistik Number 32–40px 700 + +Line height: + +1.5 standard + +1.2 for large numbers + +7. Spacing System + +Gunakan sistem 8pt grid: + +8px + +16px + +24px + +32px + +48px + +Padding Card: 24px +Gap Grid Desktop: 32px + +8. Responsivitas +Desktop + +3 kolom utama + +Statistik 3 sejajar + +Tablet + +2 kolom + +Statistik 2 + 1 + +Mobile + +1 kolom + +Statistik stacked vertical + +Chat full width + +9. State & Interactivity Requirements + +Harus tersedia: + +Hover state + +Active state + +Focus state (accessibility) + +Disabled state + +Loading state (chat) + +Keyboard support: + +Tab navigable + +Enter kirim pesan + +Esc tutup modal (jika ada) + +10. Accessibility + +Minimum contrast ratio 4.5:1 + +Focus ring visible + +Button min-height 40px + +Click area minimal 44x44px + +11. Animasi + +Durasi standar: 200ms – 300ms +Easing: ease-in-out + +Digunakan untuk: + +Hover card + +Accordion FAQ + +Chat message appear + +Button press + +12. Toggle Dark / Light Mode + +Harus tersedia: + +Theme switcher + +Persist ke localStorage + +Default mengikuti system preference + +13. Data Dinamis + +Data yang harus bisa dinamis: + +Jumlah artikel + +Jumlah video + +Status support 24/7 + +Chat message list + +FAQ content + +Dokumentasi link + +14. Non-Functional Requirements + +Clean modular component + +Reusable Card component + +Maintainable theme config + +Dark & Light share same structure + +15. Kesimpulan + +Halaman ini menggunakan: + +Card-based layout + +Grid responsive + +Dual theme (Light & Dark) + +High clarity & readability + +Modern SaaS style + +Virtual assistant as engagement point + +Implementasi wajib mengikuti spesifikasi ini agar: + +Konsistensi visual terjaga + +User experience optimal + +Maintainability tinggi + +Skalabilitas mudah \ No newline at end of file diff --git a/src/components/help-page.tsx b/src/components/help-page.tsx new file mode 100644 index 0000000..3440a4b --- /dev/null +++ b/src/components/help-page.tsx @@ -0,0 +1,246 @@ +import { Container, Grid, Title, Text, SimpleGrid, Box, Accordion, Stack } from '@mantine/core'; +import { HelpCard } from '@/components/ui/help-card'; +import { IconBook, IconVideo, IconHelpCircle, IconMessage, IconFileText, IconHeadphones } from '@tabler/icons-react'; +import { useState } from 'react'; + +const HelpPage = () => { + // Sample data for sections + const guideItems = [ + { title: 'Cara Login', description: 'Langkah-langkah untuk login ke dashboard' }, + { title: 'Navigasi Dashboard', description: 'Penjelasan tentang tata letak dan navigasi' }, + { title: 'Fitur Dasar', description: 'Panduan penggunaan fitur-fitur utama' }, + { title: 'Tips & Trik', description: 'Tips untuk meningkatkan produktivitas' }, + ]; + + const videoItems = [ + { title: 'Dashboard Overview', duration: '5:23' }, + { title: 'Analisis Data', duration: '8:45' }, + { title: 'Membuat Laporan', duration: '6:12' }, + { title: 'Export Data', duration: '4:30' }, + ]; + + const faqItems = [ + { question: 'Bagaimana cara reset password?', answer: 'Anda dapat mereset password melalui halaman login dengan klik "Lupa Password"' }, + { question: 'Apakah saya bisa mengakses data offline?', answer: 'Saat ini aplikasi hanya dapat diakses secara online' }, + { question: 'Berapa lama waktu respon support?', answer: 'Tim support kami biasanya merespon dalam waktu kurang dari 24 jam' }, + { question: 'Bagaimana cara menambahkan pengguna baru?', answer: 'Fitur penambahan pengguna dapat ditemukan di menu Pengaturan > Manajemen Pengguna' }, + ]; + + const documentationItems = [ + { title: 'API Reference', description: 'Dokumentasi lengkap untuk integrasi API' }, + { title: 'Integrasi Sistem', description: 'Cara mengintegrasikan dengan sistem eksternal' }, + { title: 'Format Data', description: 'Spesifikasi format data yang didukung' }, + { title: 'Best Practices', description: 'Praktik terbaik dalam penggunaan platform' }, + ]; + + const stats = [ + { value: '150+', label: 'Artikel Panduan' }, + { value: '50+', label: 'Video Tutorial' }, + { value: '24/7', label: 'Support Aktif' }, + ]; + + // State for chat functionality + const [messages, setMessages] = useState([ + { id: 1, text: 'Halo! Saya Jenna, asisten virtual Anda. Bagaimana saya bisa membantu hari ini?', sender: 'jenna' } + ]); + const [inputValue, setInputValue] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const handleSendMessage = () => { + if (inputValue.trim() === '') return; + + // Add user message + const newUserMessage = { + id: messages.length + 1, + text: inputValue, + sender: 'user' + }; + + setMessages(prev => [...prev, newUserMessage]); + setInputValue(''); + setIsLoading(true); + + // Simulate Jenna's response after delay + setTimeout(() => { + const jennaResponse = { + id: messages.length + 2, + text: 'Terima kasih atas pertanyaan Anda. Saat ini saya adalah versi awal dari asisten virtual. Tim kami sedang mengembangkan kemampuan saya lebih lanjut.', + sender: 'jenna' + }; + setMessages(prev => [...prev, jennaResponse]); + setIsLoading(false); + }, 1000); + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + }; + + return ( + + Pusat Bantuan + + Temukan jawaban untuk pertanyaan Anda atau hubungi tim support kami + + + {/* Statistics Section */} + + {stats.map((stat, index) => ( + + {stat.value} + {stat.label} + + ))} + + + + + + {/* Panduan Memulai */} + + } title="Panduan Memulai" h="100%"> + + {guideItems.map((item, index) => ( + alert(`Navigasi ke ${item.title}`)}> + {item.title} + {item.description} + + ))} + + + + + {/* Video Tutorial */} + + } title="Video Tutorial" h="100%"> + + {videoItems.map((item, index) => ( + alert(`Buka video: ${item.title}`)}> + {item.title} + {item.duration} + + ))} + + + + + {/* FAQ */} + + } title="FAQ" h="100%"> + + {faqItems.map((item, index) => ( + + {item.question} + + {item.answer} + + + ))} + + + + + + + + + {/* Hubungi Support */} + + } title="Hubungi Support" h="100%"> + + Email + support@example.com + + WhatsApp + +62 123 456 7890 + + Jam Kerja + Senin - Jumat, 09:00 - 17:00 WIB + + Waktu Respon + Rata-rata 2-4 jam kerja + + + + + {/* Dokumentasi */} + + } title="Dokumentasi" h="100%"> + + {documentationItems.map((item, index) => ( + alert(`Navigasi ke dokumentasi: ${item.title}`)}> + {item.title} + {item.description} + + ))} + + + + + {/* Jenna - Virtual Assistant */} + + } title="Jenna - Virtual Assistant" h="100%"> + + + {messages.map((msg) => ( + + {msg.text} + + ))} + + + + setInputValue(e.target.value)} + onKeyPress={handleKeyPress} + placeholder="Ketik pesan Anda..." + style={{ + flex: 1, + padding: '8px 12px', + borderRadius: '20px', + border: '1px solid #ccc', + }} + disabled={isLoading} + /> + + + + + + + + + + ); +}; + +export default HelpPage; \ No newline at end of file diff --git a/src/components/ui/help-card.tsx b/src/components/ui/help-card.tsx new file mode 100644 index 0000000..844c4b3 --- /dev/null +++ b/src/components/ui/help-card.tsx @@ -0,0 +1,90 @@ +import { Card, useMantineTheme, useComputedColorScheme } from '@mantine/core'; +import type { CardProps } from '@mantine/core'; +import type { ReactNode } from 'react'; + +interface HelpCardProps extends CardProps { + children: ReactNode; + icon?: ReactNode; + title?: string; + minHeight?: string | number; // Allow specifying a minimum height +} + +export const HelpCard = ({ + children, + icon, + title, + minHeight = 'auto', // Default to auto, but allow override + ...props +}: HelpCardProps) => { + const theme = useMantineTheme(); + const colorScheme = useComputedColorScheme('light'); + const isDark = colorScheme === 'dark'; + + return ( + + {(icon || title) && ( +
+ {icon && ( +
+ {icon} +
+ )} + + {title && ( +

+ {title} +

+ )} +
+ )} + +
+ {children} +
+
+ ); +}; diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 8b0a1b8..0bc961b 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -28,6 +28,7 @@ import { Route as DashboardKeamananRouteImport } from './routes/dashboard/keaman import { Route as DashboardJennaAnalyticRouteImport } from './routes/dashboard/jenna-analytic' import { Route as DashboardDemografiPekerjaanRouteImport } from './routes/dashboard/demografi-pekerjaan' import { Route as DashboardBumdesRouteImport } from './routes/dashboard/bumdes' +import { Route as DashboardBantuanRouteImport } from './routes/dashboard/bantuan' import { Route as AdminUsersRouteImport } from './routes/admin/users' import { Route as AdminSettingsRouteImport } from './routes/admin/settings' import { Route as AdminApikeyRouteImport } from './routes/admin/apikey' @@ -130,6 +131,11 @@ const DashboardBumdesRoute = DashboardBumdesRouteImport.update({ path: '/bumdes', getParentRoute: () => DashboardRouteRoute, } as any) +const DashboardBantuanRoute = DashboardBantuanRouteImport.update({ + id: '/bantuan', + path: '/bantuan', + getParentRoute: () => DashboardRouteRoute, +} as any) const AdminUsersRoute = AdminUsersRouteImport.update({ id: '/users', path: '/users', @@ -155,6 +161,7 @@ export interface FileRoutesByFullPath { '/admin/apikey': typeof AdminApikeyRoute '/admin/settings': typeof AdminSettingsRoute '/admin/users': typeof AdminUsersRoute + '/dashboard/bantuan': typeof DashboardBantuanRoute '/dashboard/bumdes': typeof DashboardBumdesRoute '/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute '/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute @@ -177,6 +184,7 @@ export interface FileRoutesByTo { '/admin/apikey': typeof AdminApikeyRoute '/admin/settings': typeof AdminSettingsRoute '/admin/users': typeof AdminUsersRoute + '/dashboard/bantuan': typeof DashboardBantuanRoute '/dashboard/bumdes': typeof DashboardBumdesRoute '/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute '/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute @@ -202,6 +210,7 @@ export interface FileRoutesById { '/admin/apikey': typeof AdminApikeyRoute '/admin/settings': typeof AdminSettingsRoute '/admin/users': typeof AdminUsersRoute + '/dashboard/bantuan': typeof DashboardBantuanRoute '/dashboard/bumdes': typeof DashboardBumdesRoute '/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute '/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute @@ -228,6 +237,7 @@ export interface FileRouteTypes { | '/admin/apikey' | '/admin/settings' | '/admin/users' + | '/dashboard/bantuan' | '/dashboard/bumdes' | '/dashboard/demografi-pekerjaan' | '/dashboard/jenna-analytic' @@ -250,6 +260,7 @@ export interface FileRouteTypes { | '/admin/apikey' | '/admin/settings' | '/admin/users' + | '/dashboard/bantuan' | '/dashboard/bumdes' | '/dashboard/demografi-pekerjaan' | '/dashboard/jenna-analytic' @@ -274,6 +285,7 @@ export interface FileRouteTypes { | '/admin/apikey' | '/admin/settings' | '/admin/users' + | '/dashboard/bantuan' | '/dashboard/bumdes' | '/dashboard/demografi-pekerjaan' | '/dashboard/jenna-analytic' @@ -437,6 +449,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardBumdesRouteImport parentRoute: typeof DashboardRouteRoute } + '/dashboard/bantuan': { + id: '/dashboard/bantuan' + path: '/bantuan' + fullPath: '/dashboard/bantuan' + preLoaderRoute: typeof DashboardBantuanRouteImport + parentRoute: typeof DashboardRouteRoute + } '/admin/users': { id: '/admin/users' path: '/users' @@ -480,6 +499,7 @@ const AdminRouteRouteWithChildren = AdminRouteRoute._addFileChildren( ) interface DashboardRouteRouteChildren { + DashboardBantuanRoute: typeof DashboardBantuanRoute DashboardBumdesRoute: typeof DashboardBumdesRoute DashboardDemografiPekerjaanRoute: typeof DashboardDemografiPekerjaanRoute DashboardJennaAnalyticRoute: typeof DashboardJennaAnalyticRoute @@ -492,6 +512,7 @@ interface DashboardRouteRouteChildren { } const DashboardRouteRouteChildren: DashboardRouteRouteChildren = { + DashboardBantuanRoute: DashboardBantuanRoute, DashboardBumdesRoute: DashboardBumdesRoute, DashboardDemografiPekerjaanRoute: DashboardDemografiPekerjaanRoute, DashboardJennaAnalyticRoute: DashboardJennaAnalyticRoute, diff --git a/src/routes/dashboard/bantuan.ts b/src/routes/dashboard/bantuan.ts new file mode 100644 index 0000000..510fc86 --- /dev/null +++ b/src/routes/dashboard/bantuan.ts @@ -0,0 +1,7 @@ +import { createFileRoute } from '@tanstack/react-router' +import HelpPage from '@/components/help-page' + +export const Route = createFileRoute('/dashboard/bantuan')({ + component: HelpPage, +}) +