Compare commits
5 Commits
b9854c9148
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 77cd655112 | |||
| 109489b191 | |||
| 7a14c80eec | |||
| 4fe4b13f39 | |||
| 61bb1ccc39 |
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
707
adventure-rental-app/package-lock.json
generated
@@ -20,7 +20,7 @@
|
||||
"@eslint/js": "^9.30.1",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint": "^9.30.1",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
@@ -28,6 +28,6 @@
|
||||
"globals": "^16.3.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^4.5.2"
|
||||
"vite": "^7.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin="" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
as="style"
|
||||
onload="this.rel='stylesheet'"
|
||||
href="https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900&family=Plus+Jakarta+Sans%3Awght%40400%3B500%3B700%3B800"
|
||||
/>
|
||||
|
||||
<title>Stitch Design</title>
|
||||
<link rel="icon" type="image/x-icon" href="data:image/x-icon;base64," />
|
||||
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="relative flex size-full min-h-screen flex-col bg-[#fcf8f8] group/design-root overflow-x-hidden" style='font-family: "Plus Jakarta Sans", "Noto Sans", sans-serif;'>
|
||||
<div class="layout-container flex h-full grow flex-col">
|
||||
<header class="flex items-center justify-between whitespace-nowrap border-b border-solid border-b-[#f3e7e8] px-10 py-3">
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="flex items-center gap-4 text-[#1b0e0e]">
|
||||
<div class="size-4">
|
||||
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M44 4H30.6666V17.3334H17.3334V30.6666H4V44H44V4Z" fill="currentColor"></path></svg>
|
||||
</div>
|
||||
<h2 class="text-[#1b0e0e] text-lg font-bold leading-tight tracking-[-0.015em]">CampRent</h2>
|
||||
</div>
|
||||
<div class="flex items-center gap-9">
|
||||
<a class="text-[#1b0e0e] text-sm font-medium leading-normal" href="#">Beranda</a>
|
||||
<a class="text-[#1b0e0e] text-sm font-medium leading-normal" href="#">Peralatan</a>
|
||||
<a class="text-[#1b0e0e] text-sm font-medium leading-normal" href="#">Tentang Kami</a>
|
||||
<a class="text-[#1b0e0e] text-sm font-medium leading-normal" href="#">Kontak</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 justify-end gap-8">
|
||||
<label class="flex flex-col min-w-40 !h-10 max-w-64">
|
||||
<div class="flex w-full flex-1 items-stretch rounded-lg h-full">
|
||||
<div
|
||||
class="text-[#994d51] flex border-none bg-[#f3e7e8] items-center justify-center pl-4 rounded-l-lg border-r-0"
|
||||
data-icon="MagnifyingGlass"
|
||||
data-size="24px"
|
||||
data-weight="regular"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
placeholder="Search"
|
||||
class="form-input flex w-full min-w-0 flex-1 resize-none overflow-hidden rounded-lg text-[#1b0e0e] focus:outline-0 focus:ring-0 border-none bg-[#f3e7e8] focus:border-none h-full placeholder:text-[#994d51] px-4 rounded-l-none border-l-0 pl-2 text-base font-normal leading-normal"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="flex min-w-[84px] max-w-[480px] cursor-pointer items-center justify-center overflow-hidden rounded-lg h-10 px-4 bg-[#e92932] text-[#fcf8f8] text-sm font-bold leading-normal tracking-[0.015em]"
|
||||
>
|
||||
<span class="truncate">Masuk</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex min-w-[84px] max-w-[480px] cursor-pointer items-center justify-center overflow-hidden rounded-lg h-10 px-4 bg-[#f3e7e8] text-[#1b0e0e] text-sm font-bold leading-normal tracking-[0.015em]"
|
||||
>
|
||||
<span class="truncate">Daftar</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="px-40 flex flex-1 justify-center py-5">
|
||||
<div class="layout-content-container flex flex-col max-w-[960px] flex-1">
|
||||
<div class="@container">
|
||||
<div class="@[480px]:p-4">
|
||||
<div
|
||||
class="flex min-h-[480px] flex-col gap-6 bg-cover bg-center bg-no-repeat @[480px]:gap-8 @[480px]:rounded-lg items-center justify-center p-4"
|
||||
style='background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 100%), url("https://lh3.googleusercontent.com/aida-public/AB6AXuCWyBJbobacHRpR2rZFfGuiAHDut1lUwnrS7Ne1qIwJmVoHIZ9jSH8pBbiNxfQVxaolM_fOzTrnkl3gJf7cvEZ82ca9FDwOAWghh3tHnGt7nmYJJwwBa_JEBLu_ZX32TtrwIevABNmTkTsFrVR_lREK-3BBHvfEfNa-B3xStitvuMhiaQ0uemjEQZ8dj18r8epa8t9Ub17dok8VigDsim9rif4kOgg7eIVn_ssoTCj5Wy-rvkUMYOE5yXcm-EdNoY2ktTTgA--gV5Q");'
|
||||
>
|
||||
<div class="flex flex-col gap-2 text-center">
|
||||
<h1
|
||||
class="text-white text-4xl font-black leading-tight tracking-[-0.033em] @[480px]:text-5xl @[480px]:font-black @[480px]:leading-tight @[480px]:tracking-[-0.033em]"
|
||||
>
|
||||
Sewa Peralatan Camping Berkualitas
|
||||
</h1>
|
||||
<h2 class="text-white text-sm font-normal leading-normal @[480px]:text-base @[480px]:font-normal @[480px]:leading-normal">
|
||||
Nikmati petualangan alam bebas dengan peralatan camping terbaik. Sewa sekarang dan rasakan pengalaman camping yang tak terlupakan.
|
||||
</h2>
|
||||
</div>
|
||||
<button
|
||||
class="flex min-w-[84px] max-w-[480px] cursor-pointer items-center justify-center overflow-hidden rounded-lg h-10 px-4 @[480px]:h-12 @[480px]:px-5 bg-[#e92932] text-[#fcf8f8] text-sm font-bold leading-normal tracking-[0.015em] @[480px]:text-base @[480px]:font-bold @[480px]:leading-normal @[480px]:tracking-[0.015em]"
|
||||
>
|
||||
<span class="truncate">Lihat Peralatan</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-[#1b0e0e] text-[22px] font-bold leading-tight tracking-[-0.015em] px-4 pb-3 pt-5">Penawaran Spesial</h2>
|
||||
<div class="flex overflow-y-auto [-ms-scrollbar-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
||||
<div class="flex items-stretch p-4 gap-3">
|
||||
<div class="flex h-full flex-1 flex-col gap-4 rounded-lg min-w-60">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-video bg-cover rounded-lg flex flex-col"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuAvRp6hsxMTXE1tJYF0MjKVRm0zysHK3ClRz5HwJfP34ELGDJvktXlsjUJQ9Fv1VPsUwEE-CU6w25CN9geU18xHw_yTh3jzpJheLyDwrnMZOChC3HaBlYmv28UKhGV76P2LHMDvu5dEtobTm0QD3YLAvwT6L7SK0m33paUlXxna9CuR2MMgKZ17P_a46q2zBwCSnYf1GuZEZNzePW-HPWd0tyuJ9UYOblL2T3_w-d44UkbiHKT9lIKk6y4piJTESqj7LuO0NfHHe7E");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Tenda Camping Kapasitas 4 Orang</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Sewa mulai dari Rp 50.000/hari</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex h-full flex-1 flex-col gap-4 rounded-lg min-w-60">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-video bg-cover rounded-lg flex flex-col"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuC5BQ5uUUuMKq6VhjKep8tyvrmeGT0nvgx86fyEVrUqTTGzcZDO8z2ayqxSxnzdk8gjpgGmjLbhZL-HnpOV-5r-GwGs3FssAhhgcBi4HoCthNTn2zUYytotzBWVDDXylxGeKAzhOw2dgbM6oD_AfknM_6kWWr6CBobDasznxJuC2JNbzV-PEDmhPT_8qzeKh6mWYY8-KXDM3jHcVZZhhEvde6tBRwBzpHGlkrDrog_6C_Yiagmz9JN9lVE4xHuAWtFLX1IQRvGfwak");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Kompor Camping Portable</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Sewa mulai dari Rp 25.000/hari</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex h-full flex-1 flex-col gap-4 rounded-lg min-w-60">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-video bg-cover rounded-lg flex flex-col"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDow9uli5aTA-tdn0N14A0jP3UNtUVlAlo6rbp-rV-YlTVoQq_A4uTMqbRo9HoIVi_K1xBfxfRYrWxF0w9ZCDh_6rydQLIHbmaqwUOeTGs0irjrBfPRa2hWAbtOFRic0JBuHPAMm_Hm7hotKO5-wj7uIg0aMMmuyE2Y92toM47T2yH0-eRtaBLUgSTNP90FWAS7Z95iOEslTdb3tdsXFC9KTc55u4vP9COi09M-GVD0PBEnSWz4PzAcCyNGfpGSs4E18CIwg9L32I8");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Ransel Camping 60L</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Sewa mulai dari Rp 30.000/hari</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-[#1b0e0e] text-[22px] font-bold leading-tight tracking-[-0.015em] px-4 pb-3 pt-5">Peralatan Terbaru</h2>
|
||||
<div class="grid grid-cols-[repeat(auto-fit,minmax(158px,1fr))] gap-3 p-4">
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuAv4vQr6Z0wpBiZoy5iTs_t1XGlkBeai4R1tIzBmjAIM9gIZKrRcT8-4JDlsLvKDfZvIQYl8loCnkO7lIFP9S2mPQVyozPsjsKkPFmSCF5wjMOBhTs1_d7D5aW6JKsAsxe89mtb2muBN9oXK-xBZHnROjxTxkNMOYJMcTwBm80Qkrvnf27WztRK_49JXRJB4Q1YJ2zLR32yuGD-6rCx5kUm8vu3fZuK-iSUxbAD232TW2A7EU54GGtPRDubJNHdeBVjR2JccH1aG_Y");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Sleeping Bag</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Nyaman dan hangat untuk tidur di alam</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuBUg3yW3gYWdIVJPyX4CV_RdT5hghqLHRTcqsAbL5SCGAHCO9P8g44Kim-ul3zrjOnPqmOg8gH-kMAtlG7DsoB5JbSwJ9_uVnpV1TVCmJEpvcYO8P3lurZNVr19zFejVvvohLzehcwy0CxSQcCuKy38ljowG5vSjqc3iP8D0KPHC6Gn44Onh3PTe6E1m7EyX-89LdFbV6yGB1_Vj_XJF2_5wcQlt7yJ362WJlJFVXjONywcWNRvh22uuj498k4yMk9ZOslxX_JRmCk");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Matras Camping</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Alas tidur yang empuk dan isolatif</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuASMEBXbQxfu1_7DcxvyUWeYt70VrkXJp8jYf_vI5-NDsvAyvnARncM3N1BP_RxnUeOR-mPihZzOZW_AmJBRbWBquoaoSsxV4RYpCGBVWQnyWLStHBXQNspO39JdHMF0SnEzNGv8TKGcd3IPFShYf0A26-Xr59HP99s5UErJb-d3DM_9T1lec9aiCPQOqcrmauDQDlOqLVLzyIX3jcArLSOwFkhyxG-ksvQb0-UgnuE2SKJ0ZKKX2obPJmK-7124grO84UipPJZ_UM");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Lampu Tenda</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Penerangan yang praktis untuk tenda</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuB_a0CqFMmJkN-KIpw5ACcEZ5mjuOHwxr_j73KyC89fRjx6okZb178lwTQz7zrGXbAsAe4Xdf52EVns7ROW3TF2OYxOtE2dQI_ldoXD0JeXLi9CvKKL_s697Xk9nC7-UmwaJ5UK4fxSNFbxS4ABmMbmvHTwJ82aIHaNdNzJzjlnWSQVCxC29fGtj3UaU7uX7GTWXuXJMJLXBUqo7EDY8iujDHj_JQZ3mFN1ydxR764qk1rnWmACbXiCA-tBk5j5GVpCeHmhphKMhyw");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Peralatan Masak</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Set lengkap untuk memasak di luar ruangan</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-[#1b0e0e] text-[22px] font-bold leading-tight tracking-[-0.015em] px-4 pb-3 pt-5">Kategori Populer</h2>
|
||||
<div class="grid grid-cols-[repeat(auto-fit,minmax(158px,1fr))] gap-3 p-4">
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDI6Hr5q3ThScjaCpB8TwRqtKk2nljIxFOtDR2xcZs7X5i3P1Fn86QsPm30m2bd_qKW_v15nErkTgj5RGMXR8bTJ1w_nLgQKDS8jzhcwJGmAf-2fv7rTSNMf4-odTSEQI0tza0bberwHeuW1vy2Thsha9IugC5bGX_AC12PIqhLOGn85KWEhi4uCsnNXuAwt1nuli81M9ew2ILQqMjco64DJpdozvGHeDmu8-MAsy6_kCuQQkEC7dBpB8S5pO3uoTVbOAnmhdByoss");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Tenda</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Berbagai pilihan tenda untuk semua kebutuhan</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuAGrYFPtDbX3TZObxkWWwy8FgZlSrrK7WxCnzMuDzQgrL2a_VeqpPy7D1-XCxOYk_P_ys4l475lvBTeZqFy8zosbb433EdiiyBrRmyIG4Wgq0N8B_tjSccIO7WAsCoa7FyMV2ttdP_8QTtXOT5Y6JDoSlxxqzlQ_qHxu-v477d7QQexUuwmnpEV3oiFrmbmoJmAUrASUzIDuA3cuaRF9tJ9o6nKNLTIs0uLyMHiskMjhQKJHEnpkrG4Q0e-Y2V-0ksI9yPX6BY2vTM");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Ransel</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Ransel dengan kapasitas dan fitur lengkap</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuBGVtDm8LwfWVZfEkxJUZFSWPkp8BfQsc9L__99XArHJE4CKYXw-0BXlGuQ7BwLTPFDgTqz-9keOWyk9SR-XJBJx7TwkL22uC76w86RcSBzvBmJsymWkKbqNZgHgYiGhszhoiFY08lcj2mUAzS88LjBNeVhWwEeBgKCDr6Y9VgDRuHA-vH6GRSzMZUU89AOBlgu5jw9xtkjILSq3NgUfBP2mmz6ntCLGLRSuH6QGv0VtsDYGzyCW8bk63XW9bzs1A-aROyELH1Yu84");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Peralatan Masak</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Set peralatan masak praktis dan ringan</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-lg"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuC3s14he8A8hsfHyQjfRX2mrYgNBXXXnndXjkC3UK8SPV7qXuZ58ZIU1DIZrDNxKJifgFAfpPcUs2h9sAWxBphoMqk1nCuABUxcqvzw3WvWFSjIoEDvmQ5_bnmOA8vklUUzq-E26X06ngQEUcqH1PyFWLAPJuPzCk9tMHgRJl9dyBq4SsMa9Q985SSzAxDmjjXzx3lUL7NNFYG9Gtoe-BPMC0rzIeWbD_WlFe-KlrsyzcbjyOedp_x8jzFZSs57vLARipoU7aPxnIY");'
|
||||
></div>
|
||||
<div>
|
||||
<p class="text-[#1b0e0e] text-base font-medium leading-normal">Peralatan Tidur</p>
|
||||
<p class="text-[#994d51] text-sm font-normal leading-normal">Sleeping bag dan matras untuk tidur nyaman</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="flex justify-center">
|
||||
<div class="flex max-w-[960px] flex-1 flex-col">
|
||||
<footer class="flex flex-col gap-6 px-5 py-10 text-center @container">
|
||||
<div class="flex flex-wrap items-center justify-center gap-6 @[480px]:flex-row @[480px]:justify-around">
|
||||
<a class="text-[#994d51] text-base font-normal leading-normal min-w-40" href="#">Tentang Kami</a>
|
||||
<a class="text-[#994d51] text-base font-normal leading-normal min-w-40" href="#">Kebijakan Privasi</a>
|
||||
<a class="text-[#994d51] text-base font-normal leading-normal min-w-40" href="#">Syarat dan Ketentuan</a>
|
||||
<a class="text-[#994d51] text-base font-normal leading-normal min-w-40" href="#">Kontak</a>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
<a href="#">
|
||||
<div class="text-[#994d51]" data-icon="InstagramLogo" data-size="24px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M128,80a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160ZM176,24H80A56.06,56.06,0,0,0,24,80v96a56.06,56.06,0,0,0,56,56h96a56.06,56.06,0,0,0,56-56V80A56.06,56.06,0,0,0,176,24Zm40,152a40,40,0,0,1-40,40H80a40,40,0,0,1-40-40V80A40,40,0,0,1,80,40h96a40,40,0,0,1,40,40ZM192,76a12,12,0,1,1-12-12A12,12,0,0,1,192,76Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#">
|
||||
<div class="text-[#994d51]" data-icon="TwitterLogo" data-size="24px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M247.39,68.94A8,8,0,0,0,240,64H209.57A48.66,48.66,0,0,0,168.1,40a46.91,46.91,0,0,0-33.75,13.7A47.9,47.9,0,0,0,120,88v6.09C79.74,83.47,46.81,50.72,46.46,50.37a8,8,0,0,0-13.65,4.92c-4.31,47.79,9.57,79.77,22,98.18a110.93,110.93,0,0,0,21.88,24.2c-15.23,17.53-39.21,26.74-39.47,26.84a8,8,0,0,0-3.85,11.93c.75,1.12,3.75,5.05,11.08,8.72C53.51,229.7,65.48,232,80,232c70.67,0,129.72-54.42,135.75-124.44l29.91-29.9A8,8,0,0,0,247.39,68.94Zm-45,29.41a8,8,0,0,0-2.32,5.14C196,166.58,143.28,216,80,216c-10.56,0-18-1.4-23.22-3.08,11.51-6.25,27.56-17,37.88-32.48A8,8,0,0,0,92,169.08c-.47-.27-43.91-26.34-44-96,16,13,45.25,33.17,78.67,38.79A8,8,0,0,0,136,104V88a32,32,0,0,1,9.6-22.92A30.94,30.94,0,0,1,167.9,56c12.66.16,24.49,7.88,29.44,19.21A8,8,0,0,0,204.67,80h16Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#">
|
||||
<div class="text-[#994d51]" data-icon="FacebookLogo" data-size="24px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm8,191.63V152h24a8,8,0,0,0,0-16H136V112a16,16,0,0,1,16-16h16a8,8,0,0,0,0-16H152a32,32,0,0,0-32,32v24H96a8,8,0,0,0,0,16h24v63.63a88,88,0,1,1,16,0Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<p class="text-[#994d51] text-base font-normal leading-normal">@2024 CampRent. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,203 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin="" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
as="style"
|
||||
onload="this.rel='stylesheet'"
|
||||
href="https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900&family=Plus+Jakarta+Sans%3Awght%40400%3B500%3B700%3B800"
|
||||
/>
|
||||
|
||||
<title>Stitch Design</title>
|
||||
<link rel="icon" type="image/x-icon" href="data:image/x-icon;base64," />
|
||||
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="relative flex size-full min-h-screen flex-col bg-[#fbf9f9] group/design-root overflow-x-hidden" style='font-family: "Plus Jakarta Sans", "Noto Sans", sans-serif;'>
|
||||
<div class="layout-container flex h-full grow flex-col">
|
||||
<header class="flex items-center justify-between whitespace-nowrap border-b border-solid border-b-[#f1e9ea] px-10 py-3">
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="flex items-center gap-4 text-[#191011]">
|
||||
<div class="size-4">
|
||||
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M44 4H30.6666V17.3334H17.3334V30.6666H4V44H44V4Z" fill="currentColor"></path></svg>
|
||||
</div>
|
||||
<h2 class="text-[#191011] text-lg font-bold leading-tight tracking-[-0.015em]">CampRent</h2>
|
||||
</div>
|
||||
<div class="flex items-center gap-9">
|
||||
<a class="text-[#191011] text-sm font-medium leading-normal" href="#">Beranda</a>
|
||||
<a class="text-[#191011] text-sm font-medium leading-normal" href="#">Kategori</a>
|
||||
<a class="text-[#191011] text-sm font-medium leading-normal" href="#">Pemesanan</a>
|
||||
<a class="text-[#191011] text-sm font-medium leading-normal" href="#">Tentang Kami</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 justify-end gap-8">
|
||||
<label class="flex flex-col min-w-40 !h-10 max-w-64">
|
||||
<div class="flex w-full flex-1 items-stretch rounded-xl h-full">
|
||||
<div
|
||||
class="text-[#8b5b5d] flex border-none bg-[#f1e9ea] items-center justify-center pl-4 rounded-l-xl border-r-0"
|
||||
data-icon="MagnifyingGlass"
|
||||
data-size="24px"
|
||||
data-weight="regular"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
placeholder="Search"
|
||||
class="form-input flex w-full min-w-0 flex-1 resize-none overflow-hidden rounded-xl text-[#191011] focus:outline-0 focus:ring-0 border-none bg-[#f1e9ea] focus:border-none h-full placeholder:text-[#8b5b5d] px-4 rounded-l-none border-l-0 pl-2 text-base font-normal leading-normal"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="flex max-w-[480px] cursor-pointer items-center justify-center overflow-hidden rounded-xl h-10 bg-[#f1e9ea] text-[#191011] gap-2 text-sm font-bold leading-normal tracking-[0.015em] min-w-0 px-2.5"
|
||||
>
|
||||
<div class="text-[#191011]" data-icon="Heart" data-size="20px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M178,32c-20.65,0-38.73,8.88-50,23.89C116.73,40.88,98.65,32,78,32A62.07,62.07,0,0,0,16,94c0,70,103.79,126.66,108.21,129a8,8,0,0,0,7.58,0C136.21,220.66,240,164,240,94A62.07,62.07,0,0,0,178,32ZM128,206.8C109.74,196.16,32,147.69,32,94A46.06,46.06,0,0,1,78,48c19.45,0,35.78,10.36,42.6,27a8,8,0,0,0,14.8,0c6.82-16.67,23.15-27,42.6-27a46.06,46.06,0,0,1,46,46C224,147.61,146.24,196.15,128,206.8Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="flex max-w-[480px] cursor-pointer items-center justify-center overflow-hidden rounded-xl h-10 bg-[#f1e9ea] text-[#191011] gap-2 text-sm font-bold leading-normal tracking-[0.015em] min-w-0 px-2.5"
|
||||
>
|
||||
<div class="text-[#191011]" data-icon="ShoppingCart" data-size="20px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M222.14,58.87A8,8,0,0,0,216,56H54.68L49.79,29.14A16,16,0,0,0,34.05,16H16a8,8,0,0,0,0,16h18L59.56,172.29a24,24,0,0,0,5.33,11.27,28,28,0,1,0,44.4,8.44h45.42A27.75,27.75,0,0,0,152,204a28,28,0,1,0,28-28H83.17a8,8,0,0,1-7.87-6.57L72.13,152h116a24,24,0,0,0,23.61-19.71l12.16-66.86A8,8,0,0,0,222.14,58.87ZM96,204a12,12,0,1,1-12-12A12,12,0,0,1,96,204Zm96,0a12,12,0,1,1-12-12A12,12,0,0,1,192,204Zm4-74.57A8,8,0,0,1,188.1,136H69.22L57.59,72H206.41Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-10"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuBxaF5v6Zbu9bB82x_wP5pkjogVV5iFabxe8ySILcRyEdf47hhsJZXw1sLOwUUS88fTlb7fgkXL_uMZqqrl23CGFaQ_TXb11ZHBSSe-_nnX8OA8nNEzJCM558mTSUEQDfd08OlgUOycu_eRnnWYkwz7j4Pq5F7aPyjpTRPvs1mA9us4DhS_EYCCUDaU6lfHerzOvHS3yP0JMS6aAGm1WLQkXoj36__9PePJvdNhbRcRVRuizP7t3Ugb70q3mtiJ8nsWCOtxRPSmZdE");'
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="px-40 flex flex-1 justify-center py-5">
|
||||
<div class="layout-content-container flex flex-col max-w-[960px] flex-1">
|
||||
<div class="flex flex-wrap justify-between gap-3 p-4"><p class="text-[#191011] tracking-light text-[32px] font-bold leading-tight min-w-72">Kategori Peralatan</p></div>
|
||||
<div class="flex gap-3 p-3 flex-wrap pr-4">
|
||||
<button class="flex h-8 shrink-0 items-center justify-center gap-x-2 rounded-xl bg-[#f1e9ea] pl-4 pr-2">
|
||||
<p class="text-[#191011] text-sm font-medium leading-normal">Harga</p>
|
||||
<div class="text-[#191011]" data-icon="CaretDown" data-size="20px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button class="flex h-8 shrink-0 items-center justify-center gap-x-2 rounded-xl bg-[#f1e9ea] pl-4 pr-2">
|
||||
<p class="text-[#191011] text-sm font-medium leading-normal">Merek</p>
|
||||
<div class="text-[#191011]" data-icon="CaretDown" data-size="20px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button class="flex h-8 shrink-0 items-center justify-center gap-x-2 rounded-xl bg-[#f1e9ea] pl-4 pr-2">
|
||||
<p class="text-[#191011] text-sm font-medium leading-normal">Kapasitas</p>
|
||||
<div class="text-[#191011]" data-icon="CaretDown" data-size="20px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-[repeat(auto-fit,minmax(158px,1fr))] gap-3 p-4">
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuCNHokO6q-y0WWuIHxzj0NwuqG6bj2wHCfFp5C925XV6UC9pzZDqunT5gN3LBK645qCCXzyqxn6-eY7k8b_zRBPm4LzV8huqbjEmoa2KfQmS2FxGkTLNyPUdBx9s27nIS1QvHo_1xRSRCdQvwUz8W-7Ff544DTny-I7nKmoLnKp4q_MhP0pr5UmHdMCWwAhAmfJop8afuMpeR2cwgJb8U-_GgLi11JRJcanuH9fVOqqqxsbuAoQXOjJPWiJ6P8UhpFmGJJpet1_AzA");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Tenda</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDrxeYAqxgHvXBLRxyW6GONxUV4CreNviEp49SxXd6r_PHkczD1Hl5f_qkFAvhnjD4BzOPXvkBALnESILNgTVoH8SRDj2655B-UgV1EeydVlo7_vqXLPL00EOej920wYxn3ciRqc8T8T5NSiwyvFzHWcY6QLJUYT2cQRemIPDpSFJR76cPAyHk2umMOh_WKXrBOoZJ7vE9kaSZOshh-PkxUi92Pi3bolxcOm7eiRXB8K8e6yqhOqLMV5KtxP5UmZg05c0IevCQYsBk");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Sleeping Bag</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuCWwcGTGRxXLPLB4NE3vT-cPBnJs0rs2JlyPQn0YSKWIaZkp5__D-P05aaUTSha1lfgbPeyIIseEOVlX30J5zkTf3oMZXYZV9HlV1ckh_3PJJULcIQJ6QdlQuxiTHEpF-qRkbhwXrsQhPLgqvXyB3eaRUxcFu179dHzmkU4OfspLCye8U6LHjdp30zT1DDn87a_SgnLm77wlIpwS9PhjbGKdC9jy_g2Kgt_Wdn-OgHwnefT9Hl7KiRfdZ2RwPz-J5dcAw6gwYEUm1I");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Matras</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuD4aZqSSgxUwH2PtTZoWZkHJyBnImCvJ3E5_XDv1fYv_2mcnq8P3dMOLoARlHsJC4aeRhxxlr58hkUk120Mq_Uk5unxMAbDFYNn4TM_h5QEm0ZBIqOKgEAx4QKf93XE68uVLwT4sB_6dnMHs9gjxMS5vdalwAPTCbwlANQrQELLiBA-055ZYSa-8Kwop__Ygzyp2OrxA4cwYcPC5wlgogD7R5BSVq-vOnKUtrcfLezPUu49wwdufZYdJ9C33pUske_FQLhLA2pHrJQ");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Kompor</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDO6RTDKia5PJXnjXf1HFAmayyakeUrXzX7OWUANvGHTUYfzrb8YlZxLm3PwBud6k9uc_r4YER-cA7SXx3kWjK5eMD9clyFU4_NdIfr4865_FLDUmM4tUmRShNQMuis0aFoVGgkiIgsRVhXqLmp2W836eIFHix1UqUxcKhGuKRu9GbPTAgQZlEo4PMntizkCPEOsNYpnLFpYtbUeD0afJ8erKPZQU3vnpxeP_G8atJBAVV1MEgHNd2W6FOMPqfVV2XhgTiu3WrIqZo");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Peralatan Masak</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDymPy_KGw4A3sbMoc2a1_u51Tgtpl2UFR9BSb8ywke8yk3d34Qz8B75ty20CinoBsqxRxOs0JsHWqBV0X7n-BRmMEfi2HAxWUptg6gh5NZZHxdSXNamBtzhhflifOM06t2A_EWDu79T_6vDDPlSG76fSwdTm_4mC_ei85AuW8mFxGBIqOxldQhZESsg9SWM0p3XPUUy6tYFxtXf7zWDb3mpU1D1XPZC5_po3yXYksmJVqngFAmV8DKOgkyPWOks88pkyc0SQks6YE");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Lampu</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuBklJDG4ebGr2t_CncSfmiA7gZEABiAjzzuSBFQIPn3vDjwkHDIJjKDjEW3SU7Mrz9QexsdDpbR3i5uuWFAr-ZhARMooHtK20wK1dmkJ-tGSYuwpWfWDQYuPnoSsGCWY2c3V7Zs_oACjHjl4tVYS_giJblk24wHx2nlVBrBGdbsPCRrcIl7wAkJvYT0TUvyQmG69ijfmjeGuoq41s3u8_wxh_dILQiaPxXScvalC_oXgM9ScT6HsYMe02uPOKSk9yG8ERhmVofTyhM");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Tas Gunung</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 pb-3">
|
||||
<div
|
||||
class="w-full bg-center bg-no-repeat aspect-square bg-cover rounded-xl"
|
||||
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuAkCyY-0wbXffws3P_0WZUFYBmPCLO4R8uFjLuPPoHykdg3djqLJkO7wgZ6P95PY_3ZMoklu8TynW3gU46rvYC-co3yvMLuYv0VZ7p5QknLqVDUBGHbcVS3QqNz5VOtw4bq8WtW_OK8-3nzePCawaZWD4N5SFByCXQ4d9MO6UwhOIfmBQP5N7xDkNIlvjxYyJDzY_rJ7NF5d1FlS0py72Bi8yFEHSLGP4k1YFji8in5T-6ZnJ4O_fNGOJ2-EveQHoR68DvH_oRvKbs");'
|
||||
></div>
|
||||
<p class="text-[#191011] text-base font-medium leading-normal">Sepatu Gunung</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end overflow-hidden px-5 pb-5">
|
||||
<button
|
||||
class="flex max-w-[480px] cursor-pointer items-center justify-center overflow-hidden rounded-xl h-14 bg-[#e8b4b7] text-[#191011] text-base font-bold leading-normal tracking-[0.015em] min-w-0 px-2 gap-4 pl-4 pr-6"
|
||||
>
|
||||
<div class="text-[#191011]" data-icon="ChatCircleDots" data-size="24px" data-weight="regular">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" fill="currentColor" viewBox="0 0 256 256">
|
||||
<path
|
||||
d="M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128ZM84,116a12,12,0,1,0,12,12A12,12,0,0,0,84,116Zm88,0a12,12,0,1,0,12,12A12,12,0,0,0,172,116Zm60,12A104,104,0,0,1,79.12,219.82L45.07,231.17a16,16,0,0,1-20.24-20.24l11.35-34.05A104,104,0,1,1,232,128Zm-16,0A88,88,0,1,0,51.81,172.06a8,8,0,0,1,.66,6.54L40,216,77.4,203.53a7.85,7.85,0,0,1,2.53-.42,8,8,0,0,1,4,1.08A88,88,0,0,0,216,128Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="flex justify-center">
|
||||
<div class="flex max-w-[960px] flex-1 flex-col">
|
||||
<footer class="flex flex-col gap-6 px-5 py-10 text-center @container">
|
||||
<div class="flex flex-wrap items-center justify-center gap-6 @[480px]:flex-row @[480px]:justify-around">
|
||||
<a class="text-[#8b5b5d] text-base font-normal leading-normal min-w-40" href="#">Kebijakan Privasi</a>
|
||||
<a class="text-[#8b5b5d] text-base font-normal leading-normal min-w-40" href="#">Syarat dan Ketentuan</a>
|
||||
<a class="text-[#8b5b5d] text-base font-normal leading-normal min-w-40" href="#">Pusat Bantuan</a>
|
||||
</div>
|
||||
<p class="text-[#8b5b5d] text-base font-normal leading-normal">@2024 CampRent. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,202 +0,0 @@
|
||||
Tentu, saya akan merevisi PRD Anda untuk mencerminkan penggunaan Create React App (CRA) sebagai tool utama untuk frontend React Anda. Perubahan utamanya akan ada di bagian teknologi yang digunakan dan beberapa penyesuaian di alur pengguna, meskipun tidak signifikan karena fokus tetap pada React.
|
||||
|
||||
Product Requirement Document (PRD) - Webapp Rental Peralatan Camping (Revisi untuk Create React App)
|
||||
1. Pendahuluan
|
||||
Dokumen ini menguraikan persyaratan fungsional dan non-fungsional untuk pengembangan aplikasi web rental peralatan camping. Tujuannya adalah menyediakan platform yang mudah digunakan bagi pelanggan untuk mencari, menyewa, dan mengembalikan peralatan camping. Bagi pemilik bisnis, ini akan membantu mengelola inventaris, pesanan, dan pelanggan secara efisien. Proyek ini akan dibangun menggunakan Create React App (CRA) untuk frontend React, n8n untuk backend, dan PostgreSQL sebagai database.
|
||||
|
||||
2. Sasaran
|
||||
Meningkatkan efisiensi operasional bisnis rental.
|
||||
|
||||
Mempermudah proses pemesanan bagi pelanggan dengan visualisasi ketersediaan melalui kalender.
|
||||
|
||||
Memperluas jangkauan pelanggan melalui platform online.
|
||||
|
||||
Memberikan visibilitas inventaris real-time.
|
||||
|
||||
Mengurangi kesalahan manual dalam pengelolaan pesanan dan inventaris.
|
||||
|
||||
3. Audiens
|
||||
Pengguna Akhir (Penyewa): Individu atau kelompok yang mencari peralatan camping untuk disewa.
|
||||
|
||||
Admin/Pemilik Bisnis: Pengelola bisnis rental yang bertanggung jawab atas inventaris, pesanan, dan operasional.
|
||||
|
||||
4. Fitur Utama
|
||||
4.1. Modul Pengguna (Penyewa)
|
||||
4.1.1. Pencarian & Penemuan Peralatan
|
||||
Pencarian Berdasarkan Kategori: Pengguna dapat mencari peralatan berdasarkan kategori (misalnya, tenda, sleeping bag, alat masak, dll.).
|
||||
|
||||
Pencarian Berdasarkan Kata Kunci: Pengguna dapat mencari peralatan dengan memasukkan kata kunci.
|
||||
|
||||
Filter & Sortir:
|
||||
|
||||
Filter: Berdasarkan harga, ketersediaan, kapasitas (untuk tenda), merek, dll.
|
||||
|
||||
Sortir: Berdasarkan harga terendah/tertinggi, popularitas, ulasan, dll.
|
||||
|
||||
Detail Produk: Menampilkan informasi lengkap setiap peralatan, termasuk:
|
||||
|
||||
Nama produk
|
||||
|
||||
Deskripsi lengkap
|
||||
|
||||
Harga sewa (per hari/paket)
|
||||
|
||||
Gambar produk (galeri)
|
||||
|
||||
Ketersediaan (Integrasi Kalender): Menampilkan kalender interaktif yang menunjukkan tanggal-tanggal ketersediaan dan ketidaktersediaan untuk setiap item. Pengguna dapat langsung memilih tanggal sewa dari kalender ini.
|
||||
|
||||
Spesifikasi (ukuran, bahan, kapasitas)
|
||||
|
||||
Ulasan dan rating pelanggan
|
||||
|
||||
4.1.2. Proses Pemesanan
|
||||
Pilihan Tanggal Sewa (Melalui Kalender): Pengguna dapat memilih tanggal mulai dan selesai sewa langsung dari kalender di halaman detail produk atau keranjang belanja. Sistem harus secara otomatis memperbarui ketersediaan dan harga.
|
||||
|
||||
Keranjang Belanja: Pengguna dapat menambahkan beberapa peralatan.
|
||||
|
||||
Ringkasan Pesanan: Menampilkan total biaya, durasi sewa, dan daftar peralatan yang disewa.
|
||||
|
||||
Formulir Data Diri: Pengguna harus mengisi data diri (nama, alamat, nomor telepon, email).
|
||||
|
||||
Metode Pembayaran: Integrasi dengan berbagai metode pembayaran (transfer bank, e-wallet, kartu kredit).
|
||||
|
||||
Konfirmasi Pesanan: Pengguna menerima konfirmasi pesanan melalui email/notifikasi dalam aplikasi.
|
||||
|
||||
4.1.3. Manajemen Akun Pengguna
|
||||
Registrasi & Login: Pengguna dapat mendaftar dengan email/nomor telepon atau melalui media sosial.
|
||||
|
||||
Profil Pengguna: Mengelola informasi pribadi, alamat, dan preferensi.
|
||||
|
||||
Riwayat Pesanan: Melihat daftar pesanan yang pernah dibuat (aktif, selesai, dibatalkan).
|
||||
|
||||
Notifikasi: Pembaruan status pesanan, promosi, pengingat pengembalian.
|
||||
|
||||
4.2. Modul Admin/Pemilik Bisnis
|
||||
4.2.1. Manajemen Inventaris
|
||||
Tambah/Edit/Hapus Peralatan: Admin dapat menambahkan produk baru dengan detail lengkap (nama, deskripsi, harga, stok, gambar, kategori, spesifikasi).
|
||||
|
||||
Manajemen Kategori: Tambah/edit/hapus kategori peralatan.
|
||||
|
||||
Pelacakan Stok Real-time (Terintegrasi Kalender):
|
||||
|
||||
Stok otomatis berkurang saat disewa dan bertambah saat dikembalikan.
|
||||
|
||||
Admin dapat melihat kalender inventaris global yang menunjukkan kapan setiap item atau kategori item tersedia atau sedang disewa.
|
||||
|
||||
Kemampuan untuk memblokir tanggal tertentu untuk perawatan atau alasan lain.
|
||||
|
||||
Status Peralatan: Menandai peralatan sebagai "tersedia," "disewa," "dalam perawatan," atau "rusak."
|
||||
|
||||
4.2.2. Manajemen Pesanan
|
||||
Daftar Pesanan: Melihat semua pesanan dengan status (pending, dikonfirmasi, sedang disewa, selesai, dibatalkan).
|
||||
|
||||
Detail Pesanan: Melihat detail lengkap setiap pesanan, termasuk informasi pelanggan, daftar peralatan yang disewa, tanggal sewa, dan total pembayaran.
|
||||
|
||||
Perubahan Status Pesanan: Admin dapat mengubah status pesanan secara manual (misalnya, dari "pending" ke "dikonfirmasi" setelah pembayaran diterima). Perubahan status ini harus memicu pembaruan ketersediaan di kalender.
|
||||
|
||||
Pencarian & Filter Pesanan: Berdasarkan tanggal, status, nama pelanggan.
|
||||
|
||||
4.2.3. Manajemen Pelanggan
|
||||
Daftar Pelanggan: Melihat daftar semua pelanggan yang terdaftar.
|
||||
|
||||
Detail Pelanggan: Melihat riwayat pesanan pelanggan.
|
||||
|
||||
4.2.4. Manajemen Pengembalian
|
||||
Pencatatan Pengembalian: Admin dapat mencatat peralatan yang telah dikembalikan.
|
||||
|
||||
Inspeksi Peralatan: Fitur untuk mencatat kondisi peralatan saat dikembalikan (baik, rusak minor, rusak parah).
|
||||
|
||||
Penalti/Denda: Jika ada kerusakan atau keterlambatan pengembalian, sistem dapat menghitung dan mencatat penalti/denda.
|
||||
|
||||
4.2.5. Laporan & Analitik
|
||||
Laporan Pendapatan: Ringkasan pendapatan harian, mingguan, bulanan.
|
||||
|
||||
Laporan Inventaris: Peralatan paling populer, peralatan yang jarang disewa, peralatan yang sering rusak.
|
||||
|
||||
Laporan Ketersediaan: Prediksi ketersediaan di masa mendatang berdasarkan data kalender.
|
||||
|
||||
5. Persyaratan Non-Fungsional
|
||||
5.1. Kinerja
|
||||
Waktu muat halaman yang memadai.
|
||||
|
||||
Dapat menangani hingga 100 transaksi bersamaan tanpa penurunan kinerja yang signifikan.
|
||||
|
||||
5.2. Keamanan
|
||||
Enkripsi SSL/TLS untuk semua komunikasi data.
|
||||
|
||||
Perlindungan terhadap serangan XSS dan SQL Injection.
|
||||
|
||||
Autentikasi dan otorisasi pengguna yang kuat.
|
||||
|
||||
Penyimpanan data sensitif pelanggan yang aman.
|
||||
|
||||
5.3. Skalabilitas
|
||||
Mampu mengakomodasi peningkatan jumlah pengguna dan inventaris di masa mendatang.
|
||||
|
||||
5.4. Ketersediaan
|
||||
Uptime minimal 99,5%.
|
||||
|
||||
5.5. Usability (Kemudahan Penggunaan)
|
||||
Antarmuka yang intuitif dan responsif untuk desktop dan perangkat mobile.
|
||||
|
||||
Proses pemesanan yang sederhana dan jelas, terutama interaksi dengan kalender.
|
||||
|
||||
5.6. Kompatibilitas
|
||||
Kompatibel dengan browser web modern (Chrome, Firefox, Safari, Edge).
|
||||
|
||||
6. Integrasi
|
||||
Payment Gateway: Integrasi dengan penyedia layanan pembayaran lokal (misalnya, Midtrans, Xendit, Duitku). n8n akan menjadi orkestrator untuk memproses webhook dari payment gateway dan memperbarui status pesanan di database.
|
||||
|
||||
Email Service Provider: Untuk pengiriman notifikasi transaksi dan promosi. n8n dapat digunakan untuk memicu pengiriman email berdasarkan event tertentu (misalnya, pesanan baru, konfirmasi pembayaran, pengingat pengembalian).
|
||||
|
||||
7. Teknologi yang Digunakan
|
||||
Frontend: Create React App (CRA) (untuk setup awal proyek React, pembangunan UI, dan development server bawaan)
|
||||
|
||||
Backend: n8n (sebagai orkestrator workflow, API endpoint, dan penghubung ke database PostgreSQL)
|
||||
|
||||
Database: PostgreSQL (untuk menyimpan data inventaris, pesanan, pengguna, dan informasi ketersediaan kalender)
|
||||
|
||||
Hosting: Server pribadi Anda
|
||||
|
||||
8. Alur Pengguna (Contoh Sederhana)
|
||||
8.1. Alur Pemesanan (Penyewa)
|
||||
Pengguna membuka webapp React yang di-build dengan CRA.
|
||||
|
||||
Mencari peralatan (misal: "tenda dome").
|
||||
|
||||
Di halaman detail tenda, komponen kalender React menampilkan ketersediaan. Data ketersediaan ini ditarik dari API yang diekspos oleh n8n (yang kemudian mengambil data dari PostgreSQL).
|
||||
|
||||
Pengguna memilih tanggal sewa dari kalender.
|
||||
|
||||
Menambahkan ke keranjang belanja.
|
||||
|
||||
Melanjutkan ke checkout.
|
||||
|
||||
Mengisi data diri.
|
||||
|
||||
Memilih metode pembayaran.
|
||||
|
||||
Melakukan pembayaran.
|
||||
|
||||
Frontend React mengirim data pesanan ke API n8n.
|
||||
|
||||
n8n menerima data pesanan, memprosesnya (misalnya, memverifikasi stok, membuat entri pesanan di PostgreSQL), dan memicu pengiriman konfirmasi email ke pengguna.
|
||||
|
||||
n8n juga memperbarui status ketersediaan item di database PostgreSQL untuk mencerminkan item yang sedang disewa, yang akan terlihat di kalender.
|
||||
|
||||
8.2. Alur Pengelolaan Pesanan (Admin)
|
||||
Admin login ke dashboard React yang di-build dengan CRA.
|
||||
|
||||
Melihat daftar pesanan baru. Data ini ditarik dari API n8n (yang mengambil dari PostgreSQL).
|
||||
|
||||
Admin memverifikasi pembayaran pesanan.
|
||||
|
||||
Admin mengubah status pesanan melalui UI React, yang mengirim permintaan ke API n8n.
|
||||
|
||||
n8n menerima permintaan, memperbarui status pesanan di PostgreSQL, dan memicu pembaruan ketersediaan di kalender secara otomatis.
|
||||
|
||||
Saat pelanggan mengembalikan peralatan, admin mencatat pengembalian melalui UI React.
|
||||
|
||||
Permintaan dikirim ke n8n, yang kemudian memperbarui status inventaris di PostgreSQL dan mengembalikan ketersediaan item di kalender.
|
||||
|
||||
Jika ada kerusakan atau keterlambatan, n8n dapat memicu workflow untuk menghitung denda dan mengirim notifikasi.
|
||||
@@ -1,8 +1,6 @@
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import AuthPage from './pages/AuthPage';
|
||||
import ChatbotPage from './pages/ChatbotPage';
|
||||
import DashboardLayout from './layouts/DashboardLayout';
|
||||
import DashboardOverview from './pages/dashboard/DashboardOverview';
|
||||
import AuthPage from './AuthPage';
|
||||
import ChatbotPage from './ChatbotPage';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -10,7 +8,6 @@ function App() {
|
||||
<Routes>
|
||||
<Route path="/admin" element={<AuthPage />} />
|
||||
<Route path="/" element={<ChatbotPage />} />
|
||||
<Route path="/dashboard" element={<DashboardLayout><DashboardOverview /></DashboardLayout>} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import LoginForm from '../components/auth/LoginForm';
|
||||
import SignupForm from '../components/auth/SignupForm';
|
||||
import OtpForm from '../components/auth/OtpForm';
|
||||
import ForgotPasswordForm from '../components/auth/ForgotPasswordForm';
|
||||
import ResetPasswordForm from '../components/auth/ResetPasswordForm';
|
||||
import LoginForm from './LoginForm';
|
||||
import SignupForm from './SignupForm';
|
||||
import OtpForm from './OtpForm';
|
||||
import ForgotPasswordForm from './ForgotPasswordForm';
|
||||
import ResetPasswordForm from './ResetPasswordForm';
|
||||
|
||||
const AuthPage = () => {
|
||||
const [view, setView] = useState('login'); // 'login', 'signup', 'otp', 'forgotPassword', 'resetPassword'
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const DashboardLayout = ({ children }) => {
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-100">
|
||||
<div className="w-64 bg-gray-800 text-white">
|
||||
{/* Sidebar */}
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold">Admin</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 p-10">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardLayout;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx';
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const DashboardOverview = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Dashboard Overview</h1>
|
||||
<p>Welcome to the admin dashboard.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardOverview;
|
||||
6
bookoomoo-app/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
.vite
|
||||
*.log
|
||||
|
||||
35
bookoomoo-app/complete-page-list.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Daftar Halaman Bookoomoo
|
||||
|
||||
Berikut adalah daftar semua halaman yang telah dibuat untuk Bookoomoo:
|
||||
|
||||
## Halaman Publik
|
||||
1. **Landing Page** - `/` (Sudah ada)
|
||||
2. **Tentang Kami** - `/about` (Sudah dibuat)
|
||||
3. **Program Donasi** - `/donation-program` (Sudah dibuat)
|
||||
4. **Kontak** - `/contact` (Sudah dibuat)
|
||||
5. **Kebijakan Privasi** - `/privacy` (Sudah dibuat)
|
||||
6. **Syarat & Ketentuan** - `/terms` (Sudah dibuat)
|
||||
7. **FAQ** - `/faq` (Sudah dibuat)
|
||||
8. **Tracking Donasi** - `/donation-tracking` (Sudah dibuat)
|
||||
|
||||
## Halaman Fungsional/User
|
||||
9. **Login/Register** - `/login` (Sudah ada)
|
||||
10. **Dashboard** - `/dashboard` (Sudah ada)
|
||||
11. **Form Pembuatan Buku** - `/dashboard/create` (Sudah ada)
|
||||
12. **Upload** - `/dashboard/upload` (Sudah ada)
|
||||
13. **Downloads** - `/dashboard/downloads` (Sudah ada)
|
||||
14. **Checkout Cetak** - `/dashboard/print` (Sudah ada)
|
||||
15. **Pesanan** - `/dashboard/orders` (Sudah ada)
|
||||
16. **Cerita** - `/dashboard/stories` (Sudah ada)
|
||||
17. **Donasi** - `/dashboard/donations` (Sudah ada)
|
||||
18. **Top Up** - `/dashboard/topup` (Sudah ada)
|
||||
19. **Konfirmasi Pesanan** - `/order-confirmation` (Sudah dibuat)
|
||||
|
||||
## Halaman yang Belum Dibuat
|
||||
- Preview Buku (interaktif)
|
||||
- Beberapa halaman detail mungkin perlu dibuat tergantung kebutuhan
|
||||
|
||||
## Catatan
|
||||
- Semua halaman sudah memiliki routing yang sesuai
|
||||
- Beberapa halaman mungkin perlu integrasi backend untuk fungsionalitas penuh
|
||||
- Desain sudah responsif dan konsisten di semua halaman
|
||||
82
bookoomoo-app/landing-page-brief.md
Normal file
@@ -0,0 +1,82 @@
|
||||
Copywriting Landing Page Bookoomoo
|
||||
Hero Section (Attention)
|
||||
Judul Utama:
|
||||
Berikan Cerita, Sebarkan Bahagia
|
||||
|
||||
Sub Judul:
|
||||
Setiap buku yang kamu buat untuk anakmu, akan menjadi hadiah untuk anak lain yang belum pernah punya buku cerita sendiri.
|
||||
Nama anakmu akan jadi tokoh utama, dan wajahnya akan hadir di bagian serial number buku donasi — sebagai penanda siapa yang berbagi kebahagiaan ini.
|
||||
|
||||
CTA Button:
|
||||
[Buat Buku + Donasi Sekarang]
|
||||
|
||||
Section 1 – Problem & Agitate (P – A dari PAS)
|
||||
Banyak anak di pelosok Indonesia tumbuh tanpa buku cerita berwarna.
|
||||
Tidak ada tokoh yang mereka kenal, tidak ada cerita yang membuat mereka tertawa atau bermimpi.
|
||||
Sementara di kota, buku anak hadir berlimpah, tapi sering kali hanya berhenti di rak-rak rumah kita.
|
||||
|
||||
Section 2 – Solution (S dari PAS)
|
||||
Bookoomoo mengubah setiap cerita yang kamu buat menjadi dua hal:
|
||||
|
||||
Buku personal untuk anakmu, dengan nama mereka sebagai tokoh utama.
|
||||
|
||||
Buku donasi fisik untuk anak lain, lengkap dengan ilustrasi AI wajah anakmu di bagian serial number — agar penerima tahu, ada teman dari jauh yang berbagi cerita untuknya.
|
||||
|
||||
Section 3 – Features & Advantages (F – A dari FAB)
|
||||
Fitur Utama:
|
||||
|
||||
Cerita personal bilingual (Indonesia & Inggris) dengan nama anakmu.
|
||||
|
||||
Ilustrasi AI wajah anakmu untuk identitas donasi.
|
||||
|
||||
Cetak fisik premium + PDF digital.
|
||||
|
||||
QR tracking untuk melihat perjalanan buku donasi.
|
||||
|
||||
Keunggulan:
|
||||
|
||||
Setiap transaksi otomatis berdampak sosial.
|
||||
|
||||
Buku donasi dibuat dari kertas & cetakan berkualitas, tahan lama di tangan anak-anak.
|
||||
|
||||
Transparansi penuh lewat halaman tracking QR.
|
||||
|
||||
Section 4 – Benefits (B dari FAB)
|
||||
Untuk Anakmu:
|
||||
|
||||
Merasa istimewa membaca buku dengan namanya sendiri sebagai tokoh utama.
|
||||
|
||||
Mendapatkan cerita edukatif yang memupuk imajinasi.
|
||||
|
||||
Untuk Anak Penerima Donasi:
|
||||
|
||||
Mendapat buku berwarna, gratis, dan berkualitas.
|
||||
|
||||
Melihat wajah “teman pemberi buku” di halaman serial number, membuat donasi terasa lebih personal.
|
||||
|
||||
Untuk Kamu:
|
||||
|
||||
Rasa bangga karena membuat dampak nyata.
|
||||
|
||||
Menjadi bagian dari gerakan berbagi literasi anak di Indonesia.
|
||||
|
||||
Section 5 – AIDA (Desire & Action)
|
||||
Desire:
|
||||
Bayangkan, anakmu tertawa membaca cerita tentang dirinya sendiri.
|
||||
Di saat yang sama, seorang anak di pelosok juga membuka buku yang sama, dan tersenyum melihat wajah anakmu di halaman depan.
|
||||
Dua anak, dua senyum, satu buku yang menyatukan.
|
||||
|
||||
Action:
|
||||
[Klik tombol di bawah untuk mulai membuat buku]
|
||||
[Buat Buku + Donasi Sekarang]
|
||||
|
||||
Section 6 – Social Proof & Testimoni
|
||||
"Waktu saya lihat foto anak penerima buku dengan wajah anak saya di halaman depan, rasanya haru banget." – Rina, Jakarta
|
||||
"Anak saya senang bukunya, tapi dia malah lebih bangga karena bisa memberi ke anak lain." – Bima, Surabaya
|
||||
|
||||
Section 7 – CTA Penutup
|
||||
Tagline:
|
||||
Bukan sekadar buku. Ini cerita yang menyatukan hati.
|
||||
CTA Button:
|
||||
[Buat Buku + Donasi Sekarang]
|
||||
|
||||
68
bookoomoo-app/list-page.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Sitemap & Daftar Halaman Bookoomoo
|
||||
|
||||
## A. Halaman Publik
|
||||
1. **Landing Page (Donasi-First)**
|
||||
- Hero
|
||||
- Problem → Agitate
|
||||
- Solution
|
||||
- Cara Kerja (3 Langkah)
|
||||
- Fitur & Keunggulan
|
||||
- Pilihan Skema Donasi
|
||||
- Preview Buku (interaktif)
|
||||
- Testimoni & Dampak Sosial
|
||||
- FAQ
|
||||
- CTA Penutup
|
||||
- Footer
|
||||
|
||||
2. **Tentang Kami (About)**
|
||||
- Visi & Misi
|
||||
- Cerita di Balik Bookoomoo
|
||||
- Tim & Partner
|
||||
|
||||
3. **Program Donasi**
|
||||
- Penjelasan Buy 1 Donate 1 & Full Donation
|
||||
- Laporan & Statistik Donasi
|
||||
- Foto dokumentasi (privasi terjaga)
|
||||
|
||||
4. **Kontak**
|
||||
- Form kontak
|
||||
- Email, WhatsApp, media sosial
|
||||
|
||||
5. **Kebijakan**
|
||||
- Kebijakan Privasi
|
||||
- Syarat & Ketentuan Layanan
|
||||
|
||||
---
|
||||
|
||||
## B. Halaman Fungsional / User
|
||||
6. **Form Pembuatan Buku**
|
||||
- Input nama anak
|
||||
- Pilih tema
|
||||
- Preview singkat
|
||||
- Pilih skema donasi
|
||||
|
||||
7. **Checkout**
|
||||
- Ringkasan pesanan
|
||||
- Metode pembayaran
|
||||
- Alamat (jika ada pengiriman fisik)
|
||||
|
||||
8. **Konfirmasi Pesanan**
|
||||
- Detail pesanan
|
||||
- Nomor order
|
||||
- Estimasi cetak & kirim
|
||||
- Link tracking QR donasi
|
||||
|
||||
9. **Tracking Donasi**
|
||||
- Status perjalanan buku donasi
|
||||
- Foto penerima (opsional, privasi terjaga)
|
||||
- Lokasi tujuan (umum)
|
||||
|
||||
10. **Login / Register**
|
||||
- Form login & daftar akun
|
||||
- Integrasi media sosial (opsional)
|
||||
|
||||
11. **Dashboard User**
|
||||
- Riwayat pesanan
|
||||
- Status donasi
|
||||
- Saldo token (jika ada)
|
||||
- Akses ke PDF yang sudah dibeli
|
||||
@@ -9,10 +9,11 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.23.12",
|
||||
"lucide-react": "^0.469.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"lucide-react": "^0.469.0"
|
||||
"react-router-dom": "^6.26.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
@@ -22,4 +23,3 @@
|
||||
"vite": "^5.4.8"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
bookoomoo-app/pages-still-to-create.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Halaman yang Masih Perlu Dibuat
|
||||
|
||||
Berdasarkan sitemap dan halaman yang sudah dibuat, berikut adalah halaman-halaman yang masih perlu dibuat:
|
||||
|
||||
## Halaman Publik
|
||||
1. **Program Donasi**
|
||||
- Penjelasan Buy 1 Donate 1 & Full Donation
|
||||
- Laporan & Statistik Donasi
|
||||
- Foto dokumentasi (privasi terjaga)
|
||||
|
||||
2. **Kebijakan Privasi**
|
||||
|
||||
3. **Syarat & Ketentuan Layanan**
|
||||
|
||||
4. **FAQ**
|
||||
|
||||
## Halaman Fungsional
|
||||
5. **Konfirmasi Pesanan**
|
||||
- Detail pesanan
|
||||
- Nomor order
|
||||
- Estimasi cetak & kirim
|
||||
- Link tracking QR donasi
|
||||
|
||||
6. **Tracking Donasi**
|
||||
- Status perjalanan buku donasi
|
||||
- Foto penerima (opsional, privasi terjaga)
|
||||
- Lokasi tujuan (umum)
|
||||
|
||||
## Catatan:
|
||||
- Halaman "Tentang Kami" dan "Kontak" sudah dibuat
|
||||
- Beberapa halaman mungkin bisa digabung (misalnya Kebijakan Privasi dan Syarat & Ketentuan dalam satu halaman)
|
||||
- FAQ bisa menjadi bagian dari halaman tersendiri atau bagian dalam halaman lain
|
||||
40
bookoomoo-app/pages-to-create.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Halaman yang Perlu Dibuat
|
||||
|
||||
Berdasarkan sitemap, berikut adalah halaman-halaman yang perlu dibuat:
|
||||
|
||||
## Halaman Publik
|
||||
1. **Tentang Kami (About)**
|
||||
- Visi & Misi
|
||||
- Cerita di Balik Bookoomoo
|
||||
- Tim & Partner
|
||||
|
||||
2. **Program Donasi**
|
||||
- Penjelasan Buy 1 Donate 1 & Full Donation
|
||||
- Laporan & Statistik Donasi
|
||||
- Foto dokumentasi (privasi terjaga)
|
||||
|
||||
3. **Kontak**
|
||||
- Form kontak
|
||||
- Email, WhatsApp, media sosial
|
||||
|
||||
4. **Kebijakan Privasi**
|
||||
|
||||
5. **Syarat & Ketentuan Layanan**
|
||||
|
||||
6. **FAQ**
|
||||
|
||||
## Halaman Fungsional
|
||||
7. **Konfirmasi Pesanan**
|
||||
- Detail pesanan
|
||||
- Nomor order
|
||||
- Estimasi cetak & kirim
|
||||
- Link tracking QR donasi
|
||||
|
||||
8. **Tracking Donasi**
|
||||
- Status perjalanan buku donasi
|
||||
- Foto penerima (opsional, privasi terjaga)
|
||||
- Lokasi tujuan (umum)
|
||||
|
||||
## Catatan:
|
||||
- Beberapa halaman mungkin bisa digabung (misalnya Kebijakan Privasi dan Syarat & Ketentuan dalam satu halaman)
|
||||
- FAQ bisa menjadi bagian dari halaman tersendiri atau bagian dalam halaman lain
|
||||
BIN
bookoomoo-app/public/hero-bg.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
1
bookoomoo-app/public/hero-image.png
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300" viewBox="0 0 400 300"><rect width="400" height="300" fill="#e0f2f1"/><text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="sans-serif" font-size="24px" fill="#374151">Hero Image Placeholder</text></svg>
|
||||
|
After Width: | Height: | Size: 296 B |
@@ -12,6 +12,14 @@ import StoriesPage from './pages/StoriesPage'
|
||||
import DonationsPage from './pages/DonationsPage'
|
||||
import TopUpPage from './pages/TopUpPage'
|
||||
import LoginPage from './pages/LoginPage'
|
||||
import AboutPage from './pages/AboutPage'
|
||||
import ContactPage from './pages/ContactPage'
|
||||
import DonationProgramPage from './pages/DonationProgramPage'
|
||||
import PrivacyPolicyPage from './pages/PrivacyPolicyPage'
|
||||
import TermsAndConditionsPage from './pages/TermsAndConditionsPage'
|
||||
import FAQPage from './pages/FAQPage'
|
||||
import OrderConfirmationPage from './pages/OrderConfirmationPage'
|
||||
import DonationTrackingPage from './pages/DonationTrackingPage'
|
||||
import { useAuth } from './lib/auth'
|
||||
|
||||
function ProtectedRoute({ children }){
|
||||
@@ -23,10 +31,17 @@ function ProtectedRoute({ children }){
|
||||
export default function App(){
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/landing" element={<LandingPage/>} />
|
||||
<Route path="/" element={<LandingPage/>} />
|
||||
<Route path="/about" element={<AboutPage/>} />
|
||||
<Route path="/contact" element={<ContactPage/>} />
|
||||
<Route path="/donation-program" element={<DonationProgramPage/>} />
|
||||
<Route path="/privacy" element={<PrivacyPolicyPage/>} />
|
||||
<Route path="/terms" element={<TermsAndConditionsPage/>} />
|
||||
<Route path="/faq" element={<FAQPage/>} />
|
||||
<Route path="/donation-tracking" element={<DonationTrackingPage/>} />
|
||||
<Route path="/login" element={<LoginPage/>} />
|
||||
<Route
|
||||
path="/"
|
||||
path="/dashboard"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Layout />
|
||||
@@ -43,7 +58,8 @@ export default function App(){
|
||||
<Route path="donations" element={<DonationsPage/>} />
|
||||
<Route path="topup" element={<TopUpPage/>} />
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/landing" replace />} />
|
||||
<Route path="/order-confirmation" element={<OrderConfirmationPage/>} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
|
||||
165
bookoomoo-app/src/pages/AboutPage.jsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, Heart, BookHeart, Users, Target } from 'lucide-react';
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<motion.h1
|
||||
className="text-4xl font-bold text-slate-900 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Tentang Bookoomoo
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-xl text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Bukan sekadar buku. Ini cerita yang menyatukan hati.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Visi & Misi */}
|
||||
<div className="grid md:grid-cols-2 gap-12 mb-20">
|
||||
<motion.div
|
||||
className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm"
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-teal-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<Target className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-slate-800">Visi</h2>
|
||||
</div>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Menjadi platform yang membangun jembatan antara kebahagiaan anak kota dengan anak di pelosok melalui kekuatan cerita dan literasi.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm"
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-orange-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<Heart className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-slate-800">Misi</h2>
|
||||
</div>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Menghadirkan pengalaman literasi yang menyenangkan dan personal bagi setiap anak.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Menyebarkan akses terhadap buku berkualitas ke anak-anak di pelosok Indonesia.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Membangun kesadaran sosial anak kota melalui program donasi berkelanjutan.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Cerita di Balik Bookoomoo */}
|
||||
<motion.div
|
||||
className="bg-gradient-to-r from-teal-500 to-teal-600 rounded-2xl p-8 md:p-12 text-white mb-20"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold mb-6 text-center">Cerita di Balik Bookoomoo</h2>
|
||||
<div className="prose prose-lg prose-invert max-w-none">
|
||||
<p className="mb-4">
|
||||
Bookoomoo lahir dari sebuah pertanyaan sederhana: "Bagaimana jika setiap buku yang dibeli anak kota bisa menjadi hadiah untuk anak di pelosok?"
|
||||
</p>
|
||||
<p className="mb-4">
|
||||
Kami percaya bahwa setiap anak berhak memiliki akses terhadap buku berkualitas. Tapi lebih dari itu, kami percaya bahwa kebahagiaan menjadi lebih bermakna saat dibagi.
|
||||
</p>
|
||||
<p>
|
||||
Dengan menggabungkan teknologi, kreativitas, dan kepedulian sosial, Bookoomoo menciptakan pengalaman literasi yang tidak hanya menyenangkan bagi anak Anda, tetapi juga berdampak nyata bagi anak-anak di pelosok Indonesia.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Tim & Partner */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-slate-900 text-center mb-12">Tim & Partner</h2>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-8 mb-16">
|
||||
<div className="bg-white p-6 rounded-2xl border border-teal-100 shadow-sm text-center">
|
||||
<div className="w-20 h-20 bg-teal-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Users className="w-10 h-10 text-teal-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-2">Tim Kreatif</h3>
|
||||
<p className="text-slate-600">
|
||||
Penulis, ilustrator, dan desainer yang berdedikasi menciptakan cerita berkualitas tinggi.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-2xl border border-teal-100 shadow-sm text-center">
|
||||
<div className="w-20 h-20 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<BookHeart className="w-10 h-10 text-orange-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-2">Tim Teknologi</h3>
|
||||
<p className="text-slate-600">
|
||||
Pengembang yang membangun platform personalisasi dan sistem tracking donasi.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-2xl border border-teal-100 shadow-sm text-center">
|
||||
<div className="w-20 h-20 bg-yellow-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Heart className="w-10 h-10 text-yellow-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-2">Tim Sosial</h3>
|
||||
<p className="text-slate-600">
|
||||
Koordinator distribusi dan mitra lokal yang memastikan donasi sampai ke tangan yang tepat.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-100 rounded-2xl p-8 text-center">
|
||||
<h3 className="text-2xl font-bold text-slate-800 mb-4">Partner Distribusi</h3>
|
||||
<p className="text-slate-600 max-w-2xl mx-auto">
|
||||
Kami bekerja sama dengan berbagai organisasi pendidikan, yayasan, dan relawan di seluruh Indonesia untuk memastikan setiap buku donasi sampai ke anak yang membutuhkan.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
201
bookoomoo-app/src/pages/ContactPage.jsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, Mail, Phone, MapPin, Send } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function ContactPage() {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
message: ''
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
// Di sini nanti akan diintegrasikan dengan backend
|
||||
console.log('Form submitted:', formData);
|
||||
alert('Terima kasih atas pesan Anda! Kami akan segera menghubungi Anda.');
|
||||
setFormData({ name: '', email: '', subject: '', message: '' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<motion.h1
|
||||
className="text-4xl font-bold text-slate-900 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Hubungi Kami
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-xl text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Punya pertanyaan atau ingin berbicara dengan kami? Kami senang mendengar Anda!
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-12">
|
||||
{/* Informasi Kontak */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-slate-900 mb-6">Informasi Kontak</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-start">
|
||||
<div className="w-12 h-12 bg-teal-100 rounded-full flex items-center justify-center text-teal-600 mr-4 flex-shrink-0">
|
||||
<Mail className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-900 mb-1">Email</h3>
|
||||
<p className="text-slate-600">hello@bookoomoo.com</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start">
|
||||
<div className="w-12 h-12 bg-orange-100 rounded-full flex items-center justify-center text-orange-600 mr-4 flex-shrink-0">
|
||||
<Phone className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-900 mb-1">Telepon</h3>
|
||||
<p className="text-slate-600">+62 21 1234 5678</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start">
|
||||
<div className="w-12 h-12 bg-yellow-100 rounded-full flex items-center justify-center text-yellow-600 mr-4 flex-shrink-0">
|
||||
<MapPin className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-900 mb-1">Alamat</h3>
|
||||
<p className="text-slate-600">Jl. Literasi No. 123<br />Jakarta, Indonesia 12345</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-12">
|
||||
<h3 className="text-xl font-bold text-slate-900 mb-4">Media Sosial</h3>
|
||||
<div className="flex space-x-4">
|
||||
<a href="#" className="w-12 h-12 bg-teal-500 rounded-full flex items-center justify-center text-white hover:bg-teal-600 transition-colors">
|
||||
<span className="font-bold">f</span>
|
||||
</a>
|
||||
<a href="#" className="w-12 h-12 bg-teal-500 rounded-full flex items-center justify-center text-white hover:bg-teal-600 transition-colors">
|
||||
<span className="font-bold">ig</span>
|
||||
</a>
|
||||
<a href="#" className="w-12 h-12 bg-teal-500 rounded-full flex items-center justify-center text-white hover:bg-teal-600 transition-colors">
|
||||
<span className="font-bold">t</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Form Kontak */}
|
||||
<motion.div
|
||||
className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm"
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-slate-900 mb-6">Kirim Pesan</h2>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium text-slate-700 mb-1">Nama Lengkap</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500 transition-colors"
|
||||
placeholder="Nama Anda"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-slate-700 mb-1">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500 transition-colors"
|
||||
placeholder="email@anda.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="subject" className="block text-sm font-medium text-slate-700 mb-1">Subjek</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500 transition-colors"
|
||||
placeholder="Subjek pesan"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="message" className="block text-sm font-medium text-slate-700 mb-1">Pesan</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
rows={5}
|
||||
className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500 transition-colors"
|
||||
placeholder="Tulis pesan Anda di sini..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-teal-500 hover:bg-teal-600 text-white font-bold py-3 px-6 rounded-lg transition-colors flex items-center justify-center"
|
||||
>
|
||||
<Send className="w-5 h-5 mr-2" />
|
||||
<span>Kirim Pesan</span>
|
||||
</button>
|
||||
</form>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
192
bookoomoo-app/src/pages/DonationProgramPage.jsx
Normal file
@@ -0,0 +1,192 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, Heart, Gift, TrendingUp, Award } from 'lucide-react';
|
||||
|
||||
export default function DonationProgramPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<motion.h1
|
||||
className="text-4xl font-bold text-slate-900 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Program Donasi Bookoomoo
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-xl text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Setiap buku yang Anda pesan membawa kebahagiaan ganda
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Program Explanation */}
|
||||
<div className="grid md:grid-cols-2 gap-8 mb-20">
|
||||
<motion.div
|
||||
className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm"
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-teal-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<Gift className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-slate-800">Buy 1 Donate 1</h2>
|
||||
</div>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Untuk setiap buku yang Anda pesan, kami secara otomatis mendonasikan satu buku yang sama ke anak di pelosok Indonesia.
|
||||
</p>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Nama anak Anda sebagai tokoh utama di kedua buku</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Wajah anak Anda muncul di halaman donasi sebagai penanda kasih</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Anda bisa melacak perjalanan buku donasi melalui QR code</span>
|
||||
</li>
|
||||
</ul>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm"
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-orange-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<Heart className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-slate-800">Full Donation</h2>
|
||||
</div>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Anda bisa membeli buku khusus donasi tanpa perlu buku untuk diri sendiri.
|
||||
</p>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Buku donasi dengan nama dan wajah anak penerima</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Dapat dilacak melalui QR code</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Harga lebih terjangkau karena tidak termasuk buku cetak untuk Anda</span>
|
||||
</li>
|
||||
</ul>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Impact Statistics */}
|
||||
<motion.div
|
||||
className="bg-gradient-to-r from-teal-500 to-teal-600 rounded-2xl p-8 md:p-12 text-white mb-20"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-center mb-12">Dampak Sosial Kami</h2>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
<div className="text-center">
|
||||
<div className="w-20 h-20 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<TrendingUp className="w-10 h-10" />
|
||||
</div>
|
||||
<div className="text-4xl font-bold mb-2">15,000+</div>
|
||||
<p className="text-teal-100">Buku Didonasikan</p>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="w-20 h-20 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Award className="w-10 h-10" />
|
||||
</div>
|
||||
<div className="text-4xl font-bold mb-2">250+</div>
|
||||
<p className="text-teal-100">Desa Terjangkau</p>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="w-20 h-20 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Heart className="w-10 h-10" />
|
||||
</div>
|
||||
<div className="text-4xl font-bold mb-2">50,000+</div>
|
||||
<p className="text-teal-100">Senyuman Dibagikan</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Documentation */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-slate-900 text-center mb-12">Dokumentasi Donasi</h2>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-12">
|
||||
<div className="bg-white rounded-xl overflow-hidden shadow-md">
|
||||
<div className="bg-gray-200 border-2 border-dashed rounded-xl w-full h-48" />
|
||||
<div className="p-4">
|
||||
<h3 className="font-bold text-slate-800 mb-2">Distribusi Buku di Papua</h3>
|
||||
<p className="text-slate-600 text-sm">Buku didistribusikan ke sekolah-sekolah di pedalaman Papua.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl overflow-hidden shadow-md">
|
||||
<div className="bg-gray-200 border-2 border-dashed rounded-xl w-full h-48" />
|
||||
<div className="p-4">
|
||||
<h3 className="font-bold text-slate-800 mb-2">Anak-anak di NTT</h3>
|
||||
<p className="text-slate-600 text-sm">Kegembiraan anak-anak saat menerima buku pertama mereka.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl overflow-hidden shadow-md">
|
||||
<div className="bg-gray-200 border-2 border-dashed rounded-xl w-full h-48" />
|
||||
<div className="p-4">
|
||||
<h3 className="font-bold text-slate-800 mb-2">Laporan Donatur</h3>
|
||||
<p className="text-slate-600 text-sm">Kami menjaga privasi penerima dengan baik.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<Link
|
||||
to="/create-story"
|
||||
className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-3 px-8 rounded-full transition-colors inline-flex items-center"
|
||||
>
|
||||
<Gift className="w-5 h-5 mr-2" />
|
||||
<span>Mulai Donasi Sekarang</span>
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
352
bookoomoo-app/src/pages/DonationTrackingPage.jsx
Normal file
@@ -0,0 +1,352 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, MapPin, Clock, CheckCircle, Truck, BookHeart, Users } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function DonationTrackingPage() {
|
||||
const [trackingId, setTrackingId] = useState("");
|
||||
const [searchResult, setSearchResult] = useState(null);
|
||||
|
||||
// Mock data for donation tracking
|
||||
const mockDonationData = {
|
||||
donationId: "DON-2023-005678",
|
||||
bookTitle: "Petualangan Budi di Negeri Ajaib",
|
||||
donorChild: "Budi",
|
||||
recipientChild: "Siti",
|
||||
recipientLocation: "Desa Sukamaju, Kabupaten Flores Timur, NTT",
|
||||
status: "Delivered",
|
||||
statusHistory: [
|
||||
{
|
||||
id: 1,
|
||||
status: "Pesanan Dikonfirmasi",
|
||||
date: "10 Mei 2023",
|
||||
time: "09:30",
|
||||
description: "Pesanan donasi telah dikonfirmasi dan sedang diproses.",
|
||||
icon: <CheckCircle className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
status: "Buku Dicetak",
|
||||
date: "12 Mei 2023",
|
||||
time: "14:15",
|
||||
description: "Buku donasi sedang dalam proses pencetakan.",
|
||||
icon: <BookHeart className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
status: "Dikirim ke Distributor",
|
||||
date: "15 Mei 2023",
|
||||
time: "11:00",
|
||||
description: "Buku donasi telah dikirim ke distributor regional.",
|
||||
icon: <Truck className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
status: "Tiba di Lokasi",
|
||||
date: "20 Mei 2023",
|
||||
time: "16:45",
|
||||
description: "Buku donasi telah tiba di sekolah penerima di Desa Sukamaju.",
|
||||
icon: <MapPin className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
status: "Telah Diserahkan",
|
||||
date: "22 Mei 2023",
|
||||
time: "10:30",
|
||||
description: "Buku donasi telah diserahkan kepada Siti di Sekolah Dasar Negeri 1 Sukamaju.",
|
||||
icon: <Users className="w-5 h-5" />
|
||||
}
|
||||
],
|
||||
photos: [
|
||||
{
|
||||
id: 1,
|
||||
title: "Pencetakan Buku",
|
||||
url: "/placeholder-photo-1.jpg"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Pengemasan Donasi",
|
||||
url: "/placeholder-photo-2.jpg"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Siti Menerima Buku",
|
||||
url: "/placeholder-photo-3.jpg"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const handleSearch = (e) => {
|
||||
e.preventDefault();
|
||||
// In a real application, this would be an API call
|
||||
if (trackingId) {
|
||||
setSearchResult(mockDonationData);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6 max-w-4xl">
|
||||
<div className="text-center mb-16">
|
||||
<motion.h1
|
||||
className="text-4xl font-bold text-slate-900 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Lacak Donasi Buku
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-xl text-slate-600 max-w-2xl mx-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Lihat perjalanan buku donasi Anda dan bagikan kebahagiaan dengan anak di pelosok Indonesia
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Search Section */}
|
||||
<motion.div
|
||||
className="bg-white rounded-2xl shadow-sm border border-teal-100 p-8 mb-12"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-slate-800 mb-6 text-center">Masukkan ID Donasi</h2>
|
||||
|
||||
<form onSubmit={handleSearch} className="max-w-md mx-auto">
|
||||
<div className="flex">
|
||||
<input
|
||||
type="text"
|
||||
value={trackingId}
|
||||
onChange={(e) => setTrackingId(e.target.value)}
|
||||
placeholder="Contoh: DON-2023-005678"
|
||||
className="flex-grow px-4 py-3 border border-slate-300 rounded-l-lg focus:ring-2 focus:ring-teal-500 focus:border-teal-500 transition-colors"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-teal-500 hover:bg-teal-600 text-white font-bold py-3 px-6 rounded-r-lg transition-colors"
|
||||
>
|
||||
Lacak
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-slate-500 text-sm mt-2 text-center">
|
||||
ID Donasi dapat ditemukan di email konfirmasi atau dashboard Anda
|
||||
</p>
|
||||
</form>
|
||||
</motion.div>
|
||||
|
||||
{/* Tracking Result */}
|
||||
{searchResult && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="bg-white rounded-2xl shadow-sm border border-teal-100 overflow-hidden mb-8">
|
||||
<div className="bg-teal-500 text-white p-6">
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold">Detail Donasi</h2>
|
||||
<p className="text-teal-100">ID Donasi: {searchResult.donationId}</p>
|
||||
</div>
|
||||
<div className="mt-4 md:mt-0">
|
||||
<span className="bg-white/20 py-1 px-3 rounded-full text-sm font-medium">
|
||||
{searchResult.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-8">
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-800 mb-3">Informasi Buku</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex justify-between">
|
||||
<span>Judul Buku:</span>
|
||||
<span className="font-medium">{searchResult.bookTitle}</span>
|
||||
</li>
|
||||
<li className="flex justify-between">
|
||||
<span>Anak Donatur:</span>
|
||||
<span className="font-medium">{searchResult.donorChild}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-800 mb-3">Informasi Penerima</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex justify-between">
|
||||
<span>Nama Penerima:</span>
|
||||
<span className="font-medium">{searchResult.recipientChild}</span>
|
||||
</li>
|
||||
<li className="flex justify-between">
|
||||
<span>Lokasi:</span>
|
||||
<span className="font-medium text-right">{searchResult.recipientLocation}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status Timeline */}
|
||||
<div className="mb-8">
|
||||
<h3 className="font-bold text-slate-800 mb-6">Perjalanan Donasi</h3>
|
||||
|
||||
<div className="relative">
|
||||
{/* Timeline line */}
|
||||
<div className="absolute left-4 top-0 bottom-0 w-0.5 bg-teal-200 transform translate-x-1/2"></div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{searchResult.statusHistory.map((status, index) => (
|
||||
<div key={status.id} className="relative flex items-start">
|
||||
<div className={`absolute left-0 w-8 h-8 rounded-full flex items-center justify-center z-10 ${
|
||||
index === searchResult.statusHistory.length - 1
|
||||
? 'bg-teal-500 text-white'
|
||||
: 'bg-teal-100 text-teal-500'
|
||||
}`}>
|
||||
{status.icon}
|
||||
</div>
|
||||
<div className="ml-12">
|
||||
<div className="flex flex-wrap items-center gap-2 mb-1">
|
||||
<h4 className="font-bold text-slate-800">{status.status}</h4>
|
||||
<div className="flex items-center text-slate-500 text-sm">
|
||||
<Clock className="w-4 h-4 mr-1" />
|
||||
<span>{status.date} pukul {status.time}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-slate-600">{status.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Photos */}
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-800 mb-6">Dokumentasi</h3>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
{searchResult.photos.map((photo) => (
|
||||
<div key={photo.id} className="bg-slate-50 rounded-xl overflow-hidden">
|
||||
<div className="bg-gray-200 border-2 border-dashed rounded-xl w-full h-40" />
|
||||
<div className="p-3">
|
||||
<p className="text-slate-700 font-medium text-sm">{photo.title}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 text-center">
|
||||
<p className="text-slate-600 mb-4">
|
||||
Untuk menjaga privasi anak, wajah penerima donasi tidak ditampilkan secara langsung.
|
||||
</p>
|
||||
<Link to="/donation-program" className="text-teal-600 hover:text-teal-700 font-medium flex items-center justify-center">
|
||||
Pelajari lebih lanjut tentang program donasi
|
||||
<svg className="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="bg-gradient-to-r from-teal-500 to-teal-600 rounded-2xl p-8 text-white text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h3 className="text-2xl font-bold mb-4">Bagikan Kebahagiaan</h3>
|
||||
<p className="mb-6 max-w-2xl mx-auto">
|
||||
Ceritakan pengalaman donasi Anda dan bantu kami menjangkau lebih banyak keluarga untuk menyebarkan kebahagiaan literasi.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<button className="bg-white text-teal-600 hover:bg-slate-100 font-bold py-3 px-6 rounded-full transition-colors">
|
||||
Bagikan di Media Sosial
|
||||
</button>
|
||||
<Link
|
||||
to="/create-story"
|
||||
className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-3 px-6 rounded-full transition-colors"
|
||||
>
|
||||
Buat Donasi Lagi
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Info Section (when no search result) */}
|
||||
{!searchResult && (
|
||||
<motion.div
|
||||
className="grid md:grid-cols-2 gap-8"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
>
|
||||
<div className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm">
|
||||
<div className="w-12 h-12 bg-teal-100 rounded-full flex items-center justify-center text-teal-600 mb-6">
|
||||
<BookHeart className="w-6 h-6" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Cara Melacak Donasi</h3>
|
||||
<ul className="text-slate-600 space-y-3">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">1.</span>
|
||||
<span>Temukan ID Donasi di email konfirmasi atau dashboard Anda</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">2.</span>
|
||||
<span>Masukkan ID Donasi di kolom pencarian di atas</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">3.</span>
|
||||
<span>Lihat detail perjalanan buku donasi</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-8 rounded-2xl border border-teal-100 shadow-sm">
|
||||
<div className="w-12 h-12 bg-orange-100 rounded-full flex items-center justify-center text-orange-600 mb-6">
|
||||
<Shield className="w-6 h-6" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Privasi Terjaga</h3>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Kami menjaga privasi penerima donasi dengan:
|
||||
</p>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Tidak menampilkan wajah penerima secara langsung</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Mengaburkan lokasi spesifik dalam dokumentasi</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Mematuhi semua regulasi perlindungan data</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
240
bookoomoo-app/src/pages/FAQPage.jsx
Normal file
@@ -0,0 +1,240 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, HelpCircle, BookHeart, CreditCard, Truck, Shield } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function FAQPage() {
|
||||
const [openIndex, setOpenIndex] = useState(null);
|
||||
|
||||
const toggleAccordion = (index) => {
|
||||
setOpenIndex(openIndex === index ? null : index);
|
||||
};
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
category: "Umum",
|
||||
icon: <HelpCircle className="w-5 h-5" />,
|
||||
questions: [
|
||||
{
|
||||
question: "Apa itu Bookoomoo?",
|
||||
answer: "Bookoomoo adalah platform yang memungkinkan Anda membuat buku cerita personal untuk anak Anda, sekaligus berdonasi buku yang sama untuk anak di pelosok Indonesia melalui program 'Buy 1 Donate 1'."
|
||||
},
|
||||
{
|
||||
question: "Bagaimana cara kerja Bookoomoo?",
|
||||
answer: "Anda memesan buku personal untuk anak Anda dengan nama mereka sebagai tokoh utama. Kami secara otomatis mendonasikan buku yang sama ke anak di pelosok, dengan wajah anak Anda muncul di halaman donasi sebagai penanda kasih."
|
||||
},
|
||||
{
|
||||
question: "Apakah buku donasi benar-benar sampai ke anak yang membutuhkan?",
|
||||
answer: "Ya, kami bekerja sama dengan berbagai organisasi pendidikan dan relawan di seluruh Indonesia untuk memastikan setiap buku donasi sampai ke tangan anak yang membutuhkan. Anda juga bisa melacak perjalanan buku donasi melalui QR code."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Pemesanan & Pembayaran",
|
||||
icon: <CreditCard className="w-5 h-5" />,
|
||||
questions: [
|
||||
{
|
||||
question: "Bagaimana cara memesan buku di Bookoomoo?",
|
||||
answer: "Anda bisa memesan buku dengan mengklik tombol 'Mulai Bercerita' di beranda kami. Ikuti langkah-langkah untuk memasukkan nama anak, memilih tema, dan menyelesaikan pembayaran."
|
||||
},
|
||||
{
|
||||
question: "Metode pembayaran apa yang tersedia?",
|
||||
answer: "Kami menerima pembayaran melalui transfer bank, kartu kredit/debit, dan berbagai platform pembayaran digital seperti GoPay, OVO, dan DANA."
|
||||
},
|
||||
{
|
||||
question: "Apakah bisa memesan lebih dari satu buku sekaligus?",
|
||||
answer: "Tentu! Anda bisa memesan buku untuk beberapa anak sekaligus. Setiap buku akan dipersonalisasi sesuai dengan nama dan informasi anak yang bersangkutan."
|
||||
},
|
||||
{
|
||||
question: "Apakah ada kebijakan pengembalian dana?",
|
||||
answer: "Karena setiap buku dipersonalisasi secara khusus, kami tidak menerima pengembalian dana untuk pembelian yang sudah diproses. Namun, jika ada kesalahan dalam konten buku, kami akan dengan senang hati memperbaikinya atau mencetak ulang."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Donasi",
|
||||
icon: <Heart className="w-5 h-5" />,
|
||||
questions: [
|
||||
{
|
||||
question: "Bagaimana program donasi bekerja?",
|
||||
answer: "Untuk setiap buku yang Anda pesan, kami secara otomatis mendonasikan satu buku yang sama ke anak di pelosok Indonesia. Wajah anak Anda akan muncul di halaman donasi sebagai penanda kasih."
|
||||
},
|
||||
{
|
||||
question: "Bisakah saya memilih lokasi donasi?",
|
||||
answer: "Saat ini Anda belum bisa memilih lokasi spesifik untuk donasi. Buku donasi akan dikirim ke lokasi yang membutuhkan berdasarkan kerjasama kami dengan mitra distribusi."
|
||||
},
|
||||
{
|
||||
question: "Apakah saya bisa melihat foto penerima donasi?",
|
||||
answer: "Untuk menjaga privasi penerima donasi, kami tidak menampilkan foto wajah mereka secara langsung. Namun, kami menyediakan laporan umum tentang distribusi donasi dan dokumentasi kegiatan secara berkala."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Buku & Konten",
|
||||
icon: <BookHeart className="w-5 h-5" />,
|
||||
questions: [
|
||||
{
|
||||
question: "Bahasa apa saja yang tersedia untuk buku?",
|
||||
answer: "Buku tersedia dalam versi bilingual Bahasa Indonesia dan Bahasa Inggris untuk memperkaya pengalaman literasi anak."
|
||||
},
|
||||
{
|
||||
question: "Berapa jumlah halaman dalam setiap buku?",
|
||||
answer: "Setiap buku memiliki 20-24 halaman yang penuh dengan ilustrasi menarik dan cerita edukatif yang disesuaikan dengan usia anak."
|
||||
},
|
||||
{
|
||||
question: "Apakah bisa mengedit cerita setelah memesan?",
|
||||
answer: "Setelah pesanan diproses, pengeditan cerita tidak dapat dilakukan. Namun, jika pesanan belum diproses, Anda bisa menghubungi layanan pelanggan kami untuk melakukan perubahan."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Pengiriman",
|
||||
icon: <Truck className="w-5 h-5" />,
|
||||
questions: [
|
||||
{
|
||||
question: "Berapa lama waktu pengiriman buku?",
|
||||
answer: "Waktu pengiriman buku biasanya 7-14 hari kerja setelah pesanan diproses. Waktu pengiriman buku donasi bisa lebih lama tergantung lokasi tujuan."
|
||||
},
|
||||
{
|
||||
question: "Apakah bisa mengirim ke alamat berbeda?",
|
||||
answer: "Ya, Anda bisa menentukan alamat pengiriman yang berbeda saat checkout. Pastikan alamat yang dimasukkan sudah benar untuk menghindari kendala pengiriman."
|
||||
},
|
||||
{
|
||||
question: "Bagaimana cara melacak pengiriman buku?",
|
||||
answer: "Anda akan menerima email dengan nomor tracking setelah buku dikirim. Untuk buku donasi, Anda bisa melacak perjalanan buku melalui QR code yang tersedia di buku Anda."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Privasi & Keamanan",
|
||||
icon: <Shield className="w-5 h-5" />,
|
||||
questions: [
|
||||
{
|
||||
question: "Bagaimana Bookoomoo melindungi data pribadi saya?",
|
||||
answer: "Kami menerapkan berbagai langkah keamanan untuk melindungi informasi pribadi Anda, termasuk enkripsi data dan akses terbatas ke server kami. Silakan baca Kebijakan Privasi kami untuk informasi lebih lanjut."
|
||||
},
|
||||
{
|
||||
question: "Apakah foto anak saya akan dipublikasikan?",
|
||||
answer: "Foto anak Anda hanya digunakan untuk personalisasi buku dan donasi. Kami tidak akan mempublikasikan foto anak Anda tanpa izin eksplisit dari Anda."
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6 max-w-4xl">
|
||||
<div className="text-center mb-16">
|
||||
<motion.h1
|
||||
className="text-4xl font-bold text-slate-900 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Pertanyaan yang Sering Diajukan
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-xl text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Temukan jawaban untuk pertanyaan Anda tentang Bookoomoo
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{faqs.map((category, categoryIndex) => (
|
||||
<motion.div
|
||||
key={categoryIndex}
|
||||
className="bg-white rounded-2xl shadow-sm border border-teal-100 overflow-hidden"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: categoryIndex * 0.1 }}
|
||||
>
|
||||
<div className="bg-teal-500 text-white p-6">
|
||||
<h2 className="text-2xl font-bold flex items-center">
|
||||
<span className="mr-3 p-2 bg-white/20 rounded-lg">
|
||||
{category.icon}
|
||||
</span>
|
||||
{category.category}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
{category.questions.map((faq, index) => (
|
||||
<div key={index} className="border-b border-slate-100 last:border-b-0">
|
||||
<button
|
||||
className="flex justify-between items-center w-full py-4 text-left font-medium text-slate-800 hover:text-teal-600 transition-colors"
|
||||
onClick={() => toggleAccordion(`${categoryIndex}-${index}`)}
|
||||
>
|
||||
<span>{faq.question}</span>
|
||||
<svg
|
||||
className={`w-5 h-5 transform transition-transform ${openIndex === `${categoryIndex}-${index}` ? 'rotate-180' : ''}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className={`overflow-hidden transition-all duration-300 ${
|
||||
openIndex === `${categoryIndex}-${index}` ? 'max-h-96 pb-4' : 'max-h-0'
|
||||
}`}
|
||||
>
|
||||
<p className="text-slate-600 pl-2">
|
||||
{faq.answer}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="bg-gradient-to-r from-teal-500 to-teal-600 rounded-2xl p-8 text-white text-center mt-16"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<h3 className="text-2xl font-bold mb-4">Masih ada pertanyaan?</h3>
|
||||
<p className="mb-6 max-w-2xl mx-auto">
|
||||
Tim kami siap membantu Anda dengan pertanyaan apa pun tentang Bookoomoo, pemesanan, atau program donasi.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
to="/contact"
|
||||
className="bg-white text-teal-600 hover:bg-slate-100 font-bold py-3 px-6 rounded-full transition-colors"
|
||||
>
|
||||
Hubungi Kami
|
||||
</Link>
|
||||
<Link
|
||||
to="/create-story"
|
||||
className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-3 px-6 rounded-full transition-colors"
|
||||
>
|
||||
Mulai Bercerita
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,147 +1,847 @@
|
||||
import React from 'react'
|
||||
import { BookOpen, Download, Gift, Printer, Sparkles, Users, Wand2 } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useState } from 'react';
|
||||
import { Star, BookHeart, Gift, Truck, Smile, Users, ArrowRight, Menu, X, Heart, Rocket, Sparkles } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
function Section({ children, className='' }){
|
||||
return <section className={`mx-auto w-full max-w-6xl px-4 ${className}`}>{children}</section>
|
||||
}
|
||||
// Helper component for Bento Grid items
|
||||
const FeatureCard = ({ icon, title, children }) => (
|
||||
<motion.div
|
||||
className="bg-white/60 backdrop-blur-sm p-6 rounded-2xl border border-teal-100 shadow-sm hover:shadow-lg transition-shadow duration-300 h-full"
|
||||
whileHover={{ y: -5 }}
|
||||
transition={{ type: "spring", stiffness: 300 }}
|
||||
>
|
||||
<div className="flex items-center justify-center w-12 h-12 bg-teal-500 text-white rounded-full mb-4">
|
||||
{icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-2">{title}</h3>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
// Helper component for Testimonial cards
|
||||
const TestimonialCard = ({ quote, author, location }) => (
|
||||
<motion.div
|
||||
className="bg-white p-6 rounded-xl shadow-md border border-gray-100 h-full flex flex-col justify-between"
|
||||
whileHover={{ y: -5 }}
|
||||
transition={{ type: "spring", stiffness: 300 }}
|
||||
>
|
||||
<p className="text-slate-700 italic">"{quote}"</p>
|
||||
<p className="text-right font-bold text-teal-600 mt-4">- {author}, {location}</p>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default function LandingPage() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
export default function LandingPage(){
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-white to-neutral-50 text-neutral-900">
|
||||
<header className="sticky top-0 z-20 bg-white/70 backdrop-blur border-b">
|
||||
<div className="mx-auto flex max-w-6xl items-center gap-3 px-4 py-3">
|
||||
<Link to="/landing" className="flex items-center gap-2">
|
||||
<div className="grid h-9 w-9 place-items-center rounded-xl bg-gradient-to-br from-amber-500 to-orange-600 text-white shadow">📚</div>
|
||||
<div className="text-lg font-semibold tracking-tight">Bookoomoo</div>
|
||||
</Link>
|
||||
<nav className="ml-auto hidden gap-5 md:flex">
|
||||
<a href="#fitur" className="text-sm text-neutral-700 hover:text-neutral-900">Fitur</a>
|
||||
<a href="#cara" className="text-sm text-neutral-700 hover:text-neutral-900">Cara Kerja</a>
|
||||
<a href="#donasi" className="text-sm text-neutral-700 hover:text-neutral-900">Donasi</a>
|
||||
<div className="bg-slate-50 text-slate-800">
|
||||
{/* Header */}
|
||||
<motion.header
|
||||
className="sticky top-0 bg-gradient-to-r from-teal-500 to-teal-600 shadow-lg z-50"
|
||||
initial={{ y: -100 }}
|
||||
animate={{ y: 0 }}
|
||||
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
||||
>
|
||||
<div className="container mx-auto px-4 sm:px-6 py-3 flex justify-between items-center">
|
||||
<motion.div
|
||||
className="flex items-center"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<div className="bg-white text-teal-600 w-10 h-10 rounded-full flex items-center justify-center font-bold text-xl mr-2">
|
||||
B
|
||||
</div>
|
||||
<Link to="/" className="text-white text-2xl font-bold tracking-tight">
|
||||
Bookoomoo
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex items-center space-x-1">
|
||||
<motion.a
|
||||
href="#features"
|
||||
className="text-white/90 hover:text-white px-4 py-2 rounded-full hover:bg-white/10 transition-all duration-300 font-medium"
|
||||
whileHover={{ y: -2 }}
|
||||
>
|
||||
Fitur
|
||||
</motion.a>
|
||||
<motion.a
|
||||
href="#impact"
|
||||
className="text-white/90 hover:text-white px-4 py-2 rounded-full hover:bg-white/10 transition-all duration-300 font-medium"
|
||||
whileHover={{ y: -2 }}
|
||||
>
|
||||
Dampak
|
||||
</motion.a>
|
||||
<motion.div whileHover={{ y: -2 }}>
|
||||
<Link to="/login" className="text-white/90 hover:text-white px-4 py-2 rounded-full hover:bg-white/10 transition-all duration-300 font-medium">Login</Link>
|
||||
</motion.div>
|
||||
<motion.div whileHover={{ scale: 1.05 }}>
|
||||
<Link to="/create-story" className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-2 px-6 rounded-full transition-all duration-300 shadow-md hover:shadow-lg flex items-center">
|
||||
<span>Mulai Bercerita</span>
|
||||
<Sparkles className="ml-2 w-4 h-4" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</nav>
|
||||
<Link to="/login" className="ml-auto md:ml-4 inline-flex items-center rounded-xl bg-neutral-900 px-4 py-2 text-sm text-white shadow hover:bg-neutral-800">Masuk</Link>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<motion.button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="md:hidden text-white z-50 p-2 rounded-full hover:bg-white/10"
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</motion.button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
<motion.div
|
||||
className={`absolute top-0 left-0 w-full bg-gradient-to-b from-teal-500 to-teal-600 shadow-xl md:hidden ${isMenuOpen ? 'block' : 'hidden'}`}
|
||||
initial={false}
|
||||
animate={{ height: isMenuOpen ? '100vh' : 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<div className="container mx-auto px-6 pt-24 pb-8 flex flex-col h-full">
|
||||
<nav className="flex flex-col items-center text-center space-y-6 flex-grow">
|
||||
<motion.a
|
||||
href="#features"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-white text-2xl hover:text-teal-100 transition-colors font-medium py-3 w-full"
|
||||
whileHover={{ x: 5 }}
|
||||
>
|
||||
Fitur
|
||||
</motion.a>
|
||||
<motion.a
|
||||
href="#impact"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-white text-2xl hover:text-teal-100 transition-colors font-medium py-3 w-full"
|
||||
whileHover={{ x: 5 }}
|
||||
>
|
||||
Dampak
|
||||
</motion.a>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/login" onClick={() => setIsMenuOpen(false)} className="text-white text-2xl hover:text-teal-100 transition-colors font-medium py-3 w-full">
|
||||
Login
|
||||
</Link>
|
||||
</motion.div>
|
||||
</nav>
|
||||
|
||||
<motion.div
|
||||
className="mt-auto pt-8"
|
||||
whileHover={{ scale: 1.03 }}
|
||||
>
|
||||
<Link
|
||||
to="/create-story"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="bg-orange-500 hover:bg-orange-600 w-full text-white font-bold py-4 px-6 rounded-full transition-all duration-300 shadow-lg hover:shadow-xl flex items-center justify-center text-lg"
|
||||
>
|
||||
<span>Mulai Bercerita</span>
|
||||
<Sparkles className="ml-2" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.header>
|
||||
|
||||
<main>
|
||||
{/* Hero */}
|
||||
<Section className="pt-14 pb-10">
|
||||
<div className="grid items-center gap-8 md:grid-cols-2">
|
||||
<div>
|
||||
<h1 className="text-3xl font-extrabold leading-tight md:text-5xl">
|
||||
Buku Cerita Personal untuk Momen Berharga
|
||||
</h1>
|
||||
<p className="mt-3 text-neutral-600 md:text-lg">
|
||||
Ciptakan buku cerita unik untuk anak: tulis nama, pilih tema, tambahkan foto, lalu unduh PDF atau cetak fisik. Setiap pembelian, kami salurkan donasi buku ke anak yang membutuhkan.
|
||||
{/* Hero Section */}
|
||||
<section
|
||||
className="relative bg-cover bg-center text-white overflow-hidden"
|
||||
style={{ backgroundImage: "url('/hero-bg.png')" }}
|
||||
>
|
||||
<div className="absolute inset-0 bg-black/40"></div>
|
||||
<div className="container mx-auto px-6 py-20 md:py-28 relative z-10">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<motion.div
|
||||
className="text-center lg:text-left"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<motion.h1
|
||||
className="text-3xl sm:text-4xl md:text-5xl font-extrabold leading-tight mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
>
|
||||
Saatnya Menyulap <span className="text-teal-300">Impian Jadi Kenyataan</span>
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="max-w-xl mx-auto lg:mx-0 text-base sm:text-lg text-gray-200 mb-8"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
>
|
||||
Bayangkan betapa senangnya anakmu membaca cerita tentang dirinya sendiri.
|
||||
Dan bayangkan lagi, ada anak lain yang juga tersenyum melihat wajah teman barunya di halaman buku.
|
||||
</motion.p>
|
||||
<motion.div
|
||||
className="flex flex-col sm:flex-row gap-3 justify-center lg:justify-start"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
>
|
||||
<Link to="/create-story" className="bg-orange-500 text-white font-bold py-3 px-6 text-sm sm:text-base rounded-full hover:bg-orange-600 transition-transform transform hover:scale-105 shadow-lg inline-block whitespace-nowrap">
|
||||
Ciptakan Kebahagiaan <ArrowRight className="inline ml-1" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
{/* The right column is empty, creating space on larger screens */}
|
||||
<div className="hidden lg:block"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating animation elements */}
|
||||
<motion.div
|
||||
className="absolute top-1/4 left-1/4 w-16 h-16 rounded-full bg-teal-300/20 blur-xl"
|
||||
animate={{
|
||||
y: [0, -20, 0],
|
||||
x: [0, 10, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: 4,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
></motion.div>
|
||||
|
||||
<motion.div
|
||||
className="absolute bottom-1/3 right-1/4 w-12 h-12 rounded-full bg-orange-500/20 blur-xl"
|
||||
animate={{
|
||||
y: [0, 20, 0],
|
||||
x: [0, -10, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
></motion.div>
|
||||
</section>
|
||||
|
||||
{/* Story Section */}
|
||||
<section className="py-20 bg-white">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<motion.h2
|
||||
className="text-sm font-bold uppercase text-teal-600 tracking-widest mb-2"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Sebuah Cerita
|
||||
</motion.h2>
|
||||
<motion.h3
|
||||
className="text-3xl md:text-4xl font-bold text-slate-800 mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Tentang Dua Senyuman
|
||||
</motion.h3>
|
||||
<motion.p
|
||||
className="text-lg text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
>
|
||||
Di sebuah desa terpencil, ada Adi yang belum pernah punya buku cerita.
|
||||
Di kota besar, ada Bima yang punya banyak buku tapi belum tahu arti berbagi.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-12 items-center mb-20">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<div className="bg-slate-50 p-8 rounded-2xl border border-teal-100 shadow-sm">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-orange-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<span className="text-xl font-bold">1</span>
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-800">Kisah Bima</h4>
|
||||
</div>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Ibu Bima memesan buku cerita khusus untuk anaknya. Di buku itu, Bima adalah pahlawan kecil yang berpetualang ke berbagai negeri.
|
||||
</p>
|
||||
<p className="text-slate-600">
|
||||
Bima tertawa terbahak-bahak membaca cerita tentang dirinya. Tapi yang membuatnya lebih bahagia adalah tahu bahwa buku ini juga akan diberikan kepada teman baru.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
<div className="bg-slate-50 p-8 rounded-2xl border border-teal-100 shadow-sm">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-teal-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<span className="text-xl font-bold">2</span>
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-800">Kisah Adi</h4>
|
||||
</div>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Di desa terpencil, Adi menerima buku cerita pertamanya. Di halaman depan, ia melihat wajah seorang anak yang tersenyum.
|
||||
</p>
|
||||
<p className="text-slate-600">
|
||||
"Itu temanku dari kota," kata Bu Guru sambil menunjuk nama Bima di halaman. Adi tersenyum, merasa punya teman baru meski belum pernah bertemu.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="text-center max-w-2xl mx-auto"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.3 }}
|
||||
>
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-teal-500 text-white rounded-full mb-6">
|
||||
<Heart className="w-8 h-8" />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-800 mb-4">Satu Buku, Dua Senyuman</h4>
|
||||
<p className="text-lg text-slate-600">
|
||||
Setiap buku yang Anda pesan untuk anak tercinta akan menjadi hadiah untuk anak lain yang belum pernah punya buku cerita.
|
||||
Karena kebahagiaan terasa lebih lengkap saat dibagi.
|
||||
</p>
|
||||
<div className="mt-6 flex flex-wrap gap-2">
|
||||
<Link to="/login" className="inline-flex items-center gap-2 rounded-2xl bg-neutral-900 px-5 py-2.5 text-white shadow hover:bg-neutral-800">
|
||||
Coba Sekarang <Wand2 className="h-4 w-4"/>
|
||||
</Link>
|
||||
<a href="#fitur" className="inline-flex items-center gap-2 rounded-2xl border px-5 py-2.5 hover:bg-neutral-50">
|
||||
Lihat Fitur
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-4 flex items-center gap-4 text-sm text-neutral-600">
|
||||
<div className="inline-flex items-center gap-2"><Users className="h-4 w-4"/>Dipercaya orang tua kreatif</div>
|
||||
<div className="inline-flex items-center gap-2"><Sparkles className="h-4 w-4"/>Desain bersih & menyenangkan</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section id="features" className="py-20 bg-slate-50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<motion.h2
|
||||
className="text-3xl md:text-4xl font-bold text-slate-900 mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Pengalaman Istimewa untuk Setiap Halaman
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
className="text-lg text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Kami membuat setiap buku menjadi pengalaman magis yang tidak akan pernah dilupakan
|
||||
</motion.p>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="aspect-[4/3] w-full rounded-2xl border bg-white shadow-sm grid place-items-center">
|
||||
<div className="text-center p-6">
|
||||
<div className="mx-auto grid h-14 w-14 place-items-center rounded-2xl bg-gradient-to-br from-amber-500 to-orange-600 text-white shadow">📖</div>
|
||||
<p className="mt-3 text-sm text-neutral-600">Contoh tampilan buku cerita personal Bookoomoo</p>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<FeatureCard icon={<BookHeart />} title="Cerita Personal Berganda">
|
||||
<p className="text-slate-600">
|
||||
Cerita unik dengan nama anak Anda sebagai bintang utama. Tersedia dalam Bahasa Indonesia dan Inggris untuk pengalaman bilingual.
|
||||
</p>
|
||||
</FeatureCard>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<FeatureCard icon={<Smile />} title="Sentuhan Personal">
|
||||
<p className="text-slate-600">
|
||||
Wajah anak Anda muncul di halaman donasi sebagai penanda kasih sayang, membuat hubungan antara dua anak dari jarak jauh.
|
||||
</p>
|
||||
</FeatureCard>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<FeatureCard icon={<Gift />} title="Dua Versi, Satu Cerita">
|
||||
<p className="text-slate-600">
|
||||
Buku cetak premium untuk koleksi keluarga dan versi digital untuk dibaca kapan saja, di mana saja.
|
||||
</p>
|
||||
</FeatureCard>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.4 }}
|
||||
>
|
||||
<FeatureCard icon={<Truck />} title="Jejak Perjalanan">
|
||||
<p className="text-slate-600">
|
||||
Lacak perjalanan buku donasi melalui kode QR dan lihat betapa senangnya anak penerima saat menerima buku.
|
||||
</p>
|
||||
</FeatureCard>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.5 }}
|
||||
>
|
||||
<FeatureCard icon={<Users />} title="Dampak Nyata">
|
||||
<p className="text-slate-600">
|
||||
Setiap pembelian secara otomatis membantu meningkatkan literasi anak di pelosok Indonesia.
|
||||
</p>
|
||||
</FeatureCard>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.6 }}
|
||||
>
|
||||
<FeatureCard icon={<Star />} title="Kualitas Premium">
|
||||
<p className="text-slate-600">
|
||||
Buku dicetak dengan kertas dan tinta berkualitas tinggi agar tahan lama dan disukai anak-anak.
|
||||
</p>
|
||||
</FeatureCard>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Journey Section */}
|
||||
<section className="py-16 bg-white">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-12">
|
||||
<motion.h2
|
||||
className="text-3xl font-bold text-slate-900 mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Perjalanan Penuh Kebahagiaan
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
className="text-lg text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Dari ide cerita hingga senyuman di wajah dua anak
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6 max-w-5xl mx-auto">
|
||||
{/* Step 1 */}
|
||||
<motion.div
|
||||
className="bg-white p-5 rounded-2xl border border-teal-100 shadow-sm text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<BookHeart className="w-8 h-8 text-orange-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Features */}
|
||||
<Section id="fitur" className="py-12">
|
||||
<h2 className="text-center text-2xl font-bold">Semua yang Kamu Butuhkan</h2>
|
||||
<p className="mx-auto mt-2 max-w-2xl text-center text-neutral-600">Dari ide hingga buku jadi — cepat, mudah, dan penuh makna.</p>
|
||||
<div className="mt-8 grid gap-4 md:grid-cols-3">
|
||||
{[{
|
||||
icon: <Wand2 className="h-5 w-5"/>,
|
||||
title: 'Buat Cerita Personal',
|
||||
desc: 'Tulis nama anak, pilih tema favorit, dan biarkan imajinasi bekerja.'
|
||||
},{
|
||||
icon: <Download className="h-5 w-5"/>,
|
||||
title: 'Unduh PDF',
|
||||
desc: 'Dapatkan versi digital berkualitas untuk dibaca di mana saja.'
|
||||
},{
|
||||
icon: <Printer className="h-5 w-5"/>,
|
||||
title: 'Cetak Fisik',
|
||||
desc: 'Pilihan cetak elegan untuk hadiah spesial dan kenang-kenangan.'
|
||||
}].map((f,i)=> (
|
||||
<div key={i} className="rounded-2xl border bg-white p-5 shadow-sm">
|
||||
<div className="mb-2 inline-grid h-10 w-10 place-items-center rounded-xl bg-neutral-900 text-white">{f.icon}</div>
|
||||
<div className="font-medium">{f.title}</div>
|
||||
<div className="mt-1 text-sm text-neutral-600">{f.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* How it works */}
|
||||
<Section id="cara" className="py-12">
|
||||
<h2 className="text-center text-2xl font-bold">Cara Kerja</h2>
|
||||
<div className="mx-auto mt-6 grid max-w-4xl gap-4 md:grid-cols-3">
|
||||
{[
|
||||
{ step: '1', title: 'Tentukan Cerita', desc: 'Isi nama anak & pilih tema favorit.' },
|
||||
{ step: '2', title: 'Personalisasi', desc: 'Tambahkan foto atau detail lain sesuai keinginan.' },
|
||||
{ step: '3', title: 'Unduh / Cetak', desc: 'Simpan sebagai PDF atau pesan versi cetak.' },
|
||||
].map((s)=> (
|
||||
<div key={s.step} className="rounded-2xl border bg-white p-5 shadow-sm">
|
||||
<div className="mb-2 inline-flex h-8 w-8 items-center justify-center rounded-xl bg-neutral-900 text-white">{s.step}</div>
|
||||
<div className="font-medium">{s.title}</div>
|
||||
<div className="mt-1 text-sm text-neutral-600">{s.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Donation */}
|
||||
<Section id="donasi" className="py-12">
|
||||
<div className="grid items-center gap-6 md:grid-cols-2">
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold">Buy 1 Donate 1</h3>
|
||||
<p className="mt-2 text-neutral-600">Setiap pembelian cetak, kamu ikut berbagi buku untuk anak lain. Membaca menyebarkan kebaikan.</p>
|
||||
<div className="mt-4 inline-flex items-center gap-2 rounded-xl border bg-white px-3 py-2 text-sm shadow-sm">
|
||||
<Gift className="h-4 w-4"/> Donasi transparan & berdampak
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border bg-white p-5 shadow-sm">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="grid h-10 w-10 place-items-center rounded-xl bg-gradient-to-br from-fuchsia-500 to-purple-600 text-white">💝</div>
|
||||
<div>
|
||||
<div className="font-medium">Ratusan buku tersalurkan</div>
|
||||
<div className="text-sm text-neutral-600">Bersama komunitas & mitra pendidikan</div>
|
||||
<div className="w-8 h-8 bg-orange-500 rounded-full flex items-center justify-center text-white mx-auto mb-3 -mt-10 relative z-10">
|
||||
<span className="font-bold text-sm">1</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-800 mb-2">Ciptakan Cerita</h3>
|
||||
<p className="text-slate-600 text-sm">
|
||||
Ceritakan petualangan unik anak Anda menjadi buku personal dengan ilustrasi indah.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Step 2 */}
|
||||
<motion.div
|
||||
className="bg-white p-5 rounded-2xl border border-teal-100 shadow-sm text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="w-16 h-16 bg-teal-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Smile className="w-8 h-8 text-teal-500" />
|
||||
</div>
|
||||
<div className="w-8 h-8 bg-teal-500 rounded-full flex items-center justify-center text-white mx-auto mb-3 -mt-10 relative z-10">
|
||||
<span className="font-bold text-sm">2</span>
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-800 mb-2">Personal Touch</h3>
|
||||
<p className="text-slate-600 text-sm">
|
||||
Wajah anak Anda diilustrasikan dengan AI canggih dan ditempatkan di halaman donasi.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Step 3 */}
|
||||
<motion.div
|
||||
className="bg-white p-5 rounded-2xl border border-teal-100 shadow-sm text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<div className="w-16 h-16 bg-yellow-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Gift className="w-8 h-8 text-yellow-500" />
|
||||
</div>
|
||||
<div className="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center text-white mx-auto mb-3 -mt-10 relative z-10">
|
||||
<span className="font-bold text-sm">3</span>
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-800 mb-2">Kirim Kebahagiaan</h3>
|
||||
<p className="text-slate-600 text-sm">
|
||||
Buku dikirim ke rumah Anda dan ke anak di pelosok dengan jejak perjalanan yang bisa dilacak.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<Section className="py-14">
|
||||
<div className="rounded-2xl border bg-white p-7 shadow-sm text-center">
|
||||
<h3 className="text-xl font-semibold">Mulai petualangan ceritamu hari ini</h3>
|
||||
<p className="mt-2 text-neutral-600">Bangun kebiasaan membaca yang menyenangkan dan penuh kenangan.</p>
|
||||
<div className="mt-5">
|
||||
<Link to="/login" className="inline-flex items-center gap-2 rounded-2xl bg-neutral-900 px-6 py-2.5 text-white shadow hover:bg-neutral-800">
|
||||
Buat Cerita Sekarang <BookOpen className="h-4 w-4"/>
|
||||
{/* Testimonials Section */}
|
||||
<section className="py-20 bg-slate-50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<motion.h2
|
||||
className="text-3xl md:text-4xl font-bold text-slate-900 mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Cerita dari Hati
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
className="text-lg text-slate-600"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Dengarkan pengalaman nyata dari keluarga yang telah bergabung dengan perjalanan Bookoomoo
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-8 max-w-4xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7 }}
|
||||
>
|
||||
<TestimonialCard
|
||||
quote="Waktu saya lihat foto anak penerima buku dengan wajah anak saya di halaman depan, rasanya haru banget. Anak saya jadi punya teman baru dari jauh."
|
||||
author="Rina"
|
||||
location="Jakarta"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
<TestimonialCard
|
||||
quote="Anak saya senang bukunya, tapi dia malah lebih bangga karena bisa memberi ke anak lain. Dia bilang, 'Aku punya teman baru di desa!'"
|
||||
author="Bima"
|
||||
location="Surabaya"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Final CTA Section */}
|
||||
<section className="py-24 bg-gradient-to-r from-teal-500 to-teal-600 text-white overflow-hidden">
|
||||
<div className="container mx-auto px-6 text-center relative">
|
||||
{/* Floating animation elements */}
|
||||
<motion.div
|
||||
className="absolute top-10 left-10 w-16 h-16 rounded-full bg-white/10 blur-xl"
|
||||
animate={{
|
||||
y: [0, -20, 0],
|
||||
x: [0, 15, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: 5,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
></motion.div>
|
||||
|
||||
<motion.div
|
||||
className="absolute bottom-10 right-10 w-12 h-12 rounded-full bg-white/10 blur-xl"
|
||||
animate={{
|
||||
y: [0, 20, 0],
|
||||
x: [0, -15, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: 4,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
></motion.div>
|
||||
|
||||
<motion.div
|
||||
className="absolute top-1/3 right-1/4 w-8 h-8 rounded-full bg-white/10 blur-xl"
|
||||
animate={{
|
||||
scale: [1, 1.5, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
></motion.div>
|
||||
|
||||
<motion.h2
|
||||
className="text-3xl md:text-4xl font-bold mb-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
Siap Menjadi Bagian dari Cerita Ini?
|
||||
</motion.h2>
|
||||
|
||||
<motion.p
|
||||
className="text-lg text-teal-100 mb-10 max-w-2xl mx-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
>
|
||||
Ciptakan buku personal untuk anak Anda dan sebarkan kebahagiaan ke anak lain.
|
||||
Karena setiap halaman yang ditulis adalah langkah menuju dunia yang lebih berwarna.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
>
|
||||
<Link to="/create-story" className="bg-orange-500 text-white font-bold py-4 px-8 rounded-full text-lg hover:bg-orange-600 transition-transform transform hover:scale-105 shadow-lg inline-block whitespace-nowrap">
|
||||
Ciptakan Kebahagiaan Sekarang <ArrowRight className="inline ml-2" />
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="border-t py-10 text-center text-xs text-neutral-500">© {new Date().getFullYear()} Bookoomoo — Be Different, Be You.</footer>
|
||||
{/* Footer */}
|
||||
<footer className="bg-gradient-to-r from-slate-800 to-slate-900 text-slate-300 pt-16 pb-8">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 mb-12">
|
||||
<div className="md:col-span-1">
|
||||
<motion.div
|
||||
className="flex items-center mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="bg-teal-500 text-white w-10 h-10 rounded-full flex items-center justify-center font-bold text-xl mr-3">
|
||||
B
|
||||
</div>
|
||||
<h3 className="text-white text-2xl font-bold">Bookoomoo</h3>
|
||||
</motion.div>
|
||||
<motion.p
|
||||
className="mb-4 text-slate-400"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
Bukan sekadar buku. Ini cerita yang menyatukan hati.
|
||||
</motion.p>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
>
|
||||
<Link to="/create-story" className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-2 px-4 rounded-full text-sm transition-all duration-300 shadow-md hover:shadow-lg inline-flex items-center">
|
||||
<span>Mulai Bercerita</span>
|
||||
<Sparkles className="ml-1 w-4 h-4" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-1">
|
||||
<motion.h4
|
||||
className="text-white text-lg font-bold mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Navigasi
|
||||
</motion.h4>
|
||||
<motion.ul
|
||||
className="space-y-2"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/" className="text-slate-400 hover:text-white transition-colors">Beranda</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<a href="#features" className="text-slate-400 hover:text-white transition-colors">Fitur</a>
|
||||
</motion.div>
|
||||
</li>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<a href="#impact" className="text-slate-400 hover:text-white transition-colors">Dampak</a>
|
||||
</motion.div>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-1">
|
||||
<motion.h4
|
||||
className="text-white text-lg font-bold mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Program
|
||||
</motion.h4>
|
||||
<motion.ul
|
||||
className="space-y-2"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/donation-program" className="text-slate-400 hover:text-white transition-colors">Program Donasi</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-1">
|
||||
<motion.h4
|
||||
className="text-white text-lg font-bold mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Informasi
|
||||
</motion.h4>
|
||||
<motion.ul
|
||||
className="space-y-2"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/about" className="text-slate-400 hover:text-white transition-colors">Tentang Kami</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/contact" className="text-slate-400 hover:text-white transition-colors">Kontak</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/faq" className="text-slate-400 hover:text-white transition-colors">FAQ</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-1">
|
||||
<motion.h4
|
||||
className="text-white text-lg font-bold mb-4"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
Legal
|
||||
</motion.h4>
|
||||
<motion.ul
|
||||
className="space-y-2"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/privacy" className="text-slate-400 hover:text-white transition-colors">Privasi</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
<li>
|
||||
<motion.div whileHover={{ x: 5 }}>
|
||||
<Link to="/terms" className="text-slate-400 hover:text-white transition-colors">Syarat & Ketentuan</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="border-t border-slate-700 pt-8 mt-8 text-center"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="flex flex-col md:flex-row justify-center items-center space-y-4 md:space-y-0 md:space-x-6 mb-4">
|
||||
<motion.div whileHover={{ y: -3 }}>
|
||||
<Link to="/facebook" className="text-slate-400 hover:text-white transition-colors">
|
||||
<Heart className="w-6 h-6" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
<motion.div whileHover={{ y: -3 }}>
|
||||
<Link to="/instagram" className="text-slate-400 hover:text-white transition-colors">
|
||||
<Sparkles className="w-6 h-6" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
<motion.div whileHover={{ y: -3 }}>
|
||||
<Link to="/twitter" className="text-slate-400 hover:text-white transition-colors">
|
||||
<BookHeart className="w-6 h-6" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
<motion.p
|
||||
className="text-sm text-slate-500"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
© {new Date().getFullYear()} Bookoomoo. All rights reserved.
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
218
bookoomoo-app/src/pages/OrderConfirmationPage.jsx
Normal file
@@ -0,0 +1,218 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { CheckCircle, BookHeart, Gift, Truck, Download, QrCode } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export default function OrderConfirmationPage() {
|
||||
const [orderData] = useState({
|
||||
orderId: "BOO-2023-001234",
|
||||
orderDate: new Date().toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' }),
|
||||
childName: "Budi",
|
||||
bookTitle: "Petualangan Budi di Negeri Ajaib",
|
||||
totalPrice: "Rp 185.000",
|
||||
deliveryAddress: "Jl. Melati No. 15, Jakarta Selatan",
|
||||
_estimatedDelivery: "7-10 hari kerja",
|
||||
donationTracking: "DON-2023-005678"
|
||||
});
|
||||
|
||||
const [countdown, setCountdown] = useState(5);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setCountdown(prev => {
|
||||
if (prev <= 1) {
|
||||
clearInterval(timer);
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6 max-w-4xl">
|
||||
<motion.div
|
||||
className="text-center mb-12"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="w-20 h-20 bg-teal-500 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<CheckCircle className="w-12 h-12 text-white" />
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-slate-900 mb-4">Pesanan Berhasil!</h1>
|
||||
<p className="text-xl text-slate-600">
|
||||
Terima kasih atas pesanan Anda. Kami akan segera memproses buku untuk {orderData.childName}.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="bg-white rounded-2xl shadow-sm border border-teal-100 overflow-hidden mb-8"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<div className="bg-teal-500 text-white p-6">
|
||||
<h2 className="text-2xl font-bold">Detail Pesanan</h2>
|
||||
<p className="text-teal-100">ID Pesanan: {orderData.orderId}</p>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-8">
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-800 mb-3 flex items-center">
|
||||
<BookHeart className="w-5 h-5 mr-2 text-teal-500" />
|
||||
Buku Personal
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex justify-between">
|
||||
<span>Judul Buku:</span>
|
||||
<span className="font-medium">{orderData.bookTitle}</span>
|
||||
</li>
|
||||
<li className="flex justify-between">
|
||||
<span>Nama Anak:</span>
|
||||
<span className="font-medium">{orderData.childName}</span>
|
||||
</li>
|
||||
<li className="flex justify-between">
|
||||
<span>Tanggal Pemesanan:</span>
|
||||
<span className="font-medium">{orderData.orderDate}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-800 mb-3 flex items-center">
|
||||
<Gift className="w-5 h-5 mr-2 text-orange-500" />
|
||||
Donasi Buku
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex justify-between">
|
||||
<span>ID Donasi:</span>
|
||||
<span className="font-medium">{orderData.donationTracking}</span>
|
||||
</li>
|
||||
<li className="flex justify-between">
|
||||
<span>Status:</span>
|
||||
<span className="font-medium text-green-600">Dikonfirmasi</span>
|
||||
</li>
|
||||
<li className="flex justify-between">
|
||||
<span>Estimasi Pengiriman:</span>
|
||||
<span className="font-medium">{orderData._estimatedDelivery}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-slate-100 pt-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-lg font-bold text-slate-800">Total Pembayaran:</span>
|
||||
<span className="text-2xl font-bold text-teal-600">{orderData.totalPrice}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="bg-white rounded-2xl shadow-sm border border-teal-100 overflow-hidden mb-8"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
>
|
||||
<div className="bg-slate-800 text-white p-6">
|
||||
<h2 className="text-2xl font-bold">Informasi Pengiriman</h2>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="flex items-start mb-6">
|
||||
<div className="w-12 h-12 bg-teal-100 rounded-full flex items-center justify-center text-teal-600 mr-4 flex-shrink-0">
|
||||
<Truck className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-slate-800 mb-1">Alamat Pengiriman</h3>
|
||||
<p className="text-slate-600">{orderData.deliveryAddress}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-50 rounded-xl p-6">
|
||||
<h3 className="font-bold text-slate-800 mb-4">Tracking Donasi</h3>
|
||||
<div className="flex flex-col md:flex-row items-center">
|
||||
<div className="bg-white p-4 rounded-lg mb-4 md:mb-0 md:mr-6">
|
||||
<QrCode className="w-24 h-24 text-slate-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Gunakan QR code di atas untuk melacak perjalanan buku donasi. Anda juga bisa mengakses tracking melalui dashboard Anda.
|
||||
</p>
|
||||
<Link to="/dashboard/donations" className="text-teal-600 hover:text-teal-700 font-medium flex items-center">
|
||||
Lihat Detail Tracking
|
||||
<svg className="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="bg-gradient-to-r from-teal-500 to-teal-600 rounded-2xl p-8 text-white text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.6 }}
|
||||
>
|
||||
<h3 className="text-2xl font-bold mb-4">Apa yang Harus Anda Lakukan Selanjutnya?</h3>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white/10 p-4 rounded-xl">
|
||||
<Download className="w-8 h-8 mx-auto mb-3" />
|
||||
<h4 className="font-bold mb-2">Unduh PDF</h4>
|
||||
<p className="text-teal-100 text-sm">
|
||||
Anda akan menerima email dengan tautan unduh versi digital buku dalam 24 jam.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 p-4 rounded-xl">
|
||||
<BookHeart className="w-8 h-8 mx-auto mb-3" />
|
||||
<h4 className="font-bold mb-2">Bagikan Kebahagiaan</h4>
|
||||
<p className="text-teal-100 text-sm">
|
||||
Ceritakan pengalaman Anda dan bantu kami menjangkau lebih banyak keluarga.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 p-4 rounded-xl">
|
||||
<Gift className="w-8 h-8 mx-auto mb-3" />
|
||||
<h4 className="font-bold mb-2">Ikuti Perjalanan</h4>
|
||||
<p className="text-teal-100 text-sm">
|
||||
Pantau perjalanan buku donasi melalui dashboard Anda.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mb-6">
|
||||
Anda akan dialihkan ke dashboard dalam {countdown} detik...
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
to="/dashboard"
|
||||
className="bg-white text-teal-600 hover:bg-slate-100 font-bold py-3 px-6 rounded-full transition-colors"
|
||||
>
|
||||
Lihat Dashboard
|
||||
</Link>
|
||||
<Link
|
||||
to="/dashboard/orders"
|
||||
className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-3 px-6 rounded-full transition-colors"
|
||||
>
|
||||
Lihat Detail Pesanan
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
196
bookoomoo-app/src/pages/PrivacyPolicyPage.jsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, Shield, Lock, Eye, User } from 'lucide-react';
|
||||
|
||||
export default function PrivacyPolicyPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6 max-w-4xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h1 className="text-4xl font-bold text-slate-900 mb-6 text-center">Kebijakan Privasi</h1>
|
||||
<p className="text-lg text-slate-600 text-center mb-12">
|
||||
Terakhir diperbarui: {new Date().toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</p>
|
||||
|
||||
<div className="bg-white rounded-2xl p-8 shadow-sm border border-teal-100">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-12 h-12 bg-teal-100 rounded-full flex items-center justify-center text-teal-600 mr-4">
|
||||
<Shield className="w-6 h-6" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-slate-800">Komitmen Kami terhadap Privasi Anda</h2>
|
||||
</div>
|
||||
|
||||
<p className="text-slate-600 mb-6">
|
||||
Di Bookoomoo, kami sangat menghargai privasi Anda. Kebijakan Privasi ini menjelaskan bagaimana kami mengumpulkan, menggunakan, dan melindungi informasi pribadi Anda saat menggunakan layanan kami.
|
||||
</p>
|
||||
|
||||
<div className="space-y-8">
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<Lock className="w-5 h-5 mr-2 text-teal-500" />
|
||||
Informasi yang Kami Kumpulkan
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span><strong>Informasi Pribadi:</strong> Nama, alamat email, nomor telepon, dan alamat pengiriman saat Anda membuat akun atau melakukan pembelian.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span><strong>Informasi Anak:</strong> Nama anak, tanggal lahir, dan foto anak untuk keperluan personalisasi buku dan donasi.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span><strong>Informasi Transaksi:</strong> Detail pembelian, metode pembayaran, dan riwayat pesanan.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span><strong>Informasi Teknis:</strong> Alamat IP, jenis browser, dan data penggunaan untuk meningkatkan layanan kami.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<Eye className="w-5 h-5 mr-2 text-teal-500" />
|
||||
Bagaimana Kami Gunakan Informasi Anda
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Untuk membuat dan mempersonalisasi buku cerita untuk anak Anda.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Untuk memproses pesanan dan pengiriman buku.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Untuk memfasilitasi program donasi dengan menjaga privasi penerima.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Untuk menghubungi Anda tentang pesanan dan layanan kami.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Untuk meningkatkan pengalaman pengguna dan mengembangkan layanan kami.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<User className="w-5 h-5 mr-2 text-teal-500" />
|
||||
Perlindungan Privasi Anak
|
||||
</h3>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Kami sangat peduli dengan privasi anak-anak:
|
||||
</p>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Foto anak hanya digunakan untuk personalisasi buku dan donasi, tidak untuk tujuan lain.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Informasi pribadi anak tidak akan kami bagikan kepada pihak ketiga tanpa izin.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Data anak akan kami hapus sesuai dengan ketentuan hukum yang berlaku.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Pengungkapan Informasi</h3>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Kami tidak menjual, memperdagangkan, atau menyewakan informasi pribadi Anda kepada pihak ketiga. Namun, kami dapat membagikan informasi dalam situasi berikut:
|
||||
</p>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Dengan mitra pengiriman untuk memfasilitasi pengiriman buku.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Dengan penyedia pembayaran untuk memproses transaksi.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Jika diwajibkan oleh hukum atau untuk melindungi hak-hak kami.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Keamanan Data</h3>
|
||||
<p className="text-slate-600">
|
||||
Kami menerapkan berbagai langkah keamanan untuk melindungi informasi pribadi Anda, termasuk enkripsi data dan akses terbatas ke server kami.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Hak Anda</h3>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Anda memiliki hak untuk:
|
||||
</p>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Mengakses dan memperbarui informasi pribadi Anda.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Meminta penghapusan data pribadi Anda.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Menarik persetujuan penggunaan data kapan saja.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Perubahan pada Kebijakan Ini</h3>
|
||||
<p className="text-slate-600">
|
||||
Kami dapat memperbarui Kebijakan Privasi ini dari waktu ke waktu. Kami akan memberi tahu Anda tentang perubahan dengan memposting kebijakan baru di halaman ini.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">Hubungi Kami</h3>
|
||||
<p className="text-slate-600">
|
||||
Jika Anda memiliki pertanyaan tentang Kebijakan Privasi ini, silakan hubungi kami di:
|
||||
</p>
|
||||
<div className="mt-4 bg-slate-50 p-4 rounded-lg">
|
||||
<p className="text-slate-600">
|
||||
<strong>Email:</strong> privacy@bookoomoo.com<br />
|
||||
<strong>Alamat:</strong> Jl. Literasi No. 123, Jakarta, Indonesia 12345
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
187
bookoomoo-app/src/pages/TermsAndConditionsPage.jsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, FileText, CheckCircle, AlertCircle } from 'lucide-react';
|
||||
|
||||
export default function TermsAndConditionsPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Link to="/" className="flex items-center text-teal-600 hover:text-teal-700 transition-colors">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
<span className="font-medium">Kembali ke Beranda</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="py-16">
|
||||
<div className="container mx-auto px-6 max-w-4xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h1 className="text-4xl font-bold text-slate-900 mb-6 text-center">Syarat dan Ketentuan</h1>
|
||||
<p className="text-lg text-slate-600 text-center mb-12">
|
||||
Terakhir diperbarui: {new Date().toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</p>
|
||||
|
||||
<div className="bg-white rounded-2xl p-8 shadow-sm border border-teal-100">
|
||||
<p className="text-slate-600 mb-8">
|
||||
Selamat datang di Bookoomoo. Dengan mengakses atau menggunakan layanan kami, Anda menyetujui syarat dan ketentuan berikut. Mohon baca dengan seksama sebelum menggunakan layanan kami.
|
||||
</p>
|
||||
|
||||
<div className="space-y-8">
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
1. Penerimaan Syarat
|
||||
</h3>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Dengan mengakses atau menggunakan layanan Bookoomoo, Anda menyetujui untuk terikat oleh Syarat dan Ketentuan ini, semua hukum dan peraturan yang berlaku, dan menyetujui bahwa Anda bertanggung jawab atas kepatuhan terhadap hukum setempat.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
2. Penggunaan Layanan
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Layanan Bookoomoo hanya boleh digunakan untuk tujuan yang sah dan sesuai dengan hukum.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Anda tidak boleh menggunakan layanan kami untuk tujuan ilegal atau tidak sah.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Anda bertanggung jawab penuh atas konten yang Anda unggah dan informasi yang Anda berikan.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
3. Hak Kekayaan Intelektual
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Seluruh konten, fitur, dan fungsionalitas di Bookoomoo adalah milik kami atau pemberi lisensi kami.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Buku yang dihasilkan adalah karya orisinal kami, namun Anda diberikan lisensi terbatas untuk penggunaan pribadi.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Foto anak yang Anda unggah tetap menjadi hak Anda, namun kami diberi izin untuk menggunakannya dalam konteks layanan kami.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
4. Pembelian dan Pembayaran
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Semua pembelian bersifat final dan tidak dapat dikembalikan, kecuali dalam kasus tertentu yang dijelaskan dalam kebijakan pengembalian kami.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Kami berhak menolak pesanan atau membatalkan pesanan dalam keadaan tertentu.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Harga dapat berubah sewaktu-waktu tanpa pemberitahuan terlebih dahulu.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
5. Program Donasi
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Program "Buy 1 Donate 1" dan "Full Donation" tunduk pada ketersediaan dan dapat diubah atau dihentikan kapan saja.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Kami berusaha menjaga privasi penerima donasi sesuai dengan kebijakan privasi kami.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Status pengiriman donasi dapat dilihat melalui fitur tracking yang tersedia.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
6. Batasan Tanggung Jawab
|
||||
</h3>
|
||||
<ul className="text-slate-600 space-y-2">
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Bookoomoo tidak bertanggung jawab atas kerusakan tidak langsung, insidental, khusus, konsekuensial atau hukuman yang timbul dari penggunaan layanan kami.</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="mr-2">•</span>
|
||||
<span>Kami tidak menjamin bahwa layanan kami akan berjalan tanpa gangguan atau bebas dari kesalahan.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<CheckCircle className="w-5 h-5 mr-2 text-teal-500" />
|
||||
7. Perubahan Syarat
|
||||
</h3>
|
||||
<p className="text-slate-600">
|
||||
Kami berhak untuk memperbarui atau mengubah Syarat dan Ketentuan ini kapan saja. Perubahan akan berlaku segera setelah diposting di situs web kami. Penggunaan layanan yang berkelanjutan setelah perubahan tersebut merupakan penerimaan atas perubahan tersebut.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4 flex items-center">
|
||||
<AlertCircle className="w-5 h-5 mr-2 text-orange-500" />
|
||||
8. Hukum yang Mengatur
|
||||
</h3>
|
||||
<p className="text-slate-600">
|
||||
Syarat dan Ketentuan ini diatur oleh dan ditafsirkan sesuai dengan hukum Indonesia. Setiap sengketa yang timbul dari penggunaan layanan kami akan diselesaikan secara eksklusif di pengadilan Indonesia.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 className="text-xl font-bold text-slate-800 mb-4">9. Kontak</h3>
|
||||
<p className="text-slate-600 mb-4">
|
||||
Jika Anda memiliki pertanyaan tentang Syarat dan Ketentuan ini, silakan hubungi kami:
|
||||
</p>
|
||||
<div className="bg-slate-50 p-4 rounded-lg">
|
||||
<p className="text-slate-600">
|
||||
<strong>Email:</strong> legal@bookoomoo.com<br />
|
||||
<strong>Telepon:</strong> +62 21 1234 5678<br />
|
||||
<strong>Alamat:</strong> Jl. Literasi No. 123, Jakarta, Indonesia 12345
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
juragankos-app/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Env
|
||||
.env.local
|
||||
.env*
|
||||
|
||||
# Editors
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
77
juragankos-app/CODEX.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# CODEX Context
|
||||
|
||||
Ringkasan konteks kerja untuk meneruskan pengembangan aplikasi chatbot pengusaha kos.
|
||||
|
||||
## Ringkasan Proyek
|
||||
- Tujuan: Aplikasi chatbot untuk pengusaha kos-kosan.
|
||||
- Fokus utama: Antarmuka chat sebagai halaman utama, login admin, dan dashboard analitik performa chatbot.
|
||||
|
||||
## Stack & Tools
|
||||
- Frontend: React 18 + TypeScript (Vite).
|
||||
- Styling: Tailwind CSS + shadcn/ui (preset CSS vars + utilities) + tailwindcss-animate.
|
||||
- Ikon: lucide-react.
|
||||
- Routing: react-router-dom.
|
||||
|
||||
## Struktur Halaman & Routing
|
||||
- `/` → Chat (UI percakapan + input pesan, respons asisten masih disimulasikan).
|
||||
- `/admin/login` → Halaman login admin (stub login di sisi klien).
|
||||
- `/admin/dashboard` → Dashboard analitik sederhana (KPI dummy + daftar percakapan terbaru).
|
||||
|
||||
Router didefinisikan di `src/main.tsx` dengan `createBrowserRouter`, layout root di `src/App.tsx` dan `Outlet` untuk konten halaman.
|
||||
|
||||
## UI Kit & Styling (shadcn/ui)
|
||||
- Tailwind dikonfigurasi untuk shadcn: `darkMode: ['class']`, warna via CSS variables, radius, dan animasi accordion.
|
||||
- File penting:
|
||||
- `tailwind.config.js` (termasuk `tailwindcss-animate`).
|
||||
- `src/index.css` (layer base + CSS variables tema light/dark shadcn).
|
||||
- `components.json` (config shadcn, alias `src/components` dan `src/lib/utils`).
|
||||
- Helper util: `src/lib/utils.ts` berisi `cn()` (clsx + tailwind-merge).
|
||||
|
||||
## Komponen yang Tersedia
|
||||
- `Button` (`src/components/ui/button.tsx`) – berbasis CVA + Radix Slot, varian: default, destructive, outline, secondary, ghost, link; size: default, sm, lg, icon.
|
||||
- `Input` (`src/components/ui/input.tsx`).
|
||||
- `Card` (`src/components/ui/card.tsx`) + `CardHeader`, `CardTitle`, `CardContent`.
|
||||
- Ikon: gunakan dari `lucide-react`, contoh: `Home`, `Moon`, `Send`, dsb.
|
||||
|
||||
## Halaman yang Ada
|
||||
- `src/pages/Chat.tsx`: daftar pesan, auto scroll, input + tombol kirim, balasan asisten disimulasikan (setTimeout). Siap dihubungkan ke backend (SSE/WebSocket/HTTP stream).
|
||||
- `src/pages/admin/Login.tsx`: form email/password; menggunakan `services/auth` untuk stub login.
|
||||
- `src/pages/admin/Dashboard.tsx`: guard sederhana (redirect ke login jika belum auth), KPI dummy dan list percakapan terbaru.
|
||||
|
||||
## Auth (sementara)
|
||||
- `src/services/auth.ts` menyimpan token dummy di `localStorage`. Fungsi: `login`, `logout`, `isAuthenticated`.
|
||||
- Perlu diganti ke autentikasi riil (JWT/session) dan proteksi route yang lebih kuat.
|
||||
|
||||
## Perintah NPM
|
||||
- `npm run dev` — jalankan Vite dev server.
|
||||
- `npm run build` — build produksi ke `dist/`.
|
||||
- `npm run preview` — preview hasil build.
|
||||
|
||||
## Berkas Penting
|
||||
- `index.html` — entry HTML.
|
||||
- `vite.config.ts` — plugin React.
|
||||
- `tsconfig.json`, `tsconfig.node.json` — TypeScript config.
|
||||
- `src/App.tsx` — layout + navigasi (Chat, Dashboard) + placeholder toggle tema.
|
||||
- `src/main.tsx` — bootstrap React + Router.
|
||||
|
||||
## Rencana Lanjutan / TODO
|
||||
- [ ] Implementasi toggle dark mode (persist di `localStorage`, set class `dark` di `document.documentElement`).
|
||||
- [ ] Integrasi backend chat (SSE/WebSocket/HTTP) + streaming token respons.
|
||||
- [ ] Ganti stub auth dengan API riil (JWT/refresh) + guard router (loader/ProtectedRoute).
|
||||
- [ ] Dashboard analitik: metrik nyata (total percakapan, CSAT, latency), grafik (mis. Recharts), filter rentang waktu.
|
||||
- [ ] Persistensi percakapan (riwayat, metadata: intent, kepuasan, sumber lead).
|
||||
- [ ] A11y chat: roles/aria proper, fokus kembali ke input setelah kirim, shortcut keyboard.
|
||||
- [ ] ESLint + Prettier + Vitest sesuai pedoman repo; tambahkan test dasar untuk komponen UI dan routing.
|
||||
- [ ] ENV config (`.env.local`) untuk URL API (`VITE_API_URL`) dan kunci lain (tanpa commit rahasia).
|
||||
|
||||
## Catatan Implementasi
|
||||
- Tema mengikuti shadcn: gunakan kelas Tailwind yang merujuk CSS variables (`bg-background`, `text-foreground`, dll.).
|
||||
- Animasi: `tailwindcss-animate` sudah aktif; siap untuk komponen seperti Accordion/Dialog apabila ditambahkan.
|
||||
- Navigasi header ada di `src/App.tsx` (link ke Chat dan Dashboard), tombol tema placeholder.
|
||||
|
||||
## Cara Menjalankan
|
||||
1. `npm install`
|
||||
2. `npm run dev` (development) atau `npm run build && npm run preview` (produksi lokal).
|
||||
|
||||
---
|
||||
Jika Anda ingin saya melanjutkan, prioritas disarankan: dark mode toggle → integrasi alur chat ke backend (streaming) → auth riil → dashboard metrik & grafik → testing & linting.
|
||||
17
juragankos-app/components.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src/index.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true
|
||||
},
|
||||
"aliases": {
|
||||
"components": "src/components",
|
||||
"utils": "src/lib/utils"
|
||||
}
|
||||
}
|
||||
|
||||
13
juragankos-app/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>JuraganKos App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
3086
juragankos-app/package-lock.json
generated
Normal file
32
juragankos-app/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "juragankos-app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.539.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^7.8.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.41",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.10"
|
||||
}
|
||||
}
|
||||
7
juragankos-app/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
1
juragankos-app/public/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
54
juragankos-app/src/App.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Button } from './components/ui/button'
|
||||
import { Home, Moon, BarChart3, MessageSquare } from 'lucide-react'
|
||||
import { Link, Outlet, useLocation } from 'react-router-dom'
|
||||
|
||||
export default function App() {
|
||||
const { pathname } = useLocation()
|
||||
const isChat = pathname === '/'
|
||||
|
||||
if (isChat) {
|
||||
return (
|
||||
<div className="relative min-h-screen overflow-hidden bg-gradient-to-br from-sky-100 via-rose-50 to-emerald-100 dark:from-slate-900 dark:via-slate-950 dark:to-indigo-950 text-foreground">
|
||||
<div className="pointer-events-none absolute inset-0 opacity-70">
|
||||
<div className="absolute -top-32 left-1/2 h-[60vh] w-[80vw] -translate-x-1/2 rounded-full bg-[radial-gradient(circle_at_center,theme(colors.sky.400)/35%,transparent_60%)] blur-3xl" />
|
||||
<div className="absolute bottom-[-20%] left-[-10%] h-[50vh] w-[50vw] rounded-full bg-[radial-gradient(circle_at_center,theme(colors.emerald.400)/25%,transparent_60%)] blur-3xl" />
|
||||
<div className="absolute bottom-[-30%] right-[-10%] h-[55vh] w-[55vw] rounded-full bg-[radial-gradient(circle_at_center,theme(colors.purple.400)/20%,transparent_60%)] blur-3xl" />
|
||||
</div>
|
||||
<main className="relative grid min-h-screen place-items-center px-4 py-6">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<header className="border-b bg-background/60 supports-[backdrop-filter]:backdrop-blur">
|
||||
<div className="mx-auto max-w-5xl px-6 py-4 flex items-center gap-4">
|
||||
<Link to="/" className="inline-flex items-center gap-2">
|
||||
<Home className="h-5 w-5" />
|
||||
<span className="text-xl font-semibold">JuraganKos</span>
|
||||
</Link>
|
||||
<nav className="flex items-center gap-2 text-sm">
|
||||
<Link to="/" className="inline-flex items-center gap-1 text-muted-foreground hover:text-foreground">
|
||||
<MessageSquare className="h-4 w-4" /> Chat
|
||||
</Link>
|
||||
<Link to="/admin/dashboard" className="inline-flex items-center gap-1 text-muted-foreground hover:text-foreground">
|
||||
<BarChart3 className="h-4 w-4" /> Dashboard
|
||||
</Link>
|
||||
</nav>
|
||||
<div className="ml-auto">
|
||||
<Button variant="secondary" size="sm">
|
||||
<Moon className="mr-2 h-4 w-4" />
|
||||
Toggle theme
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="mx-auto max-w-5xl px-6 py-6">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
53
juragankos-app/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import * as React from 'react'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
import { cn } from '../../lib/utils'
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button'
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
Button.displayName = 'Button'
|
||||
|
||||
export { Button, buttonVariants }
|
||||
|
||||
21
juragankos-app/src/components/ui/card.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import * as React from 'react'
|
||||
import { cn } from '../../lib/utils'
|
||||
|
||||
function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn('rounded-lg border bg-card text-card-foreground shadow-sm', className)} {...props} />
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
|
||||
return <h3 className={cn('text-2xl font-semibold leading-none tracking-tight', className)} {...props} />
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn('p-6 pt-0', className)} {...props} />
|
||||
}
|
||||
|
||||
export { Card, CardHeader, CardTitle, CardContent }
|
||||
|
||||
22
juragankos-app/src/components/ui/input.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as React from 'react'
|
||||
import { cn } from '../../lib/utils'
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Input.displayName = 'Input'
|
||||
|
||||
export { Input }
|
||||
|
||||
74
juragankos-app/src/index.css
Normal file
@@ -0,0 +1,74 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
7
juragankos-app/src/lib/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
26
juragankos-app/src/main.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import './index.css'
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
||||
import Chat from './pages/Chat'
|
||||
import AdminLogin from './pages/admin/Login'
|
||||
import AdminDashboard from './pages/admin/Dashboard'
|
||||
import App from './App'
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <App />,
|
||||
children: [
|
||||
{ index: true, element: <Chat /> },
|
||||
{ path: 'admin/login', element: <AdminLogin /> },
|
||||
{ path: 'admin/dashboard', element: <AdminDashboard /> },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
91
juragankos-app/src/pages/Chat.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Button } from '../components/ui/button'
|
||||
import { Send, Bot, User } from 'lucide-react'
|
||||
|
||||
type Message = {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export default function Chat() {
|
||||
const [messages, setMessages] = useState<Message[]>([{
|
||||
id: crypto.randomUUID(),
|
||||
role: 'assistant',
|
||||
content: 'Halo! Saya asisten virtual untuk pengusaha kos. Ada yang bisa saya bantu hari ini?',
|
||||
timestamp: Date.now(),
|
||||
}])
|
||||
const [input, setInput] = useState('')
|
||||
const scrollerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
scrollerRef.current?.scrollTo({ top: scrollerRef.current.scrollHeight, behavior: 'smooth' })
|
||||
}, [messages.length])
|
||||
|
||||
const sendMessage = () => {
|
||||
const trimmed = input.trim()
|
||||
if (!trimmed) return
|
||||
const userMsg: Message = { id: crypto.randomUUID(), role: 'user', content: trimmed, timestamp: Date.now() }
|
||||
setMessages((m) => [...m, userMsg])
|
||||
setInput('')
|
||||
|
||||
// Simulate assistant reply
|
||||
setTimeout(() => {
|
||||
const reply: Message = {
|
||||
id: crypto.randomUUID(),
|
||||
role: 'assistant',
|
||||
content: `Terima kasih! Saya catat: "${trimmed}". Fitur backend akan menjawab secara cerdas nanti.`,
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
setMessages((m) => [...m, reply])
|
||||
}, 600)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-2xl h-[75vh] flex flex-col rounded-2xl border bg-background/45 shadow-2xl supports-[backdrop-filter]:backdrop-blur-xl backdrop-blur-xl">
|
||||
<div className="flex items-center gap-3 border-b bg-background/40 px-4 py-3">
|
||||
<div className="relative h-8 w-8 shrink-0 overflow-hidden rounded-full border bg-secondary">
|
||||
<Bot className="h-full w-full p-1" />
|
||||
<span className="absolute bottom-0 right-0 h-2 w-2 rounded-full bg-emerald-500 ring-2 ring-background" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium">JuraganKos Assistant</div>
|
||||
<div className="text-xs text-muted-foreground">Online • Siap membantu kos Anda</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref={scrollerRef} className="flex-1 space-y-4 overflow-auto p-4">
|
||||
{messages.map((msg) => {
|
||||
const isUser = msg.role === 'user'
|
||||
return (
|
||||
<div key={msg.id} className={`flex items-start gap-3 ${isUser ? 'flex-row-reverse' : ''}`}>
|
||||
<div className={`mt-1 rounded-full p-1 ${isUser ? 'bg-primary text-primary-foreground' : 'bg-secondary'}`}>
|
||||
{isUser ? <User className="h-4 w-4" /> : <Bot className="h-4 w-4" />}
|
||||
</div>
|
||||
<div className={`max-w-[75%] rounded-lg border px-3 py-2 text-sm ${isUser ? 'bg-primary text-primary-foreground border-transparent' : 'bg-background'}`}>
|
||||
{msg.content}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}}
|
||||
className="flex items-center gap-2 border-t p-3 bg-background/30"
|
||||
>
|
||||
<input
|
||||
className="flex-1 h-10 rounded-md border bg-background/60 px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring backdrop-blur-sm"
|
||||
placeholder="Tulis pesan..."
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
/>
|
||||
<Button type="submit" size="sm" disabled={!input.trim()}>
|
||||
<Send className="mr-2 h-4 w-4" /> Kirim
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
51
juragankos-app/src/pages/admin/Dashboard.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Button } from '../../components/ui/button'
|
||||
import { LogOut, MessageSquare, TrendingUp, Clock } from 'lucide-react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { isAuthenticated, logout } from '../../services/auth'
|
||||
|
||||
export default function AdminDashboard() {
|
||||
const navigate = useNavigate()
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated()) navigate('/admin/login')
|
||||
}, [navigate])
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold">Dashboard</h2>
|
||||
<Button variant="outline" onClick={() => { logout(); navigate('/admin/login') }}>
|
||||
<LogOut className="mr-2 h-4 w-4" /> Keluar
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<StatCard icon={<MessageSquare className="h-4 w-4" />} label="Total Percakapan" value="124" />
|
||||
<StatCard icon={<TrendingUp className="h-4 w-4" />} label="CSAT (mingguan)" value="92%" />
|
||||
<StatCard icon={<Clock className="h-4 w-4" />} label="Rata respon" value="1.8s" />
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-card p-4">
|
||||
<h3 className="mb-2 font-semibold">Percakapan Terbaru</h3>
|
||||
<ul className="divide-y text-sm">
|
||||
{['Booking kamar putri', 'Tanya harga bulanan', 'Kebijakan tamu', 'Ketersediaan parkir'].map((t, i) => (
|
||||
<li key={i} className="flex items-center justify-between py-2">
|
||||
<span>{t}</span>
|
||||
<span className="text-muted-foreground">{new Date().toLocaleDateString()}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function StatCard({ icon, label, value }: { icon: React.ReactNode; label: string; value: string }) {
|
||||
return (
|
||||
<div className="rounded-lg border bg-card p-4 shadow-sm">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">{icon}<span className="text-xs">{label}</span></div>
|
||||
<div className="mt-2 text-2xl font-semibold">{value}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
48
juragankos-app/src/pages/admin/Login.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '../../components/ui/button'
|
||||
import { LogIn } from 'lucide-react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { login } from '../../services/auth'
|
||||
|
||||
export default function AdminLogin() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const onSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
await login(email, password)
|
||||
navigate('/admin/dashboard')
|
||||
} catch (err: any) {
|
||||
setError(err?.message ?? 'Gagal login')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-sm rounded-lg border bg-card p-6 shadow-sm">
|
||||
<h2 className="mb-4 text-xl font-semibold">Admin Login</h2>
|
||||
<form className="space-y-3" onSubmit={onSubmit}>
|
||||
<div className="space-y-1">
|
||||
<label htmlFor="email" className="text-sm text-muted-foreground">Email</label>
|
||||
<input id="email" type="email" autoComplete="email" required className="h-10 w-full rounded-md border bg-background px-3 text-sm" value={email} onChange={(e)=>setEmail(e.target.value)} />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<label htmlFor="password" className="text-sm text-muted-foreground">Password</label>
|
||||
<input id="password" type="password" autoComplete="current-password" required className="h-10 w-full rounded-md border bg-background px-3 text-sm" value={password} onChange={(e)=>setPassword(e.target.value)} />
|
||||
</div>
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
<Button type="submit" className="w-full" disabled={loading}>
|
||||
<LogIn className="mr-2 h-4 w-4" /> {loading ? 'Memproses...' : 'Masuk'}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
21
juragankos-app/src/services/auth.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
const KEY = 'jk_admin_token'
|
||||
|
||||
export async function login(email: string, password: string) {
|
||||
await sleep(300)
|
||||
if (!email || !password) throw new Error('Email dan password wajib diisi')
|
||||
// Placeholder: accept any values. Replace with real API later.
|
||||
localStorage.setItem(KEY, 'ok')
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
localStorage.removeItem(KEY)
|
||||
}
|
||||
|
||||
export function isAuthenticated() {
|
||||
return !!localStorage.getItem(KEY)
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
|
||||
71
juragankos-app/tailwind.config.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: ['class'],
|
||||
content: ['./index.html', './src/**/*.{ts,tsx}'],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
screens: {
|
||||
'2xl': '1400px',
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
},
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--radix-accordion-content-height)' },
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--radix-accordion-content-height)' },
|
||||
to: { height: '0' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('tailwindcss-animate')],
|
||||
}
|
||||
17
juragankos-app/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
12
juragankos-app/tsconfig.node.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
1
juragankos-app/tsconfig.tsbuildinfo
Normal file
@@ -0,0 +1 @@
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/input.tsx","./src/lib/utils.ts","./src/pages/Chat.tsx","./src/pages/admin/Dashboard.tsx","./src/pages/admin/Login.tsx","./src/services/auth.ts"],"version":"5.9.2"}
|
||||
7
juragankos-app/vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
|
||||
BIN
kloowear-app/assets/img/about.jpg
Executable file
|
After Width: | Height: | Size: 78 KiB |
BIN
kloowear-app/assets/img/apple-touch-icon.png
Executable file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
kloowear-app/assets/img/clients/bri.png
Executable file
|
After Width: | Height: | Size: 127 KiB |
BIN
kloowear-app/assets/img/clients/client-1.webp
Executable file
|
After Width: | Height: | Size: 12 KiB |
BIN
kloowear-app/assets/img/clients/client-2-l.webp
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
kloowear-app/assets/img/clients/client-2.png
Executable file
|
After Width: | Height: | Size: 100 KiB |
BIN
kloowear-app/assets/img/clients/gb.png
Executable file
|
After Width: | Height: | Size: 58 KiB |
BIN
kloowear-app/assets/img/clients/ic.webp
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
kloowear-app/assets/img/clients/ll.png
Executable file
|
After Width: | Height: | Size: 462 KiB |
BIN
kloowear-app/assets/img/clients/mh.png
Executable file
|
After Width: | Height: | Size: 139 KiB |
BIN
kloowear-app/assets/img/clients/mi.png
Executable file
|
After Width: | Height: | Size: 55 KiB |
BIN
kloowear-app/assets/img/clients/omo.png
Executable file
|
After Width: | Height: | Size: 38 KiB |
BIN
kloowear-app/assets/img/clients/uniska.png
Executable file
|
After Width: | Height: | Size: 302 KiB |
BIN
kloowear-app/assets/img/cta-bg-bak.jpg
Executable file
|
After Width: | Height: | Size: 334 KiB |
BIN
kloowear-app/assets/img/cta-bg.jpg
Executable file
|
After Width: | Height: | Size: 58 KiB |
BIN
kloowear-app/assets/img/favicon.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
kloowear-app/assets/img/hero-bg.jpg
Executable file
|
After Width: | Height: | Size: 53 KiB |
BIN
kloowear-app/assets/img/logo.png
Executable file
|
After Width: | Height: | Size: 36 KiB |
BIN
kloowear-app/assets/img/portfolio/IMG_0025 (2).JPG
Executable file
|
After Width: | Height: | Size: 5.6 MiB |
BIN
kloowear-app/assets/img/portfolio/IMG_0569 (2).JPG
Executable file
|
After Width: | Height: | Size: 4.5 MiB |
BIN
kloowear-app/assets/img/portfolio/IMG_0603.JPG
Executable file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
kloowear-app/assets/img/portfolio/IMG_1056.JPG
Executable file
|
After Width: | Height: | Size: 4.1 MiB |
BIN
kloowear-app/assets/img/portfolio/IMG_2933.JPG
Executable file
|
After Width: | Height: | Size: 5.5 MiB |
BIN
kloowear-app/assets/img/portfolio/IMG_6232.JPG
Executable file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
kloowear-app/assets/img/portfolio/Sekenples Paracord 137.jpg
Executable file
|
After Width: | Height: | Size: 266 KiB |
BIN
kloowear-app/assets/img/portfolio/app-1.jpg
Executable file
|
After Width: | Height: | Size: 66 KiB |
BIN
kloowear-app/assets/img/portfolio/app-2.jpg
Executable file
|
After Width: | Height: | Size: 72 KiB |
BIN
kloowear-app/assets/img/portfolio/app-3.jpg
Executable file
|
After Width: | Height: | Size: 41 KiB |
BIN
kloowear-app/assets/img/portfolio/b1.jpeg
Executable file
|
After Width: | Height: | Size: 87 KiB |
BIN
kloowear-app/assets/img/portfolio/b2.jpeg
Executable file
|
After Width: | Height: | Size: 57 KiB |
BIN
kloowear-app/assets/img/portfolio/b2.jpg
Executable file
|
After Width: | Height: | Size: 347 KiB |
BIN
kloowear-app/assets/img/portfolio/b3.jpeg
Executable file
|
After Width: | Height: | Size: 43 KiB |
BIN
kloowear-app/assets/img/portfolio/b4.JPG
Executable file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
kloowear-app/assets/img/portfolio/b5.JPG
Executable file
|
After Width: | Height: | Size: 675 KiB |
BIN
kloowear-app/assets/img/portfolio/b6.JPG
Executable file
|
After Width: | Height: | Size: 913 KiB |
BIN
kloowear-app/assets/img/portfolio/b7.JPG
Executable file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
kloowear-app/assets/img/portfolio/books-1.jpg
Executable file
|
After Width: | Height: | Size: 78 KiB |
BIN
kloowear-app/assets/img/portfolio/books-2.jpg
Executable file
|
After Width: | Height: | Size: 59 KiB |
BIN
kloowear-app/assets/img/portfolio/books-3.jpg
Executable file
|
After Width: | Height: | Size: 89 KiB |
BIN
kloowear-app/assets/img/portfolio/branding-1.jpg
Executable file
|
After Width: | Height: | Size: 25 KiB |