tambahannnya
111
NOTE.md
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Load environment variables from .env file
|
||||
if [ -f ".env" ]; then
|
||||
echo "Loading environment variables from .env..."
|
||||
export $(grep -v '^#' .env | xargs)
|
||||
else
|
||||
echo ".env file not found! Please create a .env file with the required variables."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Konfigurasi
|
||||
PROJECT_NAME="hipmi"
|
||||
REPO_NAME="hipmi"
|
||||
PROJECT_PATH="/root/projects/staging/$PROJECT_NAME/"
|
||||
RELEASE_NAME=$1
|
||||
RELEASE_PATH="$PROJECT_PATH/releases"
|
||||
ENV_PATH="$PROJECT_PATH/shared/env"
|
||||
SCRIPT_PATH="$PROJECT_PATH/scripts"
|
||||
ENV_NAME="staging"
|
||||
REPO="https://github.com/bipproduction/$REPO_NAME.git"
|
||||
BRANCH="staging"
|
||||
|
||||
# Jika RELEASE_NAME tidak diberikan, ambil SHA dari commit terakhir di branch staging
|
||||
if [ -z "$RELEASE_NAME" ]; then
|
||||
echo "No release name provided. Fetching the latest commit SHA from the $BRANCH branch..."
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "GitHub token not found in .env file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ambil SHA commit terbaru dari API GitHub
|
||||
API_URL="https://api.github.com/repos/bipproduction/$REPO_NAME/branches/$BRANCH"
|
||||
RESPONSE=$(curl -s -H "Authorization: token $TOKEN" "$API_URL")
|
||||
|
||||
# Ekstrak SHA commit terbaru
|
||||
SHA=$(echo "$RESPONSE" | grep -oP '"sha":\s*"\K[^"]+' | head -n 1)
|
||||
|
||||
if [ -z "$SHA" ]; then
|
||||
echo "Failed to fetch the latest commit SHA!"
|
||||
echo "API Response: $RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validasi panjang SHA (harus 40 karakter)
|
||||
if [ ${#SHA} -ne 40 ]; then
|
||||
echo "Invalid SHA length: $SHA"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RELEASE_NAME=$SHA
|
||||
echo "Using commit SHA as release name: $RELEASE_NAME"
|
||||
fi
|
||||
|
||||
# VALIDASI INPUT
|
||||
if [ -z "$RELEASE_NAME" ]; then
|
||||
echo "Release name is empty! Please provide a valid release name or allow fetching the latest commit SHA."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# CEK EXISTING DIRECTORY
|
||||
if [ -d "$RELEASE_PATH/$RELEASE_NAME" ]; then
|
||||
echo "Error: Directory '$RELEASE_PATH/$RELEASE_NAME' already exists. Please choose a different release name."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# CLONE REPOSITORY
|
||||
echo "Cloning repository..."
|
||||
git clone -b "$BRANCH" "$REPO" "$RELEASE_PATH/$RELEASE_NAME" || { echo "Failed to clone repository!"; exit 1; }
|
||||
|
||||
# CHECK ENVIRONMENT FILE
|
||||
if [ ! -f "$ENV_PATH/.env.$ENV_NAME" ]; then
|
||||
echo "Environment file $ENV_PATH/.env.$ENV_NAME not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# COPY ENV FILE
|
||||
cp "$ENV_PATH/.env.$ENV_NAME" "$RELEASE_PATH/$RELEASE_NAME/.env"
|
||||
|
||||
# NAVIGATE TO RELEASE DIRECTORY
|
||||
cd "$RELEASE_PATH/$RELEASE_NAME" || { echo "Failed to navigate to directory!"; exit 1; }
|
||||
|
||||
# INSTALL DEPENDENCIES
|
||||
echo "Installing dependencies with --smol flag..."
|
||||
bun i || { echo "Install dependencies failed!"; exit 1; }
|
||||
|
||||
# BUILD RELEASE
|
||||
echo "Building release with --smol flag..."
|
||||
nice -n 19 bun --smol run build || { echo "Build failed!"; exit 1; }
|
||||
|
||||
# PUSH PRISMA
|
||||
echo "Pushing Prisma schema to database..."
|
||||
nice -n 19 bunx prisma db push || { echo "Prisma DB push failed!"; exit 1; }
|
||||
|
||||
# PROMOTE RELEASE
|
||||
SOURCE_DIR="$RELEASE_PATH/$RELEASE_NAME"
|
||||
TARGET_DIR="$PROJECT_PATH/current"
|
||||
if [ -L "$TARGET_DIR" ]; then
|
||||
echo "Removing existing symlink at $TARGET_DIR..."
|
||||
rm "$TARGET_DIR" || { echo "Failed to remove existing symlink!"; exit 1; }
|
||||
fi
|
||||
ln -s "$SOURCE_DIR" "$TARGET_DIR" || { echo "Failed to create symlink!"; exit 1; }
|
||||
if [ ! -L "$TARGET_DIR" ]; then
|
||||
echo "Error: Symlink creation failed!"
|
||||
exit 1
|
||||
fi
|
||||
echo "Symlink successfully created from $SOURCE_DIR to $TARGET_DIR"
|
||||
|
||||
# RESTART SERVER
|
||||
echo "Restarting server..."
|
||||
pm2 restart "$PROJECT_NAME-$ENV_NAME" || { echo "Failed to restart $PROJECT_NAME-$ENV_NAME namespace!"; exit 1; }
|
||||
BIN
compressed_pdf.pdf
Normal file
@@ -1,7 +1,20 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/assets/:path*', // Path ke folder gambar
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=3600, stale-while-revalidate=600', // Cache selama 1 jam, validasi ulang setelah 10 menit
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
35
package.json
@@ -9,17 +9,46 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elysiajs/cors": "^1.2.0",
|
||||
"@elysiajs/eden": "^1.2.0",
|
||||
"@elysiajs/stream": "^1.1.0",
|
||||
"@elysiajs/swagger": "^1.2.0",
|
||||
"@mantine/carousel": "^7.16.2",
|
||||
"@mantine/core": "^7.16.2",
|
||||
"@mantine/hooks": "^7.16.2",
|
||||
"@prisma/client": "^6.3.1",
|
||||
"@types/bun": "^1.2.2",
|
||||
"@types/lodash": "^4.17.15",
|
||||
"add": "^2.0.6",
|
||||
"animate.css": "^4.1.1",
|
||||
"compress-pdf": "^0.5.2",
|
||||
"elysia": "^1.2.12",
|
||||
"embla-carousel-autoplay": "^8.5.2",
|
||||
"embla-carousel-react": "^7.1.0",
|
||||
"framer-motion": "^12.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"motion": "^12.4.1",
|
||||
"next": "15.1.6",
|
||||
"next-view-transitions": "^0.3.4",
|
||||
"prisma": "^6.3.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"next": "15.1.6"
|
||||
"react-multi-carousel": "^2.8.5",
|
||||
"react-scroll-motion": "^0.3.5",
|
||||
"readdirp": "^4.1.1",
|
||||
"swr": "^2.3.2",
|
||||
"valtio": "^2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.1.6",
|
||||
"@eslint/eslintrc": "^3"
|
||||
"postcss": "^8.5.1",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
14
postcss.config.cjs
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-simple-vars': {
|
||||
variables: {
|
||||
'mantine-breakpoint-xs': '36em',
|
||||
'mantine-breakpoint-sm': '48em',
|
||||
'mantine-breakpoint-md': '62em',
|
||||
'mantine-breakpoint-lg': '75em',
|
||||
'mantine-breakpoint-xl': '88em',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
35
prisma/data/list-layanan.json
Normal file
@@ -0,0 +1,35 @@
|
||||
[
|
||||
{
|
||||
"name": "surat keterangan domisili organisasi"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan penghasilan"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan tidak mampu"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan kelahiran"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan usaha"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan tempat usaha"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan belum kawin"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan kelakuan baik (pengantar skck)"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan kematian"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan beda biodata diri"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan yatim / piatu / yatim piatu"
|
||||
}
|
||||
]
|
||||
41
prisma/data/list-potensi.json
Normal file
@@ -0,0 +1,41 @@
|
||||
[
|
||||
{
|
||||
"name": "TPS3R Pudak Mesari"
|
||||
},
|
||||
{
|
||||
"name": "Bumdes Pudak Mesari"
|
||||
},
|
||||
{
|
||||
"name": "Pertanian"
|
||||
},
|
||||
{
|
||||
"name": "Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa"
|
||||
},
|
||||
{
|
||||
"name": "Taman Beji Cengana"
|
||||
},
|
||||
{
|
||||
"name": "Dam Tanah Putih"
|
||||
},
|
||||
{
|
||||
"name": "Gumuh Sari Water Park"
|
||||
},
|
||||
{
|
||||
"name": "UMKM"
|
||||
},
|
||||
{
|
||||
"name": "Kawasan Kuliner"
|
||||
},
|
||||
{
|
||||
"name": "IKM berbasis Pengolahan Pangan"
|
||||
},
|
||||
{
|
||||
"name": "Genteng"
|
||||
},
|
||||
{
|
||||
"name": "Peternakan Ikan Lele"
|
||||
},
|
||||
{
|
||||
"name": "Pemotongan Daging"
|
||||
}
|
||||
]
|
||||
24
prisma/schema.prisma
Normal file
@@ -0,0 +1,24 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Layanan {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
}
|
||||
|
||||
model Potensi {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
}
|
||||
39
prisma/seed.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import layanan from './data/list-layanan.json'
|
||||
import potensi from './data/list-potensi.json'
|
||||
import prisma from '@/lib/prisma';
|
||||
; (async () => {
|
||||
for (const l of layanan) {
|
||||
await prisma.layanan.upsert({
|
||||
where: {
|
||||
name: l.name
|
||||
},
|
||||
update: {
|
||||
name: l.name
|
||||
},
|
||||
create: {
|
||||
name: l.name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
console.log("layanan success ...")
|
||||
|
||||
for (const p of potensi) {
|
||||
await prisma.potensi.upsert({
|
||||
where: {
|
||||
name: p.name
|
||||
},
|
||||
update: {
|
||||
name: p.name
|
||||
},
|
||||
create: {
|
||||
name: p.name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
console.log("potensi success ...")
|
||||
|
||||
|
||||
|
||||
})();
|
||||
35
public/assets/data/list-layanan.json
Normal file
@@ -0,0 +1,35 @@
|
||||
[
|
||||
{
|
||||
"name": "surat keterangan domisili organisasi"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan penghasilan"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan tidak mampu"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan kelahiran"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan usaha"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan tempat usaha"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan belum kawin"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan kelakuan baik (pengantar skck)"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan kematian"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan beda biodata diri"
|
||||
},
|
||||
{
|
||||
"name": "surat keterangan yatim / piatu / yatim piatu"
|
||||
}
|
||||
]
|
||||
BIN
public/assets/fonts/font.otf
Normal file
BIN
public/assets/images/bg-blur.png
Normal file
|
After Width: | Height: | Size: 1009 KiB |
BIN
public/assets/images/bg-slide.png
Normal file
|
After Width: | Height: | Size: 366 KiB |
BIN
public/assets/images/bg-slide2.png
Normal file
|
After Width: | Height: | Size: 274 KiB |
BIN
public/assets/images/bg-slide3.png
Normal file
|
After Width: | Height: | Size: 495 KiB |
BIN
public/assets/images/bg.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/assets/images/darmasaba-icon.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
public/assets/images/module/bares.png
Normal file
|
After Width: | Height: | Size: 560 KiB |
BIN
public/assets/images/module/bicara-darma.png
Normal file
|
After Width: | Height: | Size: 412 KiB |
BIN
public/assets/images/module/daves.png
Normal file
|
After Width: | Height: | Size: 534 KiB |
BIN
public/assets/images/module/gelah-melah.png
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
public/assets/images/module/inovasi-desa-darmasaba.png
Normal file
|
After Width: | Height: | Size: 636 KiB |
BIN
public/assets/images/module/mangan.png
Normal file
|
After Width: | Height: | Size: 510 KiB |
BIN
public/assets/images/module/pdkt.png
Normal file
|
After Width: | Height: | Size: 512 KiB |
BIN
public/assets/images/module/sajjana-dharma-raksaka.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
public/assets/images/perbekel.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
public/assets/images/pudak-icon.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/assets/images/sosmed/facebook.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/assets/images/sosmed/gmail.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/assets/images/sosmed/instagram.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
public/assets/images/sosmed/telegram.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/assets/images/sosmed/tiktok.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
public/assets/images/sosmed/whatsapp.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
public/assets/images/sosmed/x-twitter.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/assets/images/sosmed/youtube.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/assets/images/tps.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
public/assets/videos/award.mp4
Normal file
11
src/app/_com/WebVitals.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
"use client";
|
||||
|
||||
import { useReportWebVitals } from "next/web-vitals";
|
||||
|
||||
export function WebVitals() {
|
||||
useReportWebVitals((metric) => {
|
||||
// console.log(metric);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
128
src/app/animate/page.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
motion,
|
||||
MotionValue,
|
||||
useScroll,
|
||||
useSpring,
|
||||
useTransform,
|
||||
} from "motion/react"
|
||||
import { useRef } from "react"
|
||||
|
||||
function useParallax(value: MotionValue<number>, distance: number) {
|
||||
return useTransform(value, [0, 1], [-distance, distance])
|
||||
}
|
||||
|
||||
function Image({ id }: { id: number }) {
|
||||
const ref = useRef(null)
|
||||
const { scrollYProgress } = useScroll({ target: ref })
|
||||
const y = useParallax(scrollYProgress, 300)
|
||||
|
||||
return (
|
||||
<section className="img-container">
|
||||
<div ref={ref}>
|
||||
<img
|
||||
src={`https://placehold.co/40${id}`}
|
||||
alt="A London skyscraper"
|
||||
/>
|
||||
</div>
|
||||
<motion.h2
|
||||
// Hide until scroll progress is measured
|
||||
initial={{ visibility: "hidden" }}
|
||||
animate={{ visibility: "visible" }}
|
||||
style={{ y }}
|
||||
>{`#00${id}`}</motion.h2>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Parallax() {
|
||||
const { scrollYProgress } = useScroll()
|
||||
const scaleX = useSpring(scrollYProgress, {
|
||||
stiffness: 100,
|
||||
damping: 30,
|
||||
restDelta: 0.001,
|
||||
})
|
||||
|
||||
return (
|
||||
<div id="example">
|
||||
{[1, 2, 3, 4, 5].map((image) => (
|
||||
<Image key={image} id={image} />
|
||||
))}
|
||||
<motion.div className="progress" style={{ scaleX }} />
|
||||
<StyleSheet />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ============== Styles ================
|
||||
*/
|
||||
|
||||
function StyleSheet() {
|
||||
return (
|
||||
<style>{`
|
||||
html {
|
||||
scroll-snap-type: y mandatory;
|
||||
}
|
||||
|
||||
.img-container {
|
||||
height: 100vh;
|
||||
scroll-snap-align: start;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.img-container > div {
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
margin: 20px;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.img-container img {
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.img-container > div {
|
||||
width: 150px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.img-container img {
|
||||
width: 150px;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.img-container h2 {
|
||||
color: #4ff0b7;
|
||||
margin: 0;
|
||||
font-family: JetBrains Mono, monospace;
|
||||
font-size: 50px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -3px;
|
||||
line-height: 1.2;
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
top: calc(50% - 25px);
|
||||
left: calc(50% + 120px);
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 5px;
|
||||
background: #4ff0b7;
|
||||
bottom: 50px;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
`}</style>
|
||||
)
|
||||
}
|
||||
10
src/app/api/[[...slugs]]/_lib/get-potensi.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
async function getPotensi() {
|
||||
const data = await prisma.potensi.findMany()
|
||||
return {
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
export default getPotensi
|
||||
38
src/app/api/[[...slugs]]/route.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import cors, { HTTPMethod } from "@elysiajs/cors";
|
||||
import swagger from "@elysiajs/swagger";
|
||||
import { Elysia } from "elysia";
|
||||
import getPotensi from "./_lib/get-potensi";
|
||||
|
||||
const corsConfig = {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST", "PATCH", "DELETE", "PUT"] as HTTPMethod[],
|
||||
allowedHeaders: "*",
|
||||
exposedHeaders: "*",
|
||||
maxAge: 5,
|
||||
credentials: true,
|
||||
};
|
||||
|
||||
|
||||
async function layanan() {
|
||||
const data = await prisma.layanan.findMany();
|
||||
return { data };
|
||||
}
|
||||
const ApiServer = new Elysia()
|
||||
.use(swagger({ path: "/api/docs" }))
|
||||
.use(cors(corsConfig))
|
||||
.group("/api", app => app
|
||||
.get("/layanan", layanan)
|
||||
.get("/potensi", getPotensi)
|
||||
)
|
||||
|
||||
|
||||
|
||||
export const GET = ApiServer.handle;
|
||||
export const POST = ApiServer.handle;
|
||||
export const PATCH = ApiServer.handle;
|
||||
export const DELETE = ApiServer.handle;
|
||||
export const PUT = ApiServer.handle;
|
||||
|
||||
export type AppServer = typeof ApiServer
|
||||
|
||||
7
src/app/desa/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
desa
|
||||
</Stack>
|
||||
}
|
||||
7
src/app/ekonomi/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
ekonomi
|
||||
</Stack>
|
||||
}
|
||||
@@ -1,42 +1,31 @@
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
/* styles/globals.css */
|
||||
@font-face {
|
||||
font-family: 'San Francisco';
|
||||
src: url('/assets/fonts/font.otf') format('opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
-webkit-backdrop-filter: blur(40px);
|
||||
backdrop-filter: blur(40px);
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
.glass2 {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
-webkit-backdrop-filter: blur(40px);
|
||||
backdrop-filter: blur(40px);
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color-scheme: dark;
|
||||
}
|
||||
.glass3 {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
-webkit-backdrop-filter: blur(40px);
|
||||
backdrop-filter: blur(40px);
|
||||
}
|
||||
|
||||
7
src/app/inovasi/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
inovasi
|
||||
</Stack>
|
||||
}
|
||||
7
src/app/keamanan/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
keamanan
|
||||
</Stack>
|
||||
}
|
||||
7
src/app/kesehatan/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
kesehatan
|
||||
</Stack>
|
||||
}
|
||||
@@ -1,32 +1,60 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
// Import styles of packages that you've installed.
|
||||
// All packages except `@mantine/hooks` require styles imports
|
||||
import "@mantine/carousel/styles.css";
|
||||
import "@mantine/core/styles.css";
|
||||
import "animate.css";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
import LoadDataFirstClient from "@/com/LoadDataFirstClient";
|
||||
import {
|
||||
ColorSchemeScript,
|
||||
MantineProvider,
|
||||
createTheme,
|
||||
mantineHtmlProps,
|
||||
} from "@mantine/core";
|
||||
import { MainLayout } from "../com/MainLayout";
|
||||
import { ViewTransitions } from "next-view-transitions";
|
||||
import { WebVitals } from "./_com/WebVitals";
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
export const metadata = {
|
||||
title: "desa darmasaba",
|
||||
description: "Desa Darmasaba Kabupaten Badung",
|
||||
};
|
||||
|
||||
const theme = createTheme({
|
||||
fontFamily:
|
||||
"San Francisco, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif",
|
||||
fontFamilyMonospace:
|
||||
"SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace",
|
||||
headings: { fontFamily: "San Francisco, sans-serif" },
|
||||
});
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
<ViewTransitions>
|
||||
<html lang="en" {...mantineHtmlProps}>
|
||||
<head>
|
||||
<ColorSchemeScript />
|
||||
<link
|
||||
rel="icon"
|
||||
href="/assets/images/darmasaba-icon.png"
|
||||
sizes="any"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<MantineProvider theme={theme}>
|
||||
<MainLayout>
|
||||
<WebVitals />
|
||||
{children}
|
||||
</MainLayout>
|
||||
</MantineProvider>
|
||||
</body>
|
||||
<LoadDataFirstClient />
|
||||
</html>
|
||||
</ViewTransitions>
|
||||
);
|
||||
}
|
||||
|
||||
7
src/app/lingkungan/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
lingkungan
|
||||
</Stack>
|
||||
}
|
||||
15
src/app/module/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
'use client'
|
||||
|
||||
import { Stack } from "@mantine/core"
|
||||
import { useSnapshot } from "valtio"
|
||||
import stateNav from "@/state/state-nav"
|
||||
|
||||
export default function Page() {
|
||||
const { module, isSearch } = useSnapshot(stateNav)
|
||||
|
||||
return (
|
||||
<Stack h={"100vh"} >
|
||||
{JSON.stringify({ module, isSearch })}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
.page {
|
||||
--gray-rgb: 0, 0, 0;
|
||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
||||
|
||||
--button-primary-hover: #383838;
|
||||
--button-secondary-hover: #f2f2f2;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: 20px 1fr 20px;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
min-height: 100svh;
|
||||
padding: 80px;
|
||||
gap: 64px;
|
||||
font-family: var(--font-geist-sans);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.page {
|
||||
--gray-rgb: 255, 255, 255;
|
||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
||||
|
||||
--button-primary-hover: #ccc;
|
||||
--button-secondary-hover: #1a1a1a;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
grid-row-start: 2;
|
||||
}
|
||||
|
||||
.main ol {
|
||||
font-family: var(--font-geist-mono);
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.01em;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
.main li:not(:last-of-type) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.main code {
|
||||
font-family: inherit;
|
||||
background: var(--gray-alpha-100);
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ctas {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.ctas a {
|
||||
appearance: none;
|
||||
border-radius: 128px;
|
||||
height: 48px;
|
||||
padding: 0 20px;
|
||||
border: none;
|
||||
border: 1px solid transparent;
|
||||
transition:
|
||||
background 0.2s,
|
||||
color 0.2s,
|
||||
border-color 0.2s;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
a.primary {
|
||||
background: var(--foreground);
|
||||
color: var(--background);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
a.secondary {
|
||||
border-color: var(--gray-alpha-200);
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
grid-row-start: 3;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footer img {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Enable hover only on non-touch devices */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
a.primary:hover {
|
||||
background: var(--button-primary-hover);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
a.secondary:hover {
|
||||
background: var(--button-secondary-hover);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.page {
|
||||
padding: 32px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.main {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main ol {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ctas {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ctas a {
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
a.secondary {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.logo {
|
||||
filter: invert();
|
||||
}
|
||||
}
|
||||
119
src/app/page.tsx
@@ -1,95 +1,32 @@
|
||||
import Image from "next/image";
|
||||
import styles from "./page.module.css";
|
||||
import Content1 from "@/com/main-page/content-1";
|
||||
import Content2 from "@/com/main-page/content-2";
|
||||
import Content3 from "@/com/main-page/content-3";
|
||||
import Content4 from "@/com/main-page/content-4";
|
||||
import Content5 from "@/com/main-page/content-5";
|
||||
import Content6 from "@/com/main-page/content-6";
|
||||
import colors from "@/con/colors";
|
||||
// import ApiFetch from "@/lib/api-fetch";
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Home() {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<main className={styles.main}>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol>
|
||||
<li>
|
||||
Get started by editing <code>src/app/page.tsx</code>.
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
|
||||
<div className={styles.ctas}>
|
||||
<a
|
||||
className={styles.primary}
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.secondary}
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className={styles.footer}>
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
<Stack bg={colors.grey[1]} gap={"4rem"}>
|
||||
<Content1 />
|
||||
<Content2 />
|
||||
<Content3 />
|
||||
<Content4 />
|
||||
<Content5 />
|
||||
<Content6 />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
// async function Content3Loader() {
|
||||
// const { data } = await fetch("/api/layanan").then((v) => v.json());
|
||||
// return <Content3 data={data} />;
|
||||
// }
|
||||
|
||||
// async function Content4Loader() {
|
||||
// const { data } = await ApiFetch.api.potensi.get();
|
||||
// return <Content4 data={data?.data as any} />;
|
||||
// }
|
||||
|
||||
7
src/app/pendidikan/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Stack } from "@mantine/core";
|
||||
|
||||
export default function Page() {
|
||||
return <Stack>
|
||||
pendidikan
|
||||
</Stack>
|
||||
}
|
||||
14
src/com/Footer.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
import { Stack, Container, Center, Text } from "@mantine/core";
|
||||
|
||||
function Footer() {
|
||||
return <Stack>
|
||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} h={720} >
|
||||
<Center>
|
||||
<Text fz={"3.4rem"}>Footer</Text>
|
||||
</Center>
|
||||
</Container>
|
||||
</Stack>
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
8
src/com/LoadDataFirstClient.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
"use client";
|
||||
|
||||
|
||||
|
||||
export default function LoadDataFirstClient() {
|
||||
|
||||
return null;
|
||||
}
|
||||
17
src/com/MainLayout.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Space, Stack } from "@mantine/core";
|
||||
import { Navbar } from "./Navbar";
|
||||
import Footer from "./Footer";
|
||||
|
||||
export function MainLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Stack gap={0}>
|
||||
<Navbar />
|
||||
<Space h={{
|
||||
base: "2.2rem",
|
||||
md: "2.5rem"
|
||||
}} />
|
||||
{children}
|
||||
<Footer />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
23
src/com/NavBarSearch.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import stateNav from "@/state/state-nav";
|
||||
import { Container, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
export function NavbarSearch() {
|
||||
return <Container w={{
|
||||
base: '100%',
|
||||
md: '80%',
|
||||
}} fluid py={"xl"}
|
||||
onMouseLeave={stateNav.clear}
|
||||
>
|
||||
<Stack pt={"xl"}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
styles={{
|
||||
input: {
|
||||
borderRadius: "xl",
|
||||
color: "black",
|
||||
// backgroundColor: "rgba(255, 255, 255, 0.3)"
|
||||
}
|
||||
}} size="lg" variant="transparent" placeholder="Cari" />
|
||||
</Stack>
|
||||
</Container>
|
||||
}
|
||||
32
src/com/Navbar.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
"use client";
|
||||
import colors from "@/con/colors";
|
||||
import navbarListMenu from "@/con/navbar-list-menu";
|
||||
import stateNav from "@/state/state-nav";
|
||||
import { Box, Burger, Group } from "@mantine/core";
|
||||
import { useSnapshot } from "valtio";
|
||||
import { NavbarMainMenu } from "./NavbarMainMenu";
|
||||
|
||||
export function Navbar() {
|
||||
const { item, isSearch } = useSnapshot(stateNav);
|
||||
return (
|
||||
<Box>
|
||||
<Box
|
||||
className="glass2"
|
||||
w={"100%"}
|
||||
pos={"fixed"}
|
||||
top={0}
|
||||
style={{
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
<NavbarMainMenu listNavbar={navbarListMenu} />
|
||||
<Group justify="end">
|
||||
<Box hiddenFrom="sm">
|
||||
<Burger color={colors["blue-button"]} />
|
||||
</Box>
|
||||
</Group>
|
||||
</Box>
|
||||
{(item || isSearch) && <Box className="glass" />}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
70
src/com/NavbarMainMenu.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client'
|
||||
|
||||
import colors from "@/con/colors"
|
||||
import stateNav from "@/state/state-nav"
|
||||
import { ActionIcon, Button, Container, Flex, Image, Stack } from "@mantine/core"
|
||||
import { useHover } from "@mantine/hooks"
|
||||
import { useTransitionRouter } from 'next-view-transitions'
|
||||
import { useSnapshot } from "valtio"
|
||||
import { MenuItem } from "../../types/menu-item"
|
||||
import { NavbarSearch } from "./NavBarSearch"
|
||||
import { NavbarSubMenu } from "./NavbarSubMenu"
|
||||
|
||||
export function NavbarMainMenu({ listNavbar }: {
|
||||
listNavbar: MenuItem[]
|
||||
}) {
|
||||
const { item, isSearch } = useSnapshot(stateNav)
|
||||
const router = useTransitionRouter()
|
||||
return <Stack gap={0} visibleFrom="sm" bg={colors["white-trans-1"]}>
|
||||
<Container pos={"relative"} w={{
|
||||
base: '100%',
|
||||
md: '80%',
|
||||
}} fluid>
|
||||
<Flex align={"center"} justify={"space-between"} wrap={{
|
||||
base: "wrap",
|
||||
md: "nowrap"
|
||||
}}>
|
||||
<ActionIcon radius={"100"} onClick={() => {
|
||||
router.push("/")
|
||||
stateNav.clear()
|
||||
}} >
|
||||
<Image radius={"100"} src={"/assets/images/darmasaba-icon.png"} alt="icon" w={24} h={24} loading="lazy" />
|
||||
</ActionIcon>
|
||||
{listNavbar.map((item, k) => {
|
||||
return <MenuItemCom key={k} item={item} />
|
||||
})}
|
||||
<ActionIcon variant="transparent" c={isSearch ? 'grey' : colors["blue-button"]}
|
||||
onClick={() => {
|
||||
stateNav.item = null
|
||||
stateNav.isSearch = !stateNav.isSearch
|
||||
}}
|
||||
>
|
||||
{/* TODO: add icon search */}
|
||||
{/* <MdSearch size={"1.5rem"} /> */}
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</Container>
|
||||
{item && <NavbarSubMenu item={item as MenuItem[]} />}
|
||||
{isSearch && <NavbarSearch />}
|
||||
</Stack>
|
||||
|
||||
}
|
||||
|
||||
function MenuItemCom({ item, }: { item: MenuItem }) {
|
||||
const { ref, hovered } = useHover()
|
||||
const router = useTransitionRouter()
|
||||
|
||||
return <Button
|
||||
ref={ref}
|
||||
color={hovered ? "grey" : colors["blue-button"]}
|
||||
onMouseEnter={() => {
|
||||
stateNav.item = item.children || null
|
||||
stateNav.isSearch = false
|
||||
}}
|
||||
variant="transparent"
|
||||
onClick={() => {
|
||||
router.push(item.href)
|
||||
stateNav.clear()
|
||||
}}
|
||||
>{item.name}</Button>
|
||||
}
|
||||
50
src/com/NavbarSubMenu.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import stateNav from "@/state/state-nav";
|
||||
import { Button, Container, Stack } from "@mantine/core";
|
||||
import { motion } from "motion/react";
|
||||
import Link from "next/link";
|
||||
import { MenuItem } from "../../types/menu-item";
|
||||
import _ from "lodash";
|
||||
|
||||
export function NavbarSubMenu({ item }: { item: MenuItem[] | null }) {
|
||||
return (
|
||||
<motion.div
|
||||
key={_.uniqueId()}
|
||||
initial={{ opacity: 0.5 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1 }}
|
||||
>
|
||||
<Container
|
||||
key={stateNav.item?.[0]?.id}
|
||||
onMouseLeave={() => {
|
||||
stateNav.item = null;
|
||||
stateNav.isSearch = false;
|
||||
}}
|
||||
w={{
|
||||
base: "100%",
|
||||
md: "80%",
|
||||
}}
|
||||
fluid
|
||||
>
|
||||
<Stack gap={0} align="start" py={"xl"}>
|
||||
{item &&
|
||||
item.map((item, k) => {
|
||||
return (
|
||||
<Button
|
||||
fz={"lg"}
|
||||
color="dark.9"
|
||||
variant="transparent"
|
||||
component={Link}
|
||||
href={item.href}
|
||||
key={k}
|
||||
>
|
||||
{item.name}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Container>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
49
src/com/main-page/content-1/ModuleView.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import images from "@/con/images";
|
||||
import stateNav from "@/state/state-nav";
|
||||
import { Card, Image, SimpleGrid, Stack } from "@mantine/core";
|
||||
import { useHover } from "@mantine/hooks";
|
||||
import { useTransitionRouter } from 'next-view-transitions'
|
||||
|
||||
|
||||
const listImageModule = Object.values(images.module);
|
||||
|
||||
function ModuleItem({ item }: { item: string }) {
|
||||
const router = useTransitionRouter();
|
||||
const { ref, hovered } = useHover();
|
||||
return (
|
||||
<Card
|
||||
onClick={() => {
|
||||
stateNav.module = item;
|
||||
router.push("/module");
|
||||
}}
|
||||
ref={ref}
|
||||
p={"md"}
|
||||
bg={"white"}
|
||||
radius={"32"}
|
||||
style={{
|
||||
border: `2px solid ${hovered ? "lightgray" : "transparent"}`,
|
||||
}}
|
||||
>
|
||||
<Stack justify="end" h={"100%"}>
|
||||
<Image src={item} alt="icon" fit="contain" loading="lazy" />
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleView() {
|
||||
return (
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
base: 3,
|
||||
md: 4,
|
||||
}}
|
||||
>
|
||||
{listImageModule.map((item, k) => {
|
||||
return <ModuleItem key={k} item={item} />;
|
||||
})}
|
||||
</SimpleGrid>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModuleView;
|
||||
25
src/com/main-page/content-1/SosmedView.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import images from "@/con/images";
|
||||
import { Flex, ActionIcon, Image } from "@mantine/core";
|
||||
|
||||
function SosmedView() {
|
||||
const listSosmed = Object.values(images.sosmed);
|
||||
return (
|
||||
<Flex gap={"md"}>
|
||||
{listSosmed.map((item, k) => {
|
||||
return (
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
key={k}
|
||||
w={32}
|
||||
h={32}
|
||||
pos={"relative"}
|
||||
>
|
||||
<Image src={item} alt="icon" loading="lazy" fit="contain" />
|
||||
</ActionIcon>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default SosmedView;
|
||||
203
src/com/main-page/content-1/index.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
"use client";
|
||||
import colors from "@/con/colors";
|
||||
import {
|
||||
BackgroundImage,
|
||||
Flex,
|
||||
Box,
|
||||
Card,
|
||||
Stack,
|
||||
Image,
|
||||
Text,
|
||||
Button,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import ModuleView from "./ModuleView";
|
||||
import SosmedView from "./SosmedView";
|
||||
|
||||
function Content1() {
|
||||
return (
|
||||
<BackgroundImage src={"/assets/images/bg-blur.png"}>
|
||||
<Flex
|
||||
gap={"md"}
|
||||
wrap={{
|
||||
base: "wrap",
|
||||
md: "nowrap",
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={"xl"}
|
||||
w={{
|
||||
base: "100%",
|
||||
md: "60%",
|
||||
}}
|
||||
h={820}
|
||||
py={{
|
||||
base: "xs",
|
||||
md: "72",
|
||||
}}
|
||||
px={{
|
||||
base: "xs",
|
||||
md: "100",
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
radius={"32"}
|
||||
bg={colors.grey[1]}
|
||||
p={{
|
||||
base: "xs",
|
||||
md: "32",
|
||||
}}
|
||||
>
|
||||
<Stack gap={42}>
|
||||
<Flex
|
||||
gap={"md"}
|
||||
wrap={{
|
||||
base: "wrap",
|
||||
md: "nowrap",
|
||||
}}
|
||||
>
|
||||
<Flex gap={"md"} flex={1}>
|
||||
<Box
|
||||
bg={"white"}
|
||||
w={{
|
||||
base: 64,
|
||||
md: 72,
|
||||
}}
|
||||
h={{
|
||||
base: 64,
|
||||
md: 72,
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 24,
|
||||
}}
|
||||
p={"sm"}
|
||||
>
|
||||
<Image
|
||||
src={"/assets/images/darmasaba-icon.png"}
|
||||
alt="icon"
|
||||
loading="lazy"
|
||||
fit="contain"
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
w={{
|
||||
base: 64,
|
||||
md: 72,
|
||||
}}
|
||||
h={{
|
||||
base: 64,
|
||||
md: 72,
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 24,
|
||||
}}
|
||||
p={"sm"}
|
||||
bg={"white"}
|
||||
>
|
||||
<Image
|
||||
src={"/assets/images/pudak-icon.png"}
|
||||
alt="icon"
|
||||
fit="contain"
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Stack flex={2} gap={0} justify="end" c={colors["blue-button"]}>
|
||||
<Text
|
||||
fz={{
|
||||
base: "1.5rem",
|
||||
md: "1.4rem",
|
||||
}}
|
||||
>
|
||||
Pemerintah Desa
|
||||
</Text>
|
||||
<Title>DARMASABA</Title>
|
||||
{/* <Text
|
||||
fw={"bold"}
|
||||
fz={{
|
||||
base: "3rem",
|
||||
md: "3rem",
|
||||
sm: ""
|
||||
}}
|
||||
>
|
||||
DARMASABA
|
||||
</Text> */}
|
||||
</Stack>
|
||||
</Flex>
|
||||
<ModuleView />
|
||||
<SosmedView />
|
||||
</Stack>
|
||||
</Card>
|
||||
<Stack
|
||||
align="center"
|
||||
justify={"center"}
|
||||
>
|
||||
<Text c={"white"} style={{
|
||||
textAlign: "center"
|
||||
}} maw={300}>Tambahkan Text Apa aja disini untuk lebih detail yang berwarna putih</Text>
|
||||
<Button
|
||||
radius={100}
|
||||
w={{
|
||||
base: "100%",
|
||||
sm: "300",
|
||||
}}
|
||||
px={42}
|
||||
size="lg"
|
||||
variant="fill"
|
||||
bg={"gray.1"}
|
||||
c={"dark"}
|
||||
>
|
||||
Button 1
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack
|
||||
justify={"end"}
|
||||
align={"end"}
|
||||
pos={"relative"}
|
||||
w={{
|
||||
base: "100%",
|
||||
md: "40%",
|
||||
}}
|
||||
px={"xl"}
|
||||
>
|
||||
<Image
|
||||
src={"/assets/images/perbekel.png"}
|
||||
alt="perbekel"
|
||||
fit="contain" // Menyesuaikan gambar agar sesuai dengan kontainer
|
||||
width={"100%"}
|
||||
h={{
|
||||
base: "500",
|
||||
md: "100%",
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<Box
|
||||
pos={"absolute"}
|
||||
bottom={0}
|
||||
p={{
|
||||
base: "xs",
|
||||
md: "md",
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
px={"lg"}
|
||||
radius={"32"}
|
||||
className="glass3"
|
||||
style={{
|
||||
border: `1px solid white`,
|
||||
}}
|
||||
>
|
||||
<Text>Perbekel Desa Darmasaba</Text>
|
||||
<Text c={colors["blue-button"]} fw={"bolder"} fz={"1rem"}>
|
||||
I.B. Surya Prabhawa Manuaba, S.H.,M.H.,NL.P.
|
||||
</Text>
|
||||
</Card>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Flex>
|
||||
</BackgroundImage>
|
||||
);
|
||||
}
|
||||
|
||||
export default Content1;
|
||||
65
src/com/main-page/content-2/index.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
'use client';
|
||||
import { Stack, Box, Container, Button, Text } from "@mantine/core";
|
||||
|
||||
function Content2() {
|
||||
return (
|
||||
<Stack pos={"relative"} h={720}>
|
||||
<video
|
||||
width="320"
|
||||
height="240"
|
||||
loop
|
||||
autoPlay
|
||||
muted
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<source src="/assets/videos/award.mp4" type="video/mp4" />
|
||||
</video>
|
||||
<Box
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
background: "rgba(0,0,0,0.6)",
|
||||
}}
|
||||
>
|
||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} h={720}>
|
||||
<Stack justify="center" align="center">
|
||||
<Text
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
fw={"bold"}
|
||||
fz={"2.4rem"}
|
||||
c={"white"}
|
||||
>
|
||||
Penghargaan
|
||||
</Text>
|
||||
<Stack align="center" gap={0}>
|
||||
<Text fz={"1.4rem"} c={"white"}>
|
||||
Juara 2 Lomba Video Pendek
|
||||
</Text>
|
||||
<Text fz={"1.4rem"} c={"white"}>
|
||||
Juara 2 Duta Investasi
|
||||
</Text>
|
||||
<Text fz={"1.4rem"} c={"white"}>
|
||||
Juara Favorit Lomba Video Pendek
|
||||
</Text>
|
||||
</Stack>
|
||||
<Button variant="white" radius={100}>
|
||||
Selanjutnya
|
||||
</Button>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Content2;
|
||||
118
src/com/main-page/content-3/index.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
"use client";
|
||||
import colors from "@/con/colors";
|
||||
import images from "@/con/images";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Carousel } from "@mantine/carousel";
|
||||
import {
|
||||
BackgroundImage,
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Divider,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
useMantineTheme,
|
||||
} from "@mantine/core";
|
||||
import { useMediaQuery } from "@mantine/hooks";
|
||||
import Autoplay from "embla-carousel-autoplay";
|
||||
import _ from "lodash";
|
||||
import { useRef } from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
type DataSlider = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
const textHeading = {
|
||||
title: "Layanan",
|
||||
des: "Terwujudnya Layanan umum bertajuk Sistem administrasi Kependudukan Terintegrasi di Desa berbasi Elektronik, Smart dan Aman",
|
||||
};
|
||||
function Content3() {
|
||||
const { data, isLoading } = useSWR(
|
||||
"/",
|
||||
(url) => ApiFetch.api.layanan.get().then(({ data }) => data?.data),
|
||||
{
|
||||
fallbackData: [],
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.grey[1]} gap={"42"} py={"xl"}>
|
||||
<Container w={{ base: "100%", md: "50%" }} p={"xl"}>
|
||||
<Stack align="center" gap={"0"}>
|
||||
<Text fz={"3.4rem"} fw={"bold"}>
|
||||
{textHeading.title}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{textHeading.des}
|
||||
</Text>
|
||||
<Box p={"md"}>
|
||||
<Button variant="filled" bg={"dark"} radius={100}>
|
||||
Lanjutkan
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Container>
|
||||
<Slider data={data as any} />
|
||||
<Divider />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const height = 720;
|
||||
function Slider({ data }: { data: DataSlider[] }) {
|
||||
const theme = useMantineTheme();
|
||||
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);
|
||||
const autoplay = useRef(Autoplay({ delay: 2000 }));
|
||||
|
||||
const slides = data.map((item) => (
|
||||
<Carousel.Slide key={item.id}>
|
||||
<BackgroundImage src={images["bg-slide3"]} h={height} p="xl" radius="md">
|
||||
<Stack justify="space-between" h={"100%"} gap={0}>
|
||||
<Box p={"lg"}>
|
||||
<Text
|
||||
fw={"bold"}
|
||||
c={"white"}
|
||||
size={"3.5rem"}
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{_.startCase(item.name)}
|
||||
</Text>
|
||||
</Box>
|
||||
<Group justify="center">
|
||||
<Button px={46} radius={"100"} size="md">
|
||||
Detail
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</BackgroundImage>
|
||||
</Carousel.Slide>
|
||||
));
|
||||
|
||||
return (
|
||||
<Carousel
|
||||
plugins={[autoplay.current]}
|
||||
onMouseEnter={autoplay.current.stop}
|
||||
onMouseLeave={autoplay.current.reset}
|
||||
height={height}
|
||||
slideSize={{ base: "100%", sm: "50%", md: "33.333333%" }}
|
||||
slideGap={{ base: "xl", sm: "md" }}
|
||||
loop
|
||||
align="start"
|
||||
slidesToScroll={mobile ? 1 : 2}
|
||||
>
|
||||
{slides}
|
||||
</Carousel>
|
||||
);
|
||||
}
|
||||
|
||||
export default Content3;
|
||||
126
src/com/main-page/content-4/index.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
"use client";
|
||||
import colors from "@/con/colors";
|
||||
import images from "@/con/images";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import {
|
||||
BackgroundImage,
|
||||
Box,
|
||||
Button,
|
||||
Divider,
|
||||
Group,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import _ from "lodash";
|
||||
import { motion } from "motion/react";
|
||||
import useSWR from "swr";
|
||||
|
||||
type DataPotensi = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const textHeading = {
|
||||
title: "Potensi",
|
||||
des: "segenap sumber daya alam dan sumber daya manusia yang dimiliki desa sebagai modal dasar yang perlu dikelola dan dikembangkan bagi kelangsungan dan perkembangan desa",
|
||||
};
|
||||
|
||||
function Content4() {
|
||||
const { data, isLoading } = useSWR("/", (url) =>
|
||||
ApiFetch.api.potensi.get().then(({ data }) => data?.data)
|
||||
);
|
||||
if (isLoading) return <Text>loading ...</Text>;
|
||||
return (
|
||||
<Stack p={"sm"} gap={"4rem"}>
|
||||
<Box
|
||||
w={{
|
||||
base: "100%",
|
||||
sm: "60%",
|
||||
}}
|
||||
>
|
||||
<Text fz={"4.4rem"}>{textHeading.title}</Text>
|
||||
<Text size={"1.4rem"}>{textHeading.des}</Text>
|
||||
</Box>
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
base: 1,
|
||||
sm: 2,
|
||||
}}
|
||||
>
|
||||
{_.take(data, 4).map((v, k) => (
|
||||
<motion.div
|
||||
key={k}
|
||||
whileHover={{ scale: 1.01 }}
|
||||
whileTap={{ scale: 0.8 }}
|
||||
>
|
||||
<BackgroundImage
|
||||
src={images.tps}
|
||||
h={320}
|
||||
key={k}
|
||||
radius={16}
|
||||
pos={"relative"}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
borderRadius: 16,
|
||||
zIndex: 0,
|
||||
}}
|
||||
pos={"absolute"}
|
||||
w={"100%"}
|
||||
h={"100%"}
|
||||
bg={colors.trans.dark[2]}
|
||||
/>
|
||||
<Stack
|
||||
justify="end"
|
||||
h={"100%"}
|
||||
p={"md"}
|
||||
align="start"
|
||||
pos={"absolute"}
|
||||
style={{
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<Text fw={"bold"} c={"gray.1"} size={"2.4rem"}>
|
||||
{v.name}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
c={"blue"}
|
||||
>
|
||||
Tambahkan Text Indikasi Keberhasilan
|
||||
</Text>
|
||||
</Stack>
|
||||
</BackgroundImage>
|
||||
</motion.div>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
<Stack align="center">
|
||||
<Group>
|
||||
<Stack gap={0}>
|
||||
<Title>Text Lanjutan Mengarahkan</Title>
|
||||
<Text
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
deskripsi singkat sebelum tombol dibawah setelah ini
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
<Group>
|
||||
<Button variant="outline" radius={100} size="md">
|
||||
Selanjutnya
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
<Divider />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Content4;
|
||||
38
src/com/main-page/content-5/FlipScroll.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from "react";
|
||||
import { motion, useScroll, useTransform } from "framer-motion";
|
||||
import { Image } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
|
||||
const FlipOnScroll = () => {
|
||||
// Menggunakan hook useScroll untuk mendeteksi posisi scroll
|
||||
const { scrollYProgress } = useScroll();
|
||||
|
||||
// Menggunakan useTransform untuk mengubah nilai scroll menjadi rotasi
|
||||
const rotate = useTransform(scrollYProgress, [0, 1], [10, 360 * 1]); // Rotasi dari 0 hingga 360 derajat
|
||||
|
||||
useShallowEffect(() => {
|
||||
rotate.on("change", (latest) => {
|
||||
console.log(latest)
|
||||
})
|
||||
}, [])
|
||||
return (
|
||||
<div style={{ backgroundColor: "gray", padding: "50px" }}>
|
||||
<h1>Scroll ke bawah untuk melihat animasi flip</h1>
|
||||
|
||||
<motion.div
|
||||
style={{
|
||||
width: "500px",
|
||||
height: "500px",
|
||||
backgroundColor: "blue",
|
||||
borderRadius: "10px",
|
||||
|
||||
}}
|
||||
>
|
||||
<Image src={"https://awsimages.detik.net.id/community/media/visual/2023/04/14/gambar-pemandangan-6_169.jpeg?w=1200"} alt="a" />
|
||||
</motion.div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FlipOnScroll;
|
||||
21
src/com/main-page/content-5/index.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
"use client";
|
||||
import { Center, Container, Stack, Text } from "@mantine/core";
|
||||
import { useRef } from "react";
|
||||
import FlipOnScroll from "./FlipScroll";
|
||||
|
||||
function Content5() {
|
||||
const ref = useRef(null);
|
||||
|
||||
return (
|
||||
<Stack ref={ref}>
|
||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} h={720}>
|
||||
<Center>
|
||||
<Text fz={"3.4rem"}>CONTENT 5</Text>
|
||||
</Center>
|
||||
<FlipOnScroll />
|
||||
</Container>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Content5;
|
||||
30
src/com/main-page/content-6/Count.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { Stack } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { animate, motion, useMotionValue, useTransform } from "motion/react";
|
||||
|
||||
export default function Count() {
|
||||
const count = useMotionValue(0);
|
||||
const rounded = useTransform(() => Math.round(count.get()));
|
||||
|
||||
useShallowEffect(() => {
|
||||
const controls = animate(count, 100, { duration: 5 });
|
||||
return () => controls.stop();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<motion.pre style={text}>{rounded}</motion.pre>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ============== Styles ================
|
||||
*/
|
||||
|
||||
const text = {
|
||||
fontSize: 64,
|
||||
color: "#4ff0b7",
|
||||
};
|
||||
18
src/com/main-page/content-6/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
"use client";
|
||||
import { Stack, Container, Center, Text } from "@mantine/core";
|
||||
import Count from "./Count";
|
||||
|
||||
function Content6() {
|
||||
return (
|
||||
<Stack>
|
||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} h={720}>
|
||||
<Center>
|
||||
<Text fz={"3.4rem"}>CONTENT 6</Text>
|
||||
</Center>
|
||||
<Count />
|
||||
</Container>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Content6;
|
||||
20
src/con/colors.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const colors = {
|
||||
"blue-button": "#0A4E78",
|
||||
"white-1": "#FBFBFC",
|
||||
"white-trans-1": "rgba(255, 255, 255, 0.5)",
|
||||
"white-trans-2": "rgba(255, 255, 255, 0.7)",
|
||||
"white-trans-3": "rgba(255, 255, 255, 0.9)",
|
||||
"trans": {
|
||||
"dark": {
|
||||
"1": "rgba(0, 0, 0, 0.5)",
|
||||
"2": "rgba(0, 0, 0, 0.7)",
|
||||
"3": "rgba(0, 0, 0, 0.9)"
|
||||
}
|
||||
},
|
||||
"grey": {
|
||||
"1": "#F4F5F6"
|
||||
}
|
||||
}
|
||||
|
||||
export default colors
|
||||
export type ColorsType = typeof colors
|
||||
32
src/con/images.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
const images = {
|
||||
"pudak-icon": "/assets/images/pudak-icon.png",
|
||||
"module": {
|
||||
"daves": "/assets/images/module/daves.png",
|
||||
"mangan": "/assets/images/module/mangan.png",
|
||||
"bicara-darma": "/assets/images/module/bicara-darma.png",
|
||||
"bares": "/assets/images/module/bares.png",
|
||||
"sajjana-dharma-raksaka": "/assets/images/module/sajjana-dharma-raksaka.png",
|
||||
"pdkt": "/assets/images/module/pdkt.png",
|
||||
"gelah-melah": "/assets/images/module/gelah-melah.png",
|
||||
"inovasi-desa-darmasaba": "/assets/images/module/inovasi-desa-darmasaba.png"
|
||||
},
|
||||
"bg-slide": "/assets/images/bg-slide.png",
|
||||
"sosmed": {
|
||||
"telegram": "/assets/images/sosmed/telegram.png",
|
||||
"instagram": "/assets/images/sosmed/instagram.png",
|
||||
"gmail": "/assets/images/sosmed/gmail.png",
|
||||
"x-twitter": "/assets/images/sosmed/x-twitter.png",
|
||||
"tiktok": "/assets/images/sosmed/tiktok.png",
|
||||
"youtube": "/assets/images/sosmed/youtube.png",
|
||||
"whatsapp": "/assets/images/sosmed/whatsapp.png",
|
||||
"facebook": "/assets/images/sosmed/facebook.png"
|
||||
},
|
||||
"bg-blur": "/assets/images/bg-blur.png",
|
||||
"darmasaba-icon": "/assets/images/darmasaba-icon.png",
|
||||
"bg-slide3": "/assets/images/bg-slide3.png",
|
||||
"bg-slide2": "/assets/images/bg-slide2.png",
|
||||
"perbekel": "/assets/images/perbekel.png",
|
||||
"tps": "/assets/images/tps.png",
|
||||
"bg": "/assets/images/bg.png"
|
||||
};
|
||||
export default images;
|
||||
282
src/con/navbar-list-menu.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
const navbarListMenu = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Desa",
|
||||
href: "/desa",
|
||||
children: [
|
||||
{
|
||||
id: "1.1",
|
||||
name: "profile",
|
||||
href: "/desa/profile"
|
||||
},
|
||||
{
|
||||
id: "1.2",
|
||||
name: "potensi",
|
||||
href: "/desa/potensi"
|
||||
},
|
||||
{
|
||||
id: "1.3",
|
||||
name: "berita",
|
||||
href: "/desa/berita"
|
||||
},
|
||||
{
|
||||
id: "1.4",
|
||||
name: "pengumuman",
|
||||
href: "/desa/pengumuman"
|
||||
},
|
||||
{
|
||||
id: "1.5",
|
||||
name: "galery",
|
||||
href: "/desa/galery"
|
||||
},
|
||||
{
|
||||
id: "1.6",
|
||||
name: "layanan",
|
||||
href: "/desa/layanan"
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Kesehatan",
|
||||
href: "/kesehatan",
|
||||
children: [
|
||||
{
|
||||
id: "2.1",
|
||||
name: "Posyandu",
|
||||
href: "/kesehatan/posyandu"
|
||||
},
|
||||
{
|
||||
id: "2.2",
|
||||
name: "Data Kesehatan Warga",
|
||||
href: "/kesehatan/data-kesehatan-warga"
|
||||
},
|
||||
{
|
||||
id: "2.3",
|
||||
name: "Puskesmas",
|
||||
href: "/kesehatan/puskesmas"
|
||||
},
|
||||
{
|
||||
id: "2.4",
|
||||
name: "Program Kesehatan",
|
||||
href: "/kesehatan/program-kesehatan"
|
||||
},
|
||||
{
|
||||
id: "2.5",
|
||||
name: "Penanganan Darurat",
|
||||
href: "/kesehatan/penanganan-darurat"
|
||||
},
|
||||
{
|
||||
id: "2.6",
|
||||
name: "Kontak Darurat",
|
||||
href: "/kesehatan/kontak-darurat"
|
||||
},
|
||||
{
|
||||
id: "2.7",
|
||||
name: "Info Wabah/Penyakit",
|
||||
href: "/kesehatan/info-wabah-penyakit"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Keamanan",
|
||||
href: "/keamanan",
|
||||
children: [
|
||||
{
|
||||
id: "3.1",
|
||||
name: "Keamanan Lingkungan (Pecalang/Patwal)",
|
||||
href: "/keamanan/keamanan-lingkungan"
|
||||
},
|
||||
{
|
||||
id: "3.2",
|
||||
name: "Polsek Terdekat",
|
||||
href: "/keamanan/polsek-terdekat"
|
||||
},
|
||||
{
|
||||
id: "3.3",
|
||||
name: "Kontak Darurat",
|
||||
href: "/keamanan/kontak-darurat"
|
||||
},
|
||||
{
|
||||
id: "3.4",
|
||||
name: "Pencegahan Kriminalitas",
|
||||
href: "/keamanan/pencegahan-kriminalitas"
|
||||
},
|
||||
{
|
||||
id: "3.5",
|
||||
name: "Laporan Publik",
|
||||
href: "/keamanan/laporan-publik"
|
||||
},
|
||||
{
|
||||
id: "3.6",
|
||||
name: "Tips Keamanan",
|
||||
href: "/keamanan/tips-keamanan"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Ekonomi",
|
||||
href: "/ekonomi",
|
||||
children: [
|
||||
{
|
||||
id: "4.1",
|
||||
name: "Pasar Desa",
|
||||
href: "/ekonomi/pasar-desa"
|
||||
},
|
||||
{
|
||||
id: "4.2",
|
||||
name: "Koperasi",
|
||||
href: "/ekonomi/koperasi"
|
||||
},
|
||||
{
|
||||
id: "4.3",
|
||||
name: "UMKM",
|
||||
href: "/ekonomi/umkm"
|
||||
},
|
||||
{
|
||||
id: "4.4",
|
||||
name: "Data Ekonomi Desa",
|
||||
href: "/ekonomi/data-ekonomi-desa"
|
||||
},
|
||||
{
|
||||
id: "4.5",
|
||||
name: "Pelatihan Wirausaha",
|
||||
href: "/ekonomi/pelatihan-wirausaha"
|
||||
},
|
||||
{
|
||||
id: "4.6",
|
||||
name: "Bantuan & Pendanaan",
|
||||
href: "/ekonomi/bantuan-pendanaan"
|
||||
},
|
||||
{
|
||||
id: "4.7",
|
||||
name: "Investasi Desa",
|
||||
href: "/ekonomi/investasi-desa"
|
||||
},
|
||||
{
|
||||
id: "4.8",
|
||||
name: "Produk Unggulan",
|
||||
href: "/ekonomi/produk-unggulan"
|
||||
},
|
||||
{
|
||||
id: "4.9",
|
||||
name: "Lowongan Kerja Lokal",
|
||||
href: "/ekonomi/lowongan-kerja-lokal"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
id: "5",
|
||||
name: "Inovasi",
|
||||
href: "/inovasi",
|
||||
children: [
|
||||
{
|
||||
id: "5.1",
|
||||
name: "Desa Digital/Smart Village",
|
||||
href: "/inovasi/desa-digital-smart-village"
|
||||
},
|
||||
{
|
||||
id: "5.2",
|
||||
name: "Layanan Online Desa",
|
||||
href: "/inovasi/layanan-online-desa"
|
||||
},
|
||||
{
|
||||
id: "5.3",
|
||||
name: "Program Kreatif Desa",
|
||||
href: "/inovasi/program-kreatif-desa"
|
||||
},
|
||||
{
|
||||
id: "5.4",
|
||||
name: "Kolaborasi Inovasi",
|
||||
href: "/inovasi/kolaborasi-inovasi"
|
||||
},
|
||||
{
|
||||
id: "5.5",
|
||||
name: "Info Teknologi Tepat Guna",
|
||||
href: "/inovasi/info-teknologi-tepat-guna"
|
||||
},
|
||||
{
|
||||
id: "5.6",
|
||||
name: "Ajukan Ide Inovatif",
|
||||
href: "/inovasi/ajukan-ide-inovatif"
|
||||
}
|
||||
|
||||
]
|
||||
}, {
|
||||
id: "6",
|
||||
name: "Lingkungan",
|
||||
href: "/lingkungan",
|
||||
children: [
|
||||
{
|
||||
id: "6.1",
|
||||
name: "Pengelolaan Sampah (Bank Sampah)",
|
||||
href: "/lingkungan/pengelolaan-sampah-bank-sampah"
|
||||
},
|
||||
{
|
||||
id: "6.2",
|
||||
name: "Program Penghijauan",
|
||||
href: "/lingkungan/program-penghijauan"
|
||||
},
|
||||
{
|
||||
id: "6.3",
|
||||
name: "Data Lingkungan Desa",
|
||||
href: "/lingkungan/data-lingkungan-desa"
|
||||
},
|
||||
{
|
||||
id: "6.4",
|
||||
name: "Gotong Royong",
|
||||
href: "/lingkungan/gotong-royong"
|
||||
},
|
||||
{
|
||||
id: "6.5",
|
||||
name: "Edukasi Lingkungan",
|
||||
href: "/lingkungan/edukasi-lingkungan"
|
||||
},
|
||||
{
|
||||
id: "6.6",
|
||||
name: "Konservasi Adat Bali",
|
||||
href: "/lingkungan/konservasi-adat-bali"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
id: "7",
|
||||
name: "Pendidikan",
|
||||
href: "/pendidikan",
|
||||
children: [
|
||||
{
|
||||
id: "7.1",
|
||||
name: "Info Sekolah & PAUD",
|
||||
href: "/pendidikan/info-sekolah-paud"
|
||||
},
|
||||
{
|
||||
id: "7.2",
|
||||
name: "Beasiswa Desa",
|
||||
href: "/pendidikan/beasiswa-desa"
|
||||
},
|
||||
{
|
||||
id: "7.3",
|
||||
name: "Program Pendidikan Anak",
|
||||
href: "/pendidikan/program-pendidikan-anak"
|
||||
},
|
||||
{
|
||||
id: "7.4",
|
||||
name: "Bimbingan Belajar Desa",
|
||||
href: "/pendidikan/bimbingan-belajar-desa"
|
||||
},
|
||||
{
|
||||
id: "7.5",
|
||||
name: "Pendidikan Non Formal",
|
||||
href: "/pendidikan/pendidikan-non-formal"
|
||||
},
|
||||
{
|
||||
id: "7.6",
|
||||
name: "Perpustakaan Desa",
|
||||
href: "/pendidikan/perpustakaan-desa"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export default navbarListMenu
|
||||
26
src/con/sosmed.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
const sosmed = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Facebook",
|
||||
href: "https://facebook.com",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Instagram",
|
||||
href: "https://instagram.com",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Twitter",
|
||||
href: "https://twitter.com",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Youtube",
|
||||
href: "https://youtube.com",
|
||||
}
|
||||
]
|
||||
|
||||
export default sosmed
|
||||
6
src/lib/api-fetch.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
import { AppServer } from '@/app/api/[[...slugs]]/route'
|
||||
import { treaty } from '@elysiajs/eden'
|
||||
const ApiFetch = treaty<AppServer>(process.env.NEXT_PUBLIC_HOST || 'localhost:3000')
|
||||
|
||||
export default ApiFetch
|
||||
44
src/lib/prisma.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
let prisma: PrismaClient;
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
prisma = new PrismaClient();
|
||||
} else {
|
||||
const globalWithPrisma = global as typeof globalThis & {
|
||||
prisma: PrismaClient;
|
||||
};
|
||||
if (!globalWithPrisma.prisma) {
|
||||
globalWithPrisma.prisma = new PrismaClient();
|
||||
}
|
||||
prisma = globalWithPrisma.prisma;
|
||||
}
|
||||
|
||||
// Handle uncaught errors
|
||||
process.on('uncaughtException', async (error) => {
|
||||
console.error('Uncaught Exception:', error);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Handle unhandled promise rejections
|
||||
process.on('unhandledRejection', async (error) => {
|
||||
console.error('Unhandled Rejection:', error);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Handle graceful shutdown
|
||||
process.on('SIGINT', async () => {
|
||||
console.log('Received SIGINT signal. Closing database connections...');
|
||||
await prisma.$disconnect();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
console.log('Received SIGTERM signal. Closing database connections...');
|
||||
await prisma.$disconnect();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
export default prisma;
|
||||
46
src/middleware.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// app/middleware.js
|
||||
import { NextResponse, NextRequest } from 'next/server';
|
||||
|
||||
// Daftar route yang diizinkan tanpa login (public routes)
|
||||
const publicRoutes = [
|
||||
'/*', // Home page
|
||||
'/about', // About page
|
||||
'/public/*', // Wildcard untuk semua route di bawah /public
|
||||
'/login', // Halaman login
|
||||
];
|
||||
|
||||
// Fungsi untuk memeriksa apakah route saat ini adalah route publik
|
||||
function isPublicRoute(pathname: string) {
|
||||
return publicRoutes.some((route) => {
|
||||
// Jika route mengandung wildcard (*), gunakan regex untuk mencocokkan
|
||||
if (route.endsWith('*')) {
|
||||
const baseRoute = route.replace('*', ''); // Hapus wildcard
|
||||
return pathname.startsWith(baseRoute); // Cocokkan dengan pathname
|
||||
}
|
||||
return pathname === route; // Cocokkan exact path
|
||||
});
|
||||
}
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// Jika route adalah public, izinkan akses
|
||||
if (isPublicRoute(pathname)) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
// Jika bukan public route, periksa apakah pengguna sudah login
|
||||
const isLoggedIn = request.cookies.get('darmasaba-auth-token'); // Contoh: cek cookie auth-token
|
||||
if (!isLoggedIn) {
|
||||
// Redirect ke halaman login jika belum login
|
||||
return NextResponse.redirect(new URL('/login', request.url));
|
||||
}
|
||||
|
||||
// Jika sudah login, izinkan akses
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
// Konfigurasi untuk menentukan path mana yang akan dijalankan middleware
|
||||
export const config = {
|
||||
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], // Jalankan middleware untuk semua route kecuali file statis
|
||||
};
|
||||
19
src/state/state-layanan.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { proxy } from "valtio";
|
||||
import useSwr from "swr";
|
||||
|
||||
type Layanan = {
|
||||
layanan: string | null
|
||||
useLoad: any
|
||||
}
|
||||
|
||||
const stateLayanan = proxy<Layanan>({
|
||||
layanan: null,
|
||||
useLoad: () => {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
export default stateLayanan
|
||||
22
src/state/state-nav.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { proxy } from "valtio"
|
||||
import { MenuItem } from "../../types/menu-item"
|
||||
|
||||
const stateNav = proxy<{
|
||||
hover: boolean,
|
||||
item: MenuItem[] | null
|
||||
isSearch: boolean,
|
||||
clear: () => void,
|
||||
module: string | null
|
||||
}>({
|
||||
hover: false,
|
||||
item: null,
|
||||
isSearch: false,
|
||||
clear: () => {
|
||||
stateNav.hover = false
|
||||
stateNav.item = null
|
||||
stateNav.isSearch = false
|
||||
},
|
||||
module: null
|
||||
})
|
||||
|
||||
export default stateNav
|
||||
@@ -22,6 +22,6 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/page.wibu", "src/app/api/[[...slugs]]/builder/handle.js", "src/app/destack/page.js"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
6
types/menu-item.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export type MenuItem = {
|
||||
id: string,
|
||||
name: string,
|
||||
href: string,
|
||||
children?: MenuItem[]
|
||||
}
|
||||
11
xcoba.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { compress } from 'compress-pdf';
|
||||
|
||||
(async () => {
|
||||
const pdf = path.resolve("/Users/bip/Downloads", 'komoditas.pdf');
|
||||
const buffer = await compress(pdf);
|
||||
|
||||
const compressedPdf = path.resolve(__dirname, 'compressed_pdf.pdf');
|
||||
await fs.promises.writeFile(compressedPdf, buffer);
|
||||
})();
|
||||
70
zgen/image.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
// Fungsi untuk membaca direktori secara rekursif
|
||||
function readDirectoryRecursive(dir: string, callback: (filePath: string) => void) {
|
||||
const items = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item.name);
|
||||
if (item.isDirectory()) {
|
||||
// Jika item adalah direktori, baca secara rekursif
|
||||
readDirectoryRecursive(fullPath, callback);
|
||||
} else if (item.isFile()) {
|
||||
// Jika item adalah file, panggil callback
|
||||
callback(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk generate object image
|
||||
function generateImageObject() {
|
||||
const publicFolderPath = path.join(process.cwd(), "public"); // Path ke folder public
|
||||
const imagesFolderPath = path.join(publicFolderPath, "assets", "images"); // Path ke folder images
|
||||
|
||||
// Log path untuk debugging
|
||||
console.log("Images folder path:", imagesFolderPath);
|
||||
|
||||
// Objek untuk menyimpan hasil
|
||||
const images: Record<string, any> = {};
|
||||
|
||||
try {
|
||||
// Baca direktori secara rekursif
|
||||
readDirectoryRecursive(imagesFolderPath, (filePath) => {
|
||||
const ext = path.extname(filePath).toLowerCase(); // Ekstensi file
|
||||
const allowedExtensions = [".png", ".jpg", ".jpeg"];
|
||||
// Filter hanya file gambar
|
||||
if (allowedExtensions.includes(ext)) {
|
||||
const relativePath = path.relative(imagesFolderPath, filePath); // Path relatif terhadap folder images
|
||||
const dirName = path.dirname(relativePath); // Nama folder induk
|
||||
const fileNameWithoutExt = path.basename(filePath, ext); // Nama file tanpa ekstensi
|
||||
const relativeUrl = `/assets/images/${relativePath.replace(/\\/g, "/")}`; // Ganti \ dengan / untuk URL
|
||||
|
||||
// Jika file berada langsung di folder images, tambahkan ke root objek
|
||||
if (dirName === ".") {
|
||||
images[fileNameWithoutExt] = relativeUrl;
|
||||
} else {
|
||||
// Jika file berada di subfolder, kelompokkan berdasarkan nama folder
|
||||
if (!images[dirName]) {
|
||||
images[dirName] = {};
|
||||
}
|
||||
images[dirName][fileNameWithoutExt] = relativeUrl;
|
||||
}
|
||||
|
||||
console.log(`Processed file: ${dirName}/${fileNameWithoutExt} -> ${relativeUrl}`); // Log setiap file yang diproses
|
||||
}
|
||||
});
|
||||
|
||||
// Simpan hasil ke file setelah semua file diproses
|
||||
const outputPath = path.join(process.cwd(), "src", "con", "images.ts");
|
||||
const content = `const images = ${JSON.stringify(images, null, 2)};\nexport default images;`;
|
||||
fs.writeFileSync(outputPath, content);
|
||||
console.log(`File generated successfully at: ${outputPath}`);
|
||||
console.log("Generated images object:", images); // Log hasil akhir
|
||||
} catch (error) {
|
||||
console.error("Error generating image object:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Jalankan fungsi
|
||||
generateImageObject();
|
||||