Fix QC Kak Ayu Tgl 12
Fix QC Kak Ino Tgl 12 Fix UI Mobile Menu Keamanan Fix UI Mobile Admin Menu Landing Page
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Tabs,
|
||||
@@ -74,36 +75,76 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
keepMounted={false}
|
||||
>
|
||||
{/* ✅ Scroll horizontal wrapper */}
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||
}}
|
||||
<Box visibleFrom='md'>
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
</ScrollArea>
|
||||
</Box>
|
||||
|
||||
<Box hiddenFrom='md'>
|
||||
<ScrollArea
|
||||
type="auto"
|
||||
offsetScrollbars={false}
|
||||
w="100%"
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
</ScrollArea>
|
||||
|
||||
<TabsList
|
||||
p="xs" // lebih kecil
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
width: "max-content", // ⬅️ kunci
|
||||
maxWidth: "100%", // ⬅️ penting
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
</ScrollArea>
|
||||
</Box>
|
||||
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsPanel
|
||||
|
||||
@@ -177,7 +177,7 @@ function EditMediaSosial() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'xs' }} py="xs">
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
|
||||
@@ -50,7 +50,7 @@ function DetailMediaSosial() {
|
||||
const data = stateMediaSosial.findUnique.data;
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'xs' }} py="xs">
|
||||
<Button
|
||||
variant="subtle"
|
||||
onClick={() => router.back()}
|
||||
@@ -62,7 +62,7 @@ function DetailMediaSosial() {
|
||||
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "50%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
|
||||
@@ -25,7 +25,6 @@ import { useProxy } from 'valtio/utils';
|
||||
import profileLandingPageState from '../../../../_state/landing-page/profile';
|
||||
import SelectSosialMedia from '@/app/admin/(dashboard)/_com/selectSocialMedia';
|
||||
|
||||
|
||||
// ⭐ Tambah type SosmedKey
|
||||
type SosmedKey =
|
||||
| 'facebook'
|
||||
@@ -88,7 +87,6 @@ export default function CreateMediaSosial() {
|
||||
stateMediaSosial.create.form.imageId = null;
|
||||
stateMediaSosial.create.form.icon = sosmedMap[selectedSosmed].src!;
|
||||
|
||||
|
||||
await stateMediaSosial.create.create();
|
||||
resetForm();
|
||||
router.push('/admin/landing-page/profil/media-sosial');
|
||||
@@ -129,13 +127,13 @@ export default function CreateMediaSosial() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
<Title order={2} ml="sm" c="dark" lh={1.2} fz={{ base: 'md', md: 'lg' }}>
|
||||
Tambah Media Sosial
|
||||
</Title>
|
||||
</Group>
|
||||
@@ -155,7 +153,7 @@ export default function CreateMediaSosial() {
|
||||
{/* Custom icon uploader */}
|
||||
{selectedSosmed === 'custom' && (
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
<Text fw="bold" fz={{ base: 'sm', md: 'md' }} lh={1.45} mb={6}>
|
||||
Upload Custom Icon
|
||||
</Text>
|
||||
|
||||
@@ -185,8 +183,10 @@ export default function CreateMediaSosial() {
|
||||
</Dropzone.Idle>
|
||||
|
||||
<Stack align="center" gap="xs">
|
||||
<Text fw={500}>Seret gambar atau klik untuk pilih</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
<Text fw={500} fz={{ base: 'sm', md: 'md' }} lh={1.45}>
|
||||
Seret gambar atau klik untuk pilih
|
||||
</Text>
|
||||
<Text fz={{ base: 12, md: 'sm' }} c="dimmed" lh={1.4}>
|
||||
Maksimal 5MB, format .png, .jpg, .jpeg, webp
|
||||
</Text>
|
||||
</Stack>
|
||||
@@ -229,7 +229,11 @@ export default function CreateMediaSosial() {
|
||||
|
||||
{/* Input name */}
|
||||
<TextInput
|
||||
label="Nama Media Sosial"
|
||||
label={
|
||||
<Text fw={500} fz={{ base: 'sm', md: 'md' }} lh={1.45}>
|
||||
Nama Media Sosial
|
||||
</Text>
|
||||
}
|
||||
placeholder="Masukkan nama media sosial"
|
||||
value={stateMediaSosial.create.form.name ?? ''}
|
||||
onChange={(e) => (stateMediaSosial.create.form.name = e.target.value)}
|
||||
@@ -238,7 +242,11 @@ export default function CreateMediaSosial() {
|
||||
|
||||
{/* Input link */}
|
||||
<TextInput
|
||||
label="Link / Kontak"
|
||||
label={
|
||||
<Text fw={500} fz={{ base: 'sm', md: 'md' }} lh={1.45}>
|
||||
Link / Kontak
|
||||
</Text>
|
||||
}
|
||||
placeholder="Masukkan link atau nomor"
|
||||
value={stateMediaSosial.create.form.iconUrl ?? ''}
|
||||
onChange={(e) => (stateMediaSosial.create.form.iconUrl = e.target.value)}
|
||||
@@ -266,4 +274,4 @@ export default function CreateMediaSosial() {
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,25 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Group, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Group,
|
||||
Image,
|
||||
Pagination,
|
||||
Paper,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Table,
|
||||
TableTbody,
|
||||
TableTd,
|
||||
TableTh,
|
||||
TableThead,
|
||||
TableTr,
|
||||
Text,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -28,11 +46,11 @@ function MediaSosial() {
|
||||
}
|
||||
|
||||
function ListMediaSosial({ search }: { search: string }) {
|
||||
const stateMediaSosial = useProxy(profileLandingPageState.mediaSosial)
|
||||
const stateMediaSosial = useProxy(profileLandingPageState.mediaSosial);
|
||||
const router = useRouter();
|
||||
|
||||
const getIconSource = (item: any) => {
|
||||
if (item.image?.link) return item.image.link;
|
||||
if (item.image?.link) return item.image.link;
|
||||
if (item.icon && sosmedMap[item.icon as keyof typeof sosmedMap]?.src) {
|
||||
return sosmedMap[item.icon as keyof typeof sosmedMap].src;
|
||||
}
|
||||
@@ -48,101 +66,201 @@ function ListMediaSosial({ search }: { search: string }) {
|
||||
} = stateMediaSosial.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
const filteredData = data || []
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'sm', sm: 'md' }}>
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Media Sosial</Title>
|
||||
<Button leftSection={<IconPlus size={18} />} color="blue" variant="light" onClick={() => router.push('/admin/landing-page/profil/media-sosial/create')}>
|
||||
<Box py={{ base: 'sm', sm: 'md' }}>
|
||||
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', sm: 'lg' }} shadow="md" radius="md">
|
||||
<Group justify="space-between" mb={{ base: 'sm', sm: 'md' }}>
|
||||
<Title order={4} lh={1.15}>
|
||||
Daftar Media Sosial
|
||||
</Title>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
variant="light"
|
||||
onClick={() => router.push('/admin/landing-page/profil/media-sosial/create')}
|
||||
fz={{ base: 'xs', sm: 'sm' }}
|
||||
>
|
||||
Tambah Baru
|
||||
</Button>
|
||||
</Group>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table highlightOnHover>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '25%' }}>Nama Media Sosial / Kontak</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Gambar</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Link / No. Telepon</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ width: '25%', }}>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%' }}>
|
||||
<Box w={50} h={50} style={{ borderRadius: 8, overflow: 'hidden' }}>
|
||||
|
||||
{(() => {
|
||||
const src = getIconSource(item);
|
||||
|
||||
if (src) {
|
||||
return (
|
||||
<Image
|
||||
loading="lazy"
|
||||
src={src}
|
||||
alt={item.name}
|
||||
fit={item.image?.link ? "cover" : "contain"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <Box bg={colors['blue-button']} w="100%" h="100%" />;
|
||||
})()}
|
||||
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%', }}>
|
||||
<Box w={250}>
|
||||
<Text truncate fz="sm" c="dimmed" lineClamp={1}>
|
||||
{item.iconUrl || item.noTelp || '-'}
|
||||
<Box>
|
||||
{/* Desktop: Table | Mobile: Card-based vertical layout */}
|
||||
<Box visibleFrom="md">
|
||||
<Table highlightOnHover>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '25%' }}>
|
||||
<Text fw={600} fz="md" lh={1.45}>
|
||||
Nama Media Sosial / Kontak
|
||||
</Text>
|
||||
</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>
|
||||
<Text fw={600} fz="md" lh={1.45}>
|
||||
Gambar
|
||||
</Text>
|
||||
</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>
|
||||
<Text fw={600} fz="md" lh={1.45}>
|
||||
Link / No. Telepon
|
||||
</Text>
|
||||
</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>
|
||||
<Text fw={600} fz="md" lh={1.45}>
|
||||
Aksi
|
||||
</Text>
|
||||
</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ width: '25%' }}>
|
||||
<Text fw={500} fz="md" lh={1.5} truncate="end" lineClamp={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '15%' }}>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImacCog size={16} />}
|
||||
onClick={() => router.push(`/admin/landing-page/profil/media-sosial/${item.id}`)}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%' }}>
|
||||
<Box w={50} h={50} style={{ borderRadius: 8, overflow: 'hidden' }}>
|
||||
{(() => {
|
||||
const src = getIconSource(item);
|
||||
if (src) {
|
||||
return (
|
||||
<Image
|
||||
loading="lazy"
|
||||
src={src}
|
||||
alt={item.name}
|
||||
fit={item.image?.link ? 'cover' : 'contain'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <Box bg={colors['blue-button']} w="100%" h="100%" />;
|
||||
})()}
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%' }}>
|
||||
<Box w={250}>
|
||||
<Text truncate fz="sm" lh={1.5} c="dimmed" lineClamp={1}>
|
||||
{item.iconUrl || item.noTelp || '-'}
|
||||
</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '15%' }}>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImacCog size={16} />}
|
||||
onClick={() =>
|
||||
router.push(`/admin/landing-page/profil/media-sosial/${item.id}`)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={4}>
|
||||
<Center py={20}>
|
||||
<Text c="dimmed" fz="md" lh={1.5}>
|
||||
Tidak ada data media sosial yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={4}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">Tidak ada data media sosial yang cocok</Text>
|
||||
</Center>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
)}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
)}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
|
||||
{/* Mobile layout */}
|
||||
<Stack hiddenFrom="md" gap="xs">
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
<Paper key={item.id} withBorder p="sm" radius="md">
|
||||
<Group justify="space-between" wrap="nowrap" align='center'>
|
||||
<Box>
|
||||
<Text fw={600} fz="sm" lh={1.45}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box w={40} h={40} style={{ borderRadius: 6, overflow: 'hidden' }}>
|
||||
{(() => {
|
||||
const src = getIconSource(item);
|
||||
if (src) {
|
||||
return (
|
||||
<Image
|
||||
loading="lazy"
|
||||
src={src}
|
||||
alt={item.name}
|
||||
fit={item.image?.link ? 'cover' : 'contain'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <Box bg={colors['blue-button']} w="100%" h="100%" />;
|
||||
})()}
|
||||
</Box>
|
||||
</Group>
|
||||
<Box>
|
||||
<a
|
||||
href={item.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
<Text
|
||||
fz="sm"
|
||||
c="blue"
|
||||
truncate
|
||||
>
|
||||
{item.iconUrl || item.noTelp || '-'}
|
||||
</Text>
|
||||
</a>
|
||||
</Box>
|
||||
<Group mt="sm" justify="flex-end">
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImacCog size={16} />}
|
||||
onClick={() =>
|
||||
router.push(`/admin/landing-page/profil/media-sosial/${item.id}`)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Group>
|
||||
</Paper>
|
||||
))
|
||||
) : (
|
||||
<Center py={24}>
|
||||
<Text c="dimmed" fz="sm" lh={1.5}>
|
||||
Tidak ada data media sosial yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
@@ -161,4 +279,4 @@ function ListMediaSosial({ search }: { search: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default MediaSosial;
|
||||
export default MediaSosial;
|
||||
@@ -178,7 +178,7 @@ function EditPejabatDesa() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Stack gap="xs">
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
|
||||
|
||||
@@ -3,7 +3,6 @@ import profileLandingPageState from '@/app/admin/(dashboard)/_state/landing-page
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Divider, Grid, GridCol, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
@@ -35,15 +34,15 @@ function Page() {
|
||||
<Title order={3} c={colors['blue-button']}>Preview Pejabat Desa</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button
|
||||
c="green"
|
||||
variant="light"
|
||||
leftSection={<IconEdit size={18} stroke={2} />}
|
||||
radius="md"
|
||||
onClick={() => router.push(`/admin/landing-page/profil/pejabat-desa/${allList.findUnique.data?.id}`)}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
style={{fontSize: 15, fontWeight: "bold"}}
|
||||
c="green"
|
||||
variant="light"
|
||||
radius="md"
|
||||
onClick={() => router.push(`/admin/landing-page/profil/pejabat-desa/${allList.findUnique.data?.id}`)}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
{dataArray.map((item) => (
|
||||
@@ -52,7 +51,7 @@ function Page() {
|
||||
<Grid>
|
||||
<GridCol span={12}>
|
||||
<Center>
|
||||
<Image src="/darmasaba-icon.png" w={{ base: 100, md: 150 }} alt="Logo Desa" loading="lazy"/>
|
||||
<Image src="/darmasaba-icon.png" w={{ base: 100, md: 150 }} alt="Logo Desa" loading="lazy" />
|
||||
</Center>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
@@ -93,7 +92,7 @@ function Page() {
|
||||
</Paper>
|
||||
<Box mt="lg">
|
||||
<Text fz={{ base: "1.125rem", md: "1.5rem" }} fw="bold" mb={4}>Jabatan</Text>
|
||||
<Text fz={{ base: "1rem", md: "1.4rem" }} ta="justify" c={colors['blue-button']}>
|
||||
<Text fz={{ base: "1rem", md: "1.4rem" }} ta="left" c={colors['blue-button']}>
|
||||
{item.position}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -130,7 +130,7 @@ function EditProgramInovasi() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
|
||||
@@ -40,13 +40,15 @@ function DetailProgramInovasi() {
|
||||
const data = stateProgramInovasi.findUnique.data
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'md', md: 'xl' }} py="lg">
|
||||
<Button variant="subtle" onClick={() => router.back()} leftSection={<IconArrowBack size={22} color={colors['blue-button']} />}>
|
||||
Kembali
|
||||
</Button>
|
||||
<Box px={{ base: 0, md: 'xs' }} py="xs">
|
||||
<Box pb="20">
|
||||
<Button variant="subtle" onClick={() => router.back()} leftSection={<IconArrowBack size={22} color={colors['blue-button']} />}>
|
||||
Kembali
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Paper
|
||||
w={{ base: "100%", md: "60%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
@@ -68,9 +70,9 @@ function DetailProgramInovasi() {
|
||||
<Box>
|
||||
<Text fz="lg" fw="bold">Gambar</Text>
|
||||
{data.image?.link ? (
|
||||
<Image
|
||||
src={data.image.link}
|
||||
alt="Gambar Program"
|
||||
<Image
|
||||
src={data.image.link}
|
||||
alt="Gambar Program"
|
||||
radius="md"
|
||||
style={{ maxWidth: '100%', maxHeight: 300, objectFit: 'contain' }}
|
||||
loading="lazy"
|
||||
@@ -106,28 +108,28 @@ function DetailProgramInovasi() {
|
||||
</Box>
|
||||
|
||||
<Group gap="sm">
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() => {
|
||||
setSelectedId(data.id);
|
||||
setModalHapus(true);
|
||||
}}
|
||||
variant="light"
|
||||
radius="md"
|
||||
size="md"
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() => {
|
||||
setSelectedId(data.id);
|
||||
setModalHapus(true);
|
||||
}}
|
||||
variant="light"
|
||||
radius="md"
|
||||
size="md"
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="green"
|
||||
onClick={() => router.push(`/admin/landing-page/profil/program-inovasi/${data.id}/edit`)}
|
||||
variant="light"
|
||||
radius="md"
|
||||
size="md"
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
color="green"
|
||||
onClick={() => router.push(`/admin/landing-page/profil/program-inovasi/${data.id}/edit`)}
|
||||
variant="light"
|
||||
radius="md"
|
||||
size="md"
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
@@ -76,7 +76,7 @@ function CreateProgramInovasi() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
|
||||
@@ -13,7 +13,7 @@ function ProgramInovasi() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
return (
|
||||
<Box px="md" py="lg">
|
||||
<Box px={{base: 0, md: "md"}} py="lg">
|
||||
<HeaderSearch
|
||||
title="Program Inovasi"
|
||||
placeholder="Cari program inovasi..."
|
||||
@@ -52,75 +52,137 @@ function ListProgramInovasi({ search }: { search: string }) {
|
||||
<Group justify='space-between'>
|
||||
<Title order={4}>Daftar Program Inovasi</Title>
|
||||
<Button
|
||||
color="blue"
|
||||
leftSection={<IconPlus size={18} />}
|
||||
variant="light"
|
||||
radius="md"
|
||||
onClick={() => router.push('/admin/landing-page/profil/program-inovasi/create')}
|
||||
>
|
||||
Tambah Program
|
||||
</Button>
|
||||
color="blue"
|
||||
leftSection={<IconPlus size={18} />}
|
||||
variant="light"
|
||||
radius="md"
|
||||
onClick={() => router.push('/admin/landing-page/profil/program-inovasi/create')}
|
||||
>
|
||||
Tambah Program
|
||||
</Button>
|
||||
</Group>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover striped verticalSpacing="sm">
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Program</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Link</TableTh>
|
||||
<TableTh>Aksi</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length === 0 ? (
|
||||
<Box visibleFrom='md'>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover striped verticalSpacing="sm">
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTd colSpan={4}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">Belum ada data program inovasi</Text>
|
||||
</Center>
|
||||
</TableTd>
|
||||
<TableTh>Nama Program</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Link</TableTh>
|
||||
<TableTh>Aksi</TableTh>
|
||||
</TableTr>
|
||||
) : (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Text fw={500}>{item.name}</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ maxWidth: 250 }}>
|
||||
<Text fz="sm" lineClamp={1} dangerouslySetInnerHTML={{ __html: item.description || '-' }}></Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ maxWidth: 250 }}>
|
||||
<Tooltip label="Buka tautan program" position="top" withArrow>
|
||||
<a
|
||||
href={item.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: colors['blue-button'], textDecoration: 'underline' }}
|
||||
>
|
||||
<Text truncate fz="sm">{item.link}</Text>
|
||||
</a>
|
||||
</Tooltip>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImacCog size={16} />}
|
||||
onClick={() =>
|
||||
router.push(`/admin/landing-page/profil/program-inovasi/${item.id}`)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length === 0 ? (
|
||||
<TableTr>
|
||||
<TableTd colSpan={4}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">Belum ada data program inovasi</Text>
|
||||
</Center>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
)}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
) : (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Text fw={500}>{item.name}</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ maxWidth: 250 }}>
|
||||
<Text fz="sm" lineClamp={1} dangerouslySetInnerHTML={{ __html: item.description || '-' }}></Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ maxWidth: 250 }}>
|
||||
<Tooltip label="Buka tautan program" position="top" withArrow>
|
||||
<a
|
||||
href={item.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: colors['blue-button'], textDecoration: 'underline' }}
|
||||
>
|
||||
<Text truncate fz="sm">{item.link}</Text>
|
||||
</a>
|
||||
</Tooltip>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImacCog size={16} />}
|
||||
onClick={() =>
|
||||
router.push(`/admin/landing-page/profil/program-inovasi/${item.id}`)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
)}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box hiddenFrom="md" pt={20}>
|
||||
<Stack gap="sm">
|
||||
{filteredData.map((item) => (
|
||||
<Paper
|
||||
key={item.id}
|
||||
withBorder
|
||||
radius="md"
|
||||
p="md"
|
||||
shadow="xs"
|
||||
>
|
||||
<Stack gap={6}>
|
||||
{/* Title */}
|
||||
<Text fw={600}>{item.name}</Text>
|
||||
|
||||
{/* Description */}
|
||||
<Text fz="sm" c="gray.7" lineClamp={2}>
|
||||
{item.description || '-'}
|
||||
</Text>
|
||||
|
||||
{/* Link */}
|
||||
<Box>
|
||||
<a
|
||||
href={item.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
<Text
|
||||
fz="sm"
|
||||
c="blue"
|
||||
truncate
|
||||
>
|
||||
{item.link}
|
||||
</Text>
|
||||
</a>
|
||||
</Box>
|
||||
|
||||
{/* Action */}
|
||||
<Group justify="flex-end" mt="xs">
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImacCog size={16} />}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/admin/landing-page/profil/program-inovasi/${item.id}`
|
||||
)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{filteredData.length > 0 && (
|
||||
<Center mt="md">
|
||||
<Pagination
|
||||
|
||||
Reference in New Issue
Block a user