API & UI Menu Landing Page, Submenu Profile

This commit is contained in:
2025-07-22 17:13:56 +08:00
parent 9b2201ea57
commit d4efcacf1b
35 changed files with 2119 additions and 50 deletions

View File

@@ -0,0 +1,15 @@
import Elysia from "elysia";
import MediaSosial from "./profile/media-sosial";
import ProgramInovasi from "./profile/program-inovasi";
import PejabatDesa from "./profile/pejabat-desa";
const LandingPage = new Elysia({
prefix: "/api/landingpage",
tags: ["Landing Page/Profile"]
})
.use(MediaSosial)
.use(ProgramInovasi)
.use(PejabatDesa)
export default LandingPage

View File

@@ -0,0 +1,34 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
imageId: string;
iconUrl: string;
};
export default async function mediaSosialCreate(context: Context) {
const body = context.body as FormCreate;
try {
const result = await prisma.mediaSosial.create({
data: {
imageId: body.imageId,
iconUrl: body.iconUrl,
},
include: {
image: true,
},
});
return {
success: true,
message: "Berhasil membuat media sosial",
data: result,
};
} catch (error) {
console.error("Error creating media sosial:", error);
throw new Error(
"Gagal membuat media sosial: " + (error as Error).message
);
}
}

View File

@@ -0,0 +1,21 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function mediaSosialDelete(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
throw new Error("ID tidak ditemukan dalam parameter");
}
const deleted = await prisma.mediaSosial.delete({
where: { id },
});
return {
success: true,
message: "Berhasil menghapus media sosial",
data: deleted,
};
}

View File

@@ -0,0 +1,43 @@
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function mediaSosialFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
try {
const [data, total] = await Promise.all([
prisma.mediaSosial.findMany({
where: { isActive: true },
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}),
prisma.mediaSosial.count({
where: { isActive: true },
}),
]);
return {
success: true,
message: "Success fetch media sosial with pagination",
data,
page,
totalPages: Math.ceil(total / limit),
total,
};
} catch (e) {
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch media sosial with pagination",
};
}
}
export default mediaSosialFindMany;

View File

@@ -0,0 +1,28 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function mediaSosialFindUnique(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
throw new Error("ID tidak ditemukan dalam parameter");
}
const data = await prisma.mediaSosial.findUnique({
where: { id },
include: {
image: true,
},
});
if (!data) {
throw new Error("Media sosial tidak ditemukan");
}
return {
success: true,
message: "Data media sosial ditemukan",
data,
};
}

View File

@@ -0,0 +1,37 @@
import Elysia, { t } from "elysia";
import MediaSosialCreate from "./create";
import MediaSosialDelete from "./del";
import MediaSosialFindMany from "./findMany";
import MediaSosialFindUnique from "./findUnique";
import MediaSosialUpdate from "./updt";
const MediaSosial = new Elysia({
prefix: "/mediasosial",
tags: ["Landing Page/Profile/Media Sosial"],
})
// ✅ Find all
.get("/find-many", MediaSosialFindMany)
// ✅ Find by ID
.get("/:id", MediaSosialFindUnique)
// ✅ Create
.post("/create", MediaSosialCreate, {
body: t.Object({
imageId: t.String(),
iconUrl: t.String(),
}),
})
// ✅ Update
.put("/:id", MediaSosialUpdate, {
body: t.Object({
imageId: t.Optional(t.String()),
iconUrl: t.Optional(t.String()),
}),
})
// ✅ Delete
.delete("/del/:id", MediaSosialDelete);
export default MediaSosial;

View File

@@ -0,0 +1,47 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdateMediaSosial = {
imageId?: string;
iconUrl?: string;
};
export default async function mediaSosialUpdate(context: Context) {
const body = context.body as FormUpdateMediaSosial;
const id = context.params.id;
if (!id) {
return {
success: false,
message: "ID media sosial wajib diisi",
};
}
try {
const updated = await prisma.mediaSosial.update({
where: { id },
data: {
imageId: body.imageId,
iconUrl: body.iconUrl,
},
include: {
image: true,
},
});
return {
success: true,
message: "Media sosial berhasil diperbarui",
data: updated,
};
} catch (error: any) {
console.error("❌ Error update media sosial:", error);
return {
success: false,
message: "Gagal memperbarui data media sosial",
error: error.message,
};
}
}

