This commit is contained in:
Kevin Dwi Wijaya
2025-08-20 10:10:13 +00:00
parent 144736c02c
commit e356aabc31
20 changed files with 17862 additions and 1 deletions

38
src/App.css Normal file
View File

@@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

767
src/App.js Normal file
View File

@@ -0,0 +1,767 @@
import React, { useState, useEffect, useRef } from 'react';
import './index.css'; // Pastikan untuk mengimpor file CSS Anda
function App() {
// State untuk mengontrol tampilan: 'landing' atau 'dashboard'
const [view, setView] = useState('landing');
// State untuk modal login/register
const [isAuthModalOpen, setAuthModalOpen] = useState(false);
const [authForm, setAuthForm] = useState('login'); // 'login' atau 'register'
// State untuk bagian aktif di dashboard
const [activeDashboardSection, setActiveDashboardSection] = useState('chatbot');
// State untuk fungsionalitas Chatbot
const [messages, setMessages] = useState([
{ sender: 'bot', text: 'Halo! Ada yang bisa saya bantu hari ini?' }
]);
const [chatInput, setChatInput] = useState('');
const [isChatLoading, setChatLoading] = useState(false);
const chatMessagesEndRef = useRef(null);
// State untuk fungsionalitas Image Generation
const [imagePrompt, setImagePrompt] = useState('');
const [generatedImageUrl, setGeneratedImageUrl] = useState('');
const [isImageLoading, setImageLoading] = useState(false);
// State untuk koneksi media sosial di profil
const [socialConnections, setSocialConnections] = useState({
twitter: false,
instagram: false,
});
// Fungsi untuk scroll otomatis ke pesan terbaru
const scrollToBottom = () => {
chatMessagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(() => {
if (view === 'dashboard' && activeDashboardSection === 'chatbot') {
scrollToBottom();
}
}, [messages, view, activeDashboardSection]);
// --- Fungsi Handler ---
const showDashboard = () => {
setAuthModalOpen(false);
setView('dashboard');
};
const showLanding = () => {
setView('landing');
};
const handleLogin = (e) => {
e.preventDefault();
// Logika login simulasi
const email = e.target.loginEmail.value;
const password = e.target.loginPassword.value;
if (email === "user@example.com" && password === "password123") {
alert("Login berhasil!");
showDashboard();
} else {
alert("Email atau kata sandi salah.");
}
};
const handleRegister = (e) => {
e.preventDefault();
// Logika registrasi simulasi
const password = e.target.registerPassword.value;
const confirmPassword = e.target.confirmPassword.value;
if (password !== confirmPassword) {
alert("Kata sandi tidak cocok.");
return;
}
alert("Registrasi berhasil! Silakan masuk.");
setAuthForm('login');
};
const handleSendMessage = async () => {
const userMessage = chatInput.trim();
if (userMessage === "") return;
// Tambahkan pesan pengguna ke state
setMessages(prev => [...prev, { sender: 'user', text: userMessage }]);
setChatInput('');
setChatLoading(true);
// Simulasi panggilan API ke Gemini
setTimeout(() => {
// Ini adalah respons palsu. Ganti dengan panggilan fetch API asli Anda.
const botResponse = `Ini adalah respons AI untuk: "${userMessage}". Dalam aplikasi nyata, ini akan datang dari API.`;
setMessages(prev => [...prev, { sender: 'bot', text: botResponse }]);
setChatLoading(false);
}, 1500);
};
const handleGenerateImage = async () => {
if (imagePrompt.trim() === "") {
alert("Mohon masukkan deskripsi gambar.");
return;
}
setImageLoading(true);
setGeneratedImageUrl('');
// Simulasi panggilan API pembuatan gambar
setTimeout(() => {
// Ganti URL ini dengan hasil dari API Anda
const mockImageUrl = `https://placehold.co/512x512/142F32/E5FFCC?text=Hasil:+${encodeURIComponent(imagePrompt.substring(0, 20))}`;
setGeneratedImageUrl(mockImageUrl);
setImageLoading(false);
}, 2000);
};
const handleConnectSocial = (platform) => {
setSocialConnections(prev => ({
...prev,
[platform]: !prev[platform]
}));
};
const handleLogout = () => {
// Logika logout
showLanding();
alert("Anda telah berhasil keluar.");
};
// --- Komponen-Komponen Kecil ---
const Header = () => (
<header className="header-bg py-4 px-6 flex justify-between items-center fixed top-0 left-0 w-full z-10">
<div className="flex items-center">
<img src="https://placehold.co/40x40/142F32/E5FFCC?text=M" alt="Logo Maragen" className="h-8 w-8 mr-2 rounded-full" />
<span className="text-2xl font-bold text-gray-800">Maragen</span>
</div>
<nav className="hidden md:flex space-x-8">
<a href="#beranda" className="text-gray-600 hover:text-gray-900 font-medium">Beranda</a>
<a href="#tentang" className="text-gray-600 hover:text-gray-900 font-medium">Tentang</a>
<a href="#layanan" className="text-gray-600 hover:text-gray-900 font-medium">Layanan</a>
<a href="#kontak" className="text-gray-600 hover:text-gray-900 font-medium">Kontak</a>
</nav>
<button onClick={() => setAuthModalOpen(true)} className="button-sign-up px-6 py-2 rounded-lg font-semibold hidden md:block">
Daftar
</button>
<button className="md:hidden text-gray-600 hover:text-gray-900 focus:outline-none">
<i className="fas fa-bars text-2xl"></i>
</button>
</header>
);
const LandingPage = () => (
<div id="landingPage" className="landing-page-content">
{/* Hero Section */}
<section className="hero-section pt-24 pb-12">
<div className="max-w-4xl mx-auto">
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold mb-4 leading-tight text-gray-900">
Masa Depan Otomatisasi dengan <span className="text-142F32">Teknologi Terbaru</span>
</h1>
<p className="text-lg sm:text-xl mb-8 text-gray-600">
Ahli teknologi untuk meningkatkan bisnis Anda. Mari bawa bisnis Anda lebih jauh.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<button
onClick={() => setAuthModalOpen(true)}
className="button-primary px-8 py-4 text-lg sm:text-xl font-semibold rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50"
>
Mulai Sekarang
</button>
<button
onClick={showDashboard}
className="button-secondary px-8 py-4 text-lg sm:text-xl font-semibold rounded-lg focus:outline-none focus:ring-4 focus:ring-gray-300 focus:ring-opacity-50"
>
Coba Demo
</button>
</div>
<div className="mt-6 flex items-center justify-center text-gray-600">
<i className="fas fa-star text-yellow-400 mr-1"></i>
<i className="fas fa-star text-yellow-400 mr-1"></i>
<i className="fas fa-star text-yellow-400 mr-1"></i>
<i className="fas fa-star text-yellow-400 mr-1"></i>
<i className="fas fa-star-half-alt text-yellow-400 mr-2"></i>
<span>5.0</span>
<span className="ml-2">dari 80+ ulasan</span>
</div>
</div>
{/* Stat Cards Grid */}
<div className="max-w-6xl mx-auto stats-grid mt-16 px-4">
<div className="stat-card overflow-hidden">
<img src="https://placehold.co/400x300/142F32/E5FFCC?text=Otomatisasi" alt="Otomatisasi Visual" className="card-image" />
<div className="p-4 text-center">
<p className="text-gray-700 font-semibold">Visual Otomatisasi</p>
</div>
</div>
<div className="stat-card p-6 dark-bg">
<div className="flex items-center mb-2">
<i className="fas fa-users text-E5FFCC text-2xl mr-3"></i>
<p className="text-3xl font-bold text-white">100+</p>
</div>
<p className="text-gray-400 text-sm">Klien & Mitra Terhormat Kami</p>
</div>
<div className="stat-card p-6">
<div className="flex items-center mb-2">
<i className="fas fa-project-diagram text-142F32 text-2xl mr-3"></i>
<p className="text-3xl font-bold text-gray-800">1951+</p>
</div>
<p className="text-gray-600 text-sm">Total Proyek <span className="text-green-500">126 bulan ini</span></p>
</div>
<div className="stat-card p-6 dark-bg">
<div className="flex items-center mb-2">
<i className="fas fa-calendar-alt text-E5FFCC text-2xl mr-3"></i>
<p className="text-3xl font-bold text-white">6+</p>
</div>
<p className="text-gray-400 text-sm">Tahun Layanan Khusus</p>
</div>
</div>
</section>
{/* Bottom Section (Layanan Otomatisasi) */}
<section className="bottom-section">
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-extrabold mb-4 text-white">
Layanan Otomatisasi yang Efisien dan Terintegrasi
</h2>
<p className="text-lg sm:text-xl mb-12 opacity-80">
Sederhanakan operasi Anda dengan layanan berkualitas dan berorientasi efisiensi.
</p>
<div className="bottom-grid">
<div className="bottom-card">
<div className="flex items-center justify-between mb-4">
<i className="fas fa-robot text-E5FFCC text-3xl"></i>
<i className="fas fa-arrow-up-right-from-square text-gray-500 hover:text-E5FFCC cursor-pointer"></i>
</div>
<h3 className="text-xl font-semibold mb-2 text-white">Alur Kerja Otomatis</h3>
<p className="text-gray-400 text-sm">
Detail tentang proses otomatisasi, kemampuan integrasi, dan jenis alur kerja yang didukung.
</p>
</div>
<div className="bottom-card">
<div className="flex items-center justify-between mb-4">
<i className="fas fa-cogs text-E5FFCC text-3xl"></i>
<i className="fas fa-arrow-up-right-from-square text-gray-500 hover:text-E5FFCC cursor-pointer"></i>
</div>
<h3 className="text-xl font-semibold mb-2 text-white">Kustomisasi Solusi</h3>
<p className="text-gray-400 text-sm">
Pembuatan solusi otomatisasi khusus dengan opsi desain dan kustomisasi.
</p>
</div>
<div className="bottom-card">
<div className="flex items-center justify-between mb-4">
<i className="fas fa-chart-line text-E5FFCC text-3xl"></i>
<i className="fas fa-arrow-up-right-from-square text-gray-500 hover:text-E5FFCC cursor-pointer"></i>
</div>
<h3 className="text-xl font-semibold mb-2 text-white">Pemantauan & Analisis</h3>
<p className="text-gray-400 text-sm">
Prosedur dan sistem untuk memastikan kualitas output otomatisasi yang tinggi.
</p>
</div>
</div>
</div>
</section>
{/* Key Benefits Section */}
<section className="key-benefits-section">
<div className="benefits-image-card">
<div className="flex items-center justify-between mb-4">
<span className="text-gray-800 font-semibold">Total Proyek</span>
<span className="text-green-500 text-sm">56%</span>
</div>
<div className="space-y-2 text-sm text-gray-600 mb-6">
<div className="flex justify-between">
<span>Selesai</span>
<span>10%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div className="bg-blue-600 h-2.5 rounded-full" style={{width: '10%'}}></div>
</div>
<div className="flex justify-between">
<span>Sedang Berjalan</span>
<span>13%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div className="bg-yellow-500 h-2.5 rounded-full" style={{width: '13%'}}></div>
</div>
<div className="flex justify-between">
<span>Ditolak</span>
<span>11%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div className="bg-red-500 h-2.5 rounded-full" style={{width: '11%'}}></div>
</div>
</div>
<div className="stat-card p-6 dark-bg p-4 mt-4">
<div className="flex items-center mb-2">
<i className="fas fa-project-diagram text-E5FFCC text-2xl mr-3"></i>
<p className="text-3xl font-bold text-white">1951+</p>
</div>
<p className="text-gray-400 text-sm">Peningkatan <span className="text-green-500">126 bulan ini</span></p>
</div>
</div>
<div className="benefits-content">
<h2 className="text-3xl sm:text-4xl font-extrabold mb-6 text-gray-900">
Manfaat Utama Sistem Kami untuk <span className="text-142F32">Efisiensi Bisnis Anda</span>
</h2>
<p className="text-lg sm:text-xl mb-8 text-gray-600">
Sistem kami meningkatkan produktivitas, mengurangi biaya, dan mendorong pertumbuhan bisnis.
</p>
<div className="space-y-6">
<div className="benefit-item">
<i className="fas fa-check-circle text-142F32"></i>
<div>
<h3 className="text-xl font-semibold text-gray-800">Meningkatkan Kualitas dengan Teknologi</h3>
<p className="text-gray-600 text-base">
Dengan teknologi canggih, kami membantu Anda mencapai kualitas produk terbaik. Pelajari bagaimana kami dapat meningkatkan standar Anda.
</p>
</div>
</div>
<div className="benefit-item">
<i className="fas fa-check-circle text-142F32"></i>
<div>
<h3 className="text-xl font-semibold text-gray-800">Optimasi Proses Produksi</h3>
<p className="text-gray-600 text-base">
Meningkatkan efisiensi dan produktivitas pabrik dengan solusi inovatif kami. Lihat bagaimana teknologi terbaru dapat memaksimalkan output Anda.
</p>
</div>
</div>
<div className="benefit-item">
<i className="fas fa-check-circle text-142F32"></i>
<div>
<h3 className="text-xl font-semibold text-gray-800">Produksi Berbasis AI</h3>
<p className="text-gray-600 text-base">
Manfaatkan kekuatan AI untuk mengubah proses manufaktur Anda, mencapai hasil yang lebih cepat dan efektif.
</p>
</div>
</div>
</div>
</div>
</section>
{/* Tailored Plans Section */}
<section className="tailored-plans-section">
<div className="max-w-4xl mx-auto mb-12">
<h2 className="text-3xl sm:text-4xl font-extrabold mb-4 text-white">
Paket yang Disesuaikan untuk <span className="text-E5FFCC">Skala Bisnis Anda</span>
</h2>
<p className="text-lg sm:text-xl opacity-80">
Harga fleksibel untuk setiap ukuran bisnis.
</p>
</div>
<div className="pricing-grid">
<div className="pricing-card">
<h3 className="text-2xl font-semibold mb-2 text-white">Starter</h3>
<p className="text-gray-400 text-base mb-4">
Paket ini menawarkan fitur dasar yang Anda butuhkan untuk memulai.
</p>
<div className="flex items-baseline mb-6">
<span className="price">$39</span>
<span className="price-per-month">/ bulan</span>
</div>
<button className="button-primary w-full py-3 rounded-lg font-semibold text-lg">
Mulai Sekarang
</button>
<div className="feature-list mt-6">
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Produksi hingga 10.000 unit per bulan</span></div>
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Dukungan teknis 24/7</span></div>
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Akses ke dasbor produksi</span></div>
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Pengaturan awal</span></div>
</div>
</div>
<div className="pricing-card">
<h3 className="text-2xl font-semibold mb-2 text-white">Enterprise</h3>
<p className="text-gray-400 text-base mb-4">
Paket ini menyediakan akses penuh ke semua fitur premium.
</p>
<div className="flex items-baseline mb-6">
<span className="price">$99</span>
<span className="price-per-month">/ bulan</span>
</div>
<button className="button-primary w-full py-3 rounded-lg font-semibold text-lg">
Mulai Sekarang
</button>
<div className="feature-list mt-6">
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Unit produksi tak terbatas</span></div>
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Manajer akun khusus</span></div>
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Solusi manufaktur yang disesuaikan</span></div>
<div className="feature-item"><i className="fas fa-check-circle text-E5FFCC"></i><span>Optimalisasi produksi prediktif</span></div>
</div>
</div>
</div>
</section>
{/* Seamless Integrations Section */}
<section className="integrations-section">
<div className="integrations-content">
<h2 className="text-3xl sm:text-4xl font-extrabold mb-6 text-gray-900">
Memberdayakan Perusahaan Top dengan <span className="text-142F32">Integrasi Tanpa Batas</span>
</h2>
<p className="text-lg sm:text-xl mb-8 text-gray-600">
Rasakan koneksi tanpa batas dengan solusi inovatif kami, dirancang untuk berintegrasi dengan mudah dengan sistem Anda yang ada, meningkatkan produktivitas, dan mendorong bisnis Anda menuju kesuksesan yang lebih besar.
</p>
<button className="button-primary px-8 py-4 text-lg sm:text-xl font-semibold rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50">
Bekerja Bersama Kami
</button>
</div>
<div className="integrations-visual">
<div className="integration-icon-grid">
<div className="integration-icon"><i className="fab fa-slack"></i></div>
<div className="integration-icon"><i className="fab fa-google-drive"></i></div>
<div className="integration-icon"><i className="fab fa-dropbox"></i></div>
<div className="integration-icon"><i className="fab fa-jira"></i></div>
<div className="integration-icon"><i className="fab fa-github"></i></div>
<div className="integration-icon"><i className="fab fa-trello"></i></div>
<div className="integration-icon"><i className="fab fa-salesforce"></i></div>
<div className="integration-icon"><i className="fab fa-microsoft"></i></div>
<div className="integration-icon"><i className="fab fa-aws"></i></div>
</div>
</div>
</section>
{/* From Idea to Production Section */}
<section className="idea-to-production-section">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-extrabold mb-4 text-white">
Dari Ide ke Produksi dalam Hitungan Hari
</h2>
<p className="text-lg sm:text-xl mb-8 opacity-80">
Percepat produksi Anda dengan teknologi kami. Kurangi waktu henti dan optimalkan biaya. Dapatkan penawaran spesial sekarang!
</p>
<button className="button-primary px-8 py-4 text-lg sm:text-xl font-semibold rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50">
Bekerja Bersama Kami
</button>
</div>
</section>
<Footer />
</div>
);
const Footer = () => (
<footer className="footer-section">
<div className="max-w-6xl mx-auto footer-grid">
<div className="footer-col">
<div className="flex items-center mb-4">
<img src="https://placehold.co/40x40/142F32/E5FFCC?text=P" alt="Logo Maragen" className="h-8 w-8 mr-2 rounded-full"/>
<span className="text-2xl font-bold text-white">Maragen</span>
</div>
<p className="mb-4">
Solusi kami membuat produksi lebih cepat dan lebih murah. Hubungi kami untuk informasi lebih lanjut.
</p>
</div>
<div className="footer-col">
<h3>Perusahaan</h3>
<ul>
<li><a href="#">Tentang Kami</a></li>
<li><a href="#">Pelanggan</a></li>
<li><a href="#">Berita</a></li>
<li><a href="#">Acara</a></li>
</ul>
</div>
<div className="footer-col">
<h3>Industri</h3>
<ul>
<li><a href="#">Manufaktur Logam Presisi</a></li>
<li><a href="#">Manufaktur Industri</a></li>
<li><a href="#">Teknologi Tinggi & Elektronik</a></li>
<li><a href="#">Dirgantara</a></li>
</ul>
</div>
<div className="footer-col">
<h3>Produk</h3>
<ul>
<li><a href="#">Sistem Eksekusi Manufaktur</a></li>
<li><a href="#">Perencanaan Sumber Daya Perusahaan</a></li>
<li><a href="#">Sistem Manajemen Kualitas</a></li>
<li><a href="#">Perencanaan Rantai Pasokan</a></li>
</ul>
</div>
<div className="footer-col">
<h3>Hubungi Kami</h3>
<p className="mb-4">halo@maragen.com</p>
<div className="social-icons">
<a href="#"><i className="fab fa-linkedin"></i></a>
<a href="#"><i className="fab fa-instagram"></i></a>
<a href="#"><i className="fab fa-facebook"></i></a>
<a href="#"><i className="fab fa-twitter"></i></a>
</div>
</div>
</div>
<div className="text-center mt-8 pt-8 border-t border-gray-700">
<p>&copy; 2024 Maragen. Semua hak dilindungi undang-undang.</p>
<div className="flex justify-center space-x-4 mt-2">
<a href="#" className="hover:text-white">Syarat & Ketentuan</a>
<a href="#" className="hover:text-white">Kebijakan Privasi</a>
</div>
</div>
</footer>
);
const AuthModal = () => (
<div className={`modal ${isAuthModalOpen ? 'show' : ''}`}>
<div className="modal-content">
<span className="close-button" onClick={() => setAuthModalOpen(false)}>&times;</span>
<h2 className="text-3xl font-extrabold mb-6 text-gray-900">Masuk atau Daftar</h2>
{authForm === 'login' ? (
<>
<form onSubmit={handleLogin} className="space-y-4">
<input type="email" name="loginEmail" placeholder="Email" className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32" required defaultValue="user@example.com"/>
<input type="password" name="loginPassword" placeholder="Kata Sandi" className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32" required defaultValue="password123"/>
<button type="submit" className="button-primary w-full py-3 rounded-lg font-semibold">Masuk</button>
</form>
<p className="mt-4 text-gray-600">
Belum punya akun? <a href="#" onClick={(e) => { e.preventDefault(); setAuthForm('register'); }} className="text-142F32 font-semibold hover:underline">Daftar di sini</a>
</p>
</>
) : (
<>
<form onSubmit={handleRegister} className="space-y-4">
<input type="email" name="registerEmail" placeholder="Email" className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32" required/>
<input type="password" name="registerPassword" placeholder="Kata Sandi" className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32" required/>
<input type="password" name="confirmPassword" placeholder="Konfirmasi Kata Sandi" className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32" required/>
<button type="submit" className="button-primary w-full py-3 rounded-lg font-semibold">Daftar</button>
</form>
<p className="mt-4 text-gray-600">
Sudah punya akun? <a href="#" onClick={(e) => { e.preventDefault(); setAuthForm('login'); }} className="text-142F32 font-semibold hover:underline">Masuk di sini</a>
</p>
</>
)}
</div>
</div>
);
const Dashboard = () => {
const sidebarItems = [
{ id: 'chatbot', icon: 'fa-comments', label: 'Chatbot' },
{ id: 'imageGen', icon: 'fa-image', label: 'Buat Gambar' },
{ id: 'scheduling', icon: 'fa-calendar-alt', label: 'Penjadwalan' },
{ id: 'monitoring', icon: 'fa-chart-line', label: 'Pemantauan Konten' },
{ id: 'notifications', icon: 'fa-bell', label: 'Notifikasi' },
{ id: 'profile', icon: 'fa-user-circle', label: 'Pengaturan Profil' },
];
return (
<div id="userDashboard" className="dashboard-container">
{/* Sidebar */}
<aside className="dashboard-sidebar">
<h2 className="text-3xl font-bold mb-8 text-E5FFCC">Dashboard</h2>
<div className="w-full">
{sidebarItems.map(item => (
<div
key={item.id}
className={`dashboard-sidebar-item ${activeDashboardSection === item.id ? 'active' : ''}`}
onClick={() => setActiveDashboardSection(item.id)}
>
<i className={`fas ${item.icon} mr-3`}></i> {item.label}
</div>
))}
</div>
<button onClick={handleLogout} className="button-secondary mt-auto w-full py-2 rounded-lg text-E5FFCC border-E5FFCC hover:bg-E5FFCC hover:text-142F32">
<i className="fas fa-sign-out-alt mr-2"></i> Keluar
</button>
</aside>
{/* Main Content */}
<main className="dashboard-main-content">
{activeDashboardSection === 'chatbot' && (
<section className="dashboard-section-content">
<h2 className="text-142F32">Chatbot AI</h2>
<p className="mb-6">Ajukan pertanyaan atau mulai percakapan dengan AI kami.</p>
<div className="chat-container">
<div className="chat-messages">
{messages.map((msg, index) => (
<div key={index} className={`chat-message ${msg.sender}`}>
{msg.text}
</div>
))}
<div ref={chatMessagesEndRef} />
</div>
{isChatLoading && <div className="loading-indicator show">Mengetik...</div>}
<div className="chat-input-area">
<input
type="text"
placeholder="Ketik pesan Anda..."
value={chatInput}
onChange={(e) => setChatInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
autoFocus
/>
<button onClick={handleSendMessage}>Kirim</button>
</div>
</div>
</section>
)}
{activeDashboardSection === 'imageGen' && (
<section className="dashboard-section-content">
<h2 className="text-142F32">Buat Gambar AI</h2>
<p className="mb-6">Masukkan deskripsi untuk membuat gambar.</p>
<div className="flex flex-col gap-4">
<input
type="text"
placeholder="Deskripsikan gambar yang ingin Anda buat (mis: 'seekor kucing di luar angkasa')"
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32"
value={imagePrompt}
onChange={(e) => setImagePrompt(e.target.value)}
/>
<button onClick={handleGenerateImage} className="button-primary py-3 rounded-lg font-semibold" disabled={isImageLoading}>
{isImageLoading ? 'Membuat...' : 'Buat Gambar'}
</button>
{isImageLoading && <div className="loading-indicator show">Membuat gambar...</div>}
{generatedImageUrl && (
<div className="text-center">
<img src={generatedImageUrl} className="image-gen-preview" alt="Gambar yang Dihasilkan" />
<div className="flex justify-center gap-4 mt-4">
<button className="button-secondary py-2 px-4 rounded-lg">Jadwalkan</button>
<button className="button-primary py-2 px-4 rounded-lg">Posting Langsung</button>
</div>
</div>
)}
</div>
</section>
)}
{activeDashboardSection === 'scheduling' && (
<section className="dashboard-section-content">
<h2 className="text-142F32">Penjadwalan Konten</h2>
<p className="mb-6">Kelola konten yang akan dipublikasikan di masa mendatang.</p>
<div>
<ul>
<li>
<span>Postingan Blog: "Masa Depan Otomatisasi"</span>
<span className="text-gray-500 text-sm">2025-08-15 10:00 AM</span>
</li>
<li>
<span>Gambar AI: "Pemandangan Kota Futuristik"</span>
<span className="text-gray-500 text-sm">2025-08-18 02:30 PM</span>
</li>
</ul>
</div>
<button className="button-primary mt-6 py-2 px-4 rounded-lg">Tambah Jadwal Baru</button>
</section>
)}
{activeDashboardSection === 'monitoring' && (
<section className="dashboard-section-content">
<h2>Pemantauan & Analisis Tren</h2>
<p className="mb-6">Pantau kinerja konten Anda dan temukan tren viral berikutnya.</p>
<div>
<h3>Kinerja Konten Saya</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="stat-card p-6 p-4">
<p className="text-gray-600 text-sm">Total Penayangan</p>
<p className="text-2xl font-bold text-gray-800">1.2M</p>
<p className="text-green-500 text-xs">15% bulan ini</p>
</div>
<div className="stat-card p-6 p-4">
<p className="text-gray-600 text-sm">Total Suka</p>
<p className="text-2xl font-bold text-gray-800">89K</p>
<p className="text-green-500 text-xs">12% bulan ini</p>
</div>
<div className="stat-card p-6 p-4">
<p className="text-gray-600 text-sm">Pengikut Baru</p>
<p className="text-2xl font-bold text-gray-800">5,210</p>
<p className="text-green-500 text-xs">8% bulan ini</p>
</div>
</div>
</div>
<div className="mt-8">
<h3>Konten yang Sedang Tren</h3>
<div className="space-y-4">
{/* Konten tren akan diisi oleh data dari API */}
<p className="text-gray-500">Fitur ini sedang dalam pengembangan.</p>
</div>
</div>
</section>
)}
{activeDashboardSection === 'notifications' && (
<section className="dashboard-section-content">
<h2>Notifikasi</h2>
<p className="mb-6">Pembaruan terkini tentang akun dan konten Anda.</p>
<ul className="space-y-3">
{/* Notifikasi akan diisi oleh data dari API */}
<li>Postingan Anda "Masa Depan Otomatisasi" telah dijadwalkan.</li>
<li>Selamat! Anda mendapatkan 50 pengikut baru minggu ini.</li>
</ul>
</section>
)}
{activeDashboardSection === 'profile' && (
<section className="dashboard-section-content">
<h2>Pengaturan Profil</h2>
<p className="mb-6">Kelola informasi akun dan koneksi media sosial Anda.</p>
<div className="space-y-6 max-w-lg mx-auto text-left">
<div>
<label htmlFor="profileName" className="block text-sm font-medium text-gray-700">Nama Pengguna</label>
<input type="text" id="profileName" defaultValue="Pengguna Maragen"
className="mt-1 w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-142F32" />
</div>
<div>
<label htmlFor="profileEmail" className="block text-sm font-medium text-gray-700">Email</label>
<input type="email" id="profileEmail" defaultValue="user@example.com" disabled
className="mt-1 w-full p-3 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed" />
</div>
<div className="pt-4 border-t">
<h3 className="text-lg font-semibold text-gray-800 mb-4">Autentikasi Media Sosial</h3>
<div className="flex justify-between items-center p-3 bg-gray-50 rounded-lg mb-3">
<div className="flex items-center">
<i className="fab fa-twitter text-2xl text-blue-400 mr-4"></i>
<div>
<p className="font-semibold text-gray-700">Twitter</p>
<span className={`connection-status ${socialConnections.twitter ? 'connected' : 'not-connected'}`}>
{socialConnections.twitter ? 'Terhubung' : 'Belum Terhubung'}
</span>
</div>
</div>
<button onClick={() => handleConnectSocial('twitter')} className="button-primary py-2 px-4 rounded-lg text-sm">
{socialConnections.twitter ? 'Putuskan' : 'Hubungkan'}
</button>
</div>
<div className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<div className="flex items-center">
<i className="fab fa-instagram text-2xl text-pink-500 mr-4"></i>
<div>
<p className="font-semibold text-gray-700">Instagram</p>
<span className={`connection-status ${socialConnections.instagram ? 'connected' : 'not-connected'}`}>
{socialConnections.instagram ? 'Terhubung' : 'Belum Terhubung'}
</span>
</div>
</div>
<button onClick={() => handleConnectSocial('instagram')} className="button-primary py-2 px-4 rounded-lg text-sm">
{socialConnections.instagram ? 'Putuskan' : 'Hubungkan'}
</button>
</div>
</div>
<div className="pt-4">
<button className="button-primary py-3 px-6 rounded-lg font-semibold">Simpan Perubahan</button>
<button onClick={handleLogout} className="button-secondary py-3 px-6 rounded-lg font-semibold ml-4">
Keluar
</button>
</div>
</div>
</section>
)}
</main>
</div>
);
};
return (
<>
<Header />
{view === 'landing' && <LandingPage />}
{view === 'dashboard' && <Dashboard />}
{isAuthModalOpen && <AuthModal />}
</>
);
}
export default App;

8
src/App.test.js Normal file
View File

@@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

614
src/index.css Normal file
View File

@@ -0,0 +1,614 @@
/* Direktif Tailwind */
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
font-family: 'Manrope', sans-serif;
background-color: #f8fafc; /* Background utama, mirip dengan brand guide */
color: #282930; /* Teks gelap utama */
margin: 0;
overflow-x: hidden; /* Mencegah scroll horizontal */
}
/* Custom styles for sections and elements */
.header-bg {
background-color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.hero-section {
background-color: #ffffff;
/* padding: 4rem 2rem; */
text-align: center;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr)); /* 1 kolom di layar kecil */
gap: 1.5rem;
margin-top: 3rem;
}
@media (min-width: 640px) { /* sm breakpoint */
.stats-grid {
grid-template-columns: repeat(2, minmax(0, 1fr)); /* 2 kolom di layar sedang */
}
}
@media (min-width: 1024px) { /* lg breakpoint */
.stats-grid {
grid-template-columns: repeat(4, minmax(0, 1fr)); /* 4 kolom di layar besar */
}
}
.stat-card {
background-color: #ffffff;
border-radius: 1rem;
text-align: left;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out;
}
.stat-card.dark-bg {
background-color: #282930; /* Menggunakan warna gelap dari brand guide */
color: #e2e8f0;
}
.stat-card.light-green-bg {
background-color: #E5FFCC; /* Menggunakan warna hijau terang dari brand guide */
color: #142F32; /* Teks gelap di atas hijau terang */
}
.stat-card:hover {
transform: translateY(-5px);
}
.bottom-section {
background-color: #282930; /* Menggunakan warna gelap dari brand guide */
padding: 4rem 2rem;
color: #e2e8f0;
text-align: center;
}
.bottom-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr)); /* 1 kolom di layar kecil */
gap: 1.5rem;
margin-top: 3rem;
}
@media (min-width: 768px) { /* md breakpoint */
.bottom-grid {
grid-template-columns: repeat(3, minmax(0, 1fr)); /* 3 kolom di layar sedang */
}
}
.bottom-card {
background-color: #142F32; /* Menggunakan warna hijau gelap dari brand guide */
border-radius: 1rem;
padding: 2rem;
text-align: left;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2), 0 4px 6px -2px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease-in-out;
}
.bottom-card:hover {
transform: translateY(-5px);
}
.button-primary {
background-color: #142F32; /* Menggunakan warna hijau gelap dari brand guide */
color: white;
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.button-primary:hover {
background-color: #0d1e20; /* Hijau gelap lebih gelap */
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
}
.button-secondary {
background-color: transparent;
border: 1px solid #9ca3af; /* Gray border */
color: #4b5563; /* Dark gray text */
transition: all 0.3s ease;
}
.button-secondary:hover {
background-color: #f3f4f6; /* Light gray background */
transform: translateY(-2px);
}
.button-sign-up {
background-color: #142F32; /* Menggunakan warna hijau gelap dari brand guide */
color: white;
transition: all 0.3s ease;
}
.button-sign-up:hover {
background-color: #0d1e20; /* Hijau gelap lebih gelap */
}
/* Responsive image for the first card */
.card-image {
width: 100%;
height: auto;
border-radius: 0.75rem; /* Sudut membulat untuk gambar */
object-fit: cover; /* Memastikan gambar menutupi area */
}
/* Styles for Key Benefits Section */
.key-benefits-section {
background-color: #f8fafc;
padding: 4rem 2rem;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
@media (min-width: 768px) {
.key-benefits-section {
flex-direction: row;
text-align: left;
justify-content: center;
gap: 4rem;
}
}
.benefits-content {
flex: 1;
max-width: 600px;
}
.benefits-image-card {
flex: 1;
background-color: #ffffff;
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
margin-top: 2rem;
max-width: 400px;
}
@media (min-width: 768px) {
.benefits-image-card {
margin-top: 0;
}
}
.benefit-item {
display: flex;
align-items: flex-start;
margin-bottom: 1rem;
}
.benefit-item i {
color: #22c55e; /* Hijau centang */
margin-right: 0.75rem;
font-size: 1.25rem;
margin-top: 0.25rem;
}
/* Styles for Tailored Plans Section */
.tailored-plans-section {
background-color: #282930; /* Menggunakan warna gelap dari brand guide */
padding: 4rem 2rem;
color: #e2e8f0;
text-align: center;
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 2rem;
margin-top: 3rem;
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
@media (min-width: 768px) {
.pricing-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.pricing-card {
background-color: #142F32; /* Menggunakan warna hijau gelap dari brand guide */
border-radius: 1rem;
padding: 2.5rem;
text-align: left;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2), 0 4px 6px -2px rgba(0, 0, 0, 0.1);
}
.pricing-card .price {
font-size: 3rem;
font-weight: 800;
color: #ffffff;
margin-bottom: 0.5rem;
}
.pricing-card .price-per-month {
color: #9ca3af;
font-size: 1rem;
}
.pricing-card .feature-list {
margin-top: 1.5rem;
margin-bottom: 2rem;
}
.pricing-card .feature-item {
display: flex;
align-items: center;
margin-bottom: 0.75rem;
color: #cbd5e0;
}
.pricing-card .feature-item i {
color: #22c55e; /* Hijau centang */
margin-right: 0.5rem;
}
/* New styles for Seamless Integrations Section */
.integrations-section {
background-color: #f8fafc;
padding: 4rem 2rem;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
@media (min-width: 768px) {
.integrations-section {
flex-direction: row;
text-align: left;
justify-content: center;
gap: 4rem;
}
}
.integrations-content {
flex: 1;
max-width: 600px;
}
.integrations-visual {
flex: 1;
background-color: #E5FFCC; /* Menggunakan warna hijau terang dari brand guide */
border-radius: 1rem;
padding: 2rem;
margin-top: 2rem;
max-width: 500px;
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
}
@media (min-width: 768px) {
.integrations-visual {
margin-top: 0;
}
}
.integration-icon-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
width: 100%;
height: 100%;
position: relative;
justify-items: center;
align-items: center;
}
.integration-icon {
background-color: #ffffff;
border-radius: 50%;
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
font-size: 1.5rem;
color: #4a5568;
}
/* From Idea to Production Section */
.idea-to-production-section {
background-color: #282930; /* Menggunakan warna gelap dari brand guide */
padding: 4rem 2rem;
color: #e2e8f0;
text-align: center;
}
/* Footer Section */
.footer-section {
background-color: #282930; /* Menggunakan warna gelap dari brand guide */
padding: 4rem 2rem;
color: #a0aec0; /* Teks abu-abu lebih terang */
font-size: 0.9rem;
}
.footer-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 2rem;
}
@media (min-width: 640px) {
.footer-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.footer-grid {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
}
.footer-col h3 {
font-weight: 600;
color: #ffffff;
margin-bottom: 1rem;
}
.footer-col ul li {
margin-bottom: 0.5rem;
}
.footer-col ul li a {
color: #a0aec0;
transition: color 0.2s ease;
}
.footer-col ul li a:hover {
color: #ffffff;
}
.social-icons a {
color: #a0aec0;
font-size: 1.25rem;
margin-right: 1rem;
transition: color 0.2s ease;
}
.social-icons a:hover {
color: #ffffff;
}
/* Modal Styles */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal.show {
opacity: 1;
visibility: visible;
}
.modal-content {
background-color: #ffffff;
padding: 2.5rem;
border-radius: 1rem;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
max-width: 450px;
width: 90%;
text-align: center;
position: relative;
transform: translateY(-20px);
transition: transform 0.3s ease;
}
.modal.show .modal-content {
transform: translateY(0);
}
.close-button {
position: absolute;
top: 1rem;
right: 1rem;
font-size: 1.5rem;
cursor: pointer;
color: #4b5563;
}
/* Dashboard Styles */
.dashboard-container {
display: none; /* Hidden by default */
grid-template-columns: 250px 1fr; /* Sidebar fixed width, content fills */
min-height: 100vh;
background-color: #f8fafc;
color: #282930;
}
@media (max-width: 768px) {
.dashboard-container {
grid-template-columns: 1fr; /* Stack on small screens */
}
.dashboard-sidebar {
width: 100%;
position: relative;
box-shadow: none;
}
}
.dashboard-sidebar {
background-color: #142F32; /* Hijau gelap */
color: #E5FFCC;
padding: 2rem 1rem;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
}
.dashboard-sidebar-item {
width: 100%;
padding: 0.75rem 1rem;
margin-bottom: 0.5rem;
border-radius: 0.5rem;
display: flex;
align-items: center;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.dashboard-sidebar-item:hover {
background-color: rgba(229, 255, 204, 0.1); /* Hijau terang transparan */
color: #E5FFCC;
}
.dashboard-sidebar-item.active {
background-color: #E5FFCC;
color: #142F32;
font-weight: 600;
}
.dashboard-main-content {
padding: 2rem;
background-color: #f8fafc;
overflow-y: auto;
}
.chat-container {
display: flex;
flex-direction: column;
height: calc(100vh - 4rem); /* Adjust for padding */
background-color: #ffffff;
border-radius: 1rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.chat-messages {
flex-grow: 1;
padding: 1.5rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
}
.chat-message {
max-width: 80%;
padding: 0.75rem 1rem;
border-radius: 0.75rem;
word-wrap: break-word;
}
.chat-message.user {
background-color: #E5FFCC;
align-self: flex-end;
color: #142F32;
}
.chat-message.bot {
background-color: #f1f5f9;
align-self: flex-start;
color: #282930;
}
.chat-input-area {
display: flex;
padding: 1rem;
border-top: 1px solid #e2e8f0;
background-color: #ffffff;
}
.chat-input-area input {
flex-grow: 1;
padding: 0.75rem 1rem;
border: 1px solid #cbd5e0;
border-radius: 0.5rem;
margin-right: 0.75rem;
font-size: 1rem;
}
.chat-input-area button {
background-color: #142F32;
color: white;
padding: 0.75rem 1.25rem;
border-radius: 0.5rem;
transition: background-color 0.2s ease;
}
.chat-input-area button:hover {
background-color: #0d1e20;
}
.loading-indicator {
display: none;
text-align: center;
padding: 1rem;
color: #777C90;
}
.loading-indicator.show {
display: block;
}
.image-gen-preview {
max-width: 100%;
height: auto;
border-radius: 0.75rem;
margin-top: 1.5rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.dashboard-section-content {
background-color: #ffffff;
padding: 2rem;
border-radius: 1rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
min-height: calc(100vh - 8rem); /* Adjust for header/footer/padding */
}
.dashboard-section-content h2 {
font-size: 2.25rem;
font-weight: 800;
margin-bottom: 1.5rem;
color: #282930;
}
.dashboard-section-content p {
color: #4b5563;
margin-bottom: 1rem;
}
.dashboard-section-content ul {
list-style: none;
padding: 0;
}
.dashboard-section-content ul li {
background-color: #f1f5f9;
padding: 1rem;
border-radius: 0.75rem;
margin-bottom: 0.75rem;
display: flex;
justify-content: space-between;
align-items: center;
}

17
src/index.js Normal file
View File

@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
src/logo.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

13
src/reportWebVitals.js Normal file
View File

@@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.js Normal file
View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';