Compare commits
2 Commits
nico/13-ju
...
nico/16-ju
| Author | SHA1 | Date | |
|---|---|---|---|
| f7437708c0 | |||
| 7bf5ee69d5 |
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "Pelayanan Penduduk Non-Permanent",
|
||||||
|
"deskripsi": "<p>Surat Keterangan Penduduk Non-Permanent adalah dokumen yang dikeluarkan oleh pihak berwenang untuk memberikan keterangan bahwa seseorang atau kelompok orang memiliki status penduduk non-permanent di suatu wilayah. Dokumen ini biasanya digunakan untuk keperluan administratif atau legal, seperti mendapatkan akses ke layanan kesehatan, pendidikan, atau pelayanan publik lainnya.</p>"
|
||||||
|
}
|
||||||
|
]
|
||||||
8
prisma/data/desa/layanan/pelayananPerizinanBerusaha.json
Normal file
8
prisma/data/desa/layanan/pelayananPerizinanBerusaha.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "Pelayanan Perizinan Berusaha Berbasis Risiko Melalui Sistem ONLINE SINGLE SUBMISSION (OSS)",
|
||||||
|
"deskripsi": "<p>Penyelenggaraan Perizinan Berusaha Berbasis Risiko melalui Sistem Online Single Submission (OSS) merupakan pelaksanaan Undang-Undang Nomor 11 Tahun 2020 Tentang Cipta Kerja. OSS Berbasis Risiko wajib digunakan oleh Pelaku Usaha, Kementerian/Lembaga, Pemerintah Daerah, Administrator Kawasan Ekonomi Khusus (KEK), dan Badan Pengusahaan Kawasan Perdagangan Bebas Pelabuhan Bebas (KPBPB).Berdasarkan Peraturan Pemerintah Nomor 5 Tahun 2021 terdapat 1.702 kegiatan usaha yang terdiri atas 1.349 Klasifikasi Baku Lapangan Usaha Indonesia (KBLI) yang sudah diimplementasikan dalam Sistem OSS Berbasis Risiko.</p>",
|
||||||
|
"link" : "https://oss.go.id/"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -69,6 +69,8 @@ model FileStorage {
|
|||||||
GalleryFoto GalleryFoto[]
|
GalleryFoto GalleryFoto[]
|
||||||
|
|
||||||
PelayananSuratKeterangan PelayananSuratKeterangan[]
|
PelayananSuratKeterangan PelayananSuratKeterangan[]
|
||||||
|
|
||||||
|
Penghargaan Penghargaan[]
|
||||||
}
|
}
|
||||||
|
|
||||||
//========================================= MENU PPID ========================================= //
|
//========================================= MENU PPID ========================================= //
|
||||||
@@ -365,27 +367,62 @@ model GalleryVideo {
|
|||||||
|
|
||||||
// ========================================= LAYANAN DESA ========================================= //
|
// ========================================= LAYANAN DESA ========================================= //
|
||||||
model PelayananSuratKeterangan {
|
model PelayananSuratKeterangan {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
deskripsi String @db.Text
|
deskripsi String @db.Text
|
||||||
image FileStorage @relation(fields: [imageId], references: [id])
|
image FileStorage @relation(fields: [imageId], references: [id])
|
||||||
imageId String
|
imageId String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
model PelayananTelunjukSaktiDesa {
|
model PelayananTelunjukSaktiDesa {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
deskripsi String @db.Text
|
deskripsi String @db.Text
|
||||||
|
link String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model PelayananPerizinanBerusaha {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
deskripsi String @db.Text
|
||||||
|
link String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
model PelayananPendudukNonPermanen {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
deskripsi String @db.Text
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PENGHARGAAN ========================================= //
|
||||||
|
model Penghargaan {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
juara String
|
||||||
|
deskripsi String @db.Text
|
||||||
|
image FileStorage @relation(fields: [imageId], references: [id])
|
||||||
|
imageId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================= MENU KESEHATAN ========================================= //
|
// ========================================= MENU KESEHATAN ========================================= //
|
||||||
// ========================================= DATA KESEHATAN WARGA ========================================= //
|
// ========================================= DATA KESEHATAN WARGA ========================================= //
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
|
|||||||
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
|
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
|
||||||
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
|
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
|
||||||
import strukturPPID from "./data/ppid/struktur-ppid/strukturPPID.json";
|
import strukturPPID from "./data/ppid/struktur-ppid/strukturPPID.json";
|
||||||
|
import pelayananPerizinanBerusaha from "./data/desa/layanan/pelayananPerizinanBerusaha.json";
|
||||||
|
import pelayananPendudukNonPermanen from "./data/desa/layanan/pelayanaPendudukNonPermanen.json";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const l of layanan) {
|
for (const l of layanan) {
|
||||||
@@ -28,6 +30,46 @@ import strukturPPID from "./data/ppid/struktur-ppid/strukturPPID.json";
|
|||||||
|
|
||||||
console.log("layanan success ...");
|
console.log("layanan success ...");
|
||||||
|
|
||||||
|
for (const l of pelayananPerizinanBerusaha) {
|
||||||
|
await prisma.pelayananPerizinanBerusaha.upsert({
|
||||||
|
where: {
|
||||||
|
id: l.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: l.name,
|
||||||
|
deskripsi: l.deskripsi,
|
||||||
|
link: l.link,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: l.id,
|
||||||
|
name: l.name,
|
||||||
|
deskripsi: l.deskripsi,
|
||||||
|
link: l.link,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("pelayanan perizinan berusaha success ...");
|
||||||
|
|
||||||
|
for (const l of pelayananPendudukNonPermanen) {
|
||||||
|
await prisma.pelayananPendudukNonPermanen.upsert({
|
||||||
|
where: {
|
||||||
|
id: l.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: l.name,
|
||||||
|
deskripsi: l.deskripsi,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: l.id,
|
||||||
|
name: l.name,
|
||||||
|
deskripsi: l.deskripsi,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("pelayanan perizinan berusaha success ...");
|
||||||
|
|
||||||
for (const s of strukturPPID) {
|
for (const s of strukturPPID) {
|
||||||
await prisma.strukturPPID.upsert({
|
await prisma.strukturPPID.upsert({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -16,6 +16,60 @@ const suratKeteranganForm = {
|
|||||||
imageId: "",
|
imageId: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const telunjukSaktiDesaForm = {
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
link: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const templateTelunjukSaktiDesaForm = z.object({
|
||||||
|
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||||
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const templatePelayananPerizinanBerusaha = z.object({
|
||||||
|
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||||
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
|
link: z.string().min(3, "Link minimal 3 karakter"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type pelayananPerizinanBerusahaForm =
|
||||||
|
Prisma.PelayananPerizinanBerusahaGetPayload<{
|
||||||
|
select: {
|
||||||
|
id: true;
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
link: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const pelayananPerizinanBerusahaForm = {
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
link: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const templatePelayananPendudukNonPermanen = z.object({
|
||||||
|
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||||
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type pelayananPendudukNonPermanenForm =
|
||||||
|
Prisma.PelayananPendudukNonPermanenGetPayload<{
|
||||||
|
select: {
|
||||||
|
id: true;
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const pelayananPendudukNonPermanenForm = {
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const suratKeterangan = proxy({
|
const suratKeterangan = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: { ...suratKeteranganForm },
|
form: { ...suratKeteranganForm },
|
||||||
@@ -173,20 +227,25 @@ const suratKeterangan = proxy({
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
suratKeterangan.edit.loading = true;
|
suratKeterangan.edit.loading = true;
|
||||||
const response = await fetch(`/api/desa/layanan/pelayanansuratketerangan/${this.id}`, {
|
const response = await fetch(
|
||||||
method: "PUT",
|
`/api/desa/layanan/pelayanansuratketerangan/${this.id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "PUT",
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
"Content-Type": "application/json",
|
||||||
name: this.form.name,
|
},
|
||||||
deskripsi: this.form.deskripsi,
|
body: JSON.stringify({
|
||||||
imageId: this.form.imageId,
|
name: this.form.name,
|
||||||
}),
|
deskripsi: this.form.deskripsi,
|
||||||
});
|
imageId: this.form.imageId,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
throw new Error(
|
||||||
|
errorData.message || `HTTP error! status: ${response.status}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -194,11 +253,17 @@ const suratKeterangan = proxy({
|
|||||||
await suratKeterangan.findMany.load(); // refresh list
|
await suratKeterangan.findMany.load(); // refresh list
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.message || "Gagal mengupdate surat keterangan");
|
throw new Error(
|
||||||
|
result.message || "Gagal mengupdate surat keterangan"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating surat keterangan:", error);
|
console.error("Error updating surat keterangan:", error);
|
||||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update surat keterangan");
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update surat keterangan"
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
suratKeterangan.edit.loading = false;
|
suratKeterangan.edit.loading = false;
|
||||||
@@ -207,9 +272,447 @@ const suratKeterangan = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pelayananTelunjukSaktiDesa = proxy({
|
||||||
|
create: {
|
||||||
|
form: { ...telunjukSaktiDesaForm },
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateTelunjukSaktiDesaForm.safeParse(
|
||||||
|
pelayananTelunjukSaktiDesa.create.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
pelayananTelunjukSaktiDesa.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
|
||||||
|
"create"
|
||||||
|
].post(pelayananTelunjukSaktiDesa.create.form);
|
||||||
|
if (res.status === 200) {
|
||||||
|
pelayananTelunjukSaktiDesa.findMany.load();
|
||||||
|
return toast.success("Telunjuk Sakti Desa berhasil disimpan!");
|
||||||
|
}
|
||||||
|
return toast.error("Gagal menyimpan telunjuk sakti desa");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
pelayananTelunjukSaktiDesa.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
pelayananTelunjukSaktiDesa.create.form = { ...telunjukSaktiDesaForm };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: [] as Prisma.PelayananTelunjukSaktiDesaGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}>[],
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
|
||||||
|
"find-many"
|
||||||
|
].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
pelayananTelunjukSaktiDesa.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.PelayananTelunjukSaktiDesaGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/desa/layanan/pelayanantelunjuksaktidesa/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
pelayananTelunjukSaktiDesa.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch telunjuk sakti desa:", res.statusText);
|
||||||
|
pelayananTelunjukSaktiDesa.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching telunjuk sakti desa:", error);
|
||||||
|
pelayananTelunjukSaktiDesa.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
try {
|
||||||
|
pelayananTelunjukSaktiDesa.delete.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/layanan/pelayanantelunjuksaktidesa/del/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const result = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
toast.success(result.message || "Telunjuk Sakti Desa berhasil dihapus");
|
||||||
|
await pelayananTelunjukSaktiDesa.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result.message || "Gagal menghapus telunjuk sakti desa");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus telunjuk sakti desa");
|
||||||
|
} finally {
|
||||||
|
pelayananTelunjukSaktiDesa.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
id: "",
|
||||||
|
form: { ...telunjukSaktiDesaForm },
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/layanan/pelayanantelunjuksaktidesa/${id}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
name: data.name,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
link: data.link,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching telunjuk sakti desa:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
|
const cek = templateTelunjukSaktiDesaForm.safeParse(
|
||||||
|
pelayananTelunjukSaktiDesa.edit.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
pelayananTelunjukSaktiDesa.edit.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/layanan/pelayanantelunjuksaktidesa/${this.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: this.form.name,
|
||||||
|
deskripsi: this.form.deskripsi,
|
||||||
|
link: this.form.link,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
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(result.message || "Telunjuk Sakti Desa berhasil diupdate");
|
||||||
|
await pelayananTelunjukSaktiDesa.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
result.message || "Gagal mengupdate telunjuk sakti desa"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating telunjuk sakti desa:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update telunjuk sakti desa"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
pelayananTelunjukSaktiDesa.edit.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const pelayananPerizinanBerusaha = proxy({
|
||||||
|
findById: {
|
||||||
|
data: null as pelayananPerizinanBerusahaForm | null,
|
||||||
|
loading: false,
|
||||||
|
initialize() {
|
||||||
|
pelayananPerizinanBerusaha.findById.data = {
|
||||||
|
id: "",
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
link: "",
|
||||||
|
} as pelayananPerizinanBerusahaForm;
|
||||||
|
},
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
pelayananPerizinanBerusaha.findById.loading = true;
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/desa/layanan/pelayananperizinanberusaha/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
pelayananPerizinanBerusaha.findById.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to fetch pelayanan perizinan berusaha:",
|
||||||
|
res.statusText
|
||||||
|
);
|
||||||
|
pelayananPerizinanBerusaha.findById.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching pelayanan perizinan berusaha:", error);
|
||||||
|
pelayananPerizinanBerusaha.findById.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: { ...pelayananPerizinanBerusahaForm },
|
||||||
|
loading: false,
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak boleh kosong");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/layanan/pelayananperizinanberusaha/${id}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
pelayananPerizinanBerusaha.update.id = data.id;
|
||||||
|
pelayananPerizinanBerusaha.update.form = {
|
||||||
|
name: data.name,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
link: data.link,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching pelayanan perizinan berusaha:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async update(data: pelayananPerizinanBerusahaForm) {
|
||||||
|
const cek = templatePelayananPerizinanBerusaha.safeParse(data);
|
||||||
|
if (!cek.success) {
|
||||||
|
const errors = cek.error.issues
|
||||||
|
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||||
|
.join(", ");
|
||||||
|
toast.error(`Form tidak valid: ${errors}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pelayananPerizinanBerusaha.update.loading = true;
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/desa/layanan/pelayananperizinanberusaha/${data.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
toast.success("Pelayanan perizinan berusaha berhasil diupdate");
|
||||||
|
await pelayananPerizinanBerusaha.findById.load(data.id);
|
||||||
|
} else {
|
||||||
|
toast.error("Gagal mengupdate pelayanan perizinan berusaha");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating pelayanan perizinan berusaha:", error);
|
||||||
|
toast.error(
|
||||||
|
"Terjadi kesalahan saat mengupdate pelayanan perizinan berusaha"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
pelayananPerizinanBerusaha.update.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pelayananPendudukNonPermanen = proxy({
|
||||||
|
findById: {
|
||||||
|
data: null as pelayananPendudukNonPermanenForm | null,
|
||||||
|
loading: false,
|
||||||
|
initialize() {
|
||||||
|
pelayananPendudukNonPermanen.findById.data = {
|
||||||
|
id: "",
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
} as pelayananPendudukNonPermanenForm;
|
||||||
|
},
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
pelayananPendudukNonPermanen.findById.loading = true;
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/desa/layanan/pelayananpenduduknonpermanen/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
pelayananPendudukNonPermanen.findById.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to fetch pelayanan penduduk non permanen:",
|
||||||
|
res.statusText
|
||||||
|
);
|
||||||
|
pelayananPendudukNonPermanen.findById.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching pelayanan penduduk non permanen:", error);
|
||||||
|
pelayananPendudukNonPermanen.findById.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: { ...pelayananPendudukNonPermanenForm },
|
||||||
|
loading: false,
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak boleh kosong");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/layanan/pelayananpenduduknonpermanen/${id}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
pelayananPendudukNonPermanen.update.id = data.id;
|
||||||
|
pelayananPendudukNonPermanen.update.form = {
|
||||||
|
name: data.name,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching pelayanan penduduk non permanen:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async update(data: pelayananPendudukNonPermanenForm) {
|
||||||
|
const cek = templatePelayananPendudukNonPermanen.safeParse(data);
|
||||||
|
if (!cek.success) {
|
||||||
|
const errors = cek.error.issues
|
||||||
|
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||||
|
.join(", ");
|
||||||
|
toast.error(`Form tidak valid: ${errors}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pelayananPendudukNonPermanen.update.loading = true;
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/desa/layanan/pelayananpenduduknonpermanen/${data.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
toast.success("Pelayanan penduduk non permanen berhasil diupdate");
|
||||||
|
await pelayananPendudukNonPermanen.findById.load(data.id);
|
||||||
|
} else {
|
||||||
|
toast.error("Gagal mengupdate pelayanan penduduk non permanen");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating pelayanan penduduk non permanen:", error);
|
||||||
|
toast.error(
|
||||||
|
"Terjadi kesalahan saat mengupdate pelayanan penduduk non permanen"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
pelayananPendudukNonPermanen.update.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const stateLayananDesa = proxy({
|
const stateLayananDesa = proxy({
|
||||||
suratKeterangan,
|
suratKeterangan,
|
||||||
|
pelayananPerizinanBerusaha,
|
||||||
|
pelayananTelunjukSaktiDesa,
|
||||||
|
pelayananPendudukNonPermanen,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default stateLayananDesa;
|
export default stateLayananDesa;
|
||||||
|
|
||||||
|
|||||||
221
src/app/admin/(dashboard)/_state/desa/penghargaan.ts
Normal file
221
src/app/admin/(dashboard)/_state/desa/penghargaan.ts
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { proxy } from "valtio";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const templateForm = z.object({
|
||||||
|
name: z.string().min(1).max(50),
|
||||||
|
juara: z.string().min(1).max(50),
|
||||||
|
deskripsi: z.string().min(1).max(5000),
|
||||||
|
imageId: z.string().min(1).max(50),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultForm = {
|
||||||
|
name: "",
|
||||||
|
juara: "",
|
||||||
|
deskripsi: "",
|
||||||
|
imageId: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const penghargaanState = proxy({
|
||||||
|
create: {
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateForm.safeParse(penghargaanState.create.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
penghargaanState.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.desa.penghargaan["create"].post(
|
||||||
|
penghargaanState.create.form
|
||||||
|
);
|
||||||
|
if (res.status === 200) {
|
||||||
|
penghargaanState.findMany.load();
|
||||||
|
return toast.success("success create");
|
||||||
|
}
|
||||||
|
console.log(res);
|
||||||
|
return toast.error("failed create");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
penghargaanState.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.PenghargaanGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
};
|
||||||
|
}>[]
|
||||||
|
| null,
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.penghargaan["find-many"].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
penghargaanState.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.PenghargaanGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
};
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/desa/penghargaan/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
penghargaanState.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
penghargaanState.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading penghargaan:", error);
|
||||||
|
penghargaanState.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
penghargaanState.delete.loading = true;
|
||||||
|
const response = await fetch(`/api/desa/penghargaan/del/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
toast.success(result.message || "Penghargaan berhasil dihapus");
|
||||||
|
await penghargaanState.findMany.load();
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus penghargaan");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus penghargaan");
|
||||||
|
} finally {
|
||||||
|
penghargaanState.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/desa/penghargaan/${id}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
name: data.name,
|
||||||
|
juara: data.juara,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
imageId: data.imageId,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading penghargaan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
|
const cek = templateForm.safeParse(penghargaanState.edit.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
penghargaanState.edit.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/penghargaan/${penghargaanState.edit.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: this.form.name,
|
||||||
|
juara: this.form.juara,
|
||||||
|
deskripsi: this.form.deskripsi,
|
||||||
|
imageId: this.form.imageId,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
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 penghargaan");
|
||||||
|
await penghargaanState.findMany.load();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal update penghargaan");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating penghargaan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update penghargaan"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
penghargaanState.edit.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
penghargaanState.edit.id = "";
|
||||||
|
penghargaanState.edit.form = { ...defaultForm };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default penghargaanState;
|
||||||
18
src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
Normal file
18
src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export function convertYoutubeUrlToEmbed(url: string): string | null {
|
||||||
|
const watchRegex = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/;
|
||||||
|
const shortRegex = /(?:https?:\/\/)?youtu\.be\/([^?]+)/;
|
||||||
|
|
||||||
|
const matchWatch = url.match(watchRegex);
|
||||||
|
const matchShort = url.match(shortRegex);
|
||||||
|
|
||||||
|
if (matchWatch) {
|
||||||
|
return `https://www.youtube.com/embed/${matchWatch[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchShort) {
|
||||||
|
return `https://www.youtube.com/embed/${matchShort[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
33
src/app/admin/(dashboard)/desa/gallery/lib/youtubeEmbed.tsx
Normal file
33
src/app/admin/(dashboard)/desa/gallery/lib/youtubeEmbed.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// components/YoutubeEmbed.tsx
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Box, Text } from "@mantine/core";
|
||||||
|
|
||||||
|
type YoutubeEmbedProps = {
|
||||||
|
url?: string;
|
||||||
|
showRawUrl?: boolean; // opsional, buat nampilin URL mentahnya
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function YoutubeEmbed({ url, showRawUrl = false }: YoutubeEmbedProps) {
|
||||||
|
if (!url || !url.includes("embed")) {
|
||||||
|
return <Text c="red">Link embed Youtube tidak valid</Text>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box
|
||||||
|
component="iframe"
|
||||||
|
src={url}
|
||||||
|
width="100%"
|
||||||
|
height={300}
|
||||||
|
allowFullScreen
|
||||||
|
style={{ borderRadius: 8 }}
|
||||||
|
/>
|
||||||
|
{showRawUrl && (
|
||||||
|
<Text fz="sm" c="dimmed" mt={5}>
|
||||||
|
{url}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,18 +3,19 @@
|
|||||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ActionIcon, Box, Button, Flex, Group, Image, Modal, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import { convertYoutubeUrlToEmbed } from '../../../lib/youtube-utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function EditVideo() {
|
function EditVideo() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [modalHapus, setModalHapus] = useState(false);
|
const [embedLink, setEmbedLink] = useState("");
|
||||||
const videoState = useProxy(stateGallery.video)
|
const videoState = useProxy(stateGallery.video)
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@@ -31,10 +32,12 @@ function EditVideo() {
|
|||||||
const data = await videoState.update.load(id);
|
const data = await videoState.update.load(id);
|
||||||
if (data) {
|
if (data) {
|
||||||
setFormData({
|
setFormData({
|
||||||
name: data.name || '',
|
name: data.name || '',
|
||||||
deskripsi: data.deskripsi || '',
|
deskripsi: data.deskripsi || '',
|
||||||
linkVideo: data.linkVideo || '',
|
linkVideo: data.linkVideo || '',
|
||||||
});
|
});
|
||||||
|
const embed = convertYoutubeUrlToEmbed(data.linkVideo);
|
||||||
|
setEmbedLink(embed || "");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading video:', error);
|
console.error('Error loading video:', error);
|
||||||
@@ -52,6 +55,11 @@ function EditVideo() {
|
|||||||
deskripsi: formData.deskripsi,
|
deskripsi: formData.deskripsi,
|
||||||
linkVideo: formData.linkVideo,
|
linkVideo: formData.linkVideo,
|
||||||
};
|
};
|
||||||
|
const converted = convertYoutubeUrlToEmbed(formData.linkVideo);
|
||||||
|
if (!converted) {
|
||||||
|
toast.error("Link YouTube tidak valid. Pastikan formatnya benar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
await videoState.update.update();
|
await videoState.update.update();
|
||||||
toast.success('Video berhasil diperbarui!');
|
toast.success('Video berhasil diperbarui!');
|
||||||
router.push('/admin/desa/gallery/video');
|
router.push('/admin/desa/gallery/video');
|
||||||
@@ -85,22 +93,30 @@ function EditVideo() {
|
|||||||
/>
|
/>
|
||||||
<Box>
|
<Box>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Link Video Youtube <Text span c="red" inherit>*</Text></Text>}
|
label="Link Video YouTube"
|
||||||
placeholder='Masukkan link video youtube'
|
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||||
value={formData.linkVideo}
|
value={formData.linkVideo}
|
||||||
onChange={(val) => {
|
onChange={(e) => {
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
linkVideo: val.target.value,
|
linkVideo: e.currentTarget.value,
|
||||||
})
|
})
|
||||||
|
const embed = convertYoutubeUrlToEmbed(e.currentTarget.value);
|
||||||
|
setEmbedLink(embed || "");
|
||||||
}}
|
}}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<Flex pt={5} align={"center"} gap={"xs"}>
|
|
||||||
<Text c={"dimmed"} fw={"bold"} fz={"xs"}>Cara mendapatkan link video youtube</Text>
|
{embedLink && (
|
||||||
<ActionIcon size={"xs"} radius={"xl"} color='black' variant='filled' onClick={() => setModalHapus(true)}>
|
<iframe
|
||||||
<Text fw={"bold"} fz={"xs"} c="white">?</Text>
|
className="rounded"
|
||||||
</ActionIcon>
|
width="100%"
|
||||||
</Flex>
|
height="200"
|
||||||
|
src={embedLink}
|
||||||
|
title="Preview Video"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
||||||
@@ -119,36 +135,6 @@ function EditVideo() {
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
|
||||||
<Modal
|
|
||||||
opened={modalHapus}
|
|
||||||
onClose={() => setModalHapus(false)}
|
|
||||||
title={<Text fw={"bold"} fz={"xl"}>Cara mendapatkan link video youtube</Text>}
|
|
||||||
>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 1</Text>
|
|
||||||
<Text fz={"sm"}>Buka video youtube yang ingin Anda bagikan lalu klik icon titik tiga</Text>
|
|
||||||
<Image src="/video.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 2</Text>
|
|
||||||
<Text fz={"sm"}>Klik bagikan</Text>
|
|
||||||
<Image src="/Share.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 3</Text>
|
|
||||||
<Text fz={"sm"}>Klik dibagian sematkan</Text>
|
|
||||||
<Image src="/bagikanPostingan.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 4</Text>
|
|
||||||
<Text fz={"sm"}>Lalu copy pada bagaian srcnya aja</Text>
|
|
||||||
<Image src="/sematkan.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Modal>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { useParams, useRouter } from 'next/navigation';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
function DetailVideo() {
|
function DetailVideo() {
|
||||||
const videoState = useProxy(stateGallery.video)
|
const videoState = useProxy(stateGallery.video)
|
||||||
const [modalHapus, setModalHapus] = useState(false);
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
@@ -55,9 +56,17 @@ function DetailVideo() {
|
|||||||
<Text fz={"lg"}>{videoState.findUnique.data?.name}</Text>
|
<Text fz={"lg"}>{videoState.findUnique.data?.name}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"lg"}>Link Video</Text>
|
<Text fw={"bold"} fz={"lg"}>Video</Text>
|
||||||
<Text fz={"lg"}>{videoState.findUnique.data?.linkVideo}</Text>
|
<Box component="iframe"
|
||||||
|
src={convertToEmbedUrl(videoState.findUnique.data?.linkVideo)}
|
||||||
|
width="100%"
|
||||||
|
height={300}
|
||||||
|
allowFullScreen
|
||||||
|
style={{ borderRadius: 8 }}
|
||||||
|
/>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"lg"}>Tanggal Video</Text>
|
<Text fw={"bold"} fz={"lg"}>Tanggal Video</Text>
|
||||||
<Text fz={"lg"}>{new Date(videoState.findUnique.data?.createdAt).toDateString()}</Text>
|
<Text fz={"lg"}>{new Date(videoState.findUnique.data?.createdAt).toDateString()}</Text>
|
||||||
@@ -105,7 +114,20 @@ function DetailVideo() {
|
|||||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
function convertToEmbedUrl(youtubeUrl: string): string {
|
||||||
|
try {
|
||||||
|
const url = new URL(youtubeUrl);
|
||||||
|
const videoId = url.searchParams.get("v");
|
||||||
|
if (!videoId) return youtubeUrl;
|
||||||
|
|
||||||
|
return `https://www.youtube.com/embed/${videoId}`;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error converting YouTube URL to embed:", err);
|
||||||
|
return youtubeUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DetailVideo;
|
export default DetailVideo;
|
||||||
|
|||||||
@@ -2,18 +2,21 @@
|
|||||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||||
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ActionIcon, Box, Button, Flex, Group, Image, Modal, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import { convertYoutubeUrlToEmbed } from '../../lib/youtube-utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function CreateVideo() {
|
function CreateVideo() {
|
||||||
const videoState = useProxy(stateGallery.video)
|
const videoState = useProxy(stateGallery.video)
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [modalHapus, setModalHapus] = useState(false);
|
const [link, setLink] = useState("");
|
||||||
|
const [embedLink, setEmbedLink] = useState("");
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
videoState.create.form = {
|
videoState.create.form = {
|
||||||
@@ -22,8 +25,12 @@ function CreateVideo() {
|
|||||||
linkVideo: "",
|
linkVideo: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
const converted = convertYoutubeUrlToEmbed(videoState.create.form.linkVideo);
|
||||||
|
if (!converted) {
|
||||||
|
toast.error("Link YouTube tidak valid. Pastikan formatnya benar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
await videoState.create.create();
|
await videoState.create.create();
|
||||||
resetForm();
|
resetForm();
|
||||||
router.push("/admin/desa/gallery/video")
|
router.push("/admin/desa/gallery/video")
|
||||||
@@ -49,20 +56,28 @@ function CreateVideo() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Box>
|
<Box>
|
||||||
<TextInput
|
<Stack gap={"xs"}>
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Link Video Youtube <Text span c="red" inherit>*</Text></Text>}
|
<TextInput
|
||||||
placeholder='Masukkan link video youtube'
|
label="Link Video YouTube"
|
||||||
value={videoState.create.form.linkVideo}
|
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||||
onChange={(val) => {
|
value={link}
|
||||||
videoState.create.form.linkVideo = val.target.value;
|
onChange={(e) => {
|
||||||
}}
|
setLink(e.currentTarget.value);
|
||||||
/>
|
const embed = convertYoutubeUrlToEmbed(e.currentTarget.value);
|
||||||
<Flex pt={5} align={"center"} gap={"xs"}>
|
setEmbedLink(embed || "");
|
||||||
<Text c={"dimmed"} fw={"bold"} fz={"xs"}>Cara mendapatkan link video youtube</Text>
|
}}
|
||||||
<ActionIcon size={"xs"} radius={"xl"} color='black' variant='filled' onClick={() => setModalHapus(true)}>
|
required
|
||||||
<Text fw={"bold"} fz={"xs"} c="white">?</Text>
|
/>
|
||||||
</ActionIcon>
|
|
||||||
</Flex>
|
{embedLink && (
|
||||||
|
<iframe
|
||||||
|
style={{ borderRadius: 10, width: "100%", height: 400 }}
|
||||||
|
src={embedLink}
|
||||||
|
title="Preview Video"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
||||||
@@ -78,36 +93,6 @@ function CreateVideo() {
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
|
||||||
<Modal
|
|
||||||
opened={modalHapus}
|
|
||||||
onClose={() => setModalHapus(false)}
|
|
||||||
title={<Text fw={"bold"} fz={"xl"}>Cara mendapatkan link video youtube</Text>}
|
|
||||||
>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 1</Text>
|
|
||||||
<Text fz={"sm"}>Buka video youtube yang ingin Anda bagikan lalu klik icon titik tiga</Text>
|
|
||||||
<Image src="/video.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 2</Text>
|
|
||||||
<Text fz={"sm"}>Klik bagikan</Text>
|
|
||||||
<Image src="/Share.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 3</Text>
|
|
||||||
<Text fz={"sm"}>Klik dibagian sematkan</Text>
|
|
||||||
<Image src="/bagikanPostingan.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Langkah 4</Text>
|
|
||||||
<Text fz={"sm"}>Lalu copy pada bagaian srcnya aja</Text>
|
|
||||||
<Image src="/sematkan.png" w={300} alt="" />
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Modal>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
'use client'
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, 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 EditPelayananPendudukNonPermanent() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams()
|
||||||
|
const statePendudukNonPermanent = useProxy(stateLayananDesa.pelayananPendudukNonPermanen)
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: statePendudukNonPermanent.findById.data?.name || '',
|
||||||
|
deskripsi: statePendudukNonPermanent.findById.data?.deskripsi || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPelayananPerizinan = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
try {
|
||||||
|
const data = await statePendudukNonPermanent.update.load(id);
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
name: data.name || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading pelayanan perizinan berusaha:", error);
|
||||||
|
toast.error("Gagal memuat data pelayanan perizinan berusaha");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadPelayananPerizinan();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (statePendudukNonPermanent.findById.data) {
|
||||||
|
statePendudukNonPermanent.findById.data.name = formData.name;
|
||||||
|
statePendudukNonPermanent.findById.data.deskripsi = formData.deskripsi;
|
||||||
|
statePendudukNonPermanent.update.update(statePendudukNonPermanent.findById.data)
|
||||||
|
}
|
||||||
|
router.push('/admin/desa/layanan/pelayanan_penduduk_non_permanent')
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={'xs'}>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
variant={'subtle'}
|
||||||
|
onClick={() => router.back()}
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10} w={{ base: '100%', md: '50%' }}>
|
||||||
|
<Stack gap={'xs'}>
|
||||||
|
<Title order={3}>Edit Pelayanan Penduduk Non Permanent</Title>
|
||||||
|
<Text fw={"bold"}>Judul</Text>
|
||||||
|
<TextInput
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
name: val.target.value,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text fw={"bold"}>Deskripsi</Text>
|
||||||
|
<EditEditor
|
||||||
|
value={formData.deskripsi}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
deskripsi: val,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={statePendudukNonPermanent.update.loading}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPelayananPendudukNonPermanent;
|
||||||
@@ -1,40 +1,48 @@
|
|||||||
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||||
|
|
||||||
function SuratKeterangan() {
|
function SuratKeterangan() {
|
||||||
|
const router = useRouter()
|
||||||
|
const pelayananPendudukNonPermanen = useProxy(stateLayananDesa.pelayananPendudukNonPermanen)
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
pelayananPendudukNonPermanen.findById.load('1')
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!pelayananPendudukNonPermanen.findById.data) {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Skeleton radius={10} h={800} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
<JudulListTab
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
title='List Pelayanan Penduduk Non-Permanent'
|
<Box py={15}>
|
||||||
href='/admin/desa/layanan/pelayanan_penduduk_non_permanent/create'
|
<Stack gap={"xs"}>
|
||||||
placeholder='pencarian'
|
<Grid>
|
||||||
searchIcon={<IconSearch size={16} />}
|
<GridCol span={{ base: 12, md: 11 }}>
|
||||||
/>
|
<Text fz={"h4"} fw={"bold"}>Preview Pelayanan Perizinan Berusaha</Text>
|
||||||
<Table striped withTableBorder withRowBorders>
|
</GridCol>
|
||||||
<TableThead>
|
<GridCol span={{ base: 12, md: 1 }}>
|
||||||
<TableTr>
|
<Button bg={colors['blue-button']} onClick={() => router.push('/admin/desa/layanan/pelayanan_penduduk_non_permanent/edit')}>
|
||||||
<TableTh>Nama</TableTh>
|
<IconEdit size={16} />
|
||||||
<TableTh>Deskripsi</TableTh>
|
|
||||||
<TableTh>Detail</TableTh>
|
|
||||||
</TableTr>
|
|
||||||
</TableThead>
|
|
||||||
<TableTbody>
|
|
||||||
<TableTr>
|
|
||||||
<TableTd>Pelayanan Penduduk Non-Permanent</TableTd>
|
|
||||||
<TableTd>Deskripsi Pelayanan Penduduk Non-Permanent</TableTd>
|
|
||||||
<TableTd>
|
|
||||||
<Text>
|
|
||||||
<Button>
|
|
||||||
<IconDeviceImac size={20} />
|
|
||||||
</Button>
|
</Button>
|
||||||
</Text>
|
</GridCol>
|
||||||
</TableTd>
|
</Grid>
|
||||||
</TableTr>
|
</Stack>
|
||||||
</TableTbody>
|
</Box>
|
||||||
</Table>
|
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>{pelayananPendudukNonPermanen.findById.data.name}</Text>
|
||||||
|
<Text py={10} ta={"justify"} fz={{ base: "sm", md: 'h3' }} dangerouslySetInnerHTML={{ __html: pelayananPendudukNonPermanen.findById.data.deskripsi }} />
|
||||||
|
</Paper>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
'use client'
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, 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 EditPelayananPerizinanBerusaha() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams()
|
||||||
|
const statePerizinanBerusaha = useProxy(stateLayananDesa.pelayananPerizinanBerusaha)
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: statePerizinanBerusaha.findById.data?.name || '',
|
||||||
|
deskripsi: statePerizinanBerusaha.findById.data?.deskripsi || '',
|
||||||
|
link: statePerizinanBerusaha.findById.data?.link || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPelayananPerizinan = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
try {
|
||||||
|
const data = await statePerizinanBerusaha.update.load(id);
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
name: data.name || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
link: data.link || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading pelayanan perizinan berusaha:", error);
|
||||||
|
toast.error("Gagal memuat data pelayanan perizinan berusaha");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadPelayananPerizinan();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (statePerizinanBerusaha.findById.data) {
|
||||||
|
statePerizinanBerusaha.findById.data.name = formData.name;
|
||||||
|
statePerizinanBerusaha.findById.data.deskripsi = formData.deskripsi;
|
||||||
|
statePerizinanBerusaha.findById.data.link = formData.link;
|
||||||
|
statePerizinanBerusaha.update.update(statePerizinanBerusaha.findById.data)
|
||||||
|
}
|
||||||
|
router.push('/admin/desa/layanan/pelayanan_perizinan_berusaha')
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={'xs'}>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
variant={'subtle'}
|
||||||
|
onClick={() => router.back()}
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10} w={{ base: '100%', md: '50%' }}>
|
||||||
|
<Stack gap={'xs'}>
|
||||||
|
<Title order={3}>Edit Pelayanan Perizinan Berusaha</Title>
|
||||||
|
<Text fw={"bold"}>Judul</Text>
|
||||||
|
<TextInput
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
name: val.target.value,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text fw={"bold"}>Link</Text>
|
||||||
|
<TextInput
|
||||||
|
value={formData.link}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
link: val.target.value,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text fw={"bold"}>Deskripsi</Text>
|
||||||
|
<EditEditor
|
||||||
|
value={formData.deskripsi}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
deskripsi: val,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={statePerizinanBerusaha.update.loading}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPelayananPerizinanBerusaha;
|
||||||
@@ -1,40 +1,94 @@
|
|||||||
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import { Box, Button, Grid, GridCol, Group, Paper, Skeleton, Stack, Stepper, StepperCompleted, StepperStep, Text } from '@mantine/core';
|
||||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
|
||||||
function PerizinanBerusaha() {
|
function PerizinanBerusaha() {
|
||||||
|
const router = useRouter()
|
||||||
|
const pelayananPerizinanBerusaha = useProxy(stateLayananDesa.pelayananPerizinanBerusaha)
|
||||||
|
const [active, setActive] = useState(1);
|
||||||
|
const nextStep = () => setActive((current) => (current < 6 ? current + 1 : current));
|
||||||
|
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
|
||||||
|
useShallowEffect(() => {
|
||||||
|
pelayananPerizinanBerusaha.findById.load('1')
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if(!pelayananPerizinanBerusaha.findById.data) {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Skeleton radius={10} h={800} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
<JudulListTab
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
title='List Pelayanan Perizinan Berusaha'
|
<Box py={15}>
|
||||||
href='/admin/desa/layanan/pelayanan_perizinan_berusaha/create'
|
<Stack gap={"xs"}>
|
||||||
placeholder='pencarian'
|
<Grid>
|
||||||
searchIcon={<IconSearch size={16} />}
|
<GridCol span={{ base: 12, md: 11 }}>
|
||||||
/>
|
<Text fz={"h4"} fw={"bold"}>Preview Pelayanan Perizinan Berusaha</Text>
|
||||||
<Table striped withTableBorder withRowBorders>
|
</GridCol>
|
||||||
<TableThead>
|
<GridCol span={{ base: 12, md: 1 }}>
|
||||||
<TableTr>
|
<Button bg={colors['blue-button']} onClick={() => router.push('/admin/desa/layanan/pelayanan_perizinan_berusaha/edit')}>
|
||||||
<TableTh>Nama</TableTh>
|
<IconEdit size={16} />
|
||||||
<TableTh>Deskripsi</TableTh>
|
|
||||||
<TableTh>Detail</TableTh>
|
|
||||||
</TableTr>
|
|
||||||
</TableThead>
|
|
||||||
<TableTbody>
|
|
||||||
<TableTr>
|
|
||||||
<TableTd>Pelayanan Perizinan Berusaha</TableTd>
|
|
||||||
<TableTd>Deskripsi Pelayanan Perizinan Berusaha</TableTd>
|
|
||||||
<TableTd>
|
|
||||||
<Text>
|
|
||||||
<Button>
|
|
||||||
<IconDeviceImac size={20} />
|
|
||||||
</Button>
|
</Button>
|
||||||
</Text>
|
</GridCol>
|
||||||
</TableTd>
|
</Grid>
|
||||||
</TableTr>
|
</Stack>
|
||||||
</TableTbody>
|
</Box>
|
||||||
</Table>
|
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>{pelayananPerizinanBerusaha.findById.data.name}</Text>
|
||||||
|
<Text py={10} ta={"justify"} fz={{ base: "sm", md: 'h3' }} dangerouslySetInnerHTML={{__html: pelayananPerizinanBerusaha.findById.data.deskripsi}} />
|
||||||
|
<Text py={10} fz={{ base: "sm", md: 'h3' }}>Proses pendaftaran NIB melalui OSS mencakup beberapa langkah umum, seperti:</Text>
|
||||||
|
<Box p={"xl"} w={{ base: "100%", md: "100%" }} >
|
||||||
|
<Stepper active={active} onStepClick={setActive} orientation="vertical"
|
||||||
|
styles={{
|
||||||
|
separator: {
|
||||||
|
marginLeft: 25
|
||||||
|
},
|
||||||
|
|
||||||
|
step: {
|
||||||
|
padding: '12px 0'
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<StepperStep label="Langkah Pertama" description="Pendaftaran Akun">
|
||||||
|
Pendaftaran akun pada portal OSS
|
||||||
|
</StepperStep>
|
||||||
|
<StepperStep label="Langkah Kedua" description="Pengisian Data Perusahaan">
|
||||||
|
Mengisi informasi perusahaan, termasuk data pemegang saham, alamat perusahaan, dan lainnya
|
||||||
|
</StepperStep>
|
||||||
|
<StepperStep label="Langkah Ketiga" description="Pemilihan KBLI ">
|
||||||
|
Memilih KBLI dengan jenis usaha yang akan didaftarkan
|
||||||
|
</StepperStep>
|
||||||
|
<StepperStep label="Langkah Keempat" description="Pengunggahan Dokumen">
|
||||||
|
Mengunggah dokumen-dokumen yang diperlukan, seperti akta pendirian perusahaan, surat izin usaha, dan dokumen lainnya sesuai dengan ketentuan yang berlaku
|
||||||
|
</StepperStep>
|
||||||
|
<StepperStep label="Langkah Kelima" description="Verifikasi dan Persetujuan">
|
||||||
|
Proses verifikasi dan persetujuan oleh instansi terkait
|
||||||
|
</StepperStep>
|
||||||
|
<StepperStep label="Langkah Keenam" description="Penerimaan NIB">
|
||||||
|
Jika proses sebelumnya berhasil, perusahaan akan menerima NIB sebagai identitas resmi usaha anda
|
||||||
|
</StepperStep>
|
||||||
|
<StepperCompleted >
|
||||||
|
Selesai, anda telah mengikuti proses pendaftaran NIB melalui OSS
|
||||||
|
</StepperCompleted>
|
||||||
|
</Stepper>
|
||||||
|
|
||||||
|
<Group justify="center" mt="xl">
|
||||||
|
<Button variant="default" onClick={prevStep}>Back</Button>
|
||||||
|
<Button onClick={nextStep}>Next step</Button>
|
||||||
|
</Group>
|
||||||
|
<Text py={35} ta={"justify"} fz={{ base: "sm", md: 'h3' }}>Penting untuk diingat bahwa prosedur dan persyaratan dapat berubah
|
||||||
|
seiring waktu. Untuk informasi yang lebih akurat dan terkini, saya sarankan untuk mengunjungi situs
|
||||||
|
resmi OSS <a href={pelayananPerizinanBerusaha.findById.data.link}>{pelayananPerizinanBerusaha.findById.data.link}</a> atau menghubungi instansi terkait di pemerintah Indonesia yang bertanggung jawab atas urusan perizinan usaha.</Text>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
'use client'
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, 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 EditPelayananTelunjukSakti() {
|
||||||
|
const stateTelunjukDesa = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||||
|
const router = useRouter()
|
||||||
|
const params = useParams()
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: stateTelunjukDesa.edit.form.name,
|
||||||
|
deskripsi: stateTelunjukDesa.edit.form.deskripsi,
|
||||||
|
link: stateTelunjukDesa.edit.form.link,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPelayananTelunjukSakti = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
try {
|
||||||
|
const data = await stateTelunjukDesa.edit.load(id);
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
name: data.name,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
link: data.link,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading pelayanan telunjuk sakti:", error);
|
||||||
|
toast.error("Gagal memuat data pelayanan telunjuk sakti");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadPelayananTelunjukSakti();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
stateTelunjukDesa.edit.form = {
|
||||||
|
...stateTelunjukDesa.edit.form,
|
||||||
|
name: formData.name,
|
||||||
|
deskripsi: formData.deskripsi,
|
||||||
|
link: formData.link,
|
||||||
|
}
|
||||||
|
await stateTelunjukDesa.edit.update()
|
||||||
|
toast.success("Pelayanan telunjuk sakti berhasil diperbarui!")
|
||||||
|
router.push("/admin/desa/layanan/pelayanan_telunjuk_sakti_desa")
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating pelayanan telunjuk sakti:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat memperbarui pelayanan telunjuk sakti");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Edit Surat Keterangan</Title>
|
||||||
|
<TextInput
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({ ...formData, name: val.target.value });
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Surat Keterangan</Text>}
|
||||||
|
placeholder="masukkan nama surat keterangan"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
value={formData.link}
|
||||||
|
onChange={(val) => {
|
||||||
|
setFormData({ ...formData, link: val.target.value });
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Link</Text>}
|
||||||
|
placeholder="masukkan link"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||||
|
<EditEditor
|
||||||
|
value={formData.deskripsi}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
setFormData({ ...formData, deskripsi: htmlContent });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPelayananTelunjukSakti;
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
'use client'
|
||||||
|
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function DetailPelayananTelunjukSakti() {
|
||||||
|
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||||
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
const params = useParams()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
telunjukSaktiState.findUnique.load(params?.id as string)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
telunjukSaktiState.delete.byId(selectedId)
|
||||||
|
setModalHapus(false)
|
||||||
|
setSelectedId(null)
|
||||||
|
router.push("/admin/desa/layanan/pelayanan_telunjuk_sakti_desa")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!telunjukSaktiState.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
{Array.from({ length: 10 }).map((_, k) => (
|
||||||
|
<Skeleton key={k} h={40} />
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Pelayanan Telunjuk Sakti Desa</Text>
|
||||||
|
{telunjukSaktiState.findUnique.data ? (
|
||||||
|
<Paper key={telunjukSaktiState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Nama</Text>
|
||||||
|
<Text fz={"lg"}>{telunjukSaktiState.findUnique.data?.name}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Link</Text>
|
||||||
|
<Text fz={"lg"}>{telunjukSaktiState.findUnique.data?.link}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
||||||
|
<Text fz={"lg"}dangerouslySetInnerHTML={{ __html: telunjukSaktiState.findUnique.data?.deskripsi }}></Text>
|
||||||
|
</Box>
|
||||||
|
<Flex gap={"xs"} mt={10}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (telunjukSaktiState.findUnique.data) {
|
||||||
|
setSelectedId(telunjukSaktiState.findUnique.data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={telunjukSaktiState.delete.loading || !telunjukSaktiState.findUnique.data}
|
||||||
|
color={"red"}
|
||||||
|
>
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (telunjukSaktiState.findUnique.data) {
|
||||||
|
router.push(`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${telunjukSaktiState.findUnique.data.id}/edit`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={!telunjukSaktiState.findUnique.data}
|
||||||
|
color={"green"}
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPelayananTelunjukSakti;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
'use client'
|
||||||
|
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function CreatePelayananTelunjukDesa() {
|
||||||
|
const stateTelunjukDesa = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
stateTelunjukDesa.create.form = {
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
link: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await stateTelunjukDesa.create.create()
|
||||||
|
resetForm()
|
||||||
|
router.push("/admin/desa/layanan/pelayanan_telunjuk_sakti_desa")
|
||||||
|
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Create Pelayanan Telunjuk Sakti Desa</Title>
|
||||||
|
<TextInput
|
||||||
|
value={stateTelunjukDesa.create.form.name}
|
||||||
|
onChange={(val) => {
|
||||||
|
stateTelunjukDesa.create.form.name = val.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Pelayanan Telunjuk Sakti Desa</Text>}
|
||||||
|
placeholder="masukkan nama pelayanan telunjuk sakti desa"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
value={stateTelunjukDesa.create.form.link}
|
||||||
|
onChange={(val) => {
|
||||||
|
stateTelunjukDesa.create.form.link = val.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Link</Text>}
|
||||||
|
placeholder="masukkan link"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||||
|
<CreateEditor
|
||||||
|
value={stateTelunjukDesa.create.form.deskripsi}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
stateTelunjukDesa.create.form.deskripsi = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePelayananTelunjukDesa;
|
||||||
@@ -1,9 +1,29 @@
|
|||||||
|
'use client'
|
||||||
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
|
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
function PelayananTelunjukSakti() {
|
||||||
|
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
telunjukSaktiState.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!telunjukSaktiState.findMany.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton h={500} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function SuratKeterangan() {
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
@@ -22,17 +42,19 @@ function SuratKeterangan() {
|
|||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
<TableTr>
|
{telunjukSaktiState.findMany.data?.map((item) => (
|
||||||
<TableTd>Pelayanan Telunjuk Sakti Desa</TableTd>
|
<TableTr key={item.id}>
|
||||||
<TableTd>Deskripsi Pelayanan Telunjuk Sakti Desa</TableTd>
|
<TableTd>{item.name}</TableTd>
|
||||||
|
<TableTd><Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} /></TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text>
|
<Text>
|
||||||
<Button>
|
<Button onClick={() => router.push(`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${item.id}`)}>
|
||||||
<IconDeviceImac size={20} />
|
<IconDeviceImac size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
))}
|
||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -40,4 +62,4 @@ function SuratKeterangan() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SuratKeterangan;
|
export default PelayananTelunjukSakti;
|
||||||
|
|||||||
147
src/app/admin/(dashboard)/desa/penghargaan/[id]/edit/page.tsx
Normal file
147
src/app/admin/(dashboard)/desa/penghargaan/[id]/edit/page.tsx
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
'use client'
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
|
import penghargaanState from '@/app/admin/(dashboard)/_state/desa/penghargaan';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import ApiFetch from '@/lib/api-fetch';
|
||||||
|
import { Box, Button, Paper, Stack, Title, TextInput, FileInput, Center, Text, Image } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function EditPenghargaan() {
|
||||||
|
const statePenghargaan = useProxy(penghargaanState)
|
||||||
|
const router = useRouter()
|
||||||
|
const params = useParams()
|
||||||
|
const [previewImage, setPreviewImage] = useState<string | null>(null)
|
||||||
|
const [file, setFile] = useState<File | null>(null)
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: statePenghargaan.findUnique.data?.name || '',
|
||||||
|
juara: statePenghargaan.findUnique.data?.juara || '',
|
||||||
|
deskripsi: statePenghargaan.findUnique.data?.deskripsi || '',
|
||||||
|
imageId: statePenghargaan.findUnique.data?.imageId || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPenghargaan = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await statePenghargaan.edit.load(id);
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
name: data.name || '',
|
||||||
|
juara: data.juara || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
imageId: data.imageId || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data?.image?.link) {
|
||||||
|
setPreviewImage(data.image.link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading penghargaan:", error);
|
||||||
|
toast.error("Gagal memuat data penghargaan");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPenghargaan();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
statePenghargaan.edit.form = {
|
||||||
|
...statePenghargaan.edit.form,
|
||||||
|
name: formData.name,
|
||||||
|
juara: formData.juara,
|
||||||
|
deskripsi: formData.deskripsi,
|
||||||
|
imageId: formData.imageId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||||
|
const uploaded = res.data?.data;
|
||||||
|
|
||||||
|
if (!uploaded?.id) {
|
||||||
|
return toast.error("Gagal upload gambar");
|
||||||
|
}
|
||||||
|
|
||||||
|
statePenghargaan.edit.form.imageId = uploaded.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
await statePenghargaan.edit.update();
|
||||||
|
toast.success("Penghargaan berhasil diperbarui!");
|
||||||
|
router.push("/admin/desa/penghargaan");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating penghargaan:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat memperbarui penghargaan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors["blue-button"]} size={30} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Edit Penghargaan</Title>
|
||||||
|
<TextInput
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||||
|
placeholder="masukkan judul"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
value={formData.juara}
|
||||||
|
onChange={(e) => setFormData({ ...formData, juara: e.target.value })}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Juara</Text>}
|
||||||
|
placeholder="masukkan juara"
|
||||||
|
/>
|
||||||
|
<FileInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar Baru (Opsional)</Text>}
|
||||||
|
value={file}
|
||||||
|
onChange={async (e) => {
|
||||||
|
if (!e) return;
|
||||||
|
setFile(e);
|
||||||
|
const base64 = await e.arrayBuffer().then((buf) =>
|
||||||
|
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||||
|
);
|
||||||
|
setPreviewImage(base64);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{previewImage ? (
|
||||||
|
<Image alt="" src={previewImage} w={200} h={200} />
|
||||||
|
) : (
|
||||||
|
<Center w={200} h={200} bg={"gray"}>
|
||||||
|
<IconImageInPicture />
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>
|
||||||
|
<EditEditor
|
||||||
|
value={formData.deskripsi}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||||
|
statePenghargaan.edit.form.deskripsi = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Button onClick={handleSubmit}>Edit Penghargaan</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPenghargaan;
|
||||||
111
src/app/admin/(dashboard)/desa/penghargaan/[id]/page.tsx
Normal file
111
src/app/admin/(dashboard)/desa/penghargaan/[id]/page.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import penghargaanState from '../../../_state/desa/penghargaan';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react';
|
||||||
|
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailPenghargaan() {
|
||||||
|
const statePenghargaan = useProxy(penghargaanState)
|
||||||
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
const router = useRouter()
|
||||||
|
const params = useParams()
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
statePenghargaan.findUnique.load(params?.id as string)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
statePenghargaan.delete.byId(selectedId)
|
||||||
|
setModalHapus(false)
|
||||||
|
setSelectedId(null)
|
||||||
|
router.push("/admin/desa/penghargaan")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statePenghargaan.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton h={500} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Penghargaan</Text>
|
||||||
|
{statePenghargaan.findUnique.data ? (
|
||||||
|
<Paper key={statePenghargaan.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Judul</Text>
|
||||||
|
<Text fz={"lg"}>{statePenghargaan.findUnique.data?.name}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Juara</Text>
|
||||||
|
<Text fz={"lg"}>{statePenghargaan.findUnique.data?.juara}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
||||||
|
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: statePenghargaan.findUnique.data?.deskripsi }} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
|
||||||
|
<Image w={{ base: 150, md: 150, lg: 150 }} src={statePenghargaan.findUnique.data?.image?.link} alt="gambar" />
|
||||||
|
</Box>
|
||||||
|
<Flex gap={"xs"} mt={10}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (statePenghargaan.findUnique.data) {
|
||||||
|
setSelectedId(statePenghargaan.findUnique.data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={statePenghargaan.delete.loading || !statePenghargaan.findUnique.data}
|
||||||
|
color={"red"}
|
||||||
|
>
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (statePenghargaan.findUnique.data) {
|
||||||
|
router.push(`/admin/desa/penghargaan/${statePenghargaan.findUnique.data.id}/edit`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={!statePenghargaan.findUnique.data}
|
||||||
|
color={"green"}
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text='Apakah anda yakin ingin menghapus penghargaan ini?'
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPenghargaan;
|
||||||
114
src/app/admin/(dashboard)/desa/penghargaan/create/page.tsx
Normal file
114
src/app/admin/(dashboard)/desa/penghargaan/create/page.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import penghargaanState from '../../../_state/desa/penghargaan';
|
||||||
|
import ApiFetch from '@/lib/api-fetch';
|
||||||
|
import CreateEditor from '../../../_com/createEditor';
|
||||||
|
|
||||||
|
|
||||||
|
function CreatePenghargaan() {
|
||||||
|
const statePenghargaan = useProxy(penghargaanState)
|
||||||
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
statePenghargaan.create.form = {
|
||||||
|
name: "",
|
||||||
|
juara: "",
|
||||||
|
deskripsi: "",
|
||||||
|
imageId: "",
|
||||||
|
}
|
||||||
|
setPreviewImage(null)
|
||||||
|
setFile(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!file) {
|
||||||
|
return toast.error("Silahkan pilih file gambar terlebih dahulu")
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.fileStorage.create.post({
|
||||||
|
file: file,
|
||||||
|
name: file.name
|
||||||
|
})
|
||||||
|
|
||||||
|
const uploaded = res.data?.data
|
||||||
|
if (!uploaded?.id) {
|
||||||
|
return toast.error("Gagal upload gambar")
|
||||||
|
}
|
||||||
|
|
||||||
|
statePenghargaan.create.form.imageId = uploaded.id
|
||||||
|
|
||||||
|
await statePenghargaan.create.create()
|
||||||
|
resetForm()
|
||||||
|
router.push("/admin/desa/penghargaan")
|
||||||
|
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Create Penghargaan</Title>
|
||||||
|
<TextInput
|
||||||
|
value={statePenghargaan.create.form.name}
|
||||||
|
onChange={(val) => {
|
||||||
|
statePenghargaan.create.form.name = val.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Penghargaan</Text>}
|
||||||
|
placeholder="masukkan nama penghargaan"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
value={statePenghargaan.create.form.juara}
|
||||||
|
onChange={(val) => {
|
||||||
|
statePenghargaan.create.form.juara = val.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Juara</Text>}
|
||||||
|
placeholder="masukkan juara"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>
|
||||||
|
<CreateEditor
|
||||||
|
value={statePenghargaan.create.form.deskripsi}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
statePenghargaan.create.form.deskripsi = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<FileInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar Konten</Text>}
|
||||||
|
value={file}
|
||||||
|
onChange={async (e) => {
|
||||||
|
if (!e) return;
|
||||||
|
setFile(e);
|
||||||
|
const base64 = await e.arrayBuffer().then((buf) =>
|
||||||
|
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||||
|
);
|
||||||
|
setPreviewImage(base64);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{previewImage ? (
|
||||||
|
<Image alt="" src={previewImage} w={200} h={200} />
|
||||||
|
) : (
|
||||||
|
<Center w={200} h={200} bg={"gray"}>
|
||||||
|
<IconImageInPicture />
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePenghargaan;
|
||||||
@@ -1,34 +1,69 @@
|
|||||||
|
'use client'
|
||||||
|
import penghargaanState from '@/app/admin/(dashboard)/_state/desa/penghargaan';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||||
import React from 'react';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import Penghargaan from './ui/penghargaan/page';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
import GambarPerhargaan from './ui/gambar_perhargaan/page';
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import JudulListTab from '../../_com/jusulListTab';
|
||||||
|
|
||||||
function Page() {
|
function Penghargaan() {
|
||||||
|
const state = useProxy(penghargaanState)
|
||||||
|
const router = useRouter()
|
||||||
|
useShallowEffect(() => {
|
||||||
|
state.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!state.findMany.data) {
|
||||||
|
return(
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton h={500} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Stack>
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
<Title order={3}>Penghargaan</Title>
|
<JudulListTab
|
||||||
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Penghargaan"}>
|
title='List Penghargaan'
|
||||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
href='/admin/desa/penghargaan/create'
|
||||||
<TabsTab value="Penghargaan">
|
placeholder='pencarian'
|
||||||
Penghargaan
|
searchIcon={<IconSearch size={16} />}
|
||||||
</TabsTab>
|
/>
|
||||||
<TabsTab value="Gambar Penghargaan">
|
<Table striped withTableBorder withRowBorders>
|
||||||
Gambar Penghargaan
|
<TableThead>
|
||||||
</TabsTab>
|
<TableTr>
|
||||||
</TabsList>
|
<TableTh>Nama</TableTh>
|
||||||
|
<TableTh>Deskripsi</TableTh>
|
||||||
<TabsPanel value="Penghargaan">
|
<TableTh>Image</TableTh>
|
||||||
<Penghargaan/>
|
<TableTh>Detail</TableTh>
|
||||||
</TabsPanel>
|
</TableTr>
|
||||||
<TabsPanel value="Gambar Penghargaan">
|
</TableThead>
|
||||||
<GambarPerhargaan/>
|
<TableTbody>
|
||||||
</TabsPanel>
|
{state.findMany.data?.map((item) => (
|
||||||
</Tabs>
|
<TableTr key={item.id}>
|
||||||
</Stack>
|
<TableTd>{item.name}</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Image w={100} src={item.image?.link} alt="gambar" />
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Text>
|
||||||
|
<Button onClick={() => router.push(`/admin/desa/penghargaan/${item.id}`)}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</Text>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
))}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Penghargaan;
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListGambarPenghargaan() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack>
|
|
||||||
<Title order={3}>List Gambar Penghargaan</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListGambarPenghargaan;
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, SimpleGrid, Stack, Title, Text, Group, Button, TextInput, Center } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
import { DesaEditor } from '../../../_com/desaEditor';
|
|
||||||
import ListGambarPenghargaan from './listPage';
|
|
||||||
import { IconUpload } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
|
|
||||||
function GambarPerhargaan() {
|
|
||||||
return (
|
|
||||||
<Box py={10}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Tambah Gambar Penghargaan</Title>
|
|
||||||
<TextInput
|
|
||||||
label="Judul Gambar Penghargaan"
|
|
||||||
placeholder='masukkan judul gambar penghargaan'
|
|
||||||
/>
|
|
||||||
<Text fw={"bold"}>Deskripsi Gambar Penghargaan</Text>
|
|
||||||
<DesaEditor showSubmit={false} />
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"}>Upload Gambar Penghargaan</Text>
|
|
||||||
<Box bg={colors['BG-trans']} p={"md"}>
|
|
||||||
<Center>
|
|
||||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
|
||||||
</Center>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<ListGambarPenghargaan />
|
|
||||||
</SimpleGrid>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GambarPerhargaan;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListPenghargaan() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack>
|
|
||||||
<Title order={3}>List Penghargaan</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListPenghargaan;
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
import { DesaEditor } from '../../../_com/desaEditor';
|
|
||||||
import ListPenghargaan from './listPage';
|
|
||||||
|
|
||||||
function Penghargaan() {
|
|
||||||
return (
|
|
||||||
<Box py={10}>
|
|
||||||
<Stack>
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Penghargaan</Title>
|
|
||||||
<Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Deskripsi Penghargaan</Text>
|
|
||||||
<DesaEditor
|
|
||||||
showSubmit={false}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<ListPenghargaan/>
|
|
||||||
</SimpleGrid>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Penghargaan;
|
|
||||||
@@ -6,6 +6,7 @@ import PotensiDesa from "./potensi";
|
|||||||
import GalleryFoto from "./gallery/foto";
|
import GalleryFoto from "./gallery/foto";
|
||||||
import GalleryVideo from "./gallery/video";
|
import GalleryVideo from "./gallery/video";
|
||||||
import LayananDesa from "./layanan";
|
import LayananDesa from "./layanan";
|
||||||
|
import Penghargaan from "./penghargaan";
|
||||||
|
|
||||||
|
|
||||||
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
||||||
@@ -16,5 +17,6 @@ const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
|||||||
.use(GalleryFoto)
|
.use(GalleryFoto)
|
||||||
.use(GalleryVideo)
|
.use(GalleryVideo)
|
||||||
.use(LayananDesa)
|
.use(LayananDesa)
|
||||||
|
.use(Penghargaan)
|
||||||
|
|
||||||
export default Desa;
|
export default Desa;
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import Elysia from "elysia";
|
import Elysia from "elysia";
|
||||||
import PelayananSuratKeterangan from "./pelayanan_surat_keterangan";
|
import PelayananSuratKeterangan from "./pelayanan_surat_keterangan";
|
||||||
|
import PelayananTelunjukSaktiDesa from "./pelayanan_telunjuk_sakti_desa";
|
||||||
|
import PelayananPerizinanBerusaha from "./pelayanan_perizinan_berusaha";
|
||||||
|
import PelayananPendudukNonPermanen from "./pelayanan_penduduk_non_permanen";
|
||||||
|
|
||||||
const LayananDesa = new Elysia({ prefix: "/layanan", tags: ["Desa/Layanan"] })
|
const LayananDesa = new Elysia({ prefix: "/layanan", tags: ["Desa/Layanan"] })
|
||||||
.use(PelayananSuratKeterangan)
|
.use(PelayananSuratKeterangan)
|
||||||
|
.use(PelayananTelunjukSaktiDesa)
|
||||||
|
.use(PelayananPerizinanBerusaha)
|
||||||
|
.use(PelayananPendudukNonPermanen)
|
||||||
|
|
||||||
export default LayananDesa
|
export default LayananDesa
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function pelayananPendudukNonPermanenFindUnique(request: Request) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const pathSegments = url.pathname.split("/");
|
||||||
|
const id = pathSegments[pathSegments.length - 1];
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak boleh kosong",
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof id !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak valid",
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const data = await prisma.pelayananPendudukNonPermanen.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "Pelayanan penduduk non permanen tidak ditemukan",
|
||||||
|
},
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch pelayanan penduduk non permanen by ID",
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Find by ID error:", e);
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "Gagal mengambil pelayanan penduduk non permanen: " + (e instanceof Error ? e.message : 'Unknown error'),
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import pelayananPendudukNonPermanenFindUnique from "./findUnique";
|
||||||
|
import pelayananPendudukNonPermanenUpdate from "./updt";
|
||||||
|
|
||||||
|
const PelayananPendudukNonPermanen = new Elysia({ prefix: "/pelayananpenduduknonpermanen", tags: ["Desa/Layanan/Pelayanan Penduduk Non Permanen"] })
|
||||||
|
.get("/:id", async (context) => {
|
||||||
|
const response = await pelayananPendudukNonPermanenFindUnique(new Request(context.request));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.put("/:id", async (context) => {
|
||||||
|
const response = await pelayananPendudukNonPermanenUpdate(context);
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: t.Object({
|
||||||
|
name: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
export default PelayananPendudukNonPermanen;
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormUpdate = Prisma.PelayananPendudukNonPermanenGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
export default async function pelayananPendudukNonPermanenUpdate(context: Context) {
|
||||||
|
try {
|
||||||
|
const id = context.params?.id;
|
||||||
|
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||||
|
|
||||||
|
const { name, deskripsi } = body;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak diberikan",
|
||||||
|
}), {
|
||||||
|
status: 400,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const existing = await prisma.pelayananPendudukNonPermanen.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Pelayanan penduduk non permanen tidak ditemukan",
|
||||||
|
}), {
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.pelayananPendudukNonPermanen.update({
|
||||||
|
where: { id },
|
||||||
|
data: { name, deskripsi },
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Success update pelayanan penduduk non permanen",
|
||||||
|
data: updated,
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Update error:", e);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Gagal mengupdate pelayanan penduduk non permanen: " + (e instanceof Error ? e.message : 'Unknown error'),
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function pelayananPerizinanBerusahaFindUnique(
|
||||||
|
request: Request
|
||||||
|
) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const pathSegments = url.pathname.split("/");
|
||||||
|
const id = pathSegments[pathSegments.length - 1];
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak boleh kosong",
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof id !== "string") {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak valid",
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const data = await prisma.pelayananPerizinanBerusaha.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "Pelayanan perizinan berusaha tidak ditemukan",
|
||||||
|
},
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch pelayanan perizinan berusaha by ID",
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Find by ID error:", error);
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message:
|
||||||
|
"Gagal mengambil pelayanan perizinan berusaha: " +
|
||||||
|
(error instanceof Error ? error.message : "Unknown error"),
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import pelayananPerizinanBerusahaFindUnique from "./findUnique";
|
||||||
|
import pelayananPerizinanBerusahaUpdate from "./updt";
|
||||||
|
|
||||||
|
const PelayananPerizinanBerusaha = new Elysia({ prefix: "/pelayananperizinanberusaha", tags: ["Desa/Layanan/Pelayanan Perizinan Berusaha"] })
|
||||||
|
.get("/:id", async (context) => {
|
||||||
|
const response = await pelayananPerizinanBerusahaFindUnique(new Request(context.request));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.put("/:id", async (context) => {
|
||||||
|
const response = await pelayananPerizinanBerusahaUpdate(context);
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: t.Object({
|
||||||
|
name: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
link: t.String(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
export default PelayananPerizinanBerusaha;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormUpdate = Prisma.PelayananPerizinanBerusahaGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
link: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
export default async function pelayananPerizinanBerusahaUpdate(context: Context) {
|
||||||
|
try {
|
||||||
|
const id = context.params?.id;
|
||||||
|
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||||
|
|
||||||
|
const { name, deskripsi, link } = body;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak diberikan",
|
||||||
|
}), {
|
||||||
|
status: 400,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const existing = await prisma.pelayananPerizinanBerusaha.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Pelayanan perizinan berusaha tidak ditemukan",
|
||||||
|
}), {
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.pelayananPerizinanBerusaha.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
deskripsi,
|
||||||
|
link,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Success update pelayanan perizinan berusaha",
|
||||||
|
data: updated,
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating pelayanan perizinan berusaha:", error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Terjadi kesalahan saat mengupdate pelayanan perizinan berusaha",
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormCreate = Prisma.PelayananTelunjukSaktiDesaGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
link: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
export default async function pelayananTelunjukSaktiDesaCreate(context: Context) {
|
||||||
|
const body = context.body as FormCreate;
|
||||||
|
|
||||||
|
await prisma.pelayananTelunjukSaktiDesa.create({
|
||||||
|
data: {
|
||||||
|
name: body.name,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
link: body.link,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success create pelayanan telunjuk sakti desa",
|
||||||
|
data: {
|
||||||
|
...body,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
export default async function pelayananTelunjukSaktiDesaDelete(context: Context) {
|
||||||
|
const id = context.params?.id as string;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: "ID tidak diberikan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const pelayananTelunjukSaktiDesa = await prisma.pelayananTelunjukSaktiDesa.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pelayananTelunjukSaktiDesa) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: "Pelayanan telunjuk sakti desa tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.pelayananTelunjukSaktiDesa.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: "Pelayanan telunjuk sakti desa berhasil dihapus",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function pelayananTelunjukSaktiDesaFindMany() {
|
||||||
|
try {
|
||||||
|
const data = await prisma.pelayananTelunjukSaktiDesa.findMany({
|
||||||
|
where: { isActive: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch pelayanan telunjuk sakti desa",
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Find many error:", e);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Failed fetch pelayanan telunjuk sakti desa",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function pelayananTelunjukSaktiDesaFindUnique(request: Request){
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const pathSegments = url.pathname.split('/');
|
||||||
|
const id = pathSegments[pathSegments.length - 1];
|
||||||
|
|
||||||
|
if(!id){
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak boleh kosong",
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof id !== 'string') {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak valid",
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await prisma.pelayananTelunjukSaktiDesa.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Pelayanan telunjuk sakti desa tidak ditemukan",
|
||||||
|
}, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch pelayanan telunjuk sakti desa by ID",
|
||||||
|
data,
|
||||||
|
}, { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Find by ID error:", error);
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Gagal mengambil pelayanan telunjuk sakti desa: " + (error instanceof Error ? error.message : 'Unknown error'),
|
||||||
|
}, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import pelayananTelunjukSaktiDesaFindUnique from "./findUnique";
|
||||||
|
import pelayananTelunjukSaktiDesaUpdate from "./updt";
|
||||||
|
import pelayananTelunjukSaktiDesaCreate from "./create";
|
||||||
|
import pelayananTelunjukSaktiDesaDelete from "./del";
|
||||||
|
import pelayananTelunjukSaktiDesaFindMany from "./find-many";
|
||||||
|
|
||||||
|
const pelayananTelunjukSaktiDesa = new Elysia({ prefix: "/pelayanantelunjuksaktidesa", tags: ["Desa/Layanan/Pelayanan Telunjuk Sakti Desa"] })
|
||||||
|
.get("/find-many", pelayananTelunjukSaktiDesaFindMany)
|
||||||
|
.get("/:id", async (context) => {
|
||||||
|
const response = await pelayananTelunjukSaktiDesaFindUnique(new Request(context.request));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.put("/:id", async (context) => {
|
||||||
|
const response = await pelayananTelunjukSaktiDesaUpdate(context);
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: t.Object({
|
||||||
|
name: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
link: t.String(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.post("/create", pelayananTelunjukSaktiDesaCreate, {
|
||||||
|
body: t.Object({
|
||||||
|
name: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
link: t.String(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.delete("/:id", pelayananTelunjukSaktiDesaDelete)
|
||||||
|
export default pelayananTelunjukSaktiDesa;
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormUpdate = Prisma.PelayananTelunjukSaktiDesaGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
link: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
export default async function pelayananTelunjukSaktiDesaUpdate(context: Context) {
|
||||||
|
try {
|
||||||
|
const id = context.params?.id;
|
||||||
|
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||||
|
|
||||||
|
const { name, deskripsi, link } = body;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak diberikan",
|
||||||
|
}), {
|
||||||
|
status: 400,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await prisma.pelayananTelunjukSaktiDesa.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
deskripsi,
|
||||||
|
link,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Gagal update pelayanan telunjuk sakti desa",
|
||||||
|
}), {
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.pelayananTelunjukSaktiDesa.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
deskripsi,
|
||||||
|
link,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Success update pelayanan telunjuk sakti desa",
|
||||||
|
data: updated,
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Gagal update pelayanan telunjuk sakti desa",
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/app/api/[[...slugs]]/_lib/desa/penghargaan/create.ts
Normal file
32
src/app/api/[[...slugs]]/_lib/desa/penghargaan/create.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormCreate = Prisma.PenghargaanGetPayload<{
|
||||||
|
select: {
|
||||||
|
juara: true;
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
imageId: true;
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
export default async function penghargaanCreate(context: Context){
|
||||||
|
const body = context.body as FormCreate;
|
||||||
|
|
||||||
|
await prisma.penghargaan.create({
|
||||||
|
data: {
|
||||||
|
juara: body.juara,
|
||||||
|
name: body.name,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
imageId: body.imageId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success create penghargaan",
|
||||||
|
data: {
|
||||||
|
...body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/app/api/[[...slugs]]/_lib/desa/penghargaan/del.ts
Normal file
53
src/app/api/[[...slugs]]/_lib/desa/penghargaan/del.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const penghargaanDelete = async (context: Context) => {
|
||||||
|
const id = context.params?.id as string;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: "ID tidak diberikan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const penghargaan = await prisma.penghargaan.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!penghargaan) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: "Penghargaan tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (penghargaan.image) {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(penghargaan.image.path, penghargaan.image.name);
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
await prisma.fileStorage.delete({
|
||||||
|
where: { id: penghargaan.image.id },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal hapus file image:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.penghargaan.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Penghargaan berhasil dihapus",
|
||||||
|
status: 200,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default penghargaanDelete
|
||||||
23
src/app/api/[[...slugs]]/_lib/desa/penghargaan/find-many.ts
Normal file
23
src/app/api/[[...slugs]]/_lib/desa/penghargaan/find-many.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function penghargaanFindMany() {
|
||||||
|
try {
|
||||||
|
const data = await prisma.penghargaan.findMany({
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch penghargaan",
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Find many error:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Failed fetch penghargaan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/app/api/[[...slugs]]/_lib/desa/penghargaan/findUnique.ts
Normal file
49
src/app/api/[[...slugs]]/_lib/desa/penghargaan/findUnique.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function penghargaanFindUnique(request: Request) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const pathSegments = url.pathname.split("/");
|
||||||
|
const id = pathSegments[pathSegments.length - 1];
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak ditemukan",
|
||||||
|
}, {status: 400});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof id !== 'string') {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak valid",
|
||||||
|
}, {status: 400});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await prisma.penghargaan.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Penghargaan tidak ditemukan",
|
||||||
|
}, {status: 404});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch penghargaan by ID",
|
||||||
|
data,
|
||||||
|
}, {status: 200});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Find by ID error:", error);
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Gagal mengambil penghargaan: " + (error instanceof Error ? error.message : 'Unknown error'),
|
||||||
|
}, {status: 500});
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/app/api/[[...slugs]]/_lib/desa/penghargaan/index.ts
Normal file
35
src/app/api/[[...slugs]]/_lib/desa/penghargaan/index.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import penghargaanFindMany from "./find-many";
|
||||||
|
import penghargaanFindUnique from "./findUnique";
|
||||||
|
import penghargaanCreate from "./create";
|
||||||
|
import penghargaanDelete from "./del";
|
||||||
|
import penghargaanUpdate from "./updt";
|
||||||
|
|
||||||
|
|
||||||
|
const Penghargaan = new Elysia({prefix: "/penghargaan", tags: ["Desa/Penghargaan"]})
|
||||||
|
.get("/find-many", penghargaanFindMany)
|
||||||
|
.get("/:id", async (context) => {
|
||||||
|
const response = await penghargaanFindUnique(context.request);
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.post("/create", penghargaanCreate, {
|
||||||
|
body: t.Object({
|
||||||
|
juara: t.String(),
|
||||||
|
name: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
imageId: t.String(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.delete("/del/:id", penghargaanDelete)
|
||||||
|
.put("/:id", async (context) => {
|
||||||
|
const response = await penghargaanUpdate(context);
|
||||||
|
return response;
|
||||||
|
}, {
|
||||||
|
body: t.Object({
|
||||||
|
juara: t.String(),
|
||||||
|
name: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
imageId: t.String(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export default Penghargaan;
|
||||||
106
src/app/api/[[...slugs]]/_lib/desa/penghargaan/updt.ts
Normal file
106
src/app/api/[[...slugs]]/_lib/desa/penghargaan/updt.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormUpdate = Prisma.PenghargaanGetPayload<{
|
||||||
|
select: {
|
||||||
|
id: true;
|
||||||
|
juara: true;
|
||||||
|
name: true;
|
||||||
|
deskripsi: true;
|
||||||
|
imageId: true;
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
export default async function penghargaanUpdate(context: Context) {
|
||||||
|
try {
|
||||||
|
const id = context.params?.id as string;
|
||||||
|
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||||
|
|
||||||
|
const {
|
||||||
|
juara,
|
||||||
|
name,
|
||||||
|
deskripsi,
|
||||||
|
imageId
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak ditemukan",
|
||||||
|
}), {
|
||||||
|
status: 400,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await prisma.penghargaan.findUnique({
|
||||||
|
where: {id},
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Penghargaan tidak ditemukan",
|
||||||
|
}), {
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing.imageId && existing.imageId !== imageId) {
|
||||||
|
const oldImage = existing.image;
|
||||||
|
if (oldImage) {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(oldImage.path, oldImage.name);
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
await prisma.fileStorage.delete({
|
||||||
|
where: { id: oldImage.id },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal hapus gambar lama:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.penghargaan.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
juara,
|
||||||
|
name,
|
||||||
|
deskripsi,
|
||||||
|
imageId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Penghargaan berhasil diupdate",
|
||||||
|
data: updated,
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating penghargaan:", error);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Terjadi kesalahan saat mengupdate penghargaan",
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user