View File

@@ -0,0 +1,115 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
import fs from "fs/promises";
import path from "path";
type FormUpdate = Prisma.PejabatDesaGetPayload<{
select: {
id: true;
name: true;
position: true;
imageId: true;
};
}>;
export default async function pejabatDesaFindUnique(context: Context) {
try {
const id = context.params?.id as string;
const body = (await context.body) as Omit<FormUpdate, "id">;
const { name, position, imageId } = body;
if (!id) {
return new Response(
JSON.stringify({
success: false,
message: "ID tidak boleh kosong",
}),
{
status: 400,
headers: {
"Content-Type": "application/json",
},
}
);
}
const existing = await prisma.pejabatDesa.findUnique({
where: {
id,
},
include: {
image: true,
},
});
if (!existing) {
return new Response(
JSON.stringify({
success: false,
message: "Data tidak ditemukan",
}),
{
status: 404,
headers: {
"Content-Type": "application/json",
},
}
);
}
if (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.pejabatDesa.update({
where: {
id,
},
data: {
name,
position,
imageId,
},
});
return new Response(
JSON.stringify({
success: true,
message: "Data pejabat desa berhasil ditemukan",
data: updated,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
}
);
} catch (error) {
console.error("Error updating pejabat desa:", error);
return new Response(
JSON.stringify({
success: false,
message: "Terjadi kesalahan saat mengupdate pejabat desa",
}),
{
status: 500,
headers: {
"Content-Type": "application/json",
},
}
);
}
}

View File

@@ -0,0 +1,28 @@
import Elysia, { t } from "elysia";
import pejabatDesaFindUnique from "./findUnique";
import pejabatDesaUpdate from "./updt";
const PejabatDesa = new Elysia({
prefix: "/pejabatdesa",
tags: ["PPID/Profile PPID"]
})
.get("/:id", async (context) => {
const response = await pejabatDesaFindUnique(context)
return response
})
.put("/:id", async (context) => {
const response = await pejabatDesaUpdate(context)
return response
},
{
body: t.Object({
name: t.String(),
position: t.String(),
imageId: t.String(),
})
}
)
export default PejabatDesa;

View File

@@ -0,0 +1,115 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
import fs from "fs/promises";
import path from "path";
type FormUpdate = Prisma.PejabatDesaGetPayload<{
select: {
id: true;
name: true;
position: true;
imageId: true;
};
}>;
export default async function pejabatDesaUpdate(context: Context) {
try {
const id = context.params?.id as string;
const body = (await context.body) as Omit<FormUpdate, "id">;
const { name, position, imageId } = body;
if (!id) {
return new Response(
JSON.stringify({
success: false,
message: "ID tidak boleh kosong",
}),
{
status: 400,
headers: {
"Content-Type": "application/json",
},
}
);
}
const existing = await prisma.pejabatDesa.findUnique({
where: {
id,
},
include: {
image: true,
},
});
if (!existing) {
return new Response(
JSON.stringify({
success: false,
message: "Data tidak ditemukan",
}),
{
status: 404,
headers: {
"Content-Type": "application/json",
},
}
);
}
if (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.pejabatDesa.update({
where: {
id,
},
data: {
name,
position,
imageId,
},
});
return new Response(
JSON.stringify({
success: true,
message: "Pejabat Desa Berhasil Dibuat",
data: updated,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
}
);
} catch (error) {
console.error("Error updating pejabat desa:", error);
return new Response(
JSON.stringify({
success: false,
message: "Terjadi kesalahan saat mengupdate pejabat desa",
}),
{
status: 500,
headers: {
"Content-Type": "application/json",
},
}
);
}
}

View File

@@ -0,0 +1,42 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
name: string;
description: string;
imageId: string;
link: string;
};
export default async function programInovasiCreate(context: Context) {
const body = context.body as FormCreate;
if (!body.name) {
throw new Error("name wajib diisi");
}
try {
const result = await prisma.programInovasi.create({
data: {
name: body.name,
description: body.description,
imageId: body.imageId,
link: body.link,
},
include: {
image: true,
},
});
return {
success: true,
message: "Berhasil membuat program inovasi",
data: result,
};
} catch (error) {
console.error("Error creating program inovasi:", error);
throw new Error(
"Gagal membuat program inovasi: " + (error as Error).message
);
}
}

View File

@@ -0,0 +1,21 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function programInovasiDelete(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
throw new Error("ID tidak ditemukan dalam parameter");
}
const deleted = await prisma.programInovasi.delete({
where: { id },
});
return {
success: true,
message: "Berhasil menghapus program inovasi",
data: deleted,
};
}

View File

@@ -0,0 +1,43 @@
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function programInovasiFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
try {
const [data, total] = await Promise.all([
prisma.programInovasi.findMany({
where: { isActive: true },
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}),
prisma.programInovasi.count({
where: { isActive: true },
}),
]);
return {
success: true,
message: "Success fetch program inovasi with pagination",
data,
page,
totalPages: Math.ceil(total / limit),
total,
};
} catch (e) {
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch program inovasi with pagination",
};
}
}
export default programInovasiFindMany;

