perubahan desain frontend

This commit is contained in:
Zeakeers 2025-05-08 01:53:25 +07:00
parent 90adfef859
commit fd90edfce5
7 changed files with 548 additions and 361 deletions

View File

@ -149,202 +149,236 @@ const App = () => {
} }
}; };
return ( return (
<div> <div className="min-h-screen bg-gray-50">
<Header scrollToSection={scrollToSection} /> <Header scrollToSection={scrollToSection} />
<div id="home" ref={homeRef} className="h-screen w-full bg-cover bg-center bg-no-repeat bg-black bg-opacity-50 flex items-center justify-center"
style={{ backgroundImage: "url('/images/bgwebrating.png')", backgroundSize: "105%" }}> {/* Hero Section */}
<div className={`w-full max-w-lg p-6 text-center relative ${step > 0 || rating ? 'bg-white rounded-lg shadow-md' : ''}`}> <div
{step === 0 && !rating && ( id="home"
<h1 className="text-6xl text-white text-2xl text-left font-bold ">Rating Umur Game</h1> ref={homeRef}
)} className="min-h-screen w-full bg-cover bg-center bg-no-repeat bg-black bg-opacity-50 flex items-center justify-center px-4 sm:px-6 lg:px-8 relative overflow-hidden"
{step > 0 && step <= questions.length && ( style={{
<div className="absolute top-4 right-4 flex space-x-2"> backgroundImage: "url('/images/bgwebrating.png')",
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed"
}}
>
<div className="absolute inset-0 bg-black/50"></div>
<div className={`w-full max-w-2xl p-6 sm:p-8 text-center relative ${step > 0 || rating ? 'bg-white/95 backdrop-blur-sm rounded-2xl shadow-2xl' : ''}`}>
{step === 0 && !rating && (
<h1 className="text-4xl sm:text-5xl md:text-6xl text-white font-bold tracking-tight mb-8">
Rating Umur Game
</h1>
)}
{step > 0 && step <= questions.length && (
<div className="absolute top-4 right-4 flex space-x-3 z-10">
{step > 1 && ( {step > 1 && (
<button
onClick={() => {
setAnswers(answers.slice(0, -1));
setStep(step - 1);
}}
aria-label="Kembali"
className="w-8 h-8 bg-yellow-500 rounded-full hover:bg-yellow-600 transition-transform duration-200 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-yellow-300 border-2 border-white shadow-md hover:scale-110"
/>
)}
<button <button
onClick={() => { onClick={() => {
setAnswers(answers.slice(0, -1)); setStep(0);
setStep(step - 1); setAnswers([]);
setGameName("");
}} }}
className="w-6 h-6 bg-yellow-500 rounded-full hover:bg-yellow-700" aria-label="Reset"
/> className="w-8 h-8 bg-red-500 rounded-full hover:bg-red-600 transition-transform duration-200 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-red-300 border-2 border-white shadow-md hover:scale-110"
)}
<button
onClick={() => {
setStep(0);
setAnswers([]);
setGameName("");
}}
className="w-6 h-6 bg-red-500 rounded-full hover:bg-red-700"
/> />
</div> </div>
)} )}
{!rating && step === 0 && (
<> {!rating && step === 0 && (
<p className="mt-4 text-left text-white">Masukkan nama game:</p> <div className="space-y-6">
<input <p className="text-xl text-white font-medium">Masukkan nama game:</p>
type="text" <div className="relative">
value={gameName} <input
onChange={(e) => setGameName(e.target.value)} type="text"
className="border border-gray-600 bg-gray-100 p-2 w-full mt-2 focus:bg-gray-200 focus:border-black rounded mt-1" value={gameName}
/> onChange={(e) => setGameName(e.target.value)}
{suggestions.length > 0 && ( className="w-full px-4 py-3 rounded-xl border-2 border-gray-300 focus:border-yellow-500 focus:ring-2 focus:ring-yellow-200 transition-all duration-300 bg-white/90 backdrop-blur-sm"
<ul className="border border-gray-600 bg-gray-100 w-full mt-2 focus:bg-gray-200 focus:border-black rounded text-left"> placeholder="Ketik nama game..."
{suggestions.map((suggestion, index) => ( />
<li {suggestions.length > 0 && (
key={index} <ul className="absolute w-full mt-2 bg-white rounded-xl shadow-lg border border-gray-200 max-h-60 overflow-y-auto z-[9999]">
className="p-2 cursor-pointer hover:bg-gray-300" {suggestions.map((suggestion, index) => (
onClick={() => handleSelectGame(suggestion)} <li
> key={index}
{suggestion} className="px-4 py-3 hover:bg-yellow-50 cursor-pointer transition-colors duration-200 border-b border-gray-100 last:border-b-0"
</li> onClick={() => handleSelectGame(suggestion)}
))} >
</ul> {suggestion}
)} </li>
))}
</ul>
)}
</div>
<button <button
onClick={checkGame} onClick={checkGame}
className="bg-blue-500 text-white px-4 py-2 rounded mt-4" className="w-full bg-yellow-500 hover:bg-yellow-600 text-white font-semibold px-6 py-3 rounded-xl transition-all duration-300 transform hover:scale-105 disabled:opacity-60 disabled:cursor-not-allowed"
disabled={!gameName.trim()}
> >
Cek Cek Rating
</button> </button>
{/* Daftar 5 game terbaru */}
{latestGames.length > 0 && ( {latestGames.length > 0 && (
<div className="mt-6 px-6 bg-yellowlight p-2 rounded"> <div className="mt-8 bg-white/10 backdrop-blur-sm rounded-xl p-6">
<h2 className="text-black text-bold text-lg text-left font-semibold mb-2">Game Terbaru:</h2> <h2 className="text-white text-xl font-semibold mb-4">Game Terbaru:</h2>
<div className=" flex flex-wrap max-w-full gap-x-6 gap-y-2 "> <div className="flex flex-wrap gap-3">
{latestGames.map((game, index) => ( {latestGames.map((game, index) => (
<button <button
key={index} key={index}
className="text-black text-bold border-b border-gray-400 hover:text-white-300 cursor-pointer text-left whitespace-nowrap" className="px-4 py-2 bg-white/20 hover:bg-white/30 text-white rounded-lg transition-all duration-300"
onClick={() => fetchGameRating(game)} onClick={() => fetchGameRating(game)}
> >
{game} {game}
</button> </button>
))} ))}
{latestGames.length >= 4 && ( {latestGames.length >= 4 && (
<a href="/list-games" className="text-blue-800 hover:underline ml-2"> <a
href="/list-games"
className="px-4 py-2 bg-yellow-500 hover:bg-yellow-600 text-white rounded-lg transition-all duration-300"
>
Selengkapnya Selengkapnya
</a> </a>
)} )}
</div> </div>
</div> </div>
)} )}
</> </div>
)} )}
{!rating && step > 0 && step <= questions.length && (
<>
<p className="mt-4">{questions[step - 1]}</p>
{options.map((option) => (
<button
key={option}
onClick={() => handleOptionClick(option)}
className="block bg-gray-300 text-black px-4 py-2 rounded mt-2 hover:bg-gray-400 w-full"
>
{option}
</button>
))}
</>
)}
{!rating && step > questions.length && (
<>
{/* Menampilkan tabel hasil jawaban */}
<div className="mt-4">
<h2 className="text-lg font-semibold">Hasil Jawaban Anda:</h2>
<table className="table-auto w-full border mt-4">
<thead>
<tr className="bg-gray-200">
<th className="border px-4 py-2">Pertanyaan</th>
<th className="border px-4 py-2">Jawaban</th>
</tr>
</thead>
<tbody>
{questions.map((question, index) => (
<tr key={index}>
<td className="border px-4 py-2">{question}</td>
<td className="border px-4 py-2">{answers[index]}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Tombol kembali ke input game */}
<button
onClick={() => {
setStep(0);
setAnswers([]);
setGameName("");
}}
className="bg-red-500 text-white px-4 py-2 rounded mt-4 w-full"
>
Kembali
</button>
{/* Tombol untuk konfirmasi hasil */}
<button
onClick={submitAnswers}
className="bg-green-500 text-white px-4 py-2 rounded mt-4 w-full"
>
Cek Hasil
</button>
</>
)}
{rating && ( {!rating && step > 0 && step <= questions.length && (
<> <div className="space-y-6 pt-8">
{easterEggActive ? ( <h2 className="text-2xl font-semibold text-gray-800 mb-6">
<h2 className="text-xl font-bold text-center text-red-600 mt-4"> {questions[step - 1]}
</h2>
<div className="grid gap-3">
{options.map((option) => (
<button
key={option}
onClick={() => handleOptionClick(option)}
className="w-full px-6 py-4 bg-white hover:bg-yellow-50 text-gray-800 rounded-xl border-2 border-gray-200 hover:border-yellow-500 transition-all duration-300 text-left"
>
{option}
</button>
))}
</div>
</div>
)}
{!rating && step > questions.length && (
<div className="space-y-6">
<h2 className="text-2xl font-semibold text-gray-800 mb-6">Hasil Jawaban Anda:</h2>
<div className="bg-white rounded-xl shadow-lg overflow-hidden">
<table className="w-full">
<thead>
<tr className="bg-gray-50">
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-600">Pertanyaan</th>
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-600">Jawaban</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{questions.map((question, index) => (
<tr key={index} className="hover:bg-gray-50">
<td className="px-6 py-4 text-sm text-gray-800">{question}</td>
<td className="px-6 py-4 text-sm text-gray-800">{answers[index]}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="flex gap-4">
<button
onClick={() => {
setStep(0);
setAnswers([]);
setGameName("");
}}
className="flex-1 bg-red-500 hover:bg-red-600 text-white font-semibold px-6 py-3 rounded-xl transition-all duration-300"
>
Kembali
</button>
<button
onClick={submitAnswers}
className="flex-1 bg-green-500 hover:bg-green-600 text-white font-semibold px-6 py-3 rounded-xl transition-all duration-300"
>
Cek Hasil
</button>
</div>
</div>
)}
{rating && (
<div className="space-y-6">
{easterEggActive ? (
<div className="text-center">
<h2 className="text-3xl font-bold text-red-600 mb-4">
🎉 BISMILLAH LULUS 2025 DENGAN SEDIKIT REVISI 🎓 🎉 BISMILLAH LULUS 2025 DENGAN SEDIKIT REVISI 🎓
</h2> </h2>
) : ( </div>
<> ) : (
<h2 className="text-xl font-semibold mt-4"> <div className="text-center">
Game <span className="font-bold text-blue-500">{gameName}</span> memiliki rating <h2 className="text-2xl font-semibold text-gray-800 mb-4">
<span className="font-bold text-red-500"> {rating}</span> Game <span className="font-bold text-blue-600">{gameName}</span> memiliki rating
</h2> <span className="font-bold text-red-600"> {rating}</span>
<p className="mt-2"><strong>Penjelasan:</strong> {ratingDescriptions[rating]}</p> </h2>
</> <p className="text-gray-600 mb-6">
)} <strong>Penjelasan:</strong> {ratingDescriptions[rating]}
</p>
<button </div>
onClick={() => { )}
setStep(0); <button
setAnswers([]); onClick={() => {
setRating(null); setStep(0);
setGameName(""); setAnswers([]);
setEasterEggActive(false); setRating(null);
window.location.reload(); setGameName("");
}} setEasterEggActive(false);
className="bg-red-500 text-white px-4 py-2 rounded mt-4 w-full" window.location.reload();
> }}
Coba Lagi className="w-full bg-red-500 hover:bg-red-600 text-white font-semibold px-6 py-3 rounded-xl transition-all duration-300"
</button> >
</> Coba Lagi
)} </button>
</div>
)}
{/* Pop-up konfirmasi pilihan jawaban */}
<PopUp
isOpen={isPopUpOpen}
onClose={() => setIsPopUpOpen(false)}
onConfirm={confirmSelection}
selectedOption={selectedOption}
/>
{/* Pop-up konfirmasi cek hasil */}
<PopUp
isOpen={isConfirmPopupOpen}
onClose={() => setIsConfirmPopupOpen(false)}
onConfirm={confirmSubmit}
selectedOption="Apakah Anda yakin ingin melihat hasil rating?"
/>
</div>
</div>
<div id="tentang" ref={tentangRef}>
<Tentang />
</div> </div>
<KategoriRating /> </div>
<div id="kontak" ref={kontakRef}>
<div id="tentang" ref={tentangRef}>
<Tentang />
</div>
<KategoriRating />
<div id="kontak" ref={kontakRef}>
<Footer /> <Footer />
</div> </div>
{/* Pop-ups */}
<PopUp
isOpen={isPopUpOpen}
onClose={() => setIsPopUpOpen(false)}
onConfirm={confirmSelection}
selectedOption={selectedOption}
/>
<PopUp
isOpen={isConfirmPopupOpen}
onClose={() => setIsConfirmPopupOpen(false)}
onConfirm={confirmSubmit}
selectedOption="Apakah Anda yakin ingin melihat hasil rating?"
/>
</div> </div>
); );
}; };

