import React, { useState, useEffect } from 'react';
import {
Home, Briefcase, User, Volume2, ZoomIn, ZoomOut, Eye,
ArrowRight, ArrowLeft, Monitor, ChefHat, Scissors, Package,
Palette, Folder, Wifi, CloudRain, Play, TreePine, BookOpen,
Flower, Wrench, Search, Microscope, Coffee, Calculator,
IdCard, Award, Frown, Meh, Smile, LayoutGrid, ShoppingCart,
Circle, Star, Pen, Cookie, Power, Music, Footprints, Droplets,
CheckCircle, LogOut, Shirt, Store
} from 'lucide-react';
// --- KOMPONEN JAM (Diasingkan supaya tidak mengganggu aplikasi utama) ---
const HeaderClock = () => {
const [currentTime, setCurrentTime] = useState('');
useEffect(() => {
const timer = setInterval(() => {
const now = new Date();
setCurrentTime(now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }));
}, 1000);
return () => clearInterval(timer);
}, []);
return {currentTime || '11:11 AM'};
};
export default function App() {
// State Management
const [currentView, setCurrentView] = useState('home');
const [isHighContrast, setIsHighContrast] = useState(false);
const [zoomLevel, setZoomLevel] = useState(0);
const [malayVoice, setMalayVoice] = useState(null);
const [selectedCareer, setSelectedCareer] = useState(null);
// --- PROFIL MURID ---
const [isProfileComplete, setIsProfileComplete] = useState(false);
const [userProfile, setUserProfile] = useState({ name: '', age: '', gender: '' });
const [quizResult, setQuizResult] = useState(null);
// --- KUIZ STATE ---
const [quizPhase, setQuizPhase] = useState('intro');
const [quizIndex, setQuizIndex] = useState(0);
const [quizAnswers, setQuizAnswers] = useState([]);
// Load Voices
useEffect(() => {
const loadVoices = () => {
const voices = window.speechSynthesis.getVoices();
const preferredVoice =
voices.find(voice => voice.lang === 'ms-MY' || voice.lang === 'ms_MY') ||
voices.find(voice => voice.lang.includes('ms')) ||
voices.find(voice => voice.lang.includes('id-ID'));
if (preferredVoice) setMalayVoice(preferredVoice);
};
loadVoices();
if ('speechSynthesis' in window) window.speechSynthesis.onvoiceschanged = loadVoices;
}, []);
const speakText = (text) => {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
const cleanText = text.replace(/[*_#✨📋🏢🛠️]/g, '');
const utterance = new SpeechSynthesisUtterance(cleanText);
utterance.lang = 'ms-MY';
if (malayVoice) utterance.voice = malayVoice;
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
} else {
console.warn("Fungsi bacaan suara tidak disokong pelayar anda.");
}
};
const SpeakButton = ({ text, className = "" }) => (
);
const getSizes = (level) => {
const map = {
'-2': { base: 'text-sm', card: 'text-base', quiz: 'text-lg', main: 'text-xl' },
'-1': { base: 'text-base', card: 'text-lg', quiz: 'text-xl', main: 'text-2xl' },
'0': { base: 'text-lg', card: 'text-xl', quiz: 'text-2xl', main: 'text-3xl' },
'1': { base: 'text-xl', card: 'text-2xl', quiz: 'text-3xl', main: 'text-4xl' },
'2': { base: 'text-2xl', card: 'text-3xl', quiz: 'text-4xl', main: 'text-5xl' }
};
return map[level];
};
const { base: textSize, card: cardTitleSize, quiz: quizTextSize, main: headingSize } = getSizes(zoomLevel);
const retroShadow = 'shadow-[6px_6px_0px_0px_rgba(0,0,0,1)]';
const retroShadowHover = 'hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] hover:translate-y-[4px] hover:translate-x-[4px] transition-all cursor-pointer';
const retroBorder = 'border-4 border-black';
const navigateTo = (view) => {
setCurrentView(view);
setSelectedCareer(null);
window.scrollTo(0, 0);
};
// --- DATA KERJAYA (14 SENARAI) ---
const careers = [
{ id: 1, title: 'Pembantu Bakeri', icon: , desc: 'Menyediakan bahan masakan, mengadun dan membakar roti.', category: 'R (Realistik)', color: 'bg-green-300',
details: { tasks: ['Menguli tepung dengan tangan.', 'Menyapu jem pada roti.', 'Menyusun roti di dalam dulang.'], env: 'Dapur yang berbau wangi.', tools: ['Ketuhar', 'Dulang', 'Sarung Tangan'] } },
{ id: 2, title: 'Pembantu Landskap', icon: , desc: 'Menanam, menyiram dan menjaga tumbuh-tumbuhan taman.', category: 'R (Realistik)', color: 'bg-emerald-300',
details: { tasks: ['Menyiram pokok bunga.', 'Menyapu daun kering.', 'Menanam benih baru.'], env: 'Taman dan luar rumah.', tools: ['Penyapu', 'Paip Air', 'Kereta Sorong'] } },
{ id: 3, title: 'Pembantu Dobi', icon: , desc: 'Membasuh, mengering dan melipat pakaian dengan kemas.', category: 'R (Realistik)', color: 'bg-teal-300',
details: { tasks: ['Memasukkan baju ke dalam mesin.', 'Melipat baju dengan lurus.', 'Menyusun baju ikut bakul.'], env: 'Kedai dobi, bau sabun.', tools: ['Mesin Basuh', 'Bakul', 'Penyangkut'] } },
{ id: 4, title: 'Pembantu Mekanik', icon: , desc: 'Membantu membaiki kenderaan dan menyusun peralatan bengkel.', category: 'R (Realistik)', color: 'bg-slate-300',
details: { tasks: ['Menyusun spanar di rak.', 'Mengelap cermin kereta.', 'Menyapu lantai bengkel.'], env: 'Bengkel kereta, bising sedikit.', tools: ['Spanar', 'Kain Lap', 'Troli'] } },
{ id: 5, title: 'Pembantu Makmal', icon: , desc: 'Mencuci kelalang, menyusun radas dan menjaga kebersihan.', category: 'I (Investigatif)', color: 'bg-blue-400',
details: { tasks: ['Membasuh tabung uji.', 'Menyusun radas sains.', 'Mengelap meja makmal.'], env: 'Makmal sains yang bersih.', tools: ['Berus Cuci', 'Kain Lap', 'Rak Tabung Uji'] } },
{ id: 6, title: 'Penyemak Kualiti', icon: , desc: 'Memerhati dan memastikan barang kilang tiada kerosakan.', category: 'I (Investigatif)', color: 'bg-indigo-300',
details: { tasks: ['Mengasingkan barang yang calar.', 'Menyusun barang yang elok.', 'Melihat label dengan teliti.'], env: 'Kilang atau meja kerja.', tools: ['Bakul Asing', 'Sarung Tangan', 'Kanta Pembesar'] } },
{ id: 7, title: 'Pembantu Kedai Bunga', icon: , desc: 'Menyusun dan menjaga keindahan bunga segar di kedai.', category: 'A (Artistik)', color: 'bg-pink-300',
details: { tasks: ['Memotong daun lebih.', 'Menyusun bunga dalam pasu.', 'Mengikat reben cantik.'], env: 'Kedai bunga, sejuk dan wangi.', tools: ['Gunting', 'Reben', 'Pasu'] } },
{ id: 8, title: 'Pembuat Kraf Tangan', icon: , desc: 'Menggunting dan menghasilkan pelbagai barangan seni kraf.', category: 'A (Artistik)', color: 'bg-yellow-300',
details: { tasks: ['Menggunting kertas warna.', 'Menampal gam pada kraf.', 'Mewarnakan hasil seni.'], env: 'Bilik kraf yang ceria.', tools: ['Gunting', 'Gam', 'Warna'] } },
{ id: 9, title: 'Pembantu Cafe', icon: , desc: 'Menyambut tetamu, mengambil pesanan dan menghidang makanan.', category: 'S (Sosial)', color: 'bg-orange-300',
details: { tasks: ['Mengelap meja makan.', 'Menghantar minuman kepada pelanggan.', 'Menyusun kerusi dengan lurus.'], env: 'Kedai makan, ramai orang.', tools: ['Kain Lap', 'Dulang', 'Buku Menu'] } },
{ id: 10, title: 'Usahawan', icon: , desc: 'Membuka perniagaan sendiri dan menjual produk kepada pelanggan.', category: 'E (Enterprising)', color: 'bg-red-300',
details: { tasks: ['Senyum dan sapa orang.', 'Menyusun barang niaga.', 'Berikan beg plastik kepada pembeli.'], env: 'Kedai atau pasar.', tools: ['Barang Niaga', 'Kalkulator', 'Beg Plastik'] } },
{ id: 11, title: 'Jurutunai', icon: , desc: 'Mengira bil, menerima bayaran dan memastikan pelanggan gembira.', category: 'E (Enterprising)', color: 'bg-cyan-300',
details: { tasks: ['Scan barang (bunyi tit!).', 'Menekan butang mesin duit.', 'Memberi resit kepada pelanggan.'], env: 'Pasar raya, kaunter bayaran.', tools: ['Mesin Tunai', 'Scanner', 'Duit Baki'] } },
{ id: 12, title: 'Pembantu Stor', icon: , desc: 'Menyusun barang, mengangkat kotak dan merekod jumlah stok.', category: 'C (Konvensional)', color: 'bg-amber-300',
details: { tasks: ['Mengangkat kotak kecil.', 'Menyusun kotak di rak lurus.', 'Mengira jumlah kotak.'], env: 'Gudang atau stor besar.', tools: ['Troli Barang', 'Kotak', 'Papan Klip'] } },
{ id: 13, title: 'Pembantu Perpustakaan', icon: , desc: 'Menyusun buku di rak dan merekod pinjaman bahan bacaan.', category: 'C (Konvensional)', color: 'bg-blue-300',
details: { tasks: ['Menyusun buku mengikut warna/nombor.', 'Menolak troli buku.', 'Memastikan meja sentiasa kemas.'], env: 'Perpustakaan, senyap dan tenang.', tools: ['Troli Buku', 'Rak Buku', 'Cap Tarikh'] } },
{ id: 14, title: 'Kerani Data', icon: , desc: 'Menaip dan memasukkan maklumat ke dalam komputer dengan teliti.', category: 'C (Konvensional)', color: 'bg-purple-300',
details: { tasks: ['Menaip huruf di komputer.', 'Menekan tetikus (mouse).', 'Menyalin maklumat dari kertas.'], env: 'Pejabat, meja komputer.', tools: ['Papan Kekunci', 'Komputer', 'Fail Kertas'] } },
];
const quizQuestions = [
{ id: 1, code: 'R1', text: "Adakah kamu suka menyusun blok jadi tinggi?", icon: , color: "bg-emerald-300", category: "R (Realistik)" },
{ id: 2, code: 'R2', text: "Adakah kamu suka menolak kereta mainan / troli?", icon: , color: "bg-green-300", category: "R (Realistik)" },
{ id: 3, code: 'I1', text: "Adakah kamu suka asingkan butang ikut warna?", icon: , color: "bg-indigo-300", category: "I (Investigatif)" },
{ id: 4, code: 'I2', text: "Adakah kamu suka cari benda tersembunyi dalam pasir?", icon: , color: "bg-blue-400", category: "I (Investigatif)" },
{ id: 5, code: 'A1', text: "Adakah kamu suka mewarna gambar?", icon: , color: "bg-pink-300", category: "A (Artistik)" },
{ id: 6, code: 'A2', text: "Adakah kamu suka lekatkan pelekat (sticker)?", icon: , color: "bg-yellow-300", category: "A (Artistik)" },
{ id: 7, code: 'S1', text: "Adakah kamu suka hulur pensel kepada kawan?", icon: , color: "bg-red-300", category: "S (Sosial)" },
{ id: 8, code: 'S2', text: "Adakah kamu suka bagi makanan ringan kat kawan?", icon: , color: "bg-orange-300", category: "S (Sosial)" },
{ id: 9, code: 'E1', text: "Adakah kamu suka tekan suis lampu untuk cikgu?", icon: , color: "bg-teal-300", category: "E (Enterprising)" },
{ id: 10, code: 'E2', text: "Adakah kamu suka pilih lagu untuk kelas dengar?", icon: , color: "bg-cyan-300", category: "E (Enterprising)" },
{ id: 11, code: 'C1', text: "Adakah kamu suka susun kasut di rak lurus-lurus?", icon: , color: "bg-purple-300", category: "C (Konvensional)" },
{ id: 12, code: 'C2', text: "Adakah kamu suka lap meja lepas makan?", icon: , color: "bg-blue-300", category: "C (Konvensional)" },
];
// --- GOOGLE SHEETS SKELETON ---
const SCRIPT_URL = "URL_ANDA_DI_SINI";
const saveToGoogleSheets = async (profile, resultData) => {
if (SCRIPT_URL === "URL_ANDA_DI_SINI") return;
try {
const payload = {
timestamp: new Date().toLocaleString('ms-MY'),
name: profile.name,
age: profile.age,
gender: profile.gender,
dominant1: resultData.riasec[0] || '-',
dominant2: resultData.riasec[1] || '-',
scoreR: resultData.scores['R (Realistik)'],
scoreI: resultData.scores['I (Investigatif)'],
scoreA: resultData.scores['A (Artistik)'],
scoreS: resultData.scores['S (Sosial)'],
scoreE: resultData.scores['E (Enterprising)'],
scoreC: resultData.scores['C (Konvensional)']
};
await fetch(SCRIPT_URL, { method: 'POST', mode: 'no-cors', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
} catch (error) {
console.error(error);
}
};
// --- QUIZ LOGIC ---
const handleQuizAnswer = (score) => {
const newAnswers = [...quizAnswers, { questionId: quizQuestions[quizIndex].id, score }];
setQuizAnswers(newAnswers);
if (quizIndex < quizQuestions.length - 1) {
setQuizIndex(quizIndex + 1);
} else {
const scores = { 'R (Realistik)': 0, 'I (Investigatif)': 0, 'A (Artistik)': 0, 'S (Sosial)': 0, 'E (Enterprising)': 0, 'C (Konvensional)': 0 };
newAnswers.forEach(ans => {
const cat = quizQuestions.find(q => q.id === ans.questionId).category;
scores[cat] += ans.score;
});
const sortedCats = Object.entries(scores).sort((a, b) => b[1] - a[1]);
const dominantCats = [sortedCats[0][0], sortedCats[1][0]];
const recommended = careers.filter(c => dominantCats.includes(c.category));
const finalResultData = { riasec: dominantCats, scores: scores, careers: recommended };
setQuizResult(finalResultData);
setQuizPhase('result');
saveToGoogleSheets(userProfile, finalResultData);
}
};
const startQuiz = () => {
setQuizPhase('questions');
};
const resetQuiz = () => {
setQuizIndex(0);
setQuizAnswers([]);
setQuizPhase('intro');
};
const handleNewStudent = () => {
setIsProfileComplete(false);
setUserProfile({ name: '', age: '', gender: '' });
setQuizResult(null);
resetQuiz();
setCurrentView('home');
setSelectedCareer(null);
window.scrollTo(0, 0);
};
// --- VIEWS ---
const OnboardingView = () => {
const [tempName, setTempName] = useState(userProfile.name);
const [tempAge, setTempAge] = useState(userProfile.age);
const [tempGender, setTempGender] = useState(userProfile.gender);
const handleSaveProfile = () => {
setUserProfile({ name: tempName.trim() || 'Kawan', age: tempAge || '-', gender: tempGender || '-' });
setIsProfileComplete(true);
navigateTo('home');
};
return (
Isi Biodata Diri
Maklumat ini akan disimpan dalam Profil Saya.
);
};
const HomeView = () => (
Selamat Datang, {userProfile.name}!
Pilih folder untuk bermula
{ setQuizPhase('intro'); navigateTo('quiz'); }} className="flex flex-col items-center group cursor-pointer">
Kuiz Kerjaya
navigateTo('gallery')} className="flex flex-col items-center group cursor-pointer">
Senarai Kerja
navigateTo('profile')} className="flex flex-col items-center group cursor-pointer">
Profil Saya
);
const QuizView = () => {
return (
{quizPhase === 'intro' && (
Sedia Untuk Mula?
Terdapat 12 soalan. Pilih Skor 1, 2, atau 3 untuk setiap soalan.
)}
{quizPhase === 'questions' && (
<>
Soalan {quizIndex + 1} / {quizQuestions.length}
{quizQuestions[quizIndex].icon}
{quizQuestions[quizIndex].text}
handleQuizAnswer(1)}
className={`flex-1 flex flex-col items-center gap-3 p-5 rounded-xl border-4 border-black bg-red-400 text-white relative ${retroShadowHover}`}>
TIDAK SUKA (1)
handleQuizAnswer(2)}
className={`flex-1 flex flex-col items-center gap-3 p-5 rounded-xl border-4 border-black bg-yellow-400 text-black relative ${retroShadowHover}`}>
BIASA SAHAJA (2)
handleQuizAnswer(3)}
className={`flex-1 flex flex-col items-center gap-3 p-5 rounded-xl border-4 border-black bg-green-400 text-white relative ${retroShadowHover}`}>
SANGAT SUKA (3)
>
)}
{quizPhase === 'result' && (
Tahniah, {userProfile.name}!
Dua Profil Paling Dominan Anda:
{quizResult.riasec.map(cat => (
{cat.split(' ')[0]}
{cat.substring(4)}
Skor: {quizResult.scores[cat]} / 6
))}
)}
);
};
const CareerDetailView = ({ career }) => (
Butiran Pekerjaan
{React.cloneElement(career.icon, { size: 64 })}
{career.title}
Kategori: {career.category}
{career.desc}
📋 Tugas Harian:
{career.details.tasks.map(task => (
- {task}
))}
🏢 Persekitaran
{career.details.env}
🛠️ Peralatan Utama
{career.details.tools.join(', ')}
Info Pekerjaan
info pekerjaan by MAISARA QISTINA BINTI AZROL NIZAM IPG-Pelajar
);
const GalleryView = () => {
if (selectedCareer) return ;
return (
Senarai Kerjaya
{careers.map((career) => (
{career.icon}
{career.title}
{career.category}
{career.desc}
setSelectedCareer(career)}
className={`flex-grow py-3 rounded-xl font-black uppercase bg-pink-300 border-4 border-black flex items-center justify-center gap-2 ${retroShadowHover} cursor-pointer text-sm md:text-base`}
>
Butiran Penuh
))}
);
};
const ProfileView = () => (
Profil Saya
{userProfile.name}
Umur:
{userProfile.age} Tahun
Jantina:
{userProfile.gender}
Rekod Kerjaya
{!quizResult ? (
Anda belum mengambil Kuiz Kerjaya.
) : (
Dua Profil Paling Dominan:
{quizResult.riasec.map(cat => (
{cat.split(' ')[0]}
{cat.substring(4)}
Skor: {quizResult.scores[cat]}
))}
Kerjaya Pilihan Anda:
{quizResult.careers.map(career => (
{career.icon}
{career.title}
{career.category}
))}
)}
);
return (
{!isProfileComplete ? (
) : (
<>
{currentView === 'home' &&
}
{currentView === 'quiz' &&
}
{currentView === 'gallery' &&
}
{currentView === 'profile' &&
}
>
)}
{zoomLevel > 0 ? `+${zoomLevel}` : zoomLevel}
);
}