This commit is contained in:
Vassshhhh
2025-09-15 11:46:19 +07:00
parent e356aabc31
commit e0c7ffca2c
12 changed files with 1317 additions and 775 deletions

54
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.1.1", "react-dom": "^19.1.1",
"react-router-dom": "^7.7.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
@@ -12866,6 +12867,53 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-router": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.1.tgz",
"integrity": "sha512-jVKHXoWRIsD/qS6lvGveckwb862EekvapdHJN/cGmzw40KnJH5gg53ujOJ4qX6EKIK9LSBfFed/xiQ5yeXNrUA==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-router-dom": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.1.tgz",
"integrity": "sha512-bavdk2BA5r3MYalGKZ01u8PGuDBloQmzpBZVhDLrOOv1N943Wq6dcM9GhB3x8b7AbqPMEezauv4PeGkAJfy7FQ==",
"license": "MIT",
"dependencies": {
"react-router": "7.7.1"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/react-router/node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/react-scripts": { "node_modules/react-scripts": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -13703,6 +13751,12 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",

View File

@@ -9,6 +9,7 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.1.1", "react-dom": "^19.1.1",
"react-router-dom": "^7.7.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },

767
src/App copy.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;

View File

@@ -1,766 +1,17 @@
import React, { useState, useEffect, useRef } from 'react'; import { Routes, Route } from 'react-router-dom';
import './index.css'; // Pastikan untuk mengimpor file CSS Anda import LoginPage from './pages/LoginPage';
import OrganizationPage from './pages/OrganizationPage';
import DashboardPage from './pages/DashboardPage';
import InputDataPage from './pages/InputDataPage';
function App() { 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 ( return (
<> <Routes>
<Header /> <Route path="/" element={<LoginPage />} />
{view === 'landing' && <LandingPage />} <Route path="/select-organization" element={<OrganizationPage />} />
{view === 'dashboard' && <Dashboard />} <Route path="/dashboard" element={<DashboardPage />} />
{isAuthModalOpen && <AuthModal />} <Route path="/input-data/:docType" element={<InputDataPage />} />
</> </Routes>
); );
} }

View File