View File

@ -1,30 +1,35 @@
import react from "react"; import React from "react";
const Footer = () => { const Footer = () => {
return ( return (
<div className="bg-black p-4 text-center text-white"> <footer className="w-full bg-black text-white py-6 px-4 mt-auto">
<p>&copy; 2025 Rating Umur Game. All rights reserved.</p> <div className="max-w-5xl mx-auto flex flex-col md:flex-row items-center justify-between gap-4">
<p> <div className="text-center md:text-left">
Developed by <a href="#" className="text-white hover:text-white">Dimas Adi</a> <p className="text-base md:text-lg font-semibold">&copy; 2025 Rating Umur Game. All rights reserved.</p>
</p> <p className="text-sm mt-1">
<ul className="flex justify-center mb-4"> Developed by <a href="#" className="underline hover:text-yellow-400 transition-colors duration-200">Dimas Adi</a>
<li className="mr-4"> </p>
<a href="#" className="text-white hover:text-white"> </div>
<i className="fab fa-facebook-f"></i> <ul className="flex items-center justify-center gap-4 mt-2 md:mt-0">
</a> <li>
</li> <a href="#" className="hover:text-yellow-400 transition-colors duration-200" aria-label="Facebook">
<li className="mr-4"> <i className="fab fa-facebook-f text-xl"></i>
<a href="#" className="text-white hover:text-white"> </a>
<i className="fab fa-twitter"></i> </li>
</a> <li>
</li> <a href="#" className="hover:text-yellow-400 transition-colors duration-200" aria-label="Twitter">
<li> <i className="fab fa-twitter text-xl"></i>
<a href="#" className="text-white hover:text-white"> </a>
<i className="fab fa-instagram"></i> </li>
</a> <li>
</li> <a href="#" className="hover:text-yellow-400 transition-colors duration-200" aria-label="Instagram">
</ul> <i className="fab fa-instagram text-xl"></i>
</div> </a>
); </li>
}; </ul>
</div>
</footer>
);
};
export default Footer; export default Footer;

