Sinkronisasi UI & API Admin - User Submenu Berita

This commit is contained in:
2025-08-07 10:53:56 +08:00
parent d4af56b508
commit 0ac9fa1f53
85 changed files with 1135 additions and 3908 deletions

View File

@@ -0,0 +1,133 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Container, Grid, GridCol, Stack, Tabs, TabsList, TabsPanel, TabsTab, Text, TextInput } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import BackButton from '../../layanan/_com/BackButto';
type HeaderSearchProps = {
placeholder?: string;
searchIcon?: React.ReactNode;
value?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
children?: React.ReactNode;
};
function LayoutTabsBerita({
children,
placeholder = "pencarian",
searchIcon = <IconSearch size={20} />,
value,
onChange }: HeaderSearchProps) {
const router = useRouter()
const pathname = usePathname()
const tabs = [
{
label: "Semua",
value: "semua",
href: "/darmasaba/desa/berita/semua"
},
{
label: "Budaya",
value: "budaya",
href: "/darmasaba/desa/berita/budaya"
},
{
label: "Pemerintahan",
value: "pemerintahan",
href: "/darmasaba/desa/berita/pemerintahan"
},
{
label: "Ekonomi",
value: "ekonomi",
href: "/darmasaba/desa/berita/ekonomi"
},
{
label: "Pembangunan",
value: "pembangunan",
href: "/darmasaba/desa/berita/pembangunan"
},
{
label: "Sosial",
value: "sosial",
href: "/darmasaba/desa/berita/sosial"
},
{
label: "Teknologi",
value: "teknologi",
href: "/darmasaba/desa/berita/teknologi"
},
];
const curentTab = tabs.find(tab => tab.href === pathname)
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
const handleTabChange = (value: string | null) => {
const tab = tabs.find(t => t.value === value)
if (tab) {
router.push(tab.href)
}
setActiveTab(value)
}
useEffect(() => {
const match = tabs.find(tab => tab.href === pathname)
if (match) {
setActiveTab(match.value)
}
}, [pathname])
return (
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
{/* Header */}
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container size="lg" px="md">
<Stack align="center" gap="0" >
<Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold" ta="center">
Portal Berita Darmasaba
</Text>
<Text ta="center" px="md">
Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
</Text>
</Stack>
</Container>
<Tabs color={colors['blue-button']} variant="pills" defaultValue="semua" value={activeTab} onChange={handleTabChange}>
<Box px={{ base: "md", md: 100 }} py="md" bg={colors['BG-trans']} >
<Grid>
<GridCol span={{ base: 12, md: 9, lg: 8, xl: 9 }}>
<TabsList>
{tabs.map((e, i) => (
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
))}
</TabsList>
</GridCol>
<GridCol span={{ base: 12, md: 3, lg: 4, xl: 3 }}>
<TextInput
radius="lg"
placeholder={placeholder}
leftSection={searchIcon}
w="100%"
value={value}
onChange={onChange}
/>
</GridCol>
</Grid>
</Box>
{tabs.map((e, i) => (
<TabsPanel key={i} value={e.value}>
{/* Konten dummy, bisa diganti tergantung routing */}
<></>
</TabsPanel>
))}
</Tabs>
{children}
</Stack>
);
}
export default LayoutTabsBerita;

View File

@@ -0,0 +1,12 @@
import React from 'react';
import LayoutTabsBerita from './_lib/layoutTabs';
function Layout({ children }: { children: React.ReactNode }) {
return (
<LayoutTabsBerita>
{children}
</LayoutTabsBerita>
);
}
export default Layout;

View File

