tambahan
This commit is contained in:
3
bun.lock
3
bun.lock
@@ -7,6 +7,7 @@
|
||||
"@elysiajs/cors": "^1.4.0",
|
||||
"@elysiajs/eden": "^1.4.5",
|
||||
"@elysiajs/jwt": "^1.4.0",
|
||||
"@elysiajs/static": "^1.4.7",
|
||||
"@elysiajs/swagger": "^1.3.1",
|
||||
"@mantine/core": "^8.3.8",
|
||||
"@mantine/dates": "^8.3.10",
|
||||
@@ -77,6 +78,8 @@
|
||||
|
||||
"@elysiajs/jwt": ["@elysiajs/jwt@1.4.0", "", { "dependencies": { "jose": "^6.0.11" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, ""],
|
||||
|
||||
"@elysiajs/static": ["@elysiajs/static@1.4.7", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-Go4kIXZ0G3iWfkAld07HmLglqIDMVXdyRKBQK/sVEjtpDdjHNb+rUIje73aDTWpZYg4PEVHUpi9v4AlNEwrQug=="],
|
||||
|
||||
"@elysiajs/swagger": ["@elysiajs/swagger@1.3.1", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, ""],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@elysiajs/cors": "^1.4.0",
|
||||
"@elysiajs/eden": "^1.4.5",
|
||||
"@elysiajs/jwt": "^1.4.0",
|
||||
"@elysiajs/static": "^1.4.7",
|
||||
"@elysiajs/swagger": "^1.3.1",
|
||||
"@mantine/core": "^8.3.8",
|
||||
"@mantine/dates": "^8.3.10",
|
||||
|
||||
@@ -13,8 +13,8 @@ import Configs from "./server/routes/configs_route";
|
||||
import { prisma } from "./server/lib/prisma";
|
||||
import JadwalShalat from "./server/routes/jadwal_shalat";
|
||||
import { JadwalShalatAdmin } from "./server/routes/jadwal_shalat_admin";
|
||||
import staticPlugin from "@elysiajs/static";
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
const Docs = new Elysia().use(
|
||||
Swagger({
|
||||
path: "/docs",
|
||||
@@ -81,6 +81,12 @@ const Api = new Elysia({
|
||||
|
||||
const app = new Elysia()
|
||||
.use(cors())
|
||||
// .use(
|
||||
// staticPlugin({
|
||||
// assets: "dist",
|
||||
// prefix: "/",
|
||||
// })
|
||||
// )
|
||||
.use(Api)
|
||||
.use(Docs)
|
||||
.use(Auth)
|
||||
@@ -115,22 +121,22 @@ const app = new Elysia()
|
||||
},
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/",
|
||||
async () => {
|
||||
const stream = await renderToReadableStream(<LandingPage />);
|
||||
return new Response(stream, {
|
||||
headers: { "Content-Type": "text/html" },
|
||||
});
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
description: "Landing page for " + packageJson.name,
|
||||
summary: "Get the main landing page",
|
||||
tags: ["General"],
|
||||
},
|
||||
},
|
||||
)
|
||||
// .get(
|
||||
// "/",
|
||||
// async () => {
|
||||
// const stream = await renderToReadableStream(<LandingPage />);
|
||||
// return new Response(stream, {
|
||||
// headers: { "Content-Type": "text/html" },
|
||||
// });
|
||||
// },
|
||||
// {
|
||||
// detail: {
|
||||
// description: "Landing page for " + packageJson.name,
|
||||
// summary: "Get the main landing page",
|
||||
// tags: ["General"],
|
||||
// },
|
||||
// },
|
||||
// )
|
||||
.get("*", html)
|
||||
.listen(PORT, () => {
|
||||
console.log(`Server running at http://localhost:${PORT}`);
|
||||
|
||||
@@ -1,37 +1,719 @@
|
||||
import clientRoutes from "@/clientRoutes";
|
||||
import { Button, Card, Container, Group, Stack, Title } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import clientRoutes from "../clientRoutes";
|
||||
|
||||
export default function Home() {
|
||||
|
||||
useShallowEffect(() => {
|
||||
window.location.reload()
|
||||
},[])
|
||||
return (
|
||||
<Container size={420} py={80}>
|
||||
<Card shadow="sm" padding="xl" radius="md">
|
||||
<Stack gap="md">
|
||||
<Title order={2} ta="center">
|
||||
Home
|
||||
</Title>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charSet="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Jadwal Sholat & Imam — Semua dalam Satu Genggaman</title>
|
||||
|
||||
<Group grow>
|
||||
<Button size="sm" component="a" href={clientRoutes["/dashboard"]}>
|
||||
Dashboard
|
||||
</Button>
|
||||
<style>{`
|
||||
:root{
|
||||
--bg-1: #0f172a;
|
||||
--accent: #4F46E5;
|
||||
--accent-2: #7c3aed;
|
||||
--glass: rgba(255,255,255,0.06);
|
||||
--muted: rgba(255,255,255,0.85);
|
||||
--card: rgba(255,255,255,0.04);
|
||||
--glass-strong: rgba(255,255,255,0.08);
|
||||
--radius-lg: 16px;
|
||||
--radius-sm: 8px;
|
||||
--max-w: 1200px;
|
||||
--glass-border: rgba(255,255,255,0.06);
|
||||
--shadow: 0 10px 30px rgba(2,6,23,0.6);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
<Button
|
||||
size="sm"
|
||||
component="a"
|
||||
href={clientRoutes["/login"]}
|
||||
variant="light"
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
html,body,#root{height:100%}
|
||||
body{
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||
background:
|
||||
radial-gradient(1200px 600px at 10% 10%, rgba(124,58,237,0.12), transparent 8%),
|
||||
radial-gradient(1000px 400px at 90% 90%, rgba(79,70,229,0.10), transparent 8%),
|
||||
linear-gradient(180deg, #071024 0%, #071229 40%, #08162f 100%);
|
||||
color: var(--muted);
|
||||
-webkit-font-smoothing:antialiased;
|
||||
-moz-osx-font-smoothing:grayscale;
|
||||
overflow-x:hidden;
|
||||
padding-bottom:80px;
|
||||
}
|
||||
|
||||
.container{
|
||||
max-width:var(--max-w);
|
||||
margin:0 auto;
|
||||
padding:0 20px;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
/* NAVBAR */
|
||||
nav{
|
||||
position:sticky;
|
||||
top:0;
|
||||
z-index:60;
|
||||
backdrop-filter: blur(8px);
|
||||
background: linear-gradient(180deg, rgba(7,10,20,0.6), rgba(7,10,20,0.35));
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
padding:14px 0;
|
||||
}
|
||||
.nav-row{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap:12px;
|
||||
}
|
||||
.brand{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:12px;
|
||||
}
|
||||
.logo{
|
||||
width:44px;
|
||||
height:44px;
|
||||
border-radius:10px;
|
||||
background: linear-gradient(135deg,var(--accent),var(--accent-2));
|
||||
display:grid;
|
||||
place-items:center;
|
||||
font-weight:700;
|
||||
color:white;
|
||||
box-shadow: var(--shadow);
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
.brand-title{
|
||||
font-weight:700;
|
||||
font-size:16px;
|
||||
letter-spacing:0.2px;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.nav-links{
|
||||
display:flex;
|
||||
gap:18px;
|
||||
align-items:center;
|
||||
}
|
||||
.nav-links a{
|
||||
color:var(--muted);
|
||||
text-decoration:none;
|
||||
font-weight:600;
|
||||
font-size:14px;
|
||||
padding:8px 10px;
|
||||
border-radius:8px;
|
||||
}
|
||||
.nav-links a:hover{background:var(--glass-strong)}
|
||||
.cta{
|
||||
padding:10px 18px;
|
||||
background: linear-gradient(90deg,var(--accent),var(--accent-2));
|
||||
color:white;
|
||||
border-radius:12px;
|
||||
font-weight:700;
|
||||
text-decoration:none;
|
||||
box-shadow: 0 8px 30px rgba(79,70,229,0.18);
|
||||
display:inline-flex;
|
||||
gap:10px;
|
||||
align-items:center;
|
||||
}
|
||||
|
||||
/* HERO */
|
||||
.hero{
|
||||
padding:100px 0 60px;
|
||||
display:grid;
|
||||
grid-template-columns: 1fr 420px;
|
||||
gap:36px;
|
||||
align-items:center;
|
||||
}
|
||||
.hero-left h1{
|
||||
font-size:40px;
|
||||
line-height:1.05;
|
||||
margin-bottom:12px;
|
||||
color: #fff;
|
||||
font-weight:800;
|
||||
}
|
||||
.kicker{
|
||||
display:inline-block;
|
||||
padding:6px 12px;
|
||||
border-radius:999px;
|
||||
background:rgba(255,255,255,0.04);
|
||||
color: #e6e9ff;
|
||||
font-weight:700;
|
||||
margin-bottom:18px;
|
||||
font-size:13px;
|
||||
}
|
||||
.hero-lead{
|
||||
font-size:18px;
|
||||
opacity:0.95;
|
||||
margin-bottom:22px;
|
||||
max-width:680px;
|
||||
}
|
||||
.hero-ctas{display:flex;gap:12px;flex-wrap:wrap}
|
||||
.btn-primary{
|
||||
background: linear-gradient(90deg,var(--accent),var(--accent-2));
|
||||
color:white;
|
||||
padding:12px 20px;
|
||||
border-radius:12px;
|
||||
font-weight:700;
|
||||
text-decoration:none;
|
||||
display:inline-flex;
|
||||
gap:10px;
|
||||
align-items:center;
|
||||
box-shadow: 0 10px 30px rgba(79,70,229,0.14);
|
||||
}
|
||||
.btn-ghost{
|
||||
background:transparent;
|
||||
border:1px solid var(--glass-border);
|
||||
color:var(--muted);
|
||||
padding:10px 18px;
|
||||
border-radius:12px;
|
||||
font-weight:700;
|
||||
text-decoration:none;
|
||||
}
|
||||
.micro-note{
|
||||
margin-top:12px;
|
||||
font-size:13px;
|
||||
opacity:0.8;
|
||||
}
|
||||
|
||||
/* HERO RIGHT: preview card (calendar + next prayer) */
|
||||
.preview-card{
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
|
||||
border: 1px solid var(--glass-border);
|
||||
padding:18px;
|
||||
border-radius:var(--radius-lg);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.preview-head{
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
gap:8px;
|
||||
margin-bottom:12px;
|
||||
}
|
||||
.location{
|
||||
font-weight:700;
|
||||
color:white;
|
||||
}
|
||||
.next-prayer{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
}
|
||||
.next-prayer .time{
|
||||
font-weight:800;
|
||||
font-size:22px;
|
||||
color:white;
|
||||
}
|
||||
.countdown{
|
||||
font-size:13px;
|
||||
opacity:0.9;
|
||||
}
|
||||
|
||||
.mini-calendar{
|
||||
margin-top:12px;
|
||||
display:grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap:6px;
|
||||
}
|
||||
.mini-calendar .day{
|
||||
padding:8px 6px;
|
||||
text-align:center;
|
||||
border-radius:8px;
|
||||
font-size:13px;
|
||||
background:transparent;
|
||||
color:var(--muted);
|
||||
border:1px solid transparent;
|
||||
}
|
||||
.mini-calendar .holiday{
|
||||
background: rgba(220,38,38,0.15);
|
||||
color: #ffd7d7;
|
||||
border:1px solid rgba(220,38,38,0.2);
|
||||
}
|
||||
.mini-calendar .today{
|
||||
background: linear-gradient(90deg,#0ea5a4, #06b6d4);
|
||||
color:white;
|
||||
font-weight:700;
|
||||
}
|
||||
|
||||
/* FEATURES */
|
||||
.section{
|
||||
padding:72px 0;
|
||||
}
|
||||
.section h2{
|
||||
font-size:28px;
|
||||
color:#fff;
|
||||
margin-bottom:18px;
|
||||
font-weight:800;
|
||||
}
|
||||
.lead{
|
||||
color:var(--muted);
|
||||
margin-bottom:28px;
|
||||
font-size:15px;
|
||||
max-width:760px;
|
||||
}
|
||||
.features-grid{
|
||||
display:grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap:20px;
|
||||
}
|
||||
.card{
|
||||
background:var(--card);
|
||||
border-radius:12px;
|
||||
padding:20px;
|
||||
border:1px solid var(--glass-border);
|
||||
transition: transform 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
.card:hover{ transform: translateY(-8px); box-shadow: 0 20px 40px rgba(2,6,23,0.6) }
|
||||
.card h3{ font-size:16px; margin-bottom:10px; color:white; font-weight:800 }
|
||||
.card p{ font-size:14px; opacity:0.9; line-height:1.5 }
|
||||
|
||||
/* STATS / BADGES */
|
||||
.badges{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
margin-top:18px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
.badge{
|
||||
padding:10px 14px;
|
||||
border-radius:999px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
font-weight:700;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
/* CTA BAR */
|
||||
.cta-bar{
|
||||
margin-top:30px;
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
flex-wrap:wrap;
|
||||
padding:18px;
|
||||
border-radius:12px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.02), transparent);
|
||||
border:1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
/* FOOTER */
|
||||
footer{
|
||||
margin-top:40px;
|
||||
padding:40px 0 80px;
|
||||
color:var(--muted);
|
||||
text-align:center;
|
||||
border-top:1px solid var(--glass-border);
|
||||
background: linear-gradient(180deg, transparent, rgba(0,0,0,0.25));
|
||||
}
|
||||
footer .frow{
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
gap:20px;
|
||||
align-items:center;
|
||||
max-width:var(--max-w);
|
||||
margin:0 auto;
|
||||
padding:0 20px;
|
||||
}
|
||||
.contact{
|
||||
text-align:right;
|
||||
}
|
||||
.contact a{ color:var(--muted); text-decoration:none; font-weight:700 }
|
||||
.copyright{ margin-top:18px; font-size:13px; opacity:0.8 }
|
||||
|
||||
/* RESPONSIVE */
|
||||
@media (max-width:1000px){
|
||||
.hero{ grid-template-columns: 1fr 360px }
|
||||
.features-grid{ grid-template-columns: repeat(2, 1fr) }
|
||||
}
|
||||
@media (max-width:768px){
|
||||
.nav-links{ display:none }
|
||||
.hero{ grid-template-columns: 1fr; padding:64px 0 32px; gap:18px }
|
||||
.preview-card{ order: -1 }
|
||||
.features-grid{ grid-template-columns: 1fr }
|
||||
.frow{ flex-direction:column; text-align:center }
|
||||
.contact{ text-align:center }
|
||||
}
|
||||
|
||||
/* small helpers */
|
||||
.muted { opacity:0.86; font-size:14px }
|
||||
a.reset { color:inherit; text-decoration:none; }
|
||||
`}</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav>
|
||||
<div className="container">
|
||||
<div className="nav-row">
|
||||
<div className="brand">
|
||||
<div className="logo" aria-hidden>
|
||||
𝕵
|
||||
</div>
|
||||
<div>
|
||||
<div className="brand-title">Jadwal Sholat & Imam</div>
|
||||
<div style={{ fontSize: 12, opacity: 0.75 }}>
|
||||
Semua jadwal, imam, & libur — satu genggaman
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="nav-links" role="navigation" aria-label="Main">
|
||||
<a href="#features">Fitur</a>
|
||||
<a href="#calendar">Kalender</a>
|
||||
<a href="#imams">Jadwal Imam</a>
|
||||
<a href="#docs">Dokumentasi</a>
|
||||
<a href={clientRoutes["/dashboard"]} className="cta">
|
||||
Masjid Saya
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
<section className="hero">
|
||||
<div className="container hero-left">
|
||||
<div className="kicker">PWA · Open Source · API</div>
|
||||
<h1>
|
||||
Semua jadwal shalat, imam, dan hari libur — dalam satu
|
||||
genggaman.
|
||||
</h1>
|
||||
<p className="hero-lead">
|
||||
Waktu adhan otomatis berdasarkan koordinat Anda. Countdown
|
||||
real-time menuju iqomah, kalender interaktif, dan daftar libur
|
||||
nasional yang ter-update setiap tahun — ringan, cepat, dan bisa
|
||||
dipasang di layar masjid.
|
||||
</p>
|
||||
|
||||
<div className="hero-ctas">
|
||||
<a href={clientRoutes["/shalat"]} className="btn-primary">
|
||||
Lihat Demo
|
||||
</a>
|
||||
<a href="#docs" className="btn-ghost">
|
||||
Dokumentasi API
|
||||
</a>
|
||||
<a href={clientRoutes["/dashboard"]} className="btn-ghost">
|
||||
Pasang di Masjid Saya
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="micro-note">
|
||||
<strong>Tips:</strong> Izinkan akses lokasi untuk akurasi adhan.
|
||||
Bisa di-install sebagai PWA untuk akses 1-tap.
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 28 }}>
|
||||
<div className="badges" aria-hidden>
|
||||
<div className="badge">Next.js + SWR</div>
|
||||
<div className="badge">Skeleton & Offline</div>
|
||||
<div className="badge">Auto-load Hari Libur ID</div>
|
||||
<div className="badge">REST API & Webhooks</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside
|
||||
className="container preview-card"
|
||||
aria-label="Preview jadwal"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Container>
|
||||
<div className="preview-head">
|
||||
<div>
|
||||
<div className="location">Masjid Al-Hikmah • Makassar</div>
|
||||
<div className="muted" style={{ fontSize: 13 }}>
|
||||
Koordinat: 5.1477° S, 119.4327° E
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="next-prayer" aria-live="polite">
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<div style={{ fontSize: 12, opacity: 0.85 }}>
|
||||
Sholat berikutnya
|
||||
</div>
|
||||
<div className="time">Maghrib — 18:03</div>
|
||||
<div className="countdown">
|
||||
Iqomah dalam <strong>15m 12s</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
marginBottom: 8,
|
||||
color: "var(--muted)",
|
||||
}}
|
||||
>
|
||||
Waktu adhan hari ini
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2,1fr)",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<div className="card" style={{ padding: 12 }}>
|
||||
<div style={{ fontSize: 13, opacity: 0.9 }}>Subuh</div>
|
||||
<div style={{ fontWeight: 800, fontSize: 16 }}>04:31</div>
|
||||
</div>
|
||||
<div className="card" style={{ padding: 12 }}>
|
||||
<div style={{ fontSize: 13, opacity: 0.9 }}>Dzuhur</div>
|
||||
<div style={{ fontWeight: 800, fontSize: 16 }}>12:03</div>
|
||||
</div>
|
||||
<div className="card" style={{ padding: 12 }}>
|
||||
<div style={{ fontSize: 13, opacity: 0.9 }}>Ashr</div>
|
||||
<div style={{ fontWeight: 800, fontSize: 16 }}>15:24</div>
|
||||
</div>
|
||||
<div className="card" style={{ padding: 12 }}>
|
||||
<div style={{ fontSize: 13, opacity: 0.9 }}>Isya</div>
|
||||
<div style={{ fontWeight: 800, fontSize: 16 }}>19:10</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 13,
|
||||
marginBottom: 8,
|
||||
color: "var(--muted)",
|
||||
}}
|
||||
>
|
||||
Mini kalender — klik tanggal untuk lihat imam
|
||||
</div>
|
||||
<div
|
||||
className="mini-calendar"
|
||||
role="grid"
|
||||
aria-label="Mini kalender"
|
||||
>
|
||||
{/* Static example days — highlight classes indicate holiday/today */}
|
||||
<div className="day muted">Min</div>
|
||||
<div className="day muted">Sen</div>
|
||||
<div className="day muted">Sel</div>
|
||||
<div className="day muted">Rab</div>
|
||||
<div className="day muted">Kam</div>
|
||||
<div className="day muted">Jum</div>
|
||||
<div className="day muted">Sab</div>
|
||||
|
||||
<div className="day">1</div>
|
||||
<div className="day">2</div>
|
||||
<div className="day holiday">3</div>
|
||||
<div className="day">4</div>
|
||||
<div className="day">5</div>
|
||||
<div className="day">6</div>
|
||||
<div className="day today">7</div>
|
||||
|
||||
{/* more days... */}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section id="features" className="section container">
|
||||
<h2>Mengapa pilih Jadwal Sholat & Imam?</h2>
|
||||
<p className="lead">
|
||||
Satu tampilan untuk semua informasi: adhan otomatis sesuai
|
||||
koordinat, countdown iqomah real-time, kalender interaktif, dan
|
||||
daftar libur nasional yang diambil otomatis tiap tahun.
|
||||
</p>
|
||||
|
||||
<div className="features-grid" style={{ marginTop: 12 }}>
|
||||
<div className="card" role="article" aria-labelledby="f1">
|
||||
<h3 id="f1">Satu tampilan, semua informasi</h3>
|
||||
<p>
|
||||
Waktu adhan (fajr, dhuhr, asr, maghrib, isha) otomatis
|
||||
menyesuaikan lokasi Anda. Icon dinamis membantu membaca
|
||||
kondisi (matahari, senja, bulan).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card" role="article" aria-labelledby="f2">
|
||||
<h3 id="f2">Kalender interaktif</h3>
|
||||
<p>
|
||||
Klik tanggal untuk melihat jadwal imam dan waktu adhan di hari
|
||||
tersebut. Hari libur nasional berwarna merah; hari ini
|
||||
di-highlight biru. Navigasi bulan & tahun cepat seperti swipe.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card" role="article" aria-labelledby="f3">
|
||||
<h3 id="f3">Jadwal imam bulanan</h3>
|
||||
<p>
|
||||
Tabel ringkasan 30 hari menampilkan siapa imam & menit iqomah
|
||||
setiap hari — cocok untuk pengingat, laporan, atau tampilan
|
||||
papan pengumuman di masjid.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card" role="article" aria-labelledby="f4">
|
||||
<h3 id="f4">Daftar libur nasional otomatis</h3>
|
||||
<p>
|
||||
Auto-load libur Indonesia tiap tahun lengkap dengan tipe
|
||||
(libur nasional / cuti bersama) dan keterangan tambahan untuk
|
||||
perencanaan kegiatan masjid.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card" role="article" aria-labelledby="f5">
|
||||
<h3 id="f5">Ringan & cepat</h3>
|
||||
<p>
|
||||
Menggunakan strategi cache dan incremental fetch (SWR /
|
||||
stale-while-revalidate) — hanya data berubah diambil ulang.
|
||||
Skeleton screen & PWA untuk pengalaman stabil di jaringan
|
||||
lambat.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card" role="article" aria-labelledby="f6">
|
||||
<h3 id="f6">Open source & mudah dikustom</h3>
|
||||
<p>
|
||||
Kode tersedia di GitHub. Ganti koordinat, tema, atau aktifkan
|
||||
push-notification dengan cepat. REST API internal siap dipakai
|
||||
untuk mobile atau display LED.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cta-bar" style={{ marginTop: 26 }}>
|
||||
<a href={clientRoutes["/shalat"]} className="btn-primary">
|
||||
Coba Demo Sekarang
|
||||
</a>
|
||||
<a href="#docs" className="btn-ghost">
|
||||
Buka Dokumentasi API
|
||||
</a>
|
||||
<a href={clientRoutes["/dashboard"]} className="btn-ghost">
|
||||
Pasang di Masjid Saya
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="imams"
|
||||
className="section container"
|
||||
style={{ paddingTop: 20 }}
|
||||
>
|
||||
<h2>Contoh — Jadwal Imam Bulanan</h2>
|
||||
<p className="lead">
|
||||
Ringkasan 30 hari: lihat siapa imam setiap hari, menit iqomah, dan
|
||||
catatan (cuti / acara khusus).
|
||||
</p>
|
||||
|
||||
<div style={{ marginTop: 12, overflowX: "auto" }}>
|
||||
<table
|
||||
style={{
|
||||
width: "100%",
|
||||
borderCollapse: "collapse",
|
||||
minWidth: 720,
|
||||
}}
|
||||
>
|
||||
<thead>
|
||||
<tr
|
||||
style={{
|
||||
textAlign: "left",
|
||||
borderBottom: "1px solid var(--glass-border)",
|
||||
}}
|
||||
>
|
||||
<th style={{ padding: 12 }}>Tanggal</th>
|
||||
<th style={{ padding: 12 }}>Imam</th>
|
||||
<th style={{ padding: 12 }}>Iqomah (menit)</th>
|
||||
<th style={{ padding: 12 }}>Catatan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
style={{ borderBottom: "1px solid rgba(255,255,255,0.03)" }}
|
||||
>
|
||||
<td style={{ padding: 12 }}>2025-12-07</td>
|
||||
<td style={{ padding: 12 }}>Ust. Ahmad</td>
|
||||
<td style={{ padding: 12 }}>10</td>
|
||||
<td style={{ padding: 12 }}>—</td>
|
||||
</tr>
|
||||
<tr
|
||||
style={{ borderBottom: "1px solid rgba(255,255,255,0.03)" }}
|
||||
>
|
||||
<td style={{ padding: 12 }}>2025-12-08</td>
|
||||
<td style={{ padding: 12 }}>Ust. Budi</td>
|
||||
<td style={{ padding: 12 }}>8</td>
|
||||
<td style={{ padding: 12 }}>Libur Nasional</td>
|
||||
</tr>
|
||||
{/* more rows — replace with dynamic data in real app */}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="docs"
|
||||
className="section container"
|
||||
style={{ paddingTop: 8 }}
|
||||
>
|
||||
<h2>Cepat Mulai</h2>
|
||||
<p className="lead">
|
||||
1) Kunjungi{" "}
|
||||
<a href="https://jadwalsholat.example.com" className="reset">
|
||||
jadwalsholat.example.com
|
||||
</a>
|
||||
<br />
|
||||
2) Izinkan akses lokasi → waktu adhan otomatis.
|
||||
<br />
|
||||
3) Klik tanggal di kalender untuk melihat jadwal imam.
|
||||
<br />
|
||||
4) Tambahkan ke layar utama sebagai PWA untuk akses 1-tap.
|
||||
</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 12,
|
||||
marginTop: 10,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<a href={clientRoutes["/shalat"]} className="btn-primary">
|
||||
Lihat Demo
|
||||
</a>
|
||||
<a href="#docs" className="btn-ghost">
|
||||
Dokumentasi API
|
||||
</a>
|
||||
<a href={clientRoutes["/dashboard"]} className="btn-ghost">
|
||||
Pasang di Masjid Saya
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div className="frow">
|
||||
<div style={{ textAlign: "left" }}>
|
||||
<div style={{ fontWeight: 800, fontSize: 16, color: "#fff" }}>
|
||||
Jadwal Sholat & Imam
|
||||
</div>
|
||||
<div style={{ marginTop: 6, fontSize: 13, opacity: 0.8 }}>
|
||||
Perangkat lunak open-source untuk manajemen jadwal masjid.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="contact">
|
||||
<div style={{ fontWeight: 700 }}>Hubungi kami</div>
|
||||
<div style={{ marginTop: 6 }}>
|
||||
<a href="mailto:hi@jadwalsholat.example.com">
|
||||
hi@jadwalsholat.example.com
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ marginTop: 6 }}>
|
||||
<a href="tel:+6281234567890">+62 812 3456 7890</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container copyright">
|
||||
<div>PERCOBAAN GRATIS · TANPA IKLAN · DATA AMAN</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
© {new Date().getFullYear()} Jadwal Sholat & Imam. All rights
|
||||
reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -678,7 +678,7 @@ function UserList() {
|
||||
<SimpleGrid
|
||||
spacing={"sm"}
|
||||
cols={{
|
||||
base: 4,
|
||||
base: 3,
|
||||
sm: 6,
|
||||
}}
|
||||
>
|
||||
@@ -687,7 +687,6 @@ function UserList() {
|
||||
return (
|
||||
<Card key={u.id} radius={"40"} withBorder bg={"dark.9"}>
|
||||
<Flex align="center" gap={"md"}>
|
||||
<IconUser size={"2rem"} color="green" />
|
||||
<Text size="1rem">{u.name}</Text>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user