View File

@ -1,46 +1,109 @@
import react from "react"; import { useState, useEffect } from "react";
const Header = ({ scrollToSection }) => { const Header = ({ scrollToSection }) => {
const handleNavigation = (section) => { const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isVisible, setIsVisible] = useState(true);
const [lastScrollY, setLastScrollY] = useState(0);
const handleNavigation = (section) => {
setIsMenuOpen(false);
if (scrollToSection) { if (scrollToSection) {
scrollToSection(section); scrollToSection(section);
} else { } else {
window.location.href = `/#${section}`; window.location.href = `/#${section}`;
} }
}; };
useEffect(() => {
const controlNavbar = () => {
const currentScrollY = window.scrollY;
if (currentScrollY > lastScrollY) {
setIsVisible(false);
} else {
setIsVisible(true);
}
setLastScrollY(currentScrollY);
};
window.addEventListener('scroll', controlNavbar);
return () => {
window.removeEventListener('scroll', controlNavbar);
};
}, [lastScrollY]);
return ( return (
<div className="bg-yellow-500 text-white p-4 flex justify-between"> <header className={`fixed w-full bg-yellow-500 shadow-md transition-transform duration-300 z-50 ${isVisible ? 'translate-y-0' : '-translate-y-full'}`}>
<h1 className="text-3xl font-bold"></h1> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<nav> <div className="flex justify-between items-center h-16">
<ul className="flex justify-end"> <div className="flex-shrink-0">
<li className="mr-4"> <h1 className="text-2xl font-bold text-white"></h1>
<button </div>
onClick={() => handleNavigation("home")}
className="text-white hover:text-gray-300" {/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="text-white hover:text-gray-200 focus:outline-none"
>
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
{isMenuOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
{/* Desktop menu */}
<nav className="hidden md:flex space-x-8">
<button
onClick={() => handleNavigation("home")}
className="text-white hover:text-gray-200 px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200"
> >
Beranda Beranda
</button> </button>
</li> <button
<li className="mr-4"> onClick={() => handleNavigation("tentang")}
<button className="text-white hover:text-gray-200 px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200"
onClick={() => handleNavigation("tentang")}
className="text-white hover:text-gray-300"
> >
Tentang Tentang
</button> </button>
</li> <button
<li> onClick={() => handleNavigation("kontak")}
<button className="text-white hover:text-gray-200 px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200"
onClick={() => handleNavigation("kontak")}
className="text-white hover:text-gray-300"
> >
Kontak Kontak
</button> </button>
</li> </nav>
</ul> </div>
</nav> </div>
</div>
); {/* Mobile menu */}
}; <div className={`md:hidden ${isMenuOpen ? 'block' : 'hidden'}`}>
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-yellow-500 shadow-lg">
<button
onClick={() => handleNavigation("home")}
className="block w-full text-left text-white hover:text-gray-200 px-3 py-2 rounded-md text-base font-medium"
>
Beranda
</button>
<button
onClick={() => handleNavigation("tentang")}
className="block w-full text-left text-white hover:text-gray-200 px-3 py-2 rounded-md text-base font-medium"
>
Tentang
</button>
<button
onClick={() => handleNavigation("kontak")}
className="block w-full text-left text-white hover:text-gray-200 px-3 py-2 rounded-md text-base font-medium"
>
Kontak
</button>
</div>
</div>
</header>
);
};
export default Header; export default Header;

View File

@ -2,89 +2,133 @@ import React, { useState } from "react";
const KategoriRating = () => { const KategoriRating = () => {
const [currentIndex, setCurrentIndex] = useState(0); const [currentIndex, setCurrentIndex] = useState(0);
const cards = [ const cards = [
{ imageUrl: "/images/SU.png", text: "Semua Umur - Dapat dimainkan oleh semua orang tanpa batasan konten." }, { imageUrl: "/images/SU.png", text: "Semua Umur - Dapat dimainkan oleh semua orang tanpa batasan konten." },
{ imageUrl: "/images/3+.png", text: "Usia 3 Tahun Keatas - Tidak ada konten dewasa, perjudian, atau interaksi online." }, { imageUrl: "/images/3+.png", text: "Usia 3 Tahun Keatas - Tidak ada konten dewasa, perjudian, atau interaksi online." },
{ imageUrl: "/images/7+.png", text: "Usia 7 Tahun Keatas - Ada unsur ringan seperti kekerasan kartun atau bahasa ringan." }, { imageUrl: "/images/7+.png", text: "Usia 7 Tahun Keatas - Ada unsur ringan seperti kekerasan kartun atau bahasa ringan." },
{ imageUrl: "/images/13+.png", text: "Usia 13 Tahun Keatas - Ada kekerasan moderat, simulasi perjudian, atau tema horor." }, { imageUrl: "/images/13+.png", text: "Usia 13 Tahun Keatas - Ada kekerasan moderat, simulasi perjudian, atau tema horor." },
{ imageUrl: "/images/18+.png", text: "Usia 18 Tahun Keatas - Mengandung konten dewasa, kekerasan realistis, atau humor kasar." } { imageUrl: "/images/18+.png", text: "Usia 18 Tahun Keatas - Mengandung konten dewasa, kekerasan realistis, atau humor kasar." }
]; ];
const nextSlide = () => { const nextSlide = () => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % cards.length); setCurrentIndex((prevIndex) => (prevIndex + 1) % cards.length);
}; };
const prevSlide = () => { const prevSlide = () => {
setCurrentIndex((prevIndex) => (prevIndex - 1 + cards.length) % cards.length); setCurrentIndex((prevIndex) => (prevIndex - 1 + cards.length) % cards.length);
}; };
return (
<div className="relative mt-[90px] mb-10 w-full h-[500px] flex items-center p-10">
<div
className="absolute inset-0 bg-cover bg-center"
style={{ backgroundImage: "url('/images/bg2.jpg')" }}
></div>
<div className="absolute inset-0 bg-yellow-500 opacity-85"></div>
{/* Konten utama */} return (
<div className="relative flex w-full h-full items-center justify-center p-10"> <div className="relative min-h-screen w-full flex items-center justify-center py-20 px-4 sm:px-6 lg:px-8">
{/* Teks di sebelah kiri */} <div
<div className="w-1/2 pr-5"> className="absolute inset-0 bg-cover bg-center bg-fixed"
<p className="text-black text-bold text-2xl"> style={{ backgroundImage: "url('/images/bg2.jpg')" }}
Dalam Peraturan Menteri Komunikasi dan Informatika Nomor 2 Tahun 2024, ></div>
gim diklasifikasikan berdasarkan kelompok usia pengguna: 3+, 7+, 13+, dan 18+. <div className="absolute inset-0 bg-yellow-500 opacity-90"></div>
Setiap kategori ini mempertimbangkan jenis konten yang ditampilkan, seperti kekerasan,
bahasa, unsur horor, hingga interaksi daring. Semakin tinggi kelompok usia,
semakin kompleks pula konten yang diizinkan, namun tetap dalam batasan yang
tidak melanggar norma dan hukum yang berlaku. Klasifikasi ini bertujuan agar
setiap pengguna dapat memainkan gim yang sesuai dengan tingkat kedewasaan mereka,
serta membantu orang tua dalam mengawasi pilihan hiburan digital anak-anaknya.
</p>
</div>
<div className="w-1/2 flex flex-col items-end gap-5 relative"> {/* Konten utama */}
<div className="flex gap-5 overflow-hidden w-[500px]"> <div className="relative max-w-7xl mx-auto w-full">
{cards.slice(currentIndex, currentIndex + 2).map((card, index) => ( <div className="text-center mb-12">
<FlipCard key={index} imageUrl={card.imageUrl} text={card.text} /> <h2 className="text-4xl md:text-5xl font-bold text-white mb-4 tracking-tight">
))} Kategori Rating
</h2>
<div className="w-24 h-1 bg-white mx-auto mb-8"></div>
</div>
<div className="flex flex-col lg:flex-row gap-8 items-center">
{/* Teks di sebelah kiri */}
<div className="w-full lg:w-1/2">
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-8 shadow-xl">
<p className="text-white text-lg md:text-xl leading-relaxed font-medium text-justify">
Dalam Peraturan Menteri Komunikasi dan Informatika Nomor 2 Tahun 2024,
gim diklasifikasikan berdasarkan kelompok usia pengguna: 3+, 7+, 13+, dan 18+.
Setiap kategori ini mempertimbangkan jenis konten yang ditampilkan, seperti kekerasan,
bahasa, unsur horor, hingga interaksi daring.
</p>
<p className="text-white text-lg md:text-xl leading-relaxed font-medium mt-4 text-justify">
Semakin tinggi kelompok usia, semakin kompleks pula konten yang diizinkan,
namun tetap dalam batasan yang tidak melanggar norma dan hukum yang berlaku.
Klasifikasi ini bertujuan agar setiap pengguna dapat memainkan gim yang sesuai
dengan tingkat kedewasaan mereka, serta membantu orang tua dalam mengawasi
pilihan hiburan digital anak-anaknya.
</p>
</div>
</div>
{/* Cards di sebelah kanan */}
<div className="w-full lg:w-1/2 flex flex-col items-center">
<div className="flex gap-4 overflow-x-auto w-full max-w-xs sm:max-w-md md:max-w-lg lg:max-w-2xl justify-center">
{cards.slice(currentIndex, currentIndex + (window.innerWidth < 1024 ? 1 : 2)).map((card, index) => (
<FlipCard key={index} imageUrl={card.imageUrl} text={card.text} />
))}
</div>
<div className="flex gap-6 mt-8">
<button
onClick={prevSlide}
className="w-12 h-12 flex items-center justify-center rounded-full bg-white/80 hover:bg-yellow-400 text-yellow-600 hover:text-white shadow transition-all duration-300 focus:outline-none"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</button>
<button
onClick={nextSlide}
className="w-12 h-12 flex items-center justify-center rounded-full bg-white/80 hover:bg-yellow-400 text-yellow-600 hover:text-white shadow transition-all duration-300 focus:outline-none"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
</div>
</div>
</div> </div>
<div className="flex gap-2">
<button onClick={prevSlide} className="w-8 h-8 bg-gray-400 rounded-full"></button>
<button onClick={nextSlide} className="w-8 h-8 bg-gray-400 rounded-full"></button>
</div>
</div>
</div> </div>
</div> );
);
}; };
const FlipCard = ({ imageUrl, text }) => { const FlipCard = ({ imageUrl, text }) => {
const [flipped, setFlipped] = useState(false); const [flipped, setFlipped] = useState(false);
return ( return (
<div <div
className="w-64 h-80 perspective-1000" className="w-full max-w-[220px] sm:max-w-[260px] md:max-w-[300px] h-[320px] sm:h-[360px] md:h-[400px] perspective-1000 cursor-pointer flex-shrink-0"
onMouseEnter={() => setFlipped(true)} onMouseEnter={() => setFlipped(true)}
onMouseLeave={() => setFlipped(false)} onMouseLeave={() => setFlipped(false)}
> >
<div <div
className={`relative w-full h-full transition-transform duration-700 transform-style-3d rounded-3xl shadow-xl ${ className={`relative w-full h-full transition-transform duration-700 transform-style-3d rounded-3xl shadow-xl ${
flipped ? "rotate-y-180" : "" flipped ? "rotate-y-180" : ""
}`} }`}
style={{ transformStyle: "preserve-3d", transition: "transform 0.7s ease-in-out", transform: flipped ? "rotateY(180deg)" : "rotateY(0deg)" }} style={{
> transformStyle: "preserve-3d",
{/* Depan - Gambar */} transition: "transform 0.7s ease-in-out",
<div className="absolute w-full h-full flex items-center justify-center rounded-3xl" style={{ backfaceVisibility: "hidden" }}> transform: flipped ? "rotateY(180deg)" : "rotateY(0deg)"
<img src={imageUrl} alt="Gambar" className="w-full h-full object-cover rounded-3xl" /> }}
>
{/* Depan - Gambar */}
<div
className="absolute w-full h-full flex items-center justify-center rounded-3xl overflow-hidden bg-white"
style={{ backfaceVisibility: "hidden" }}
>
<img
src={imageUrl}
alt="Rating"
className="w-full h-full object-contain rounded-3xl p-4"
/>
</div>
{/* Belakang - Teks */}
<div
className="absolute w-full h-full bg-white/90 backdrop-blur-sm flex items-center justify-center rounded-3xl p-6"
style={{ backfaceVisibility: "hidden", transform: "rotateY(180deg)" }}
>
<p className="text-gray-800 text-center text-lg font-medium leading-relaxed">
{text}
</p>
</div>
</div>
</div> </div>
{/* Belakang - Teks */} );
<div className="absolute w-full h-full bg-gray-200 flex items-center justify-center rounded-3xl p-4" style={{ backfaceVisibility: "hidden", transform: "rotateY(180deg)" }}>
<p className="text-black text-center px-4 py-2 text-2xl rounded-lg">{text}</p>
</div>
</div>
</div>
);
}; };
export default KategoriRating; export default KategoriRating;

