This commit is contained in:
bipproduction
2025-12-11 17:06:33 +08:00
parent eb5eee6ae9
commit 83f795a726
6 changed files with 156 additions and 204 deletions

View File

@@ -11,7 +11,8 @@ import { cors } from "@elysiajs/cors";
import packageJson from "./../package.json"; import packageJson from "./../package.json";
import Configs from "./server/routes/configs_route"; import Configs from "./server/routes/configs_route";
import { prisma } from "./server/lib/prisma"; import { prisma } from "./server/lib/prisma";
import JadwalSholat from "./server/routes/jadwal_sholat"; import JadwalShalat from "./server/routes/jadwal_shalat";
import { JadwalShalatAdmin } from "./server/routes/jadwal_shalat_admin";
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
const Docs = new Elysia().use( const Docs = new Elysia().use(
@@ -71,11 +72,12 @@ const ApiUser = new Elysia({
const Api = new Elysia({ const Api = new Elysia({
prefix: "/api", prefix: "/api",
}) })
.use(JadwalShalat)
.use(Configs) .use(Configs)
.use(apiAuth) .use(apiAuth)
.use(ApiKeyRoute) .use(ApiKeyRoute)
.use(ApiUser) .use(ApiUser)
.use(JadwalSholat); .use(JadwalShalatAdmin);
const app = new Elysia() const app = new Elysia()
.use(cors()) .use(cors())
@@ -129,7 +131,7 @@ const app = new Elysia()
}, },
}, },
) )
.get("/*", html) .get("*", html)
.listen(PORT, () => { .listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`); console.log(`Server running at http://localhost:${PORT}`);
}); });

View File