@@ -0,0 +1,136 @@
import { useState } from 'react';
// --- ICONS (no changes) ---
const CloseIcon = () => ( <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" /></svg> );
const PlusIcon = () => ( <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clipRule="evenodd" /></svg> );
const TrashIcon = () => ( <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" /></svg> );
// --- Objek Templates (no changes) ---
const templates = {
'KTP':{icon:'👤',fields:[{name:'NIK',type:'Number'},{name:'Nama',type:'Text'},{name:'Jenis Kelamin',type:'Selection',options:[{value:'Laki-laki'},{value:'Perempuan'}]},{name:'Tempat Lahir',type:'Text'},{name:'Tanggal Lahir',type:'Date'},]},
'KK':{icon:'👨‍👩‍👧‍👦',fields:[{name:'Nomor KK',type:'Number'},{name:'Nama Kepala Keluarga',type:'Text'},{name:'Alamat',type:'Text'},{name:'RT/RW',type:'Text'},{name:'Desa/Kelurahan',type:'Text'},{name:'Kecamatan',type:'Text'},{name:'Kabupaten/Kota',type:'Text'},{name:'Provinsi',type:'Text'},{name:'Tanggal Dikeluarkan',type:'Date'},]},
'Akta Kelahiran':{icon:'📜',fields:[{name:'Nomor Akta',type:'Text'},{name:'Nama Lengkap',type:'Text'},{name:'Jenis Kelamin',type:'Selection',options:[{value:'Laki-laki'},{value:'Perempuan'}]},{name:'Tempat Lahir',type:'Text'},{name:'Tanggal Lahir',type:'Date'},{name:'Nama Ayah',type:'Text'},{name:'Nama Ibu',type:'Text'},]},
'Custom':{icon:'⚙️',fields:[]}
};
const templateNames = Object.keys(templates);
export default function AddDocumentModal({ isOpen, onClose }) {
// --- State and handlers (no changes) ---
const [selectedTemplate, setSelectedTemplate] = useState(null);
const [docTypeName, setDocTypeName] = useState('');
const [fields, setFields] = useState([]);
const handleSelectTemplate = (templateName) => {
setSelectedTemplate(templateName);
const templateData = templates[templateName];
setDocTypeName(templateName === 'Custom' ? '' : templateName);
if (templateData.fields && templateData.fields.length > 0) {
const newFields = templateData.fields.map(field => ({
id: Date.now() + Math.random(),
name: field.name,
type: field.type,
options: field.options ? field.options.map(opt => ({ id: Date.now() + Math.random(), value: opt.value })) : []
}));
setFields(newFields);
} else {
setFields([{ id: Date.now(), name: '', type: 'Text', options: [] }]);
}
};
const handleAddField = () => {setFields([...fields, { id: Date.now(), name: '', type: 'Text', options: [] }]);};
const handleRemoveField = (id) => {setFields(fields.filter(field => field.id !== id));};
const handleFieldChange = (id, event) => {const { name, value } = event.target; setFields(fields.map(field => field.id === id ? { ...field, [name]: value } : field));};
const handleAddOption = (fieldId) => {setFields(fields.map(field =>field.id === fieldId? { ...field, options: [...field.options, { id: Date.now(), value: '' }] }: field));};
const handleOptionChange = (fieldId, optionId, event) => {const { value } = event.target;setFields(fields.map(field =>field.id === fieldId? { ...field, options: field.options.map(opt => opt.id === optionId ? { ...opt, value } : opt) }: field));};
const handleRemoveOption = (fieldId, optionId) => {setFields(fields.map(field =>field.id === fieldId? { ...field, options: field.options.filter(opt => opt.id !== optionId) }: field));};
const handleSave = () => {
const newDocumentType = {
template: selectedTemplate,
name: docTypeName,
fields: fields.map(({ id, name, type, options }) => ({
name,
type,
...(type === 'Selection' && { options: options.map(opt => opt.value) })
}))
};
console.log("Menyimpan data JSON:", JSON.stringify(newDocumentType, null, 2));
onClose();
setSelectedTemplate(null);
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white w-full max-w-lg rounded-2xl shadow-xl p-6 sm:p-8 relative">
<button onClick={()=>{setSelectedTemplate(null); onClose();}} className="absolute top-4 right-4 text-gray-500 hover:text-gray-800"><CloseIcon/></button>
<h3 className="text-xl font-bold text-gray-800 mb-6">Tambah Jenis Dokumen Baru</h3>
{!selectedTemplate &&
<div>
<label className="font-semibold text-gray-700">Pilih Template</label>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 mt-2">
{templateNames.map(name => (
<div key={name} onClick={() => handleSelectTemplate(name)} className={`p-4 border-2 rounded-xl text-center cursor-pointer transition-all duration-200 ${selectedTemplate === name ? 'border-yellow-400 ring-2 ring-yellow-200' : 'border-gray-200 hover:border-blue-500'}`}>
<span className="text-3xl">{templates[name].icon}</span>
<p className="font-semibold text-sm mt-2">{name}</p>
</div>
))}
</div>
</div>
}
{selectedTemplate && (
<div className="mt-6 border-t pt-6">
<div>
<label htmlFor="docTypeName" className="font-semibold text-gray-700">Nama Tipe Dokumen</label>
<input type="text" id="docTypeName" value={docTypeName} onChange={(e) => setDocTypeName(e.target.value)} placeholder="Contoh: KTP, KK, Ijazah, dll" className="w-full mt-2 p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 transition"/>
</div>
<div className="mt-6">
<label className="font-semibold text-gray-700">Ekspektasi data</label>
<div className="space-y-4 mt-2 max-h-[40vh] overflow-y-auto pr-2">
{fields.map((field) => (
// PERUBAHAN: Menambahkan class `relative` dan `pt-4` pada container field
<div key={field.id} className="p-3 pt-4 bg-gray-50 rounded-lg border relative">
{/* PERUBAHAN: Tombol hapus dipindah ke sini dan diubah gayanya */}
<button
onClick={() => handleRemoveField(field.id)}
className="absolute top-1 right-1 w-7 h-7 bg-red-500 text-white rounded-full hover:bg-red-600 flex items-center justify-center border-2 border-white"
>
<TrashIcon/>
</button>
<div className="flex flex-col sm:flex-row sm:items-center gap-2">
<input type="text" name="name" placeholder="Nama field" value={field.name} onChange={(e) => handleFieldChange(field.id, e)} className="flex-grow p-3 border border-gray-300 rounded-lg"/>
<select name="type" value={field.type} onChange={(e) => handleFieldChange(field.id, e)} className="p-3 border border-gray-300 rounded-lg bg-white w-full sm:w-auto">
<option>Text</option><option>Number</option><option>Date</option><option>Selection</option>
</select>
{/* Tombol hapus yang lama sudah dihapus dari sini */}
</div>
{field.type === 'Selection' && (
<div className="mt-3 pl-2 border-l-2 border-blue-200">
<div className="space-y-2">
{field.options.map(option => (
<div key={option.id} className="flex items-center space-x-2">
<input type="text" placeholder="Nama pilihan" value={option.value} onChange={(e) => handleOptionChange(field.id, option.id, e)} className="flex-grow p-2 text-sm border border-gray-200 rounded-md"/>
<button onClick={() => handleRemoveOption(field.id, option.id)} className="flex-shrink-0 w-7 h-7 bg-gray-200 text-gray-600 rounded hover:bg-red-200 hover:text-red-700 flex items-center justify-center text-xs">X</button>
</div>
))}
</div>
<button onClick={() => handleAddOption(field.id)} className="mt-2 text-sm text-blue-600 font-semibold hover:underline">+ Tambah Ekspektasi Pilihan</button>
</div>
)}
</div>
))}
</div>
<button onClick={handleAddField} className="w-full mt-3 bg-blue-500 text-white font-bold py-3 px-4 rounded-lg hover:bg-blue-600 transition-colors flex items-center justify-center"><PlusIcon/>Tambah Field</button>
</div>
<div className="flex flex-col-reverse sm:flex-row sm:justify-end gap-3 mt-8">
<button onClick={handleSave} className="py-2 px-6 bg-gray-800 text-white font-semibold rounded-lg hover:bg-gray-900">Tambah</button>
<button onClick={()=>{setSelectedTemplate(null); onClose();}} className="py-2 px-6 bg-gray-100 text-gray-700 font-semibold rounded-lg hover:bg-gray-200">Batal</button>
</div>
</div>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,29 @@
import { Link } from 'react-router-dom';
const DocumentIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
);
export default function LoginPage() {
return (
<div className="bg-gray-100 flex items-center justify-center h-screen">
<div className="w-full max-w-sm p-8 bg-white rounded-2xl shadow-lg text-center">
<div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-xl flex items-center justify-center">
<DocumentIcon />
</div>
</div>
<h1 className="text-3xl font-bold text-gray-800">SOLID DATA</h1>
<p className="text-gray-500 mt-2 mb-8">Kelola data dokumen Anda dengan mudah</p>
<Link
to="/select-organization"
className="block w-full bg-gradient-to-r from-blue-500 to-indigo-600 text-white font-bold py-3 px-4 rounded-lg hover:opacity-90 transition-opacity duration-300"
>
Masuk
</Link>
</div>
</div>
);
}

View File

@@ -1,17 +1,13 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client'
import './index.css'; import App from './App.js'
import App from './App'; import './index.css'
import reportWebVitals from './reportWebVitals'; import { BrowserRouter } from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root')); ReactDOM.createRoot(document.getElementById('root')).render(
root.render(
<React.StrictMode> <React.StrictMode>
<App /> <BrowserRouter>
</React.StrictMode> <App />
); </BrowserRouter>
</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();

View File

@@ -0,0 +1,64 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import AddDocumentModal from '../components/AddDocumentModal'; // Kita akan buat ini
// Data dummy
const documentTypes = [
{ name: 'Akta Kelahiran', count: 0 },
{ name: 'Ijazah', count: 0 },
{ name: 'KK', count: 0 },
{ name: 'KTP', count: 6 },
{ name: 'Polinema', count: 3 },
{ name: 'Sampul Buku', count: 1 },
];
const StatCard = ({ title, value }) => (
<div className="bg-gradient-to-br from-blue-500 to-indigo-600 text-white p-6 rounded-2xl shadow-lg">
<p className="text-sm font-medium opacity-80">{title}</p>
<p className="text-4xl font-bold mt-2">{value}</p>
</div>
);
export default function DashboardPage() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div className="bg-gray-50 min-h-screen">
<main className="p-4 sm:p-6 lg:p-8">
{/* Statistik */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<StatCard title="HARI INI" value="10" />
<StatCard title="BULAN INI" value="10" />
<StatCard title="TOTAL KESELURUHAN" value="10" />
</div>
{/* Daftar Jenis Dokumen */}
<div className="mt-8">
<div className="flex justify-between items-center mb-4">
<h2 className="text-2xl font-bold text-gray-800">Daftar Jenis Dokumen</h2>
<button
onClick={() => setIsModalOpen(true)}
className="bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors"
>
+ Tambah Jenis
</button>
</div>
<div className="bg-white rounded-2xl shadow-sm divide-y divide-gray-100">
{documentTypes.map((doc, index) => (
<Link key={doc.name} to={`/input-data/${doc.name.toLowerCase().replace(' ', '-')}`} className="flex items-center p-4 hover:bg-gray-50 transition-colors">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-blue-500 text-white flex items-center justify-center font-bold">{index + 1}</div>
<div className="ml-4 flex-grow">
<p className="font-semibold text-gray-800">{doc.name}</p>
<p className="text-sm text-gray-500">{doc.count} data tersedia</p>
</div>
{/* Arrow Icon */}
</Link>
))}
</div>
</div>
</main>
<AddDocumentModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />
</div>
);
}

152
src/pages/InputDataPage.js Normal file
View File

@@ -0,0 +1,152 @@
import { useState, useRef } from 'react';
import { useParams, Link } from 'react-router-dom';
// Ikon (tidak ada perubahan)
const BackIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" /></svg>);
const UploadIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" /></svg>);
const CameraIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 B0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" /></svg>);
const ImageIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-16 w-16 mx-auto text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1"><path strokeLinecap="round" strokeLinejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>);
const TrashIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" /></svg>);
export default function InputDataPage() {
const { docType } = useParams();
const [filesToUpload, setFilesToUpload] = useState([]);
const [isUploading, setIsUploading] = useState(false);
const fileInputRef = useRef(null);
const cameraInputRef = useRef(null);
const [isDragging, setIsDragging] = useState(false);
// --- PERUBAHAN: State baru untuk progress upload ---
const [uploadProgress, setUploadProgress] = useState(0);
const handleFiles = (newFiles) => {
const imageFiles = Array.from(newFiles).filter(file => file.type.startsWith('image/'));
setFilesToUpload(prevFiles => {
const uniqueNewFiles = imageFiles.filter(newFile =>
!prevFiles.some(existingFile => existingFile.name === newFile.name && existingFile.size === newFile.size)
);
return [...prevFiles, ...uniqueNewFiles];
});
};
const removeFile = (index) => {
setFilesToUpload(prevFiles => prevFiles.filter((_, i) => i !== index));
};
// --- PERUBAHAN: Fungsi handleUpload diubah total ---
const handleUpload = async () => {
if (filesToUpload.length === 0) return;
setIsUploading(true);
setUploadProgress(0);
const totalFiles = filesToUpload.length;
const successfulUploads = [];
const failedUploads = [];
for (let i = 0; i < totalFiles; i++) {
const file = filesToUpload[i];
setUploadProgress(i + 1); // Update progress sebelum upload
const formData = new FormData();
formData.append('document', file); // Kirim satu file
try {
console.log(`Mengupload file ${i + 1}/${totalFiles}: ${file.name}`);
const response = await fetch('https://api.kedaimaster.com/scan-documents', {
method: 'POST',
body: formData,
});
if (!response.ok) {
// Lemparkan error agar ditangkap oleh catch block
throw new Error(`Gagal mengupload ${file.name}`);
}
const resultJson = await response.json();
successfulUploads.push({ file: file.name, result: resultJson });
console.log(`Sukses mengupload ${file.name}:`, resultJson);
} catch (error) {
failedUploads.push(file.name);
console.error(`Error saat mengupload ${file.name}:`, error);
}
}
setIsUploading(false);
setUploadProgress(0);
// Beri ringkasan hasil upload
alert(`Selesai! Berhasil: ${successfulUploads.length}, Gagal: ${failedUploads.length}`);
// Reset daftar file setelah semua proses selesai
setFilesToUpload([]);
};
return (
<div className="bg-gray-100 min-h-screen">
<header className="bg-white shadow-sm p-4 flex items-center">
<Link to="/dashboard" className="text-gray-600 hover:text-blue-600 mr-4"><BackIcon /></Link>
<h1 className="text-xl font-bold text-gray-800">Input Data untuk <span className="text-blue-600 capitalize">{docType.replace('-', ' ')}</span></h1>
</header>
<main className="p-4 sm:p-6 lg:p-8">
<div className="bg-white p-6 sm:p-8 rounded-2xl shadow-sm">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div
className={`rounded-xl p-8 flex flex-col justify-center items-center text-center transition-colors duration-200 ${isDragging ? 'bg-blue-100 border-2 border-solid border-blue-500' : 'bg-gray-50 border-2 border-dashed border-gray-300'}`}
onDragEnter={() => setIsDragging(true)}
onDragLeave={() => setIsDragging(false)}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => { e.preventDefault(); setIsDragging(false); handleFiles(e.dataTransfer.files); }}
>
<input type="file" ref={fileInputRef} multiple className="hidden" onChange={(e) => handleFiles(e.target.files)}/>
<input type="file" ref={cameraInputRef} accept="image/*" capture="environment" className="hidden" onChange={(e) => handleFiles(e.target.files)}/>
<h3 className="text-xl font-semibold text-gray-700">Upload Dokumen</h3>
<p className="text-gray-500 mt-1 mb-6 text-sm">Pilih file atau ambil gambar dari kamera.</p>
<div className="w-full space-y-3">
<button onClick={() => fileInputRef.current.click()} className="w-full flex items-center justify-center bg-blue-600 text-white font-semibold py-3 px-4 rounded-lg hover:bg-blue-700 transition-colors"><UploadIcon /> Upload File</button>
<button onClick={() => cameraInputRef.current.click()} className="w-full flex items-center justify-center bg-white text-gray-700 font-semibold py-3 px-4 rounded-lg border border-gray-300 hover:bg-gray-100 transition-colors"><CameraIcon /> Ambil Gambar</button>
</div>
<p className="text-xs text-gray-400 mt-6">Atau, seret & lepas file di area ini.</p>
</div>
<div className="min-h-[200px]">
<h3 className="text-xl font-semibold text-gray-700 mb-4">Pratinjau</h3>
{filesToUpload.length > 0 ? (
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4 overflow-y-auto pr-2">
{filesToUpload.map((file, index) => (
<div key={index} className="relative group">
<img src={URL.createObjectURL(file)} alt={file.name} className="w-full h-32 object-cover rounded-lg shadow-md" />
<div className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity rounded-lg">
<button onClick={() => removeFile(index)} className="text-white p-2 bg-red-500 rounded-full hover:bg-red-600"><TrashIcon /></button>
</div>
</div>
))}
</div>
) : (
<div className="flex flex-col justify-center items-center text-center text-gray-500 bg-gray-50 rounded-xl p-8">
<ImageIcon />
<p className="mt-2">Pratinjau gambar akan muncul di sini.</p>
</div>
)}
</div>
</div>
{filesToUpload.length > 0 && (
<div className="mt-8 pt-6 border-t text-right">
<button onClick={handleUpload} disabled={isUploading} className="bg-indigo-600 text-white font-bold py-3 px-8 rounded-lg hover:bg-indigo-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed min-w-[280px]">
{/* --- PERUBAHAN: Teks tombol dinamis dengan progress --- */}
{isUploading
? `Mengupload file ${uploadProgress} dari ${filesToUpload.length}...`
: `Upload ${filesToUpload.length} Gambar & Scan Data`
}
</button>
</div>
)}
</div>
</main>
</div>
);
}

29
src/pages/LoginPage.js Normal file
View File

@@ -0,0 +1,29 @@
import { Link } from 'react-router-dom';
const DocumentIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
);
export default function LoginPage() {
return (
<div className="bg-gray-100 flex items-center justify-center h-screen">
<div className="w-full max-w-sm p-8 bg-white rounded-2xl shadow-lg text-center">
<div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-xl flex items-center justify-center">
<DocumentIcon />
</div>
</div>
<h1 className="text-3xl font-bold text-gray-800">SOLID DATA</h1>
<p className="text-gray-500 mt-2 mb-8">Kelola data dokumen Anda dengan mudah</p>
<Link
to="/select-organization"
className="block w-full bg-gradient-to-r from-blue-500 to-indigo-600 text-white font-bold py-3 px-4 rounded-lg hover:opacity-90 transition-opacity duration-300"
>
Masuk
</Link>
</div>
</div>
);
}

View File

@@ -0,0 +1,44 @@
import { Link } from 'react-router-dom';
const OrgIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 4h5m-5 4h5" /></svg>
);
const ArrowIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" /></svg>
);
const organizations = [
{ name: 'psi', id: 'BLWR-XDU-QRUV' },
{ name: 'managemen', id: 'NFTJ-POX-ZYOB' },
{ name: 'solid', id: 'TCKQ-ZNF-UFTW' },
];
export default function OrganizationPage() {
return (
<div className="bg-gray-100 flex items-center justify-center min-h-screen py-10">
<div className="w-full max-w-md p-8 text-center">
<h1 className="text-3xl font-bold text-gray-800">Pilih Organisasi</h1>
<p className="text-gray-500 mt-2 mb-10">Silakan pilih organisasi yang ingin Anda kelola.</p>
<div className="space-y-4 text-left">
{organizations.map((org) => (
<Link
key={org.id}
to="/dashboard"
className="flex items-center justify-between w-full p-5 bg-white rounded-xl shadow-sm hover:shadow-md hover:bg-gray-50 transition-all duration-300 cursor-pointer"
>
<div className="flex items-center">
<div className="p-2 bg-gray-100 rounded-lg mr-4"><OrgIcon /></div>
<div>
<p className="font-bold text-gray-800">{org.name}</p>
<p className="text-sm text-gray-400">ID: {org.id}</p>
</div>
</div>
<ArrowIcon />
</Link>
))}
</div>
</div>
</div>
);
}

19
src/pages/browser.html Normal file
View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Bypass Situs</title>
<meta charset="UTF-8">
</head>
<body>
<h2>🔓 Akses Situs Lain dari File Lokal</h2>
<ul>
<li><a href="https://www.google.com" target="_blank">Google</a></li>
<li><a href="https://www.youtube.com" target="_blank">YouTube</a></li>
<li><a href="https://www.reddit.com" target="_blank">Reddit</a></li>
<li><a href="https://www.wikipedia.org" target="_blank">Wikipedia</a></li>
<li><a href="https://www.proxysite.com" target="_blank">ProxySite</a></li>
<li><a href="https://www.croxyproxy.com" target="_blank">CroxyProxy</a></li>
</ul>
<p>Tips: Kalau tidak bisa dibuka, coba salin link-nya lalu tempel di tab baru.</p>
</body>
</html>