fix(header): fix missing Divider, Badge, IconUserShield and navigate

This commit is contained in:
2026-03-26 14:13:59 +08:00
parent ebc1242bee
commit aeedb17402
35 changed files with 2788 additions and 552 deletions

66
src/api/complaint.ts Normal file
View File

@@ -0,0 +1,66 @@
import Elysia from "elysia";
import { prisma } from "../utils/db";
import logger from "../utils/logger";
export const complaint = new Elysia({
prefix: "/complaint",
})
.get(
"/stats",
async ({ set }) => {
try {
const [total, baru, proses, selesai] = await Promise.all([
prisma.complaint.count(),
prisma.complaint.count({ where: { status: "BARU" } }),
prisma.complaint.count({ where: { status: "DIPROSES" } }),
prisma.complaint.count({ where: { status: "SELESAI" } }),
]);
return { data: { total, baru, proses, selesai } };
} catch (error) {
logger.error({ error }, "Failed to fetch complaint stats");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get complaint statistics" },
},
)
.get(
"/recent",
async ({ set }) => {
try {
const recent = await prisma.complaint.findMany({
orderBy: { createdAt: "desc" },
take: 10,
});
return { data: recent };
} catch (error) {
logger.error({ error }, "Failed to fetch recent complaints");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get recent complaints" },
},
)
.get(
"/service-stats",
async ({ set }) => {
try {
const serviceStats = await prisma.serviceLetter.groupBy({
by: ["letterType"],
_count: { _all: true },
});
return { data: serviceStats };
} catch (error) {
logger.error({ error }, "Failed to fetch service stats");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get service letter statistics by type" },
},
);

71
src/api/division.ts Normal file
View File

@@ -0,0 +1,71 @@
import Elysia from "elysia";
import { prisma } from "../utils/db";
import logger from "../utils/logger";
export const division = new Elysia({
prefix: "/division",
})
.get(
"/",
async ({ set }) => {
try {
const divisions = await prisma.division.findMany({
include: {
_count: {
select: { activities: true },
},
},
});
return { data: divisions };
} catch (error) {
logger.error({ error }, "Failed to fetch divisions");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get all divisions" },
},
)
.get(
"/activities",
async ({ set }) => {
try {
const activities = await prisma.activity.findMany({
include: {
division: {
select: { name: true, color: true },
},
},
orderBy: { createdAt: "desc" },
take: 10,
});
return { data: activities };
} catch (error) {
logger.error({ error }, "Failed to fetch activities");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get recent activities" },
},
)
.get(
"/metrics",
async ({ set }) => {
try {
const metrics = await prisma.divisionMetric.findMany({
include: { division: true },
});
return { data: metrics };
} catch (error) {
logger.error({ error }, "Failed to fetch division metrics");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get division performance metrics" },
},
);

54
src/api/event.ts Normal file
View File

@@ -0,0 +1,54 @@
import Elysia from "elysia";
import { prisma } from "../utils/db";
import logger from "../utils/logger";
export const event = new Elysia({
prefix: "/event",
})
.get(
"/",
async ({ set }) => {
try {
const events = await prisma.event.findMany({
orderBy: { startDate: "asc" },
take: 20,
});
return { data: events };
} catch (error) {
logger.error({ error }, "Failed to fetch events");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get upcoming events" },
},
)
.get(
"/today",
async ({ set }) => {
try {
const start = new Date();
start.setHours(0, 0, 0, 0);
const end = new Date();
end.setHours(23, 59, 59, 999);
const events = await prisma.event.findMany({
where: {
startDate: {
gte: start,
lte: end,
},
},
});
return { data: events };
} catch (error) {
logger.error({ error }, "Failed to fetch today's events");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get events for today" },
},
);

View File

@@ -4,7 +4,11 @@ import Elysia from "elysia";
import { apiMiddleware } from "../middleware/apiMiddleware";
import { auth } from "../utils/auth";
import { apikey } from "./apikey";
import { complaint } from "./complaint";
import { division } from "./division";
import { event } from "./event";
import { profile } from "./profile";
import { resident } from "./resident";
const isProduction = process.env.NODE_ENV === "production";
@@ -20,7 +24,11 @@ const api = new Elysia({
})
.use(apiMiddleware)
.use(apikey)
.use(profile);
.use(profile)
.use(division)
.use(complaint)
.use(resident)
.use(event);
if (!isProduction) {
api.use(

View File

@@ -2,12 +2,25 @@ import Elysia, { t } from "elysia";
import { prisma } from "../utils/db";
import logger from "../utils/logger";
interface AuthenticatedUser {
id: string;
email: string;
name?: string | null;
}
export const profile = new Elysia({
prefix: "/profile",
}).post(
"/update",
async (ctx) => {
const { body, set, user } = ctx as any;
async ({
body,
set,
user,
}: {
body: { name?: string; image?: string };
set: any;
user?: AuthenticatedUser;
}) => {
try {
if (!user) {
set.status = 401;

76
src/api/resident.ts Normal file
View File

@@ -0,0 +1,76 @@
import Elysia from "elysia";
import { prisma } from "../utils/db";
import logger from "../utils/logger";
export const resident = new Elysia({
prefix: "/resident",
})
.get(
"/stats",
async ({ set }) => {
try {
const [total, heads, poor] = await Promise.all([
prisma.resident.count(),
prisma.resident.count({ where: { isHeadOfHousehold: true } }),
prisma.resident.count({ where: { isPoor: true } }),
]);
return { data: { total, heads, poor } };
} catch (error) {
logger.error({ error }, "Failed to fetch resident stats");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get resident statistics" },
},
)
.get(
"/banjar-stats",
async ({ set }) => {
try {
const banjarStats = await prisma.banjar.findMany({
select: {
id: true,
name: true,
totalPopulation: true,
totalKK: true,
totalPoor: true,
},
});
return { data: banjarStats };
} catch (error) {
logger.error({ error }, "Failed to fetch banjar stats");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get population data per banjar" },
},
)
.get(
"/demographics",
async ({ set }) => {
try {
const [religion, gender] = await Promise.all([
prisma.resident.groupBy({
by: ["religion"],
_count: { _all: true },
}),
prisma.resident.groupBy({
by: ["gender"],
_count: { _all: true },
}),
]);
return { data: { religion, gender } };
} catch (error) {
logger.error({ error }, "Failed to fetch demographics");
set.status = 500;
return { error: "Internal Server Error" };
}
},
{
detail: { summary: "Get religious and gender demographics" },
},
);