@@ -1,102 +0,0 @@
import colors from '@/con/colors';
import { Box, Container, Grid, GridCol, Stack, Tabs, TabsList, TabsPanel, TabsTab, Text, TextInput } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import BackButton from '../layanan/_com/BackButto';
import Budaya from './(tabs)/budaya';
import Ekonomi from './(tabs)/ekonomi';
import Pemerintahan from './(tabs)/pemerintahan';
import Semua from './(tabs)/semua';
import Sosial from './(tabs)/sosial';
import Teknologi from './(tabs)/teknologi';
import Pembangunan from './(tabs)/pembangunan';
function Page() {
return (
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
{/* Header */}
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container size="lg" px="md">
<Stack align="center" gap="0" >
<Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold" ta="center">
Portal Berita Darmasaba
</Text>
<Text ta="center" px="md">
Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
</Text>
</Stack>
</Container>
{/* Tabs Menu */}
<Tabs color={colors['blue-button']} variant="pills" defaultValue="semua">
<Box px={{ base: "md", md: 100 }} py="md" bg={colors['BG-trans']} >
<Grid>
<GridCol span={{ base: 12, md: 9, lg: 8, xl: 9 }}>
<TabsList>
<TabsTab value="semua">
Semua
</TabsTab>
<TabsTab value="pemerintahan">
Pemerintahan
</TabsTab>
<TabsTab value="pembangunan" >
Pembangunan
</TabsTab>
<TabsTab value="ekonomi" >
Ekonomi
</TabsTab>
<TabsTab value="sosial" >
Sosial
</TabsTab>
<TabsTab value="budaya" >
Budaya
</TabsTab>
<TabsTab value="teknologi" >
Teknologi
</TabsTab>
</TabsList>
</GridCol>
<GridCol span={{ base: 12, md: 3, lg: 4, xl: 3 }}>
<TextInput
w={{ base: "100%", md: "100%" }}
radius="lg"
placeholder="Cari Berita"
leftSection={<IconSearch size={18} />}
/>
</GridCol>
</Grid>
</Box>
<TabsPanel value='semua'>
<Semua />
</TabsPanel>
<TabsPanel value='pemerintahan'>
<Pemerintahan />
</TabsPanel>
<TabsPanel value="pembangunan" >
<Pembangunan />
</TabsPanel>
<TabsPanel value="ekonomi" >
<Ekonomi />
</TabsPanel>
<TabsPanel value="sosial" >
<Sosial />
</TabsPanel>
<TabsPanel value="budaya" >
<Budaya />
</TabsPanel>
<TabsPanel value="teknologi" >
<Teknologi />
</TabsPanel>
</Tabs>
</Stack>
);
}
export default Page;

View File

@@ -0,0 +1,128 @@
/* eslint-disable react-hooks/exhaustive-deps */
import colors from '@/con/colors';
import { Box, Container, Grid, GridCol, Skeleton, Stack, Tabs, TabsList, TabsPanel, TabsTab, Text, TextInput } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import BackButton from '../layanan/_com/BackButto';
import Budaya from './budaya/page';
import Ekonomi from './ekonomi/page';
import Pemerintahan from './pemerintahan/page';
import Semua from './semua/page';
import Sosial from './sosial/page';
import Teknologi from './teknologi/page';
import Pembangunan from './pembangunan/page';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
function Page() {
const [selectedKategori, setSelectedKategori] = useState<string>('Semua');
const [loading, setLoading] = useState(false);
const state = useProxy(stateDashboardBerita);
useEffect(() => {
state.kategoriBerita.findMany.load()
const loadData = async () => {
try {
setLoading(true);
await stateDashboardBerita.berita.findMany.load(1, 100);
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
loadData();
}, []);
const data = state.berita.findMany.data || [];
const categories = [...new Set(
data
.filter(item => item.kategoriBerita?.name)
.map(item => item.kategoriBerita?.name)
)];
const filteredData = selectedKategori === 'Semua'
? data
: data.filter(item => item.kategoriBerita?.name === selectedKategori);
return (
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
{/* Header */}
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container size="lg" px="md">
<Stack align="center" gap="0" >
<Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold" ta="center">
Portal Berita Darmasaba
</Text>
<Text ta="center" px="md">
Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
</Text>
</Stack>
</Container>
{/* Tabs Menu */}
<Tabs color={colors['blue-button']} variant="pills" defaultValue="semua">
<Box px={{ base: "md", md: 100 }} py="md" bg={colors['BG-trans']} >
<Grid>
<GridCol span={{ base: 12, md: 9, lg: 8, xl: 9 }}>
<TabsList>
{categories.map((kategori) => (
<TabsTab key={kategori} value={kategori || ''}>
{kategori}
</TabsTab>
))}
</TabsList>
</GridCol>
<GridCol span={{ base: 12, md: 3, lg: 4, xl: 3 }}>
{loading ? (
<Skeleton height={42} />
) : filteredData.length > 0 ? (
<TextInput
w={{ base: "100%", md: "100%" }}
radius="lg"
placeholder="Cari Berita"
leftSection={<IconSearch size={18} />}
/>
) : (
<Text>Tidak ada data</Text>
)}
</GridCol>
</Grid>
</Box>
<TabsPanel value='semua'>
<Semua />
</TabsPanel>
<TabsPanel value='pemerintahan'>
<Pemerintahan />
</TabsPanel>
<TabsPanel value="pembangunan" >
<Pembangunan />
</TabsPanel>
<TabsPanel value="ekonomi" >
<Ekonomi />
</TabsPanel>
<TabsPanel value="sosial" >
<Sosial />
</TabsPanel>
<TabsPanel value="budaya" >
<Budaya />
</TabsPanel>
<TabsPanel value="teknologi" >
<Teknologi />
</TabsPanel>
</Tabs>
</Stack>
);
}
export default Page;