feat(public-ui): implement public UMKM directory, detail and product catalog pages
This commit is contained in:
@@ -28,3 +28,7 @@ Implement UMKM, ProdukUmkm, and PenjualanProduk module with CRUD API and Dashboa
|
||||
- [x] Step 12: Implement Produk UI Page
|
||||
- [x] Step 13: Implement Penjualan UI Page
|
||||
- [x] Step 14: Register UI pages in Admin Menu
|
||||
- [x] Step 15: Implement Public UMKM Directory Page
|
||||
- [x] Step 16: Implement Public UMKM Detail Page
|
||||
- [x] Step 17: Implement Public Product Catalog Page
|
||||
- [x] Step 18: Register public pages in Navbar
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
- Implemented the Admin UI with a modern tab-based layout.
|
||||
- Created four main admin pages: Dashboard, Data UMKM, Produk, and Penjualan.
|
||||
- Registered the new UMKM module in the Admin Navigation Menu for all roles.
|
||||
- Implemented the Public UI for citizens to browse local businesses.
|
||||
- Created three public pages: Direktori UMKM, UMKM Detail, and Katalog Produk.
|
||||
- Registered the public UMKM pages in the main Website Navbar under the Ekonomi section.
|
||||
- Verified the implementation with `tsc` and `bun run build`.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "desa-darmasaba",
|
||||
"version": "0.1.13",
|
||||
"version": "0.1.14",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -122,6 +122,20 @@ export const umkmState = proxy({
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
findUnique: {
|
||||
data: null as any,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fetch(`/api/ekonomi/umkm/${id}`);
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
}
|
||||
} catch (e) { console.error(e); } finally { this.loading = false; }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -132,10 +146,17 @@ export const umkmState = proxy({
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
async load(page = 1, limit = 10, search = "", umkmId = "") {
|
||||
async load(page = 1, limit = 10, search = "", umkmId = "", kategoriId = "") {
|
||||
this.loading = true;
|
||||
this.page = page;
|
||||
try {
|
||||
const params = new URLSearchParams({ page: page.toString(), limit: limit.toString(), search, umkmId });
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
limit: limit.toString(),
|
||||
search,
|
||||
umkmId,
|
||||
kategoriId
|
||||
});
|
||||
const res = await fetch(`/api/ekonomi/umkm/produk/find-many?${params}`);
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
@@ -215,6 +236,24 @@ export const umkmState = proxy({
|
||||
}
|
||||
},
|
||||
|
||||
// Kategori Produk (Share with Pasar Desa)
|
||||
kategoriProduk: {
|
||||
findManyAll: {
|
||||
data: [] as any[],
|
||||
loading: false,
|
||||
async load() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fetch("/api/ekonomi/pasar-desa/kategori-produk/find-many-all");
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
}
|
||||
} catch (e) { console.error(e); } finally { this.loading = false; }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Dashboard Module
|
||||
dashboard: {
|
||||
kpi: { data: null as any, loading: false },
|
||||
|
||||
120
src/app/darmasaba/(pages)/ekonomi/umkm/[id]/page.tsx
Normal file
120
src/app/darmasaba/(pages)/ekonomi/umkm/[id]/page.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
'use client'
|
||||
import umkmState from '@/app/admin/(dashboard)/_state/ekonomi/umkm/umkm';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Card, Flex, Grid, GridCol, Image, Paper, Skeleton, Stack, Text, Title, Badge, SimpleGrid, Group, Divider, Button } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconBrandWhatsapp, IconMapPinFilled, IconPackage, IconUser } from '@tabler/icons-react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../../desa/layanan/_com/BackButto';
|
||||
|
||||
function Page() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
const state = useProxy(umkmState.umkm.findUnique);
|
||||
|
||||
useShallowEffect(() => {
|
||||
if (id) state.load(id);
|
||||
}, [id]);
|
||||
|
||||
if (state.loading || !state.data) {
|
||||
return (
|
||||
<Stack py={10} px={{ base: 'md', md: 100 }}>
|
||||
<Skeleton h={400} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const u = state.data;
|
||||
|
||||
return (
|
||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<Grid gutter="xl">
|
||||
<GridCol span={{ base: 12, md: 4 }}>
|
||||
<Image
|
||||
radius="lg"
|
||||
src={u.image?.link || '/no-image.jpg'}
|
||||
alt={u.nama}
|
||||
w="100%"
|
||||
fallbackSrc="/no-image.jpg"
|
||||
/>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 8 }}>
|
||||
<Stack gap="xs">
|
||||
<Badge size="lg" variant="light" color="blue">{u.kategori?.nama}</Badge>
|
||||
<Title order={1} fw="bold" c={colors['blue-button']}>{u.nama}</Title>
|
||||
<Group gap="xl" mt="sm">
|
||||
<Flex align="center" gap="xs">
|
||||
<IconUser size={20} color="gray" />
|
||||
<Text fw={500}>{u.pemilik}</Text>
|
||||
</Flex>
|
||||
<Flex align="center" gap="xs">
|
||||
<IconMapPinFilled size={20} color="red" />
|
||||
<Text>{u.alamat || 'Desa Darmasaba'}</Text>
|
||||
</Flex>
|
||||
</Group>
|
||||
<Divider my="md" />
|
||||
<Text ta="justify" lh={1.6}>{u.deskripsi || 'Tidak ada deskripsi tersedia.'}</Text>
|
||||
{u.kontak && (
|
||||
<Button
|
||||
mt="md"
|
||||
leftSection={<IconBrandWhatsapp size={20} />}
|
||||
color="green"
|
||||
radius="md"
|
||||
component="a"
|
||||
href={`https://wa.me/${u.kontak}`}
|
||||
target="_blank"
|
||||
w="fit-content"
|
||||
>
|
||||
Hubungi Pemilik
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<Box px={{ base: 'md', md: 100 }} mt={40}>
|
||||
<Title order={2} mb="xl">Katalog Produk</Title>
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="lg">
|
||||
{u.produk?.map((p: any, k: number) => (
|
||||
<Card key={k} withBorder padding="lg" radius="md">
|
||||
<Card.Section>
|
||||
<Image
|
||||
src={p.image?.link || '/no-image.jpg'}
|
||||
height={160}
|
||||
alt={p.nama}
|
||||
fallbackSrc="/no-image.jpg"
|
||||
/>
|
||||
</Card.Section>
|
||||
<Group justify="space-between" mt="md" mb="xs">
|
||||
<Text fw={700} lineClamp={1}>{p.nama}</Text>
|
||||
<Badge color={p.stok > 0 ? 'green' : 'red'}>
|
||||
{p.stok > 0 ? 'Tersedia' : 'Habis'}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Text size="lg" fw={700} c="blue">
|
||||
Rp {p.harga.toLocaleString('id-ID')}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" mt="sm" lineClamp={2}>
|
||||
{p.deskripsi || 'Kualitas terbaik dari UMKM lokal.'}
|
||||
</Text>
|
||||
</Card>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
{(!u.produk || u.produk.length === 0) && (
|
||||
<Center py={40}>
|
||||
<Text c="dimmed">Belum ada produk dari UMKM ini</Text>
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
153
src/app/darmasaba/(pages)/ekonomi/umkm/page.tsx
Normal file
153
src/app/darmasaba/(pages)/ekonomi/umkm/page.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
'use client'
|
||||
import umkmState from '@/app/admin/(dashboard)/_state/ekonomi/umkm/umkm';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Flex, Grid, GridCol, Image, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title, Badge } from '@mantine/core';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconMapPinFilled, IconSearch, IconUser } from '@tabler/icons-react';
|
||||
import { motion } from 'motion/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
function Page() {
|
||||
const router = useRouter()
|
||||
const state = useProxy(umkmState.umkm)
|
||||
const [search, setSearch] = useState('');
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
|
||||
const { data, page, loading, totalPages, load } = state.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
umkmState.kategoriProduk.findManyAll.load()
|
||||
}, [])
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 8, debouncedSearch, selectedCategory || undefined)
|
||||
}, [page, debouncedSearch, selectedCategory])
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Grid align="center" px={{ base: 'md', md: 100 }}>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Title order={1} c={colors["blue-button"]} fw="bold" lh={1.15}>
|
||||
Direktori UMKM Desa Darmasaba
|
||||
</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<TextInput
|
||||
radius="lg"
|
||||
placeholder="Cari UMKM"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
leftSection={<IconSearch size={20} />}
|
||||
w={"100%"}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
|
||||
<Text
|
||||
px={{ base: 'md', md: 100 }}
|
||||
pt={20}
|
||||
ta="justify"
|
||||
fz={{ base: 'sm', md: 'md' }}
|
||||
lh={{ base: 1.5, md: 1.55 }}
|
||||
c="black"
|
||||
>
|
||||
Daftar Usaha Mikro, Kecil, dan Menengah yang ada di wilayah Desa Darmasaba.
|
||||
Mendukung pertumbuhan ekonomi lokal melalui pemberdayaan pengusaha desa.
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap="lg">
|
||||
<SimpleGrid pb={10} cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Select
|
||||
placeholder="Filter Kategori"
|
||||
data={umkmState.kategoriProduk.findManyAll.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.nama
|
||||
})) || []}
|
||||
value={selectedCategory}
|
||||
onChange={setSelectedCategory}
|
||||
clearable
|
||||
searchable
|
||||
nothingFoundMessage="Tidak ada kategori ditemukan"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="lg">
|
||||
{data?.map((v, k) => (
|
||||
<motion.div
|
||||
key={k}
|
||||
onClick={() => router.push(`/darmasaba/ekonomi/umkm/${v.id}`)}
|
||||
whileHover={{ scale: 1.03 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<Paper p="md" radius="md" withBorder shadow="xs" style={{ cursor: 'pointer', height: '100%' }}>
|
||||
<Image
|
||||
radius="md"
|
||||
src={v.image?.link || '/no-image.jpg'}
|
||||
alt={v.nama}
|
||||
h={180}
|
||||
w="100%"
|
||||
style={{ objectFit: 'cover' }}
|
||||
fallbackSrc="/no-image.jpg"
|
||||
/>
|
||||
<Badge mt="md" variant="light" color="blue">
|
||||
{v.kategori?.nama}
|
||||
</Badge>
|
||||
<Text mt="xs" fw="bold" fz="lg" lh={1.2} lineClamp={1}>
|
||||
{v.nama}
|
||||
</Text>
|
||||
<Flex mt={8} gap="xs" align="center">
|
||||
<IconUser size={16} color="gray" />
|
||||
<Text fz="sm" c="dimmed">{v.pemilik}</Text>
|
||||
</Flex>
|
||||
<Flex mt={4} gap="xs" align="center">
|
||||
<IconMapPinFilled size={16} color="red" />
|
||||
<Text fz="sm" c="dimmed" lineClamp={1}>{v.alamat || 'Darmasaba'}</Text>
|
||||
</Flex>
|
||||
</Paper>
|
||||
</motion.div>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
|
||||
{data.length === 0 && (
|
||||
<Center py={50}>
|
||||
<Text c="dimmed">Tidak ada UMKM ditemukan</Text>
|
||||
</Center>
|
||||
)}
|
||||
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
190
src/app/darmasaba/(pages)/ekonomi/umkm/produk/page.tsx
Normal file
190
src/app/darmasaba/(pages)/ekonomi/umkm/produk/page.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
'use client'
|
||||
import umkmState from '@/app/admin/(dashboard)/_state/ekonomi/umkm/umkm';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Flex, Grid, GridCol, Image, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title, Badge, Card, Group } from '@mantine/core';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconBuildingStore, IconSearch, IconTag } from '@tabler/icons-react';
|
||||
import { motion } from 'motion/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../../desa/layanan/_com/BackButto';
|
||||
|
||||
function Page() {
|
||||
const router = useRouter()
|
||||
const state = useProxy(umkmState.produk)
|
||||
const [search, setSearch] = useState('');
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [selectedUmkm, setSelectedUmkm] = useState<string | null>(null);
|
||||
|
||||
const { data, page, loading, totalPages, load } = state.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
umkmState.kategoriProduk.findManyAll.load()
|
||||
// Load all UMKM for filter
|
||||
fetch('/api/ekonomi/umkm/find-many-all')
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
(umkmState.umkm as any).allList = res.data;
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const allUmkm = useProxy(umkmState.umkm as any).allList || [];
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 12, debouncedSearch, selectedUmkm || undefined, selectedCategory || undefined)
|
||||
}, [page, debouncedSearch, selectedUmkm, selectedCategory])
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Grid align="center" px={{ base: 'md', md: 100 }}>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Title order={1} c={colors["blue-button"]} fw="bold" lh={1.15}>
|
||||
Katalog Produk UMKM
|
||||
</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<TextInput
|
||||
radius="lg"
|
||||
placeholder="Cari Produk..."
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
leftSection={<IconSearch size={20} />}
|
||||
w={"100%"}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
|
||||
<Text
|
||||
px={{ base: 'md', md: 100 }}
|
||||
pt={20}
|
||||
ta="justify"
|
||||
fz={{ base: 'sm', md: 'md' }}
|
||||
lh={{ base: 1.5, md: 1.55 }}
|
||||
c="black"
|
||||
>
|
||||
Temukan berbagai produk unggulan dari pelaku UMKM di Desa Darmasaba.
|
||||
Mulai dari kuliner, kerajinan tangan, hingga jasa tersedia untuk memenuhi kebutuhan Anda.
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap="lg">
|
||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
||||
<Select
|
||||
placeholder="Filter Berdasarkan UMKM"
|
||||
data={allUmkm.map((v: any) => ({ value: v.id, label: v.nama }))}
|
||||
value={selectedUmkm}
|
||||
onChange={setSelectedUmkm}
|
||||
clearable
|
||||
searchable
|
||||
nothingFoundMessage="UMKM tidak ditemukan"
|
||||
/>
|
||||
<Select
|
||||
placeholder="Filter Kategori"
|
||||
data={umkmState.kategoriProduk.findManyAll.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.nama
|
||||
})) || []}
|
||||
value={selectedCategory}
|
||||
onChange={setSelectedCategory}
|
||||
clearable
|
||||
searchable
|
||||
nothingFoundMessage="Kategori tidak ditemukan"
|
||||
/>
|
||||
</SimpleGrid>
|
||||
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="lg">
|
||||
{data?.map((v, k) => (
|
||||
<motion.div
|
||||
key={k}
|
||||
whileHover={{ y: -5 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Card shadow="sm" padding="lg" radius="md" withBorder style={{ height: '100%' }}>
|
||||
<Card.Section>
|
||||
<Image
|
||||
src={v.image?.link || '/no-image.jpg'}
|
||||
height={180}
|
||||
alt={v.nama}
|
||||
fallbackSrc="/no-image.jpg"
|
||||
/>
|
||||
</Card.Section>
|
||||
|
||||
<Stack justify="space-between" mt="md" h="100%">
|
||||
<Box>
|
||||
<Group justify="space-between" mb="xs">
|
||||
<Badge color={v.stok > 0 ? 'teal' : 'red'} variant="light">
|
||||
{v.stok > 0 ? 'Tersedia' : 'Habis'}
|
||||
</Badge>
|
||||
<Text size="xs" c="dimmed">Stok: {v.stok}</Text>
|
||||
</Group>
|
||||
|
||||
<Text fw={700} fz="lg" lineClamp={1}>{v.nama}</Text>
|
||||
|
||||
<Flex align="center" gap={5} mt={5}>
|
||||
<IconBuildingStore size={14} color="gray" />
|
||||
<Text size="sm" c="blue" fw={500} style={{ cursor: 'pointer' }} onClick={() => router.push(`/darmasaba/ekonomi/umkm/${v.umkmId}`)}>
|
||||
{v.umkm?.nama}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex align="center" gap={5} mt={2}>
|
||||
<IconTag size={14} color="gray" />
|
||||
<Text size="xs" c="dimmed">{v.umkm?.kategori?.nama}</Text>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
<Box mt="md">
|
||||
<Text fz="xl" fw={800} c="orange">
|
||||
Rp {v.harga.toLocaleString('id-ID')}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
|
||||
{data.length === 0 && (
|
||||
<Center py={80}>
|
||||
<Stack align="center">
|
||||
<Text c="dimmed" fz="lg">Produk tidak ditemukan</Text>
|
||||
<Text size="sm" c="dimmed">Coba gunakan kata kunci atau filter lain</Text>
|
||||
</Stack>
|
||||
</Center>
|
||||
)}
|
||||
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my="md"
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -182,41 +182,51 @@ const navbarListMenu = [
|
||||
},
|
||||
{
|
||||
id: "5.3",
|
||||
name: "Direktori UMKM",
|
||||
href: "/darmasaba/ekonomi/umkm"
|
||||
},
|
||||
{
|
||||
id: "5.4",
|
||||
name: "Produk UMKM",
|
||||
href: "/darmasaba/ekonomi/umkm/produk"
|
||||
},
|
||||
{
|
||||
id: "5.5",
|
||||
name: "Struktur Organisasi dan SK Pengurus BUMDesa",
|
||||
href: "/darmasaba/ekonomi/Struktur-Organisasi-Dan-Sk-Pengurus-BumDes"
|
||||
},
|
||||
{
|
||||
id: "5.4",
|
||||
id: "5.6",
|
||||
name: "PADesa (Pendapatan Asli Desa)",
|
||||
href: "/darmasaba/ekonomi/PADesa-pendapatan-asli-desa"
|
||||
},
|
||||
{
|
||||
id: "5.5",
|
||||
id: "5.7",
|
||||
name: "Jumlah Pengangguran",
|
||||
href: "/darmasaba/ekonomi/jumlah-pengangguran"
|
||||
},
|
||||
{
|
||||
id: "5.6",
|
||||
id: "5.8",
|
||||
name: "Jumlah penduduk usia kerja yang menganggur",
|
||||
href: "/darmasaba/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur"
|
||||
},
|
||||
{
|
||||
id: "5.7",
|
||||
id: "5.9",
|
||||
name: "Jumlah Penduduk Miskin",
|
||||
href: "/darmasaba/ekonomi/jumlah-penduduk-miskin"
|
||||
},
|
||||
{
|
||||
id: "5.8",
|
||||
id: "5.10",
|
||||
name: "Program Kemiskinan",
|
||||
href: "/darmasaba/ekonomi/program-kemiskinan"
|
||||
},
|
||||
{
|
||||
id: "5.9",
|
||||
id: "5.11",
|
||||
name: "Sektor Unggulan Desa",
|
||||
href: "/darmasaba/ekonomi/sektor-unggulan-desa"
|
||||
},
|
||||
{
|
||||
id: "5.10",
|
||||
id: "5.12",
|
||||
name: "Demografi Pekerjaan",
|
||||
href: "/darmasaba/ekonomi/demografi-pekerjaan"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user