feat: implement help page with equal height cards

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-02-13 11:34:43 +08:00
parent 4ed1c664d1
commit ab2afbb27f
5 changed files with 817 additions and 0 deletions

View File

@@ -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 (
<Container size="lg" py="xl">
<Title order={1} mb="xl" ta="center">Pusat Bantuan</Title>
<Text size="lg" color="dimmed" ta="center" mb="xl">
Temukan jawaban untuk pertanyaan Anda atau hubungi tim support kami
</Text>
{/* Statistics Section */}
<SimpleGrid cols={3} spacing="lg" mb="xl">
{stats.map((stat, index) => (
<HelpCard key={index} p="lg" style={{ textAlign: 'center' }} h="100%">
<Text size="xl" fw={700} style={{ fontSize: '32px' }}>{stat.value}</Text>
<Text size="sm" color="dimmed">{stat.label}</Text>
</HelpCard>
))}
</SimpleGrid>
<Stack gap="lg">
<Box>
<Grid gutter="lg" justify="center">
{/* Panduan Memulai */}
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
<HelpCard icon={<IconBook size={24} />} title="Panduan Memulai" h="100%">
<Box>
{guideItems.map((item, index) => (
<Box key={index} py="sm" style={{ borderBottom: '1px solid #eee', cursor: 'pointer' }} onClick={() => alert(`Navigasi ke ${item.title}`)}>
<Text fw={500}>{item.title}</Text>
<Text size="sm" color="dimmed">{item.description}</Text>
</Box>
))}
</Box>
</HelpCard>
</Grid.Col>
{/* Video Tutorial */}
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
<HelpCard icon={<IconVideo size={24} />} title="Video Tutorial" h="100%">
<Box>
{videoItems.map((item, index) => (
<Box key={index} py="sm" style={{ borderBottom: '1px solid #eee', cursor: 'pointer' }} onClick={() => alert(`Buka video: ${item.title}`)}>
<Text fw={500}>{item.title}</Text>
<Text size="sm" color="dimmed">{item.duration}</Text>
</Box>
))}
</Box>
</HelpCard>
</Grid.Col>
{/* FAQ */}
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
<HelpCard icon={<IconHelpCircle size={24} />} title="FAQ" h="100%">
<Accordion variant="separated">
{faqItems.map((item, index) => (
<Accordion.Item key={index} value={`faq-${index}`}>
<Accordion.Control>{item.question}</Accordion.Control>
<Accordion.Panel>
<Text size="sm">{item.answer}</Text>
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion>
</HelpCard>
</Grid.Col>
</Grid>
</Box>
<Box>
<Grid>
{/* Hubungi Support */}
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
<HelpCard icon={<IconHeadphones size={24} />} title="Hubungi Support" h="100%">
<Box>
<Text fw={500}>Email</Text>
<Text size="sm" color="dimmed" mb="md"><a href="mailto:support@example.com">support@example.com</a></Text>
<Text fw={500}>WhatsApp</Text>
<Text size="sm" color="dimmed" mb="md"><a href="https://wa.me/1234567890">+62 123 456 7890</a></Text>
<Text fw={500}>Jam Kerja</Text>
<Text size="sm" color="dimmed">Senin - Jumat, 09:00 - 17:00 WIB</Text>
<Text fw={500} mt="md">Waktu Respon</Text>
<Text size="sm" color="dimmed">Rata-rata 2-4 jam kerja</Text>
</Box>
</HelpCard>
</Grid.Col>
{/* Dokumentasi */}
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
<HelpCard icon={<IconFileText size={24} />} title="Dokumentasi" h="100%">
<Box>
{documentationItems.map((item, index) => (
<Box key={index} py="sm" style={{ borderBottom: '1px solid #eee', cursor: 'pointer' }} onClick={() => alert(`Navigasi ke dokumentasi: ${item.title}`)}>
<Text fw={500}>{item.title}</Text>
<Text size="sm" color="dimmed">{item.description}</Text>
</Box>
))}
</Box>
</HelpCard>
</Grid.Col>
{/* Jenna - Virtual Assistant */}
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
<HelpCard icon={<IconMessage size={24} />} title="Jenna - Virtual Assistant" h="100%">
<Box style={{ height: '300px', display: 'flex', flexDirection: 'column' }}>
<Box style={{ flex: 1, overflowY: 'auto', marginBottom: '12px', maxHeight: '200px' }}>
{messages.map((msg) => (
<Box
key={msg.id}
style={{
alignSelf: msg.sender === 'user' ? 'flex-end' : 'flex-start',
backgroundColor: msg.sender === 'user' ? '#3B82F6' : '#F3F4F6',
color: msg.sender === 'user' ? 'white' : 'black',
padding: '8px 12px',
borderRadius: '8px',
marginBottom: '8px',
maxWidth: '80%'
}}
>
{msg.text}
</Box>
))}
</Box>
<Box style={{ display: 'flex', gap: '8px' }}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Ketik pesan Anda..."
style={{
flex: 1,
padding: '8px 12px',
borderRadius: '20px',
border: '1px solid #ccc',
}}
disabled={isLoading}
/>
<button
onClick={handleSendMessage}
disabled={isLoading || inputValue.trim() === ''}
style={{
padding: '8px 16px',
borderRadius: '20px',
backgroundColor: '#3B82F6',
color: 'white',
border: 'none',
cursor: 'pointer',
}}
>
Kirim
</button>
</Box>
</Box>
</HelpCard>
</Grid.Col>
</Grid>
</Box>
</Stack>
</Container>
);
};
export default HelpPage;

View File

@@ -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 (
<Card
shadow="sm"
padding="xl"
radius="md"
withBorder
style={{
backgroundColor: isDark ? theme.colors.dark[7] : theme.white,
borderRadius: '16px',
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
border: `1px solid ${
isDark ? theme.colors.dark[4] : theme.colors.gray[3]
}`,
minHeight, // Apply the minimum height
display: 'flex',
flexDirection: 'column',
}}
{...props}
>
{(icon || title) && (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '12px',
marginBottom: '16px',
}}
>
{icon && (
<div
style={{
backgroundColor: isDark
? theme.colors.blue[8]
: theme.colors.blue[0],
borderRadius: '8px',
padding: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{icon}
</div>
)}
{title && (
<h3
style={{
margin: 0,
fontSize: '16px',
fontWeight: 600,
color: isDark
? theme.colors.dark[0]
: theme.colors.dark[9],
}}
>
{title}
</h3>
)}
</div>
)}
<div style={{ flex: 1 }}>
{children}
</div>
</Card>
);
};