Api Admin Menu Desa Sub Menu Layanan Tabs 3 Pelayanan Done
This commit is contained in:
18
src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
Normal file
18
src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export function convertYoutubeUrlToEmbed(url: string): string | null {
|
||||
const watchRegex = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/;
|
||||
const shortRegex = /(?:https?:\/\/)?youtu\.be\/([^?]+)/;
|
||||
|
||||
const matchWatch = url.match(watchRegex);
|
||||
const matchShort = url.match(shortRegex);
|
||||
|
||||
if (matchWatch) {
|
||||
return `https://www.youtube.com/embed/${matchWatch[1]}`;
|
||||
}
|
||||
|
||||
if (matchShort) {
|
||||
return `https://www.youtube.com/embed/${matchShort[1]}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
33
src/app/admin/(dashboard)/desa/gallery/lib/youtubeEmbed.tsx
Normal file
33
src/app/admin/(dashboard)/desa/gallery/lib/youtubeEmbed.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
// components/YoutubeEmbed.tsx
|
||||
"use client";
|
||||
|
||||
import { Box, Text } from "@mantine/core";
|
||||
|
||||
type YoutubeEmbedProps = {
|
||||
url?: string;
|
||||
showRawUrl?: boolean; // opsional, buat nampilin URL mentahnya
|
||||
};
|
||||
|
||||
export default function YoutubeEmbed({ url, showRawUrl = false }: YoutubeEmbedProps) {
|
||||
if (!url || !url.includes("embed")) {
|
||||
return <Text c="red">Link embed Youtube tidak valid</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box
|
||||
component="iframe"
|
||||
src={url}
|
||||
width="100%"
|
||||
height={300}
|
||||
allowFullScreen
|
||||
style={{ borderRadius: 8 }}
|
||||
/>
|
||||
{showRawUrl && (
|
||||
<Text fz="sm" c="dimmed" mt={5}>
|
||||
{url}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -3,18 +3,19 @@
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
||||
import colors from '@/con/colors';
|
||||
import { ActionIcon, Box, Button, Flex, Group, Image, Modal, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { convertYoutubeUrlToEmbed } from '../../../lib/youtube-utils';
|
||||
|
||||
|
||||
|
||||
function EditVideo() {
|
||||
const router = useRouter();
|
||||
const [modalHapus, setModalHapus] = useState(false);
|
||||
const [embedLink, setEmbedLink] = useState("");
|
||||
const videoState = useProxy(stateGallery.video)
|
||||
const params = useParams()
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -31,10 +32,12 @@ function EditVideo() {
|
||||
const data = await videoState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
linkVideo: data.linkVideo || '',
|
||||
});
|
||||
const embed = convertYoutubeUrlToEmbed(data.linkVideo);
|
||||
setEmbedLink(embed || "");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading video:', error);
|
||||
@@ -52,6 +55,11 @@ function EditVideo() {
|
||||
deskripsi: formData.deskripsi,
|
||||
linkVideo: formData.linkVideo,
|
||||
};
|
||||
const converted = convertYoutubeUrlToEmbed(formData.linkVideo);
|
||||
if (!converted) {
|
||||
toast.error("Link YouTube tidak valid. Pastikan formatnya benar.");
|
||||
return;
|
||||
}
|
||||
await videoState.update.update();
|
||||
toast.success('Video berhasil diperbarui!');
|
||||
router.push('/admin/desa/gallery/video');
|
||||
@@ -85,22 +93,30 @@ function EditVideo() {
|
||||
/>
|
||||
<Box>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Link Video Youtube <Text span c="red" inherit>*</Text></Text>}
|
||||
placeholder='Masukkan link video youtube'
|
||||
label="Link Video YouTube"
|
||||
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||
value={formData.linkVideo}
|
||||
onChange={(val) => {
|
||||
onChange={(e) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
linkVideo: val.target.value,
|
||||
linkVideo: e.currentTarget.value,
|
||||
})
|
||||
const embed = convertYoutubeUrlToEmbed(e.currentTarget.value);
|
||||
setEmbedLink(embed || "");
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<Flex pt={5} align={"center"} gap={"xs"}>
|
||||
<Text c={"dimmed"} fw={"bold"} fz={"xs"}>Cara mendapatkan link video youtube</Text>
|
||||
<ActionIcon size={"xs"} radius={"xl"} color='black' variant='filled' onClick={() => setModalHapus(true)}>
|
||||
<Text fw={"bold"} fz={"xs"} c="white">?</Text>
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
|
||||
{embedLink && (
|
||||
<iframe
|
||||
className="rounded"
|
||||
width="100%"
|
||||
height="200"
|
||||
src={embedLink}
|
||||
title="Preview Video"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
||||
@@ -119,36 +135,6 @@ function EditVideo() {
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<Modal
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
title={<Text fw={"bold"} fz={"xl"}>Cara mendapatkan link video youtube</Text>}
|
||||
>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 1</Text>
|
||||
<Text fz={"sm"}>Buka video youtube yang ingin Anda bagikan lalu klik icon titik tiga</Text>
|
||||
<Image src="/video.png" w={300} alt="" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 2</Text>
|
||||
<Text fz={"sm"}>Klik bagikan</Text>
|
||||
<Image src="/Share.png" w={300} alt="" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 3</Text>
|
||||
<Text fz={"sm"}>Klik dibagian sematkan</Text>
|
||||
<Image src="/bagikanPostingan.png" w={300} alt="" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 4</Text>
|
||||
<Text fz={"sm"}>Lalu copy pada bagaian srcnya aja</Text>
|
||||
<Image src="/sematkan.png" w={300} alt="" />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function DetailVideo() {
|
||||
const videoState = useProxy(stateGallery.video)
|
||||
const [modalHapus, setModalHapus] = useState(false);
|
||||
@@ -55,9 +56,17 @@ function DetailVideo() {
|
||||
<Text fz={"lg"}>{videoState.findUnique.data?.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Link Video</Text>
|
||||
<Text fz={"lg"}>{videoState.findUnique.data?.linkVideo}</Text>
|
||||
<Text fw={"bold"} fz={"lg"}>Video</Text>
|
||||
<Box component="iframe"
|
||||
src={convertToEmbedUrl(videoState.findUnique.data?.linkVideo)}
|
||||
width="100%"
|
||||
height={300}
|
||||
allowFullScreen
|
||||
style={{ borderRadius: 8 }}
|
||||
/>
|
||||
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Tanggal Video</Text>
|
||||
<Text fz={"lg"}>{new Date(videoState.findUnique.data?.createdAt).toDateString()}</Text>
|
||||
@@ -105,7 +114,20 @@ function DetailVideo() {
|
||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||
/>
|
||||
</Box>
|
||||
|
||||
);
|
||||
function convertToEmbedUrl(youtubeUrl: string): string {
|
||||
try {
|
||||
const url = new URL(youtubeUrl);
|
||||
const videoId = url.searchParams.get("v");
|
||||
if (!videoId) return youtubeUrl;
|
||||
|
||||
return `https://www.youtube.com/embed/${videoId}`;
|
||||
} catch (err) {
|
||||
console.error("Error converting YouTube URL to embed:", err);
|
||||
return youtubeUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DetailVideo;
|
||||
|
||||
@@ -2,18 +2,21 @@
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
||||
import colors from '@/con/colors';
|
||||
import { ActionIcon, Box, Button, Flex, Group, Image, Modal, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { convertYoutubeUrlToEmbed } from '../../lib/youtube-utils';
|
||||
|
||||
|
||||
|
||||
function CreateVideo() {
|
||||
const videoState = useProxy(stateGallery.video)
|
||||
const router = useRouter();
|
||||
const [modalHapus, setModalHapus] = useState(false);
|
||||
const [link, setLink] = useState("");
|
||||
const [embedLink, setEmbedLink] = useState("");
|
||||
|
||||
const resetForm = () => {
|
||||
videoState.create.form = {
|
||||
@@ -22,8 +25,12 @@ function CreateVideo() {
|
||||
linkVideo: "",
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const converted = convertYoutubeUrlToEmbed(videoState.create.form.linkVideo);
|
||||
if (!converted) {
|
||||
toast.error("Link YouTube tidak valid. Pastikan formatnya benar.");
|
||||
return;
|
||||
}
|
||||
await videoState.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/desa/gallery/video")
|
||||
@@ -49,20 +56,28 @@ function CreateVideo() {
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Link Video Youtube <Text span c="red" inherit>*</Text></Text>}
|
||||
placeholder='Masukkan link video youtube'
|
||||
value={videoState.create.form.linkVideo}
|
||||
onChange={(val) => {
|
||||
videoState.create.form.linkVideo = val.target.value;
|
||||
}}
|
||||
/>
|
||||
<Flex pt={5} align={"center"} gap={"xs"}>
|
||||
<Text c={"dimmed"} fw={"bold"} fz={"xs"}>Cara mendapatkan link video youtube</Text>
|
||||
<ActionIcon size={"xs"} radius={"xl"} color='black' variant='filled' onClick={() => setModalHapus(true)}>
|
||||
<Text fw={"bold"} fz={"xs"} c="white">?</Text>
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
label="Link Video YouTube"
|
||||
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||
value={link}
|
||||
onChange={(e) => {
|
||||
setLink(e.currentTarget.value);
|
||||
const embed = convertYoutubeUrlToEmbed(e.currentTarget.value);
|
||||
setEmbedLink(embed || "");
|
||||
}}
|
||||
required
|
||||
/>
|
||||
|
||||
{embedLink && (
|
||||
<iframe
|
||||
style={{ borderRadius: 10, width: "100%", height: 400 }}
|
||||
src={embedLink}
|
||||
title="Preview Video"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
||||
@@ -78,36 +93,6 @@ function CreateVideo() {
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<Modal
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
title={<Text fw={"bold"} fz={"xl"}>Cara mendapatkan link video youtube</Text>}
|
||||
>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 1</Text>
|
||||
<Text fz={"sm"}>Buka video youtube yang ingin Anda bagikan lalu klik icon titik tiga</Text>
|
||||
<Image src="/video.png" w={300} alt="" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 2</Text>
|
||||
<Text fz={"sm"}>Klik bagikan</Text>
|
||||
<Image src="/Share.png" w={300} alt="" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 3</Text>
|
||||
<Text fz={"sm"}>Klik dibagian sematkan</Text>
|
||||
<Image src="/bagikanPostingan.png" w={300} alt="" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Langkah 4</Text>
|
||||
<Text fz={"sm"}>Lalu copy pada bagaian srcnya aja</Text>
|
||||
<Image src="/sematkan.png" w={300} alt="" />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
'use client'
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditPelayananPerizinanBerusaha() {
|
||||
const router = useRouter();
|
||||
const params = useParams()
|
||||
const statePerizinanBerusaha = useProxy(stateLayananDesa.pelayananPerizinanBerusaha)
|
||||
const [formData, setFormData] = useState({
|
||||
name: statePerizinanBerusaha.findById.data?.name || '',
|
||||
deskripsi: statePerizinanBerusaha.findById.data?.deskripsi || '',
|
||||
link: statePerizinanBerusaha.findById.data?.link || '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadPelayananPerizinan = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
try {
|
||||
const data = await statePerizinanBerusaha.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
link: data.link || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pelayanan perizinan berusaha:", error);
|
||||
toast.error("Gagal memuat data pelayanan perizinan berusaha");
|
||||
}
|
||||
};
|
||||
loadPelayananPerizinan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (statePerizinanBerusaha.findById.data) {
|
||||
statePerizinanBerusaha.findById.data.name = formData.name;
|
||||
statePerizinanBerusaha.findById.data.deskripsi = formData.deskripsi;
|
||||
statePerizinanBerusaha.findById.data.link = formData.link;
|
||||
statePerizinanBerusaha.update.update(statePerizinanBerusaha.findById.data)
|
||||
}
|
||||
router.push('/admin/desa/layanan/pelayanan_perizinan_berusaha')
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={'xs'}>
|
||||
<Box>
|
||||
<Button
|
||||
variant={'subtle'}
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'} radius={10} w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Title order={3}>Edit Pelayanan Perizinan Berusaha</Title>
|
||||
<Text fw={"bold"}>Judul</Text>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
name: val.target.value,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Text fw={"bold"}>Link</Text>
|
||||
<TextInput
|
||||
value={formData.link}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
link: val.target.value,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Text fw={"bold"}>Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
deskripsi: val,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
<Group>
|
||||
<Button
|
||||
bg={colors['blue-button']}
|
||||
onClick={handleSubmit}
|
||||
loading={statePerizinanBerusaha.update.loading}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPelayananPerizinanBerusaha;
|
||||
@@ -1,40 +1,94 @@
|
||||
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { Box, Button, Grid, GridCol, Group, Paper, Skeleton, Stack, Stepper, StepperCompleted, StepperStep, Text } from '@mantine/core';
|
||||
import { IconEdit } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
function PerizinanBerusaha() {
|
||||
const router = useRouter()
|
||||
const pelayananPerizinanBerusaha = useProxy(stateLayananDesa.pelayananPerizinanBerusaha)
|
||||
const [active, setActive] = useState(1);
|
||||
const nextStep = () => setActive((current) => (current < 6 ? current + 1 : current));
|
||||
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
|
||||
useShallowEffect(() => {
|
||||
pelayananPerizinanBerusaha.findById.load('1')
|
||||
}, [])
|
||||
|
||||
if(!pelayananPerizinanBerusaha.findById.data) {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton radius={10} h={800} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulListTab
|
||||
title='List Pelayanan Perizinan Berusaha'
|
||||
href='/admin/desa/layanan/pelayanan_perizinan_berusaha/create'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
<TableTr>
|
||||
<TableTd>Pelayanan Perizinan Berusaha</TableTd>
|
||||
<TableTd>Deskripsi Pelayanan Perizinan Berusaha</TableTd>
|
||||
<TableTd>
|
||||
<Text>
|
||||
<Button>
|
||||
<IconDeviceImac size={20} />
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Box py={15}>
|
||||
<Stack gap={"xs"}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Text fz={"h4"} fw={"bold"}>Preview Pelayanan Perizinan Berusaha</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button bg={colors['blue-button']} onClick={() => router.push('/admin/desa/layanan/pelayanan_perizinan_berusaha/edit')}>
|
||||
<IconEdit size={16} />
|
||||
</Button>
|
||||
</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>{pelayananPerizinanBerusaha.findById.data.name}</Text>
|
||||
<Text py={10} ta={"justify"} fz={{ base: "sm", md: 'h3' }} dangerouslySetInnerHTML={{__html: pelayananPerizinanBerusaha.findById.data.deskripsi}} />
|
||||
<Text py={10} fz={{ base: "sm", md: 'h3' }}>Proses pendaftaran NIB melalui OSS mencakup beberapa langkah umum, seperti:</Text>
|
||||
<Box p={"xl"} w={{ base: "100%", md: "100%" }} >
|
||||
<Stepper active={active} onStepClick={setActive} orientation="vertical"
|
||||
styles={{
|
||||
separator: {
|
||||
marginLeft: 25
|
||||
},
|
||||
|
||||
step: {
|
||||
padding: '12px 0'
|
||||
}
|
||||
}}>
|
||||
<StepperStep label="Langkah Pertama" description="Pendaftaran Akun">
|
||||
Pendaftaran akun pada portal OSS
|
||||
</StepperStep>
|
||||
<StepperStep label="Langkah Kedua" description="Pengisian Data Perusahaan">
|
||||
Mengisi informasi perusahaan, termasuk data pemegang saham, alamat perusahaan, dan lainnya
|
||||
</StepperStep>
|
||||
<StepperStep label="Langkah Ketiga" description="Pemilihan KBLI ">
|
||||
Memilih KBLI dengan jenis usaha yang akan didaftarkan
|
||||
</StepperStep>
|
||||
<StepperStep label="Langkah Keempat" description="Pengunggahan Dokumen">
|
||||
Mengunggah dokumen-dokumen yang diperlukan, seperti akta pendirian perusahaan, surat izin usaha, dan dokumen lainnya sesuai dengan ketentuan yang berlaku
|
||||
</StepperStep>
|
||||
<StepperStep label="Langkah Kelima" description="Verifikasi dan Persetujuan">
|
||||
Proses verifikasi dan persetujuan oleh instansi terkait
|
||||
</StepperStep>
|
||||
<StepperStep label="Langkah Keenam" description="Penerimaan NIB">
|
||||
Jika proses sebelumnya berhasil, perusahaan akan menerima NIB sebagai identitas resmi usaha anda
|
||||
</StepperStep>
|
||||
<StepperCompleted >
|
||||
Selesai, anda telah mengikuti proses pendaftaran NIB melalui OSS
|
||||
</StepperCompleted>
|
||||
</Stepper>
|
||||
|
||||
<Group justify="center" mt="xl">
|
||||
<Button variant="default" onClick={prevStep}>Back</Button>
|
||||
<Button onClick={nextStep}>Next step</Button>
|
||||
</Group>
|
||||
<Text py={35} ta={"justify"} fz={{ base: "sm", md: 'h3' }}>Penting untuk diingat bahwa prosedur dan persyaratan dapat berubah
|
||||
seiring waktu. Untuk informasi yang lebih akurat dan terkini, saya sarankan untuk mengunjungi situs
|
||||
resmi OSS <a href={pelayananPerizinanBerusaha.findById.data.link}>{pelayananPerizinanBerusaha.findById.data.link}</a> atau menghubungi instansi terkait di pemerintah Indonesia yang bertanggung jawab atas urusan perizinan usaha.</Text>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
'use client'
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditPelayananTelunjukSakti() {
|
||||
const stateTelunjukDesa = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [formData, setFormData] = useState({
|
||||
name: stateTelunjukDesa.edit.form.name,
|
||||
deskripsi: stateTelunjukDesa.edit.form.deskripsi,
|
||||
link: stateTelunjukDesa.edit.form.link,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadPelayananTelunjukSakti = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
try {
|
||||
const data = await stateTelunjukDesa.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
link: data.link,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pelayanan telunjuk sakti:", error);
|
||||
toast.error("Gagal memuat data pelayanan telunjuk sakti");
|
||||
}
|
||||
};
|
||||
loadPelayananTelunjukSakti();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateTelunjukDesa.edit.form = {
|
||||
...stateTelunjukDesa.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
link: formData.link,
|
||||
}
|
||||
await stateTelunjukDesa.edit.update()
|
||||
toast.success("Pelayanan telunjuk sakti berhasil diperbarui!")
|
||||
router.push("/admin/desa/layanan/pelayanan_telunjuk_sakti_desa")
|
||||
} catch (error) {
|
||||
console.error("Error updating pelayanan telunjuk sakti:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui pelayanan telunjuk sakti");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Edit Surat Keterangan</Title>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(val) => {
|
||||
setFormData({ ...formData, name: val.target.value });
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Nama Surat Keterangan</Text>}
|
||||
placeholder="masukkan nama surat keterangan"
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.link}
|
||||
onChange={(val) => {
|
||||
setFormData({ ...formData, link: val.target.value });
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Link</Text>}
|
||||
placeholder="masukkan link"
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData({ ...formData, deskripsi: htmlContent });
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPelayananTelunjukSakti;
|
||||
@@ -0,0 +1,109 @@
|
||||
'use client'
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function DetailPelayananTelunjukSakti() {
|
||||
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
telunjukSaktiState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
telunjukSaktiState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/desa/layanan/pelayanan_telunjuk_sakti_desa")
|
||||
}
|
||||
}
|
||||
|
||||
if (!telunjukSaktiState.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
{Array.from({ length: 10 }).map((_, k) => (
|
||||
<Skeleton key={k} h={40} />
|
||||
))}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Pelayanan Telunjuk Sakti Desa</Text>
|
||||
{telunjukSaktiState.findUnique.data ? (
|
||||
<Paper key={telunjukSaktiState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Nama</Text>
|
||||
<Text fz={"lg"}>{telunjukSaktiState.findUnique.data?.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Link</Text>
|
||||
<Text fz={"lg"}>{telunjukSaktiState.findUnique.data?.link}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
||||
<Text fz={"lg"}dangerouslySetInnerHTML={{ __html: telunjukSaktiState.findUnique.data?.deskripsi }}></Text>
|
||||
</Box>
|
||||
<Flex gap={"xs"} mt={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (telunjukSaktiState.findUnique.data) {
|
||||
setSelectedId(telunjukSaktiState.findUnique.data.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={telunjukSaktiState.delete.loading || !telunjukSaktiState.findUnique.data}
|
||||
color={"red"}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (telunjukSaktiState.findUnique.data) {
|
||||
router.push(`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${telunjukSaktiState.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!telunjukSaktiState.findUnique.data}
|
||||
color={"green"}
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailPelayananTelunjukSakti;
|
||||
@@ -0,0 +1,70 @@
|
||||
'use client'
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreatePelayananTelunjukDesa() {
|
||||
const stateTelunjukDesa = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||
const router = useRouter()
|
||||
|
||||
const resetForm = () => {
|
||||
stateTelunjukDesa.create.form = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
link: "",
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await stateTelunjukDesa.create.create()
|
||||
resetForm()
|
||||
router.push("/admin/desa/layanan/pelayanan_telunjuk_sakti_desa")
|
||||
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Create Pelayanan Telunjuk Sakti Desa</Title>
|
||||
<TextInput
|
||||
value={stateTelunjukDesa.create.form.name}
|
||||
onChange={(val) => {
|
||||
stateTelunjukDesa.create.form.name = val.target.value;
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Nama Pelayanan Telunjuk Sakti Desa</Text>}
|
||||
placeholder="masukkan nama pelayanan telunjuk sakti desa"
|
||||
/>
|
||||
<TextInput
|
||||
value={stateTelunjukDesa.create.form.link}
|
||||
onChange={(val) => {
|
||||
stateTelunjukDesa.create.form.link = val.target.value;
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Link</Text>}
|
||||
placeholder="masukkan link"
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<CreateEditor
|
||||
value={stateTelunjukDesa.create.form.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
stateTelunjukDesa.create.form.deskripsi = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreatePelayananTelunjukDesa;
|
||||
@@ -1,9 +1,29 @@
|
||||
'use client'
|
||||
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
function PelayananTelunjukSakti() {
|
||||
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
telunjukSaktiState.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (!telunjukSaktiState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
function SuratKeterangan() {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
@@ -22,17 +42,19 @@ function SuratKeterangan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
<TableTr>
|
||||
<TableTd>Pelayanan Telunjuk Sakti Desa</TableTd>
|
||||
<TableTd>Deskripsi Pelayanan Telunjuk Sakti Desa</TableTd>
|
||||
{telunjukSaktiState.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd><Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} /></TableTd>
|
||||
<TableTd>
|
||||
<Text>
|
||||
<Button>
|
||||
<Button onClick={() => router.push(`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
@@ -40,4 +62,4 @@ function SuratKeterangan() {
|
||||
);
|
||||
}
|
||||
|
||||
export default SuratKeterangan;
|
||||
export default PelayananTelunjukSakti;
|
||||
|
||||
Reference in New Issue
Block a user