diff --git a/bun.lockb b/bun.lockb index 7d65a042..347d9ab5 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 80a7f3a7..a23894c8 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@mantine/charts": "^7.17.1", "@mantine/core": "^7.17.4", "@mantine/dates": "^8.1.0", - "@mantine/dropzone": "^7.17.0", + "@mantine/dropzone": "^8.1.1", "@mantine/form": "^8.1.0", "@mantine/hooks": "^7.17.4", "@mantine/tiptap": "^7.17.4", diff --git a/public/bungapudak.png b/public/bungapudak.png new file mode 100644 index 00000000..e35e2875 Binary files /dev/null and b/public/bungapudak.png differ diff --git a/public/klimakstari.png b/public/klimakstari.png new file mode 100644 index 00000000..e668ffdf Binary files /dev/null and b/public/klimakstari.png differ diff --git a/public/pohonpudak.png b/public/pohonpudak.png new file mode 100644 index 00000000..0782bbc7 Binary files /dev/null and b/public/pohonpudak.png differ diff --git a/uploads/image/pudak-icon.png b/public/pudak-icon.png similarity index 100% rename from uploads/image/pudak-icon.png rename to public/pudak-icon.png diff --git a/public/tarisekar.png b/public/tarisekar.png new file mode 100644 index 00000000..77dcc5d3 Binary files /dev/null and b/public/tarisekar.png differ diff --git a/src/app/admin/(dashboard)/_state/desa/profile.ts b/src/app/admin/(dashboard)/_state/desa/profile.ts index 321ab96d..d00e50e9 100644 --- a/src/app/admin/(dashboard)/_state/desa/profile.ts +++ b/src/app/admin/(dashboard)/_state/desa/profile.ts @@ -38,13 +38,13 @@ const sejarahDesa = proxy({ this.error = null; try { - const response = await fetch(`/api/desa/profile/sejarah-desa/${id}`); - + const response = await fetch(`/api/desa/profile/sejarah/${id}`); + if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); - + if (result.success) { this.data = result.data; return result.data; @@ -91,64 +91,69 @@ const sejarahDesa = proxy({ }, async submit() { - // Validate form - const validation = sejarahDesaForm.safeParse(this.form); - - if (!validation.success) { - const errors = validation.error.issues - .map((issue) => `${issue.path.join(".")}: ${issue.message}`) - .join(", "); - toast.error(`Form tidak valid: ${errors}`); - return false; + // Validate form + const validation = sejarahDesaForm.safeParse(this.form); + + if (!validation.success) { + const errors = validation.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return false; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch( + `/api/desa/profile/sejarah/${this.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), } - - this.loading = true; - this.error = null; - - try { - const response = await fetch(`/api/desa/profile/sejarah-desa/${this.id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(this.form), - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.message || `HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - if (result.success) { - toast.success("Berhasil update profile"); - // Refresh profile data - await sejarahDesa.findUnique.load(this.id); - return true; - } else { - throw new Error(result.message || "Gagal update profile"); - } - } catch (error) { - const errorMessage = (error as Error).message; - this.error = errorMessage; - console.error("Update profile error:", errorMessage); - toast.error("Terjadi kesalahan saat update profile"); - return false; - } finally { - this.loading = false; - } - }, - - // Reset form - reset() { - this.id = ""; - this.form = { ...sejarahDesaDefaultForm }; - this.error = null; - this.loading = false; - this.isReadOnly = false; + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); } - } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update profile"); + // Refresh profile data + await sejarahDesa.findUnique.load(this.id); + return true; + } else { + throw new Error(result.message || "Gagal update profile"); + } + } catch (error) { + const errorMessage = (error as Error).message; + this.error = errorMessage; + console.error("Update profile error:", errorMessage); + toast.error("Terjadi kesalahan saat update profile"); + return false; + } finally { + this.loading = false; + } + }, + + // Reset form + reset() { + this.id = ""; + this.form = { ...sejarahDesaDefaultForm }; + this.error = null; + this.loading = false; + this.isReadOnly = false; + }, + }, }); // ========================================= VISI MISI DESA ========================================= // @@ -187,12 +192,12 @@ const visiMisiDesa = proxy({ try { const response = await fetch(`/api/desa/profile/visi-misi/${id}`); - + if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); - + if (result.success) { this.data = result.data; return result.data; @@ -239,64 +244,66 @@ const visiMisiDesa = proxy({ }, async submit() { - // Validate form - const validation = visiMisiDesaForm.safeParse(this.form); - - if (!validation.success) { - const errors = validation.error.issues - .map((issue) => `${issue.path.join(".")}: ${issue.message}`) - .join(", "); - toast.error(`Form tidak valid: ${errors}`); - return false; - } - - this.loading = true; - this.error = null; - - try { - const response = await fetch(`/api/desa/profile/visi-misi/${this.id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(this.form), - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.message || `HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - if (result.success) { - toast.success("Berhasil update visi misi desa"); - // Refresh profile data - await visiMisiDesa.findUnique.load(this.id); - return true; - } else { - throw new Error(result.message || "Gagal update visi misi desa"); - } - } catch (error) { - const errorMessage = (error as Error).message; - this.error = errorMessage; - console.error("Update visi misi desa error:", errorMessage); - toast.error("Terjadi kesalahan saat update visi misi desa"); - return false; - } finally { - this.loading = false; - } - }, - - // Reset form - reset() { - this.id = ""; - this.form = { ...visiMisiDesaDefaultForm }; - this.error = null; - this.loading = false; - this.isReadOnly = false; + // Validate form + const validation = visiMisiDesaForm.safeParse(this.form); + + if (!validation.success) { + const errors = validation.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return false; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/desa/profile/visi-misi/${this.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); } - } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update visi misi desa"); + // Refresh profile data + await visiMisiDesa.findUnique.load(this.id); + return true; + } else { + throw new Error(result.message || "Gagal update visi misi desa"); + } + } catch (error) { + const errorMessage = (error as Error).message; + this.error = errorMessage; + console.error("Update visi misi desa error:", errorMessage); + toast.error("Terjadi kesalahan saat update visi misi desa"); + return false; + } finally { + this.loading = false; + } + }, + + // Reset form + reset() { + this.id = ""; + this.form = { ...visiMisiDesaDefaultForm }; + this.error = null; + this.loading = false; + this.isReadOnly = false; + }, + }, }); // ========================================= LAMBANG DESA ========================================= // @@ -334,13 +341,13 @@ const lambangDesa = proxy({ this.error = null; try { - const response = await fetch(`/api/desa/profile/lambang-desa/${id}`); - + const response = await fetch(`/api/desa/profile/lambang/${id}`); + if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); - + if (result.success) { this.data = result.data; return result.data; @@ -387,64 +394,69 @@ const lambangDesa = proxy({ }, async submit() { - // Validate form - const validation = lambangDesaForm.safeParse(this.form); - - if (!validation.success) { - const errors = validation.error.issues - .map((issue) => `${issue.path.join(".")}: ${issue.message}`) - .join(", "); - toast.error(`Form tidak valid: ${errors}`); - return false; + // Validate form + const validation = lambangDesaForm.safeParse(this.form); + + if (!validation.success) { + const errors = validation.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return false; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch( + `/api/desa/profile/lambang/${this.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), } - - this.loading = true; - this.error = null; - - try { - const response = await fetch(`/api/desa/profile/lambang-desa/${this.id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(this.form), - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.message || `HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - if (result.success) { - toast.success("Berhasil update lambang desa"); - // Refresh profile data - await lambangDesa.findUnique.load(this.id); - return true; - } else { - throw new Error(result.message || "Gagal update lambang desa"); - } - } catch (error) { - const errorMessage = (error as Error).message; - this.error = errorMessage; - console.error("Update lambang desa error:", errorMessage); - toast.error("Terjadi kesalahan saat update lambang desa"); - return false; - } finally { - this.loading = false; - } - }, - - // Reset form - reset() { - this.id = ""; - this.form = { ...lambangDesaDefaultForm }; - this.error = null; - this.loading = false; - this.isReadOnly = false; + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); } - } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update lambang desa"); + // Refresh profile data + await lambangDesa.findUnique.load(this.id); + return true; + } else { + throw new Error(result.message || "Gagal update lambang desa"); + } + } catch (error) { + const errorMessage = (error as Error).message; + this.error = errorMessage; + console.error("Update lambang desa error:", errorMessage); + toast.error("Terjadi kesalahan saat update lambang desa"); + return false; + } finally { + this.loading = false; + } + }, + + // Reset form + reset() { + this.id = ""; + this.form = { ...lambangDesaDefaultForm }; + this.error = null; + this.loading = false; + this.isReadOnly = false; + }, + }, }); // ========================================= MASKOT DESA ========================================= // @@ -502,7 +514,7 @@ const maskotDesa = proxy({ this.error = null; try { - const response = await fetch(`/api/desa/profile/maskot-desa/${id}`); + const response = await fetch(`/api/desa/profile/maskot/${id}`); const result = await response.json(); if (response.ok && result.success) { @@ -577,7 +589,7 @@ const maskotDesa = proxy({ try { const response = await fetch( - `/api/desa/profile/maskot-desa/${this.id}`, + `/api/desa/profile/maskot/${this.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, @@ -806,11 +818,12 @@ const profilPerbekel = proxy({ }, }); -const stateProfileDesa = { +const stateProfileDesa = proxy({ + lambangDesa, maskotDesa, profilPerbekel, visiMisiDesa, sejarahDesa, -}; +}); export default stateProfileDesa; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/lambang_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/lambang_desa/page.tsx deleted file mode 100644 index ca48c832..00000000 --- a/src/app/admin/(dashboard)/desa/profile/edit/lambang_desa/page.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import colors from '@/con/colors'; -import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, Text } from '@mantine/core'; -import React from 'react'; -import { DesaEditor } from '../../../_com/desaEditor'; - -function LambangDesa() { - return ( - - - - - - Lambang Desa - Deskripsi Lambang Desa - - - - - - - - - - - List Lambang Desa - - - - - - ); -} - -export default LambangDesa; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/layout.tsx b/src/app/admin/(dashboard)/desa/profile/edit/layout.tsx deleted file mode 100644 index 3e19b208..00000000 --- a/src/app/admin/(dashboard)/desa/profile/edit/layout.tsx +++ /dev/null @@ -1,13 +0,0 @@ -'use client' - -import LayoutTabsEdit from "../_lib/layoutTabsEdit" - -function Layout({children}: {children: React.ReactNode}) { - return ( - - {children} - - ); -} - -export default Layout; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/maskot_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/maskot_desa/page.tsx deleted file mode 100644 index 9fc4973b..00000000 --- a/src/app/admin/(dashboard)/desa/profile/edit/maskot_desa/page.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import colors from '@/con/colors'; -import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, Text } from '@mantine/core'; -import React from 'react'; -import { DesaEditor } from '../../../_com/desaEditor'; - -function MaskotDesa() { - return ( - - - - - - Maskot Desa - Deskripsi Maskot Desa - - - - - - - - - - - List Maskot Desa - - - - - - ); -} - -export default MaskotDesa; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/create.tsx b/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/create.tsx deleted file mode 100644 index 632194fe..00000000 --- a/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/create.tsx +++ /dev/null @@ -1,34 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core'; - - - -function SejarahDesa() { - - - return ( - - - - - - Sejarah Desa - Deskripsi Sejarah Desa - - - - - - - - - ); -} - -export default SejarahDesa; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/page.tsx deleted file mode 100644 index ae48be5a..00000000 --- a/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/page.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import colors from '@/con/colors'; -import { Box, Paper, Stack } from '@mantine/core'; - -function Page() { - return ( - - - - - - {/* - - - - - */} - - - - - - ); -} - -export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/visi_misi_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/visi_misi_desa/page.tsx deleted file mode 100644 index f0ccf437..00000000 --- a/src/app/admin/(dashboard)/desa/profile/edit/visi_misi_desa/page.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core'; -import { DesaEditor } from '../../../_com/desaEditor'; - -function VisiMisiDesa() { - return ( - - - - - - Visi Desa - Deskripsi Visi Desa - - - - - - - - - - - List Visi Desa - - - - - - - - - Misi Desa - Deskripsi Misi Desa - - - - - - - - - - - List Misi Desa - - - - - - ); -} - -export default VisiMisiDesa; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx new file mode 100644 index 00000000..d46a5a69 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx @@ -0,0 +1,126 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; +import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'; +import colors from '@/con/colors'; +import { Box, Button, Center, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function Page() { + const lambangState = useProxy(stateProfileDesa.lambangDesa) + const router = useRouter() + const params = useParams() + const [isSubmitting, setIsSubmitting] = useState(false); + + useEffect(() => { + const loadData = async () => { + const id = params?.id as string; + if (!id) { + toast.error("ID tidak valid"); + router.push("/admin/desa/profile/profile-desa"); + return; + } + + const data = await lambangState.findUnique.load(id); + if (data) { + lambangState.update.initialize(data); + } + }; + + loadData(); + + return () => { + lambangState.update.reset(); + lambangState.findUnique.reset(); // opsional: reset juga data lama + }; + }, [params?.id, router]); + + + const handleSubmit = async () => { + if (isSubmitting || !lambangState.update.form.judul.trim()) { + toast.error("Judul wajib diisi"); + return; + } + setIsSubmitting(true) + try { + const success = await lambangState.update.submit() + if (success) { + toast.success("Data berhasil disimpan"); + router.push("/admin/desa/profile/profile-desa"); + } + } catch (error) { + console.error("Error update lambang desa:", error); + toast.error("Terjadi kesalahan saat update lambang desa"); + } finally { + setIsSubmitting(false); + } + } + + const handleBack = () => { + router.back() + } + + if ( + lambangState.findUnique.loading || + !lambangState.findUnique.data || + lambangState.update.loading + ) { + return ( + +
+ Memuat data... +
+
+ ); + } + return ( + + + + + + + + + + + Edit Lambang Desa + Judul} + placeholder="Judul" + value={lambangState.update.form.judul} + onChange={(e) => lambangState.update.form.judul = e.currentTarget.value} + error={!lambangState.update.form.judul && "Judul wajib diisi"} + /> + + Deskripsi + lambangState.update.form.deskripsi = val} + /> + + + + + + + + + + + + ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx new file mode 100644 index 00000000..116db753 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx @@ -0,0 +1,244 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; +import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'; +import colors from '@/con/colors'; +import ApiFetch from '@/lib/api-fetch'; +import { Box, Button, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function Page() { + const maskotState = useProxy(stateProfileDesa.maskotDesa) + const router = useRouter() + const params = useParams() + + const [images, setImages] = useState< + Array<{ file: File; preview: string; label: string }> + >([]); + + const [formData, setFormData] = useState({ + judul: maskotState.update.form.judul || '', + deskripsi: maskotState.update.form.deskripsi || '', + images: [] as Array<{ label: string; imageId: string }> + }) + + useEffect(() => { + const loadData = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await maskotState.findUnique.load(id); + if (data) { + // 🔥 INI YANG KURANG! + maskotState.update.initialize(data); + + setFormData({ + judul: data.judul || '', + deskripsi: data.deskripsi || '', + images: (data.images || []).map((img: any) => ({ + label: img.label, + imageId: img.image?.id ?? '', + })), + }); + + if (data?.images?.length > 0 && data.images[0].image?.link) { + setImages(data.images.map((img: any) => ({ + file: null, + preview: img.image.link, + label: img.label, + }))); + } + } + } catch (error) { + console.error("Error loading berita:", error); + toast.error("Gagal memuat data berita"); + } + }; + + loadData(); + }, [params?.id]); + + + const handleBack = () => { + router.back() + } + + const handleSubmit = async () => { + try { + const uploadedImages = []; + + // Upload semua gambar baru + for (const img of images) { + if (!img.file || !(img.file instanceof File)) { + toast.error("File tidak valid untuk di-upload"); + continue; // atau return kalau kamu mau hentikan semua + } + + const res = await ApiFetch.api.fileStorage.create.post({ + file: img.file, + name: img.file.name, + }); + + const uploaded = res.data?.data; + if (!uploaded?.id) { + toast.error("Gagal upload salah satu gambar"); + return; + } + + uploadedImages.push({ + imageId: uploaded.id, + label: img.label || 'main', + }); + } + + // Update ke global state + maskotState.update.updateField("judul", formData.judul); + maskotState.update.updateField("deskripsi", formData.deskripsi); + maskotState.update.updateField("images", uploadedImages); + + const success = await maskotState.update.submit(); + + if (success) { + toast.success("Maskot berhasil diperbarui!"); + router.push("/admin/desa/profile/profile-desa"); + } + + } catch (error) { + console.error("Error update maskot:", error); + toast.error("Gagal update maskot"); + } + }; + + return ( + + + + + + + + + + + Edit Maskot Desa + Judul} + placeholder="Masukkan judul" + value={formData.judul} + onChange={(val) => setFormData({ ...formData, judul: val.currentTarget.value })} + /> + + Deskripsi + setFormData({ ...formData, deskripsi: val })} + /> + + + Gambar + + { + const newImages = files.map((file) => ({ + file, + preview: URL.createObjectURL(file), + label: '', + })); + setImages((prev) => [...prev, ...newImages]); + }} + > + + + + + + + + + + + +
+ + Drag images here or click to select files + + + Attach as many files as you like, each file should not exceed 5mb + +
+
+
+
+
+ + {images.map((img, index) => ( + + + + + + + {`Preview + { + const updated = [...images]; + updated[index].label = e.currentTarget.value; + setImages(updated); + }} + /> + + + + ))} + + + + +
+
+
+
+
+
+
+ ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx new file mode 100644 index 00000000..c108884e --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx @@ -0,0 +1,126 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; +import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'; +import colors from '@/con/colors'; +import { Box, Button, Center, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function Page() { + const sejarahState = useProxy(stateProfileDesa.sejarahDesa) + const router = useRouter() + const params = useParams() + const [isSubmitting, setIsSubmitting] = useState(false); + + useEffect(() => { + const loadData = async () => { + const id = params?.id as string; + if (!id) { + toast.error("ID tidak valid"); + router.push("/admin/desa/profile/profile-desa"); + return; + } + + const data = await sejarahState.findUnique.load(id); + if (data) { + sejarahState.update.initialize(data); + } + }; + + loadData(); + + return () => { + sejarahState.update.reset(); + sejarahState.findUnique.reset(); // opsional: reset juga data lama + }; + }, [params?.id, router]); + + + const handleSubmit = async () => { + if (isSubmitting || !sejarahState.update.form.judul.trim()) { + toast.error("Judul wajib diisi"); + return; + } + setIsSubmitting(true) + try { + const success = await sejarahState.update.submit() + if (success) { + toast.success("Data berhasil disimpan"); + router.push("/admin/desa/profile/profile-desa"); + } + } catch (error) { + console.error("Error update sejarah desa:", error); + toast.error("Terjadi kesalahan saat update sejarah desa"); + } finally { + setIsSubmitting(false); + } + } + + const handleBack = () => { + router.back() + } + + if ( + sejarahState.findUnique.loading || + !sejarahState.findUnique.data || + sejarahState.update.loading + ) { + return ( + +
+ Memuat data... +
+
+ ); + } + return ( + + + + + + + + + + + Edit Sejarah Desa + Judul} + placeholder="Judul" + value={sejarahState.update.form.judul} + onChange={(e) => sejarahState.update.form.judul = e.currentTarget.value} + error={!sejarahState.update.form.judul && "Judul wajib diisi"} + /> + + Deskripsi + sejarahState.update.form.deskripsi = val} + /> + + + + + + + + + + + + ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/visi_misi_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/visi_misi_desa/page.tsx new file mode 100644 index 00000000..ccfe2ea5 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/visi_misi_desa/page.tsx @@ -0,0 +1,124 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; +import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'; +import colors from '@/con/colors'; +import { Box, Button, Center, Group, Paper, Stack, Text, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function Page() { + const visiMisiState = useProxy(stateProfileDesa.visiMisiDesa) + const router = useRouter() + const params = useParams() + const [isSubmitting, setIsSubmitting] = useState(false); + + useEffect(() => { + const loadData = async () => { + const id = params?.id as string; + if (!id) { + toast.error("ID tidak valid"); + router.push("/admin/desa/profile/profile-desa"); + return; + } + + const data = await visiMisiState.findUnique.load(id); + if (data) { + visiMisiState.update.initialize(data); + } + }; + + loadData(); + + return () => { + visiMisiState.update.reset(); + visiMisiState.findUnique.reset(); // opsional: reset juga data lama + }; + }, [params?.id, router]); + + + const handleSubmit = async () => { + if (isSubmitting || !visiMisiState.update.form.visi.trim()) { + toast.error("Visi wajib diisi"); + return; + } + setIsSubmitting(true) + try { + const success = await visiMisiState.update.submit() + if (success) { + toast.success("Data berhasil disimpan"); + router.push("/admin/desa/profile/profile-desa"); + } + } catch (error) { + console.error("Error update sejarah desa:", error); + toast.error("Terjadi kesalahan saat update sejarah desa"); + } finally { + setIsSubmitting(false); + } + } + + const handleBack = () => { + router.back() + } + + if ( + visiMisiState.findUnique.loading || + !visiMisiState.findUnique.data || + visiMisiState.update.loading + ) { + return ( + +
+ Memuat data... +
+
+ ); + } + return ( + + + + + + + + + + + Edit Visi Misi Desa + Visi + visiMisiState.update.form.visi = val} + /> + + Misi + visiMisiState.update.form.misi = val} + /> + + + + + + + + + + + + ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx index b7574e55..b52e4053 100644 --- a/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx +++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx @@ -1,131 +1,168 @@ 'use client' + import colors from '@/con/colors'; -import { Paper, Stack, Grid, GridCol, Title, Button, Box, Text, Center, Image, SimpleGrid } from '@mantine/core'; +import { Box, Button, Card, Center, Grid, GridCol, Group, Image, Paper, Stack, Text, Title } from '@mantine/core'; +import { useSnapshot } from 'valtio'; +import stateProfileDesa from '../../../_state/desa/profile'; +import { useEffect } from 'react'; import { IconEdit } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import React from 'react'; function Page() { - const router = useRouter() + const router = useRouter(); + const snap = useSnapshot(stateProfileDesa); + + // Panggil load data sekali saat komponen mount + useEffect(() => { + stateProfileDesa.sejarahDesa.findUnique.load("1"); + stateProfileDesa.visiMisiDesa.findUnique.load("1"); + stateProfileDesa.lambangDesa.findUnique.load("1"); + stateProfileDesa.maskotDesa.findUnique.load("1"); + }, []); + + const sejarah = snap.sejarahDesa.findUnique.data; + const visiMisi = snap.visiMisiDesa.findUnique.data; + const lambang = snap.lambangDesa.findUnique.data; + const maskot = snap.maskotDesa.findUnique.data; + return ( - - - - - Preview Profile Desa - - - - - + + + Preview Profile Desa {/* Sejarah Desa */} - - - - -
- -
- Sejarah Desa -
- - - Test - + {sejarah && ( + + + + + + Preview Sejarah Desa + + + + + + +
+ +
+ {sejarah.judul} +
+ + +
-
-
-
+
+ + )} + {/* Visi Misi Desa */} - - - - -
- -
-
- - Visi Desa - - Test - + {visiMisi && ( + + + + + + Preview Visi Misi Desa + + + + + + +
+ +
+ Visi Misi Desa +
+ + Visi Desa + + Misi Desa + +
- - Misi Desa - - Test - - -
-
-
+
+ + )} + {/* Lambang Desa */} - - - - -
- -
- Lambang Desa -
- - - Test - + {lambang && ( + + + + + + Preview Lambang Desa + + + + + + +
+ +
+ Lambang Desa +
+ + +
-
-
-
+ + + )} + {/* Maskot Desa */} - - - - -
- -
- Maskot Desa -
- - - Test - + {maskot && ( + + + + + + Preview Maskot Desa + + + + + + +
+ +
+ Maskot Desa +
+ + + + {maskot.images.map((img, index) => ( + + {img.label} + {img.label} + + ))} + +
- -
- - - - Pohon Pudak - - -
-
- - - - Bunga Pudak - - -
-
- - - Test - - -
-
-
+ + + )}
); diff --git a/src/app/admin/(dashboard)/desa/profile/edit/profile_perbekel/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel/[id]/page.tsx similarity index 100% rename from src/app/admin/(dashboard)/desa/profile/edit/profile_perbekel/page.tsx rename to src/app/admin/(dashboard)/desa/profile/profile-perbekel/[id]/page.tsx diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts index 878d9f8c..f8f708f0 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts @@ -3,10 +3,10 @@ import maskotDesaFindById from "./find-by-id"; import Elysia, { t } from "elysia"; const MaskotDesa = new Elysia({ - prefix: "/maskot", - tags: ["Desa/Profile"], + prefix: "/maskot", + tags: ["Desa/Profile"], }) -.get("/:id", async (context) => { + .get("/:id", async (context) => { const response = await maskotDesaFindById(new Request(context.request)); return response; }) @@ -18,7 +18,8 @@ const MaskotDesa = new Elysia({ }, { body: t.Object({ - maskot: t.String(), + judul: t.String(), + deskripsi: t.String(), images: t.Array( t.Object({ imageId: t.String(), @@ -27,5 +28,5 @@ const MaskotDesa = new Elysia({ ), }), } - ) -export default MaskotDesa; \ No newline at end of file + ); +export default MaskotDesa; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e37ccf44..f747e5df 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,6 +11,7 @@ import '@mantine/charts/styles.css'; import '@mantine/dates/styles.css'; import '@mantine/tiptap/styles.css'; + import LoadDataFirstClient from "@/app/darmasaba/_com/LoadDataFirstClient"; import { ColorSchemeScript,