This commit is contained in:
bipproduction
2025-12-11 20:52:33 +08:00
parent e034c9ce5c
commit a28cb708b5

View File

@@ -4,6 +4,7 @@ import {
Box, Box,
Card, Card,
Container, Container,
Divider,
Flex, Flex,
Group, Group,
Loader, Loader,
@@ -152,6 +153,7 @@ export default function AdhanPage() {
return { cells, daysInMonth, startWeekday }; return { cells, daysInMonth, startWeekday };
}, [year, month]); }, [year, month]);
// ========== Prayer cards (per waktu) ========== // ========== Prayer cards (per waktu) ==========
const prayerList = useMemo(() => { const prayerList = useMemo(() => {
// adhan.adhan is expected: { fajr, sunrise, dhuhr, asr, maghrib, isha } // adhan.adhan is expected: { fajr, sunrise, dhuhr, asr, maghrib, isha }
@@ -213,117 +215,122 @@ export default function AdhanPage() {
); );
} }
const landscapeGradient =
"linear-gradient(135deg, #091622ff, #475b71ff, #706420ff, #0d3e0cff)";
return ( return (
<Container size="md" w="100%" px="sm"> <Box w={"100%"} bg={landscapeGradient}>
<Stack gap="xl" py="md"> <Container size="md" w="100%" px="sm" >
<Stack justify="apart" align="center"> <Stack gap="xl" py="md">
<Stack justify="center" align="center"> <Stack justify="apart" align="center">
<IconCalendar <Stack justify="center" align="center">
color="cyan" <IconCalendar
size={"6rem"} color="cyan"
stroke={1.3} size={"6rem"}
onClick={() => navigate("/")} stroke={1.3}
/> onClick={() => navigate("/")}
<Title order={2} fw={700}>
Jadwal Sholat & Imam
</Title>
<Text size="xs" c="dimmed">
{dayjs(date).locale("id").format("dddd, DD MMMM YYYY")}
</Text>
</Stack>
</Stack>
{JadwalHariIni(prayerList, formatCountdown)}
<SimpleGrid cols={{
base: 1,
md: 2
}}>
{JadwalImamHariIni(date, daily, adhan)}
<Card
padding="lg"
radius="md"
bg={"linear-gradient(135deg, #614c34ff, #596c2fff)"}
>
<Stack gap="xs">
<Group gap={6}>
<IconCalendarEvent size={20} />
<Text fw={600}>Pilih Tanggal</Text>
</Group>
<DatePicker
locale="id"
// value={date}
// 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>
)}
minDate={date || undefined}
maxDate={dayjs().add(40, "year").toDate()}
defaultDate={date || undefined}
onChange={setDate as any}
onYearSelect={setDate as any}
onMonthSelect={setDate as any}
/> />
<Title order={2} fw={700}>
Jadwal Sholat & Imam
</Title>
<Text size="xs" c="dimmed">
{dayjs(date).locale("id").format("dddd, DD MMMM YYYY")}
</Text>
</Stack> </Stack>
</Card> </Stack>
</SimpleGrid>
{CalendarTable(date, monthGrid, isHoliday, monthly, setDate, holidays)} {JadwalHariIni(prayerList, formatCountdown)}
{RingkasanBulalan(monthly, year, month, isHoliday)} <SimpleGrid cols={{
{FullYearHoliday(year, holidays)} base: 1,
</Stack> md: 2
</Container> }}>
{JadwalImamHariIni(date, daily, adhan)}
<Card
padding="lg"
radius="md"
bg={"linear-gradient(135deg, #614c34ff, #596c2fff)"}
>
<Stack gap="xs">
<Group gap={6}>
<IconCalendarEvent size={"2rem"} />
<Text size="2rem" fw={700}>Pilih Tanggal</Text>
</Group>
<DatePicker
locale="id"
value={date}
// 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>
)}
minDate={dayjs("2025-01-01").toDate()}
maxDate={dayjs().add(40, "year").toDate()}
// defaultDate={date || undefined}
onChange={(date) => setDate(dayjs(date).toDate())}
onYearSelect={(year) => setDate(dayjs(year).toDate())}
onMonthSelect={(month) => setDate(dayjs(month).toDate())}
/>
</Stack>
</Card>
</SimpleGrid>
{CalendarTable(date, monthGrid, isHoliday, monthly, setDate, holidays)}
{RingkasanBulalan(monthly, year, month, isHoliday)}
{FullYearHoliday(year, holidays)}
</Stack>
</Container>
</Box>
); );
} }
function JadwalImamHariIni(date: Date | null, daily: any, adhan: any) { function JadwalImamHariIni(date: Date | null, daily: any, adhan: any) {
return ( return (
<Card padding="lg" radius="md" bg={"dark.9"}> <Card padding="lg" radius="md" bg={"dark.9"}>
<Stack gap="sm"> <Stack gap="20">
<Group> <Stack>
<IconUser size={20} /> <Group>
<Title order={5} fw={700}> <IconUser size={"2rem"} />
Jadwal Imam Hari Ini <Text size="2rem" fw={700}>
</Title> Jadwal Imam Hari Ini
</Group> </Text>
</Group>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
{dayjs(date).format("dddd, DD MMMM YYYY")} {dayjs(date).format("dddd, DD MMMM YYYY")}
</Text> </Text>
</Stack>
<Group> <Stack>
<Badge <Stack gap={"xs"}>
leftSection={<IconUser size={14} />} <Flex gap={"md"}>
color="blue" <IconUser size={"3rem"} />
variant="light" <Text fw={700} c={"teal"} size="3rem">{daily.data.imam || "-"}</Text>
> </Flex>
{daily.data.imam || "-"} <Text c={"dimmed"}>Imam</Text>
</Badge> </Stack>
<Badge <Divider c={"green"} />
leftSection={<IconClock size={14} />} <Stack gap={"xs"}>
color="grape" <Flex gap={"md"}>
variant="light" <IconClock size={"2rem"} />
> <Text c={"blue.4"} size="2rem">{daily.data.ikomah || "-"}</Text>
{daily.data.ikomah || "-"} </Flex>
</Badge> <Text c={"dimmed"}>Ikomah</Text>
</Group> </Stack>
</Stack>
<div style={{ height: 8 }} /> <Text size="1.5rem" fw={700}>Waktu Adhan</Text>
<Stack gap={6} c={"dimmed"}>
<Title order={6}>Waktu Adhan</Title>
<Stack gap={6}>
{Object.entries(adhan.adhan).map(([k, v]) => ( {Object.entries(adhan.adhan).map(([k, v]) => (
<Group key={k} justify="apart"> <Group key={k} justify="apart">
<Text w={100} style={{ textTransform: "capitalize" }}> <Text w={100} style={{ textTransform: "capitalize" }}>
@@ -360,132 +367,132 @@ function CalendarTable(
overflowX: "scroll", overflowX: "scroll",
}} }}
> >
<Stack miw={700} gap="sm"> <Stack miw={700} gap="60">
<Group justify="apart"> <Stack justify="apart" gap={6}>
<Group> <Group>
<IconLayoutGrid size={20} /> <IconLayoutGrid size={"2rem"} />
<Title order={4} fw={700}> <Text size={"2rem"} fw={700}>
Kalender {dayjs(date).format("MMMM YYYY")} Kalender {dayjs(date).format("MMMM YYYY")}
</Title> </Text>
</Group> </Group>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
Klik tanggal untuk lihat detail Klik tanggal untuk lihat detail
</Text> </Text>
</Group> </Stack>
{/* weekday headers */} <Stack>
<SimpleGrid cols={7}> <SimpleGrid cols={7} p={"md"} bg={"dark.8"}>
{["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"].map((w) => ( {["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"].map((w) => (
<div key={w}>{w}</div> <Text fw={700} key={w}>{w}</Text>
))} ))}
</SimpleGrid> </SimpleGrid>
{/* month grid */} <SimpleGrid cols={7}>
<SimpleGrid cols={7}> {monthGrid.cells.map((cell, idx) => {
{monthGrid.cells.map((cell, idx) => { if (!cell.date) {
if (!cell.date) { return <div key={idx} style={{ minHeight: 80 }} />;
return <div key={idx} style={{ minHeight: 80 }} />; }
} const d = cell.date;
const d = cell.date; const dayNum = d.date();
const dayNum = d.date(); const iso = d.format("YYYY-MM-DD");
const iso = d.format("YYYY-MM-DD"); const today = d.isSame(dayjs(), "day");
const today = d.isSame(dayjs(), "day"); const holiday = isHoliday(iso);
const holiday = isHoliday(iso); const hasImam = Boolean(
const hasImam = Boolean( monthly.data.imam && monthly.data.imam[String(dayNum)],
monthly.data.imam && monthly.data.imam[String(dayNum)], );
); return (
return ( <UnstyledButton
<UnstyledButton bg={"dark"}
bg={"dark"} key={idx}
key={idx} style={{
style={{ textAlign: "left",
textAlign: "left", padding: 10,
padding: 10, borderRadius: 12,
borderRadius: 12, minHeight: 80,
minHeight: 80, background: today ? "rgba(5, 51, 104, 0.39)" : undefined,
background: today ? "rgba(5, 51, 104, 0.39)" : undefined, border: holiday ? "1px solid rgba(130, 61, 6, 1)" : undefined,
border: holiday ? "1px solid rgba(130, 61, 6, 1)" : undefined, boxShadow: today
boxShadow: today ? "0 6px 18px rgba(6,120,250,0.08)"
? "0 6px 18px rgba(6,120,250,0.08)" : undefined,
: undefined, }}
}} onClick={() => {
onClick={() => { setDate(d.toDate());
setDate(d.toDate()); }}
}}
>
<Stack
gap={0}
h={"100%"}
justify="space-around"
align="stretch"
> >
<Group justify="end"> <Stack
{ gap={0}
<Badge h={"100%"}
size="xs" justify="space-around"
bg={hasImam ? "green.4" : "gray"} align="stretch"
variant="light" >
/> <Group justify="end">
} {
</Group> <Badge
<Box> size="xs"
<Text bg={hasImam ? "green.4" : "gray"}
size="2rem" variant="light"
style={{ />
width: 34, }
height: 34, </Group>
borderRadius: 8, <Box>
display: "grid", <Text
placeItems: "center", size="2rem"
fontWeight: 700,
background: holiday
? "rgba(220,38,38,0.06)"
: "transparent",
color: holiday
? "rgb(220,38,38)"
: today
? "rgb(6,120,250)"
: undefined,
}}
>
{dayNum}
</Text>
<div>
<div
style={{ style={{
fontSize: 12, width: 34,
fontWeight: 600, height: 34,
textTransform: "capitalize", borderRadius: 8,
display: "grid",
placeItems: "center",
fontWeight: 700,
background: holiday
? "rgba(220,38,38,0.06)"
: "transparent",
color: holiday
? "rgb(220,38,38)"
: today
? "rgb(6,120,250)"
: undefined,
}} }}
> >
{d.format("dddd")} {dayNum}
</div> </Text>
<div style={{ fontSize: 11, color: "#6b7280" }}>
{holiday
? holidays.find(
(h) => dayjs(h.date).format("YYYY-MM-DD") === iso,
)?.name
: ""}
</div>
</div>
</Box>
</Stack>
{/* small footer area */} <div>
<div <div
style={{ style={{
marginTop: 8, fontSize: 12,
display: "flex", fontWeight: 600,
gap: 8, textTransform: "capitalize",
alignItems: "center", }}
}} >
></div> {d.format("dddd")}
</UnstyledButton> </div>
); <div style={{ fontSize: 11, color: "#6b7280" }}>
})} {holiday
</SimpleGrid> ? holidays.find(
(h) => dayjs(h.date).format("YYYY-MM-DD") === iso,
)?.name
: ""}
</div>
</div>
</Box>
</Stack>
{/* small footer area */}
<div
style={{
marginTop: 8,
display: "flex",
gap: 8,
alignItems: "center",
}}
></div>
</UnstyledButton>
);
})}
</SimpleGrid>
</Stack>
</Stack> </Stack>
</Card> </Card>
); );
@@ -499,11 +506,11 @@ function RingkasanBulalan(
) { ) {
return ( return (
<Card padding="md" radius="md" mt="md" bg={"dark.9"}> <Card padding="md" radius="md" mt="md" bg={"dark.9"}>
<Stack gap="sm"> <Stack gap="70">
<Group justify="apart"> <Group justify="apart">
<Group> <Group>
<IconCalendar size={18} /> <IconCalendar size={"2rem"} />
<Text fw={700}>Ringkasan Bulanan</Text> <Text size="2rem" fw={700}>Ringkasan Bulanan</Text>
</Group> </Group>
</Group> </Group>
@@ -526,7 +533,9 @@ function RingkasanBulalan(
radius="xl" radius="xl"
key={d} key={d}
p={"md"} p={"md"}
c={holiday ? "red" : isToday ? "blue" : "white"} c={holiday ? "red.9" : isToday ? "cyan" : "grey.9"}
bg={"dark.9"}
withBorder
> >
<Stack> <Stack>
<Group> <Group>
@@ -560,13 +569,13 @@ function FullYearHoliday(year: number, holidays: HolidaysTypes.Holiday[]) {
<Card padding="xl" radius="lg" shadow="md" bg={"dark.9"}> <Card padding="xl" radius="lg" shadow="md" bg={"dark.9"}>
<Stack gap="md"> <Stack gap="md">
<Group gap={6}> <Group gap={6}>
<IconGift size={24} /> <IconGift size={"2rem"} />
<Title order={4} fw={700}> <Title size={"2rem"} order={4} fw={700}>
Hari Libur Nasional Tahun {year} Hari Libur Nasional Tahun {year}
</Title> </Title>
</Group> </Group>
{holidays.length > 0 && ( {holidays.length > 0 && (
<Stack> <Stack c={"teal.9"}>
<SimpleGrid <SimpleGrid
cols={{ cols={{
base: 2, base: 2,
@@ -590,6 +599,15 @@ function FullYearHoliday(year: number, holidays: HolidaysTypes.Holiday[]) {
); );
} }
const prayerGradients = {
fajr: "linear-gradient(135deg, #6d7f8c, #778f8a, #7f998a)",
sunrise: "linear-gradient(135deg, #8d7a63, #9a846c, #a28c74)",
dhuhr: "linear-gradient(135deg, #647a69, #607463, #5c6a75)",
asr: "linear-gradient(135deg, #767c5d, #7f845f, #888d62)",
maghrib: "linear-gradient(135deg, #7c5f5f, #875f62, #8f6166)",
isha: "linear-gradient(135deg, #595f7a, #505672, #484e6b)"
};
function JadwalHariIni( function JadwalHariIni(
prayerList: { prayerList: {
name: string; name: string;
@@ -613,7 +631,7 @@ function JadwalHariIni(
{prayerList.map((p) => { {prayerList.map((p) => {
const Icon = p.Icon; const Icon = p.Icon;
return ( return (
<Card key={p.name} radius="lg" bg={"dark.9"}> <Card key={p.name} radius="lg" bg={prayerGradients[p.name as keyof typeof prayerGradients]}>
<Stack <Stack
gap="xs" gap="xs"
align="stretch" align="stretch"
@@ -630,12 +648,12 @@ function JadwalHariIni(
</Group> </Group>
<Group justify="apart" align="center"> <Group justify="apart" align="center">
<Text size="sm" c={p.isPast ? "dimmed" : undefined}> <Text size="sm" c={p.isPast ? "dark" : undefined}>
{p.isPast ? "Sudah lewat" : formatCountdown(p.dt)} {p.isPast ? "Sudah lewat" : formatCountdown(p.dt)}
</Text> </Text>
<Badge <Badge
color={p.isPast ? "gray" : "blue"} color={p.isPast ? "orange" : "blue"}
variant="light" variant="light"
radius="sm" radius="sm"
> >
@@ -669,11 +687,11 @@ function UserList() {
{data?.data?.data?.map((u) => { {data?.data?.data?.map((u) => {
if (u.active === false) return null; if (u.active === false) return null;
return ( return (
<Card key={u.id} radius={"lg"} bg={"dark"}> <Card key={u.id} radius={"40"} withBorder bg={"dark.9"}>
<Stack align="center"> <Flex align="center" gap={"md"}>
<IconUser size={"3rem"} color="green" /> <IconUser size={"2rem"} color="green" />
<Text>{u.name}</Text> <Text size="1rem">{u.name}</Text>
</Stack> </Flex>
</Card> </Card>
); );
})} })}