View File

@ -1,23 +1,26 @@
import React from "react"; import React from "react";
const PopUp = ({ isOpen, onClose, onConfirm, selectedOption }) => { const PopUp = ({ isOpen, onClose, onConfirm, selectedOption }) => {
if (!isOpen) return null; if (!isOpen) return null;
return ( return (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50"> <div className="fixed inset-0 flex items-center justify-center bg-black/40 z-50">
<div className="bg-white p-6 rounded-lg shadow-lg text-center"> <div className="bg-white rounded-2xl shadow-2xl px-8 py-7 max-w-xs sm:max-w-sm w-full text-center font-sans">
<p>Apakah Anda yakin memilih: <strong>{selectedOption}</strong>?</p> <p className="text-lg sm:text-xl font-semibold text-gray-800 mb-4">
<div className="mt-4 flex justify-center gap-4"> Apakah Anda yakin memilih:
<br />
<span className="block mt-2 text-yellow-600 text-xl font-bold">{selectedOption}</span>
</p>
<div className="mt-6 flex justify-center gap-4">
<button <button
onClick={onClose} onClick={onClose}
className="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-700" className="px-6 py-2 rounded-xl bg-gray-200 text-gray-700 font-semibold hover:bg-gray-300 transition-colors duration-200 shadow-sm focus:outline-none"
> >
Kembali Kembali
</button> </button>
<button <button
onClick={onConfirm} onClick={onConfirm}
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700" className="px-6 py-2 rounded-xl bg-yellow-500 text-white font-semibold hover:bg-yellow-600 transition-colors duration-200 shadow-sm focus:outline-none"
> >
Lanjut Lanjut
</button> </button>

View File

@ -1,13 +1,50 @@
import react from "react"; import React from "react";
const Tentang = () => { const Tentang = () => {
return ( return (
<div className="bg-[#BBBBBB] p-5 text-black text-center w-full h-[230px]"> <div className="bg-[#BBBBBB] w-full min-h-[400px] py-16 px-4 sm:px-6 lg:px-8">
<h1 className="text-3xl font-bold mt-4">Tentang</h1> <div className="max-w-4xl mx-auto">
<h1 className="text-xl mb-4">Klasifikasi gim itu penting, bukan untuk membatasi, tetapi agar semua orang terutama anak muda dapat bermain dengan aman dan sesuai usia. <div className="text-center mb-12">
Berdasarkan Peraturan Menteri Komunikasi dan Informatika Nomor 2 Tahun 2024, setiap gim yang beredar di Indonesia perlu melewati proses klasifikasi yang <h1 className="text-4xl md:text-5xl font-bold mb-6 text-gray-800 tracking-tight">
mempertimbangkan konten dan nilai-nilai budaya kita. Di sini, kami mendukung langkah ini dengan semangat terbuka, agar para pengembang dan pemain sama-sama Tentang
tahu mana yang pas buat siapa. Soal seru-seruan, tetap jalan, tapi tetap bijak juga.</h1> </h1>
<div className="w-24 h-1 bg-yellow-500 mx-auto mb-8"></div>
</div>
<div className="bg-white/30 backdrop-blur-sm rounded-2xl p-8 shadow-lg">
<p className="text-lg md:text-xl leading-relaxed text-gray-800 font-medium text-justify">
Klasifikasi gim itu penting, bukan untuk membatasi, tetapi agar semua orang terutama anak muda dapat bermain dengan aman dan sesuai usia.
</p>
<p className="text-lg md:text-xl leading-relaxed text-gray-800 mt-6 font-medium text-justify">
Berdasarkan Peraturan Menteri Komunikasi dan Informatika Nomor 2 Tahun 2024, setiap gim yang beredar di Indonesia perlu melewati proses klasifikasi yang
mempertimbangkan konten dan nilai-nilai budaya kita.
</p>
<p className="text-lg md:text-xl leading-relaxed text-gray-800 mt-6 font-medium text-justify">
Di sini, kami mendukung langkah ini dengan semangat terbuka, agar para pengembang dan pemain sama-sama
tahu mana yang pas buat siapa. Soal seru-seruan, tetap jalan, tapi tetap bijak juga.
</p>
</div>
<div className="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-white/30 backdrop-blur-sm rounded-xl p-6 text-center">
<div className="text-3xl mb-3">🏷</div>
<h3 className="text-xl font-semibold mb-2">Klasifikasi</h3>
<p className="text-gray-700">Sistem penilaian yang tepat untuk setiap gim</p>
</div>
<div className="bg-white/30 backdrop-blur-sm rounded-xl p-6 text-center">
<div className="text-3xl mb-3">🛡</div>
<h3 className="text-xl font-semibold mb-2">Keamanan</h3>
<p className="text-gray-700">Melindungi pemain sesuai usia</p>
</div>
<div className="bg-white/30 backdrop-blur-sm rounded-xl p-6 text-center">
<div className="text-3xl mb-3">🎮</div>
<h3 className="text-xl font-semibold mb-2">Developer Game Indonesia</h3>
<p className="text-gray-700">Membantu developer game Indonesia dalam menentukan rating umur pada game yang sedang dibuat.</p>
</div>
</div>
</div>
</div> </div>
); );
}; };

View File

@ -68,68 +68,69 @@ const ListGames = () => {
}; };
return ( return (
<div className="min-h-screen flex flex-col"> <div className="min-h-screen flex flex-col font-sans bg-gray-50">
<Header /> <Header />
<div className="p-10"> {/* Add top padding to prevent header overlap */}
<h1 className="text-3xl font-bold mb-6">Daftar Game</h1> <div className="pt-20 px-4 sm:px-8 lg:px-16 max-w-7xl mx-auto w-full">
<h1 className="text-4xl font-extrabold mb-8 text-gray-800 tracking-tight">Daftar Game</h1>
{/* Grid untuk filter di kiri, search + list game di kanan */} {/* Responsive grid: filter on top on mobile, left on desktop */}
<div className="grid grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{/* Bagian 1: Filter Rating */} {/* Filter */}
<aside className="bg-gray-200 p-4 rounded h-fit"> <aside className="bg-white/80 backdrop-blur-sm p-6 rounded-2xl shadow-md h-fit mb-4 md:mb-0">
<h2 className="text-xl font-bold mb-4">Filter Rating</h2> <h2 className="text-2xl font-bold mb-4 text-yellow-600">Filter Rating</h2>
<div className="space-y-2"> <div className="space-y-3">
{Object.keys(ratingDescriptions).map((rating, index) => ( {Object.keys(ratingDescriptions).map((rating, index) => (
<label key={index} className="flex items-center space-x-2"> <label key={index} className="flex items-center space-x-3 text-lg">
<input <input
type="checkbox" type="checkbox"
className="w-4 h-4" className="w-5 h-5 accent-yellow-500 rounded"
checked={selectedRatings.includes(rating)} checked={selectedRatings.includes(rating)}
onChange={() => handleRatingFilter(rating)} onChange={() => handleRatingFilter(rating)}
/> />
<span>{ratingDescriptions[rating].label}</span> <span>{ratingDescriptions[rating].label}</span>
</label> </label>
))} ))}
</div> </div>
</aside> </aside>
{/* Bagian 2: Daftar Game */} {/* Main Content */}
<main className="col-span-3"> <main className="md:col-span-3">
{/* Bagian 3: Search Form */} {/* Search Bar */}
<div className="mb-4"> <div className="mb-6">
<input <input
type="text" type="text"
placeholder="Cari game..." placeholder="Cari game..."
className="p-2 border w-full" className="w-full p-4 rounded-xl border-2 border-gray-200 focus:border-yellow-500 focus:ring-2 focus:ring-yellow-100 transition-all duration-300 bg-white/90 text-lg"
value={searchTerm} value={searchTerm}
onChange={handleSearch} onChange={handleSearch}
/> />
</div> </div>
{/* Card List Game */} {/* Game Cards */}
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredGames.length > 0 ? ( {filteredGames.length > 0 ? (
filteredGames.map((game, index) => { filteredGames.map((game, index) => {
const ratingInfo = ratingDescriptions[game.rating] || {}; const ratingInfo = ratingDescriptions[game.rating] || {};
return ( return (
<div key={index} className="border p-4 bg-gray-200 rounded shadow-md"> <div key={index} className="bg-white rounded-2xl shadow-lg p-6 flex flex-col gap-2 hover:shadow-xl transition-shadow duration-300">
<h3 className="text-lg font-bold">{game.name}</h3> <h3 className="text-xl font-bold text-gray-800">{game.name}</h3>
<p className={`font-semibold ${ratingInfo.color}`}> <p className={`font-semibold ${ratingInfo.color}`}>
Rating: {ratingInfo.label} Rating: {ratingInfo.label}
</p> </p>
<p className="text-sm text-gray-600"> <p className="text-gray-500 text-base">
{ratingInfo.description} {ratingInfo.description}
</p> </p>
</div> </div>
); );
}) })
) : ( ) : (
<p className="text-gray-500">Tidak ada game yang sesuai.</p> <p className="text-gray-500 col-span-full text-center">Tidak ada game yang sesuai.</p>
)} )}
</div> </div>
</main> </main>
</div> </div>
</div> </div>
<Footer className="mt-auto" /> <Footer className="mt-auto" />