View File

@@ -0,0 +1,28 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function programInovasiFindUnique(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
throw new Error("ID tidak ditemukan dalam parameter");
}
const data = await prisma.programInovasi.findUnique({
where: { id },
include: {
image: true,
},
});
if (!data) {
throw new Error("Program inovasi tidak ditemukan");
}
return {
success: true,
message: "Data program inovasi ditemukan",
data,
};
}

View File

@@ -0,0 +1,41 @@
import Elysia, { t } from "elysia";
import ProgramInovasiCreate from "./create";
import ProgramInovasiDelete from "./del";
import ProgramInovasiFindMany from "./findMany";
import ProgramInovasiFindUnique from "./findUnique";
import ProgramInovasiUpdate from "./updt";
const ProgramInovasi = new Elysia({
prefix: "/programinovasi",
tags: ["Landing Page/Profile/Program Inovasi"],
})
// ✅ Find all
.get("/find-many", ProgramInovasiFindMany)
// ✅ Find by ID
.get("/:id", ProgramInovasiFindUnique)
// ✅ Create
.post("/create", ProgramInovasiCreate, {
body: t.Object({
name: t.String(),
description: t.String(),
imageId: t.String(),
link: t.String(),
}),
})
// ✅ Update
.put("/:id", ProgramInovasiUpdate, {
body: t.Object({
name: t.Optional(t.String()),
description: t.Optional(t.String()),
imageId: t.Optional(t.String()),
link: t.Optional(t.String()),
}),
})
// ✅ Delete
.delete("/del/:id", ProgramInovasiDelete);
export default ProgramInovasi;

View File

@@ -0,0 +1,52 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdateProgramInovasi = {
name?: string;
description?: string;
imageId?: string;
link?: string;
};
export default async function programInovasiUpdate(context: Context) {
const body = context.body as FormUpdateProgramInovasi;
const id = context.params.id;
if (!id) {
return {
success: false,
message: "ID program inovasi wajib diisi",
};
}
try {
const updated = await prisma.programInovasi.update({
where: { id },
data: {
name: body.name,
description: body.description,
imageId: body.imageId,
link: body.link,
updatedAt: new Date(),
},
include: {
image: true,
},
});
return {
success: true,
message: "Program inovasi berhasil diperbarui",
data: updated,
};
} catch (error: any) {
console.error("❌ Error update program inovasi:", error);
return {
success: false,
message: "Gagal memperbarui data program inovasi",
error: error.message,
};
}
}

View File

@@ -21,6 +21,7 @@ import Keamanan from "./_lib/keamanan";
import Ekonomi from "./_lib/ekonomi";
import Inovasi from "./_lib/inovasi";
import Lingkungan from "./_lib/lingkungan";
import LandingPage from "./_lib/landing_page";
const ROOT = process.cwd();
@@ -85,6 +86,7 @@ const ApiServer = new Elysia()
.use(Ekonomi)
.use(Inovasi)
.use(Lingkungan)
.use(LandingPage)
.onError(({ code }) => {
if (code === "NOT_FOUND") {
return {