@@ -1,7 +1,13 @@
import clientRoutes from "@/clientRoutes"; import clientRoutes from "@/clientRoutes";
import { Button, Card, Container, Group, Stack, Title } from "@mantine/core"; import { Button, Card, Container, Group, Stack, Title } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { useNavigate } from "react-router-dom";
export default function Home() { export default function Home() {
useShallowEffect(() => {
window.location.reload()
},[])
return ( return (
<Container size={420} py={80}> <Container size={420} py={80}>
<Card shadow="sm" padding="xl" radius="md"> <Card shadow="sm" padding="xl" radius="md">

View File

@@ -1,12 +1,13 @@
import clientRoutes from "@/clientRoutes";
import apiFetch from "@/lib/apiFetch"; import apiFetch from "@/lib/apiFetch";
import { import {
ActionIcon,
Button, Button,
Card, Card,
Container, Container,
Flex, Flex,
Group, Group,
Loader, Loader,
Radio,
Stack, Stack,
Switch, Switch,
Text, Text,
@@ -15,8 +16,10 @@ import {
} from "@mantine/core"; } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks"; import { useShallowEffect } from "@mantine/hooks";
import { notifications } from "@mantine/notifications"; import { notifications } from "@mantine/notifications";
import { IconEye } from "@tabler/icons-react";
import type { Configs, User } from "generated/prisma"; import type { Configs, User } from "generated/prisma";
import { useState } from "react"; import { useState } from "react";
import { useNavigate } from "react-router";
import useSwr from "swr"; import useSwr from "swr";
export default function JadwalShalat() { export default function JadwalShalat() {
@@ -37,6 +40,7 @@ function ListUser() {
apiFetch.api["jadwal-sholat"]["user-list"].get, apiFetch.api["jadwal-sholat"]["user-list"].get,
); );
const [listUser, setListUser] = useState<User[]>([]); const [listUser, setListUser] = useState<User[]>([]);
const navigate = useNavigate();
useShallowEffect(() => { useShallowEffect(() => {
setListUser(data?.data?.data ?? []); setListUser(data?.data?.data ?? []);
}, [data]); }, [data]);
@@ -44,27 +48,38 @@ function ListUser() {
if (error) return <Text>{error.message}</Text>; if (error) return <Text>{error.message}</Text>;
return ( return (
<Card> <Stack>
<Stack> <Group justify="end">
<Title order={4}>List User</Title> <ActionIcon
{listUser.map((user) => ( variant="subtle"
<Stack key={user.id}> color="blue"
<Flex> onClick={() => navigate(clientRoutes["/shalat/shalat"])}
<Text w={200}>{user.name}</Text> >
<Switch <IconEye />
defaultChecked={user.active} </ActionIcon>
onChange={async (e) => { </Group>
const { data } = await apiFetch.api["jadwal-sholat"][ <Card>
"user-active" <Stack>
].put({ id: user.id, active: e.target.checked }); <Title order={4}>List User</Title>
mutate(); {listUser.map((user) => (
}} <Stack key={user.id}>
/> <Flex>
</Flex> <Text w={200}>{user.name}</Text>
</Stack> <Switch
))} defaultChecked={user.active}
</Stack> onChange={async (e) => {
</Card> const { data } = await apiFetch.api["jadwal-sholat-admin"][
"user-active"
].put({ id: user.id, active: e.target.checked });
mutate();
}}
/>
</Flex>
</Stack>
))}
</Stack>
</Card>
</Stack>
); );
} }
@@ -110,31 +125,33 @@ function ConfigUpdate() {
}, [data]); }, [data]);
async function handleUpdate() { async function handleUpdate() {
if (!config?.ikomahKey || !config?.imamKey) return notifications.show({ if (!config?.ikomahKey || !config?.imamKey)
title: "Error", return notifications.show({
message: "Config updated failed", title: "Error",
color: "red", message: "Config updated failed",
}); color: "red",
const { data , status} = await apiFetch.api["jadwal-sholat"]["config"].put({ });
const { data, status } = await apiFetch.api["jadwal-sholat-admin"][
"config"
].put({
id: "1", id: "1",
ikomahKey: config.ikomahKey, ikomahKey: config.ikomahKey,
imamKey: config.imamKey, imamKey: config.imamKey,
}); });
if(status === 200 ) { if (status === 200) {
notifications.show({ notifications.show({
title: "Success", title: "Success",
message: "Config updated successfully", message: "Config updated successfully",
color: "green", color: "green",
}); });
}else{ } else {
notifications.show({ notifications.show({
title: "Error", title: "Error",
message: "Config updated failed", message: "Config updated failed",
color: "red", color: "red",
}); });
} }
} }
if (isLoading) return <Loader />; if (isLoading) return <Loader />;
@@ -146,11 +163,19 @@ function ConfigUpdate() {
<Title order={4}>Config</Title> <Title order={4}>Config</Title>
<Flex> <Flex>
<Text w={200}>Imam Key</Text> <Text w={200}>Imam Key</Text>
<TextInput defaultValue={config?.imamKey} onChange={(e) => setConfig({ ...config, imamKey: e.target.value })} /> <TextInput
defaultValue={config?.imamKey}
onChange={(e) => setConfig({ ...config, imamKey: e.target.value })}
/>
</Flex> </Flex>
<Flex> <Flex>
<Text w={200}>Ikomah Key</Text> <Text w={200}>Ikomah Key</Text>
<TextInput defaultValue={config?.ikomahKey} onChange={(e) => setConfig({ ...config, ikomahKey: e.target.value })} /> <TextInput
defaultValue={config?.ikomahKey}
onChange={(e) =>
setConfig({ ...config, ikomahKey: e.target.value })
}
/>
</Flex> </Flex>
<Group justify="end"> <Group justify="end">
<Button onClick={handleUpdate}>Update</Button> <Button onClick={handleUpdate}>Update</Button>

View File

@@ -13,14 +13,13 @@ import {
Stack, Stack,
Text, Text,
Title, Title,
UnstyledButton UnstyledButton,
} from "@mantine/core"; } from "@mantine/core";
import { DatePicker, DatePickerInput } from "@mantine/dates"; import { DatePicker } from "@mantine/dates";
import dayjs from "dayjs"; import dayjs from "dayjs";
import "dayjs/locale/id"; import "dayjs/locale/id";
import duration from "dayjs/plugin/duration"; import duration from "dayjs/plugin/duration";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { DateScrollPicker } from 'react-date-wheel-picker'
import { import {
IconCalendar, IconCalendar,
@@ -39,7 +38,8 @@ import {
import DateHolidays, { type HolidaysTypes } from "date-holidays"; import DateHolidays, { type HolidaysTypes } from "date-holidays";
import useSwr from "swr"; import useSwr from "swr";
import { useNavigate } from "react-router";
import clientRoutes from "@/clientRoutes";
dayjs.locale("id"); dayjs.locale("id");
dayjs.extend(duration); dayjs.extend(duration);
@@ -62,6 +62,7 @@ export default function AdhanPage() {
const [daily, setDaily] = useState<any>(null); const [daily, setDaily] = useState<any>(null);
const [adhan, setAdhan] = useState<any>(null); const [adhan, setAdhan] = useState<any>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const navigate = useNavigate();
// tahun & bulan yang dipakai // tahun & bulan yang dipakai
const year = dayjs(date).year(); const year = dayjs(date).year();
@@ -217,7 +218,12 @@ export default function AdhanPage() {
<Stack gap="xl" py="md"> <Stack gap="xl" py="md">
<Stack justify="apart" align="center"> <Stack justify="apart" align="center">
<Stack justify="center" align="center"> <Stack justify="center" align="center">
<IconCalendar color="cyan" size={"6rem"} stroke={1.3} /> <IconCalendar
color="cyan"
size={"6rem"}
stroke={1.3}
onClick={() => navigate("/")}
/>
<Title order={2} fw={700}> <Title order={2} fw={700}>
Jadwal Sholat & Imam Jadwal Sholat & Imam
</Title> </Title>
@@ -245,17 +251,22 @@ export default function AdhanPage() {
locale="id" locale="id"
value={date} value={date}
date={date || undefined} date={date || undefined}
renderDay={(d) => <Text c={ dayjs(d).date() === dayjs(date).date() ? "green" : isHoliday(dayjs(d).format("YYYY-MM-DD")) ? "red" : ""}>{dayjs(d).format("DD")}</Text>} renderDay={(d) => (
<Text
c={
dayjs(d).date() === dayjs(date).date()
? "green"
: isHoliday(dayjs(d).format("YYYY-MM-DD"))
? "red"
: ""
}
>
{dayjs(d).format("DD")}
</Text>
)}
defaultDate={date || undefined} defaultDate={date || undefined}
onChange={setDate as any} onChange={setDate as any}
/> />
{/* <DatePickerInput
locale="id"
value={date}
onChange={setDate as any}
placeholder="Pilih tanggal"
radius="md"
/> */}
</Stack> </Stack>
</Card> </Card>
</SimpleGrid> </SimpleGrid>
@@ -531,65 +542,6 @@ function RingkasanBulalan(
); );
})} })}
</SimpleGrid> </SimpleGrid>
{/* <Paper radius="sm" >
<Table verticalSpacing="sm" >
<Table.Thead>
<Table.Tr>
<Table.Th style={{ width: 120 }}>Tanggal</Table.Th>
<Table.Th>Imam</Table.Th>
<Table.Th>Ikomah</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{Object.keys(monthly.data.imam).map((d) => {
const tglNum = Number(d);
const iso = dayjs(
`${year}-${String(month).padStart(2, "0")}-${String(tglNum).padStart(2, "0")}`,
).format("YYYY-MM-DD");
const holiday = isHoliday(iso);
const isToday = dayjs(iso).isSame(dayjs(), "day");
return (
<Table.Tr
key={d}
c={holiday ? "red.9" : isToday ? "green.9" : ""}
>
<Table.Td>
<Text fw={isToday ? 700 : 500}>
{dayjs(iso).format("ddd, DD MMM")}
</Text>
</Table.Td>
<Table.Td>
{monthly.data.imam[d] ? (
<Badge
leftSection={<IconUser size={12} />}
variant="light"
color="blue"
>
{monthly.data.imam[d]}
</Badge>
) : (
<Text c="dimmed">-</Text>
)}
</Table.Td>
<Table.Td>
{monthly.data.ikomah[d] ? (
<Badge
leftSection={<IconClock size={12} />}
variant="light"
color="grape"
>
{monthly.data.ikomah[d]}
</Badge>
) : (
<Text c="dimmed">-</Text>
)}
</Table.Td>
</Table.Tr>
);
})}
</Table.Tbody>
</Table>
</Paper> */}
</Stack> </Stack>
</Card> </Card>
); );
@@ -625,47 +577,6 @@ function FullYearHoliday(year: number, holidays: HolidaysTypes.Holiday[]) {
</SimpleGrid> </SimpleGrid>
</Stack> </Stack>
)} )}
{/* <Paper radius="lg" >
<Table
stickyHeader
verticalSpacing="md"
highlightOnHover
w="100%"
>
<Table.Thead
style={{
position: "sticky",
top: 0,
background: "white",
zIndex: 2,
}}
>
<Table.Tr>
<Table.Th style={{ width: 240 }}>Tanggal</Table.Th>
<Table.Th>Nama Libur</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{holidays.map((h, idx) => {
const tgl = dayjs(h.date).format("dddd, DD MMMM YYYY");
return (
<Table.Tr key={idx}>
<Table.Td>
<Group>
<IconCalendarStar size={16} />
<Text>{tgl}</Text>
</Group>
</Table.Td>
<Table.Td style={{ fontWeight: 700 }}>{h.name}</Table.Td>
</Table.Tr>
);
})}
</Table.Tbody>
</Table>
</Paper> */}
</Stack> </Stack>
</Card> </Card>
); );
@@ -708,9 +619,6 @@ function JadwalHariIni(
{p.name} {p.name}
</Text> </Text>
</Group> </Group>
{/* <Text size="sm" c={p.isPast ? "dimmed" : undefined}>
{p.isPast ? "Sudah lewat" : (formatCountdown(p.dt) ?? "-")}
</Text> */}
</Group> </Group>
<Group justify="apart" align="center"> <Group justify="apart" align="center">

View File

@@ -9,6 +9,7 @@ import dayjs from "dayjs";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import tz from "dayjs/plugin/timezone"; import tz from "dayjs/plugin/timezone";
import "dayjs/locale/id"; import "dayjs/locale/id";
import type { User } from "generated/prisma";
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(tz); dayjs.extend(tz);
@@ -29,7 +30,7 @@ function normalize(dateStr: string) {
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------
MAIN ROUTER MAIN ROUTER
------------------------------------------------------------------- */ ------------------------------------------------------------------- */
const JadwalSholat = new Elysia({ const JadwalShalat = new Elysia({
prefix: "/jadwal-sholat", prefix: "/jadwal-sholat",
tags: ["jadwal_sholat"], tags: ["jadwal_sholat"],
}) })
@@ -137,10 +138,12 @@ const JadwalSholat = new Elysia({
}, },
} }
) )
.get("/user-list", async () => { .get("/user-list", async (ctx) => {
const user = await prisma.user.findMany(); const { user }: { user: User } = ctx as any;
const getUsers = await prisma.user.findMany();
return { return {
data: user, data: getUsers,
}; };
}, { }, {
detail: { detail: {
@@ -148,32 +151,12 @@ const JadwalSholat = new Elysia({
description: "mendapatkan list user", description: "mendapatkan list user",
}, },
}) })
.put("/user-active", async ({ body }) => {
const { id } = body;
const user = await prisma.user.update({
where: { id },
data: { active: body.active },
});
return {
success: true,
data: user,
};
}, {
body: t.Object({
id: t.String(),
active: t.Boolean(),
}),
detail: {
summary: "Active user",
description: "mengaktifkan user",
},
})
.get("/config", async () => { .get("/config", async () => {
const config = await prisma.configs.findUnique({ const config = await prisma.configs.findUnique({
where: { id: "1" }, where: { id: "1" },
}); });
console.log(config); console.log(config);
return { return {
data: config, data: config,
}; };
@@ -182,34 +165,9 @@ const JadwalSholat = new Elysia({
summary: "Get config", summary: "Get config",
description: "mendapatkan config", description: "mendapatkan config",
}, },
})
.put("/config", async ({ body }) => {
const { imamKey, ikomahKey } = body;
const config = await prisma.configs.update({
where: { id: "1" },
data: { imamKey, ikomahKey },
});
console.log({
success: true,
config,
});
return {
success: true,
data: config,
};
}, {
body: t.Object({
id: t.String(),
imamKey: t.String(),
ikomahKey: t.String(),
}),
detail: {
summary: "Update config",
description: "mengupdate config",
},
}); });
export default JadwalSholat; export default JadwalShalat;
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------
FUNCTIONS FUNCTIONS

View File

@@ -0,0 +1,53 @@
import Elysia, { t } from "elysia";
import { prisma } from "../lib/prisma";
export const JadwalShalatAdmin = new Elysia({
prefix: "/jadwal-sholat-admin",
tags: ["jadwal_sholat_admin"],
})
.put("/user-active", async ({ body }) => {
const { id } = body;
const user = await prisma.user.update({
where: { id },
data: { active: body.active },
});
return {
success: true,
data: user,
};
}, {
body: t.Object({
id: t.String(),
active: t.Boolean(),
}),
detail: {
summary: "Active user",
description: "mengaktifkan user",
},
})
.put("/config", async ({ body }) => {
const { imamKey, ikomahKey } = body;
const config = await prisma.configs.update({
where: { id: "1" },
data: { imamKey, ikomahKey },
});
console.log({
success: true,
config,
});
return {
success: true,
data: config,
};
}, {
body: t.Object({
id: t.String(),
imamKey: t.String(),
ikomahKey: t.String(),
}),
detail: {
summary: "Update config",
description: "mengupdate config",
},
});