Fix Admin - User Menu Keamanan, Submenu Pencegahan Kriminalitas
This commit is contained in:
@@ -1254,48 +1254,15 @@ model KontakDaruratToItem {
|
|||||||
|
|
||||||
// ========================================= PENCEGAHAN KRIMINALITAS ========================================= //
|
// ========================================= PENCEGAHAN KRIMINALITAS ========================================= //
|
||||||
model PencegahanKriminalitas {
|
model PencegahanKriminalitas {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
programKeamanan ProgramKeamanan @relation(fields: [programKeamananId], references: [id])
|
judul String
|
||||||
programKeamananId String
|
deskripsi String
|
||||||
tipsKeamanan TipsKeamanan @relation(fields: [tipsKeamananId], references: [id])
|
deskripsiSingkat String
|
||||||
tipsKeamananId String
|
linkVideo String
|
||||||
videoKeamanan VideoKeamanan @relation(fields: [videoKeamananId], references: [id])
|
createdAt DateTime @default(now())
|
||||||
videoKeamananId String
|
updatedAt DateTime @updatedAt
|
||||||
createdAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
isActive Boolean @default(true)
|
||||||
deletedAt DateTime @default(now())
|
|
||||||
isActive Boolean @default(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
model ProgramKeamanan {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
nama String // contoh: "Ronda Malam"
|
|
||||||
deskripsi String? // jika mau tambahkan info detail
|
|
||||||
slug String @unique
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
PencegahanKriminalitas PencegahanKriminalitas[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model TipsKeamanan {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
judul String
|
|
||||||
konten String
|
|
||||||
slug String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
PencegahanKriminalitas PencegahanKriminalitas[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model VideoKeamanan {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
judul String
|
|
||||||
deskripsi String?
|
|
||||||
videoUrl String // link youtube atau embed url
|
|
||||||
slug String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
PencegahanKriminalitas PencegahanKriminalitas[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= LAPORAN PUBLIK ========================================= //
|
// ========================================= LAPORAN PUBLIK ========================================= //
|
||||||
|
|||||||
@@ -6,45 +6,17 @@ import { proxy } from "valtio";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
pencegahanKriminalitas: z.object({
|
judul: z.string().min(1, "Judul minimal 1 karakter"),
|
||||||
programKeamanan: z.object({
|
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||||
nama: z.string().min(1, "Nama minimal 1 karakter"),
|
deskripsiSingkat: z.string().min(1, "Deskripsi singkat minimal 1 karakter"),
|
||||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
linkVideo: z.string().min(1, "Link video minimal 1 karakter"),
|
||||||
slug: z.string().min(1, "Slug minimal 1 karakter"),
|
|
||||||
}),
|
|
||||||
tipsKeamanan: z.object({
|
|
||||||
judul: z.string().min(1, "Judul minimal 1 karakter"),
|
|
||||||
konten: z.string().min(1, "Konten minimal 1 karakter"),
|
|
||||||
slug: z.string().min(1, "Slug minimal 1 karakter"),
|
|
||||||
}),
|
|
||||||
videoKeamanan: z.object({
|
|
||||||
judul: z.string().min(1, "Judul minimal 1 karakter"),
|
|
||||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
|
||||||
videoUrl: z.string().min(1, "Video URL minimal 1 karakter"),
|
|
||||||
slug: z.string().min(1, "Slug minimal 1 karakter"),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
pencegahanKriminalitas: {
|
judul: "",
|
||||||
programKeamanan: {
|
deskripsi: "",
|
||||||
nama: "",
|
deskripsiSingkat: "",
|
||||||
deskripsi: "",
|
linkVideo: "",
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: "",
|
|
||||||
konten: "",
|
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: "",
|
|
||||||
deskripsi: "",
|
|
||||||
videoUrl: "",
|
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const pencegahanKriminalitasState = proxy({
|
const pencegahanKriminalitasState = proxy({
|
||||||
@@ -65,7 +37,7 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
pencegahanKriminalitasState.create.loading = true;
|
pencegahanKriminalitasState.create.loading = true;
|
||||||
const res = await ApiFetch.api.keamanan.pencegahankriminalitas[
|
const res = await ApiFetch.api.keamanan.pencegahankriminalitas[
|
||||||
"create"
|
"create"
|
||||||
].post(pencegahanKriminalitasState.create.form.pencegahanKriminalitas);
|
].post(pencegahanKriminalitasState.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
pencegahanKriminalitasState.findMany.load();
|
pencegahanKriminalitasState.findMany.load();
|
||||||
return toast.success("success create");
|
return toast.success("success create");
|
||||||
@@ -82,11 +54,7 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.PencegahanKriminalitasGetPayload<{
|
| Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
include: {
|
omit: { isActive: true };
|
||||||
programKeamanan: true;
|
|
||||||
tipsKeamanan: true;
|
|
||||||
videoKeamanan: true;
|
|
||||||
};
|
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -125,11 +93,7 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.PencegahanKriminalitasGetPayload<{
|
data: null as Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
include: {
|
omit: { isActive: true };
|
||||||
programKeamanan: true;
|
|
||||||
tipsKeamanan: true;
|
|
||||||
videoKeamanan: true;
|
|
||||||
};
|
|
||||||
}> | null,
|
}> | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
@@ -148,6 +112,30 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
findFirst: {
|
||||||
|
data: null as Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
loading: false,
|
||||||
|
// findFirst.load()
|
||||||
|
async load() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const res = await ApiFetch.api.keamanan.pencegahankriminalitas["find-first"].get();
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
this.data = res.data.data || null;
|
||||||
|
} else {
|
||||||
|
this.data = null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch pencegahan kriminalitas terbaru:", err);
|
||||||
|
this.data = null;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
delete: {
|
delete: {
|
||||||
loading: false,
|
loading: false,
|
||||||
async byId(id: string) {
|
async byId(id: string) {
|
||||||
@@ -213,24 +201,10 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
const data = result.data;
|
const data = result.data;
|
||||||
pencegahanKriminalitasState.update.id = data.id;
|
pencegahanKriminalitasState.update.id = data.id;
|
||||||
pencegahanKriminalitasState.update.form = {
|
pencegahanKriminalitasState.update.form = {
|
||||||
pencegahanKriminalitas: {
|
judul: data.judul,
|
||||||
programKeamanan: {
|
deskripsi: data.deskripsi,
|
||||||
nama: data.programKeamanan.nama,
|
deskripsiSingkat: data.deskripsiSingkat,
|
||||||
deskripsi: data.programKeamanan.deskripsi,
|
linkVideo: data.linkVideo,
|
||||||
slug: data.programKeamanan.slug,
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: data.tipsKeamanan.judul,
|
|
||||||
konten: data.tipsKeamanan.konten,
|
|
||||||
slug: data.tipsKeamanan.slug,
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: data.videoKeamanan.judul,
|
|
||||||
deskripsi: data.videoKeamanan.deskripsi,
|
|
||||||
videoUrl: data.videoKeamanan.videoUrl,
|
|
||||||
slug: data.videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
@@ -266,40 +240,11 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
pencegahanKriminalitas: {
|
judul: pencegahanKriminalitasState.update.form.judul,
|
||||||
programKeamanan: {
|
deskripsi: pencegahanKriminalitasState.update.form.deskripsi,
|
||||||
nama: pencegahanKriminalitasState.update.form
|
deskripsiSingkat:
|
||||||
.pencegahanKriminalitas.programKeamanan.nama,
|
pencegahanKriminalitasState.update.form.deskripsiSingkat,
|
||||||
deskripsi:
|
linkVideo: pencegahanKriminalitasState.update.form.linkVideo,
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.programKeamanan.deskripsi,
|
|
||||||
slug: pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.programKeamanan.slug,
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.tipsKeamanan.judul,
|
|
||||||
konten:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.tipsKeamanan.konten,
|
|
||||||
slug: pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.tipsKeamanan.slug,
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.judul,
|
|
||||||
deskripsi:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.deskripsi,
|
|
||||||
videoUrl:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.videoUrl,
|
|
||||||
slug: pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ function DetailLaporanPublik() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Kronologi</Text>
|
<Text fz="lg" fw="bold">Kronologi</Text>
|
||||||
<Text fz="md" c="dimmed">{data.kronologi || '-'}</Text>
|
<Text fz="md" c="dimmed" dangerouslySetInnerHTML={{ __html: data.kronologi || '' }} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@@ -135,9 +135,7 @@ function ListLaporanPublik({ search }: { search: string }) {
|
|||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={200}>
|
<Box w={200}>
|
||||||
<Text truncate fz="sm" c="dimmed" lineClamp={1}>
|
<Text truncate fz="sm" c="dimmed" lineClamp={1} dangerouslySetInnerHTML={{ __html: item.kronologi || '' }} />
|
||||||
{item.kronologi}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
|
|||||||
@@ -2,39 +2,35 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
import pencegahanKriminalitasState from '@/app/admin/(dashboard)/_state/keamanan/pencegahan-kriminalitas';
|
import pencegahanKriminalitasState from '@/app/admin/(dashboard)/_state/keamanan/pencegahan-kriminalitas';
|
||||||
|
import { convertYoutubeUrlToEmbed } from '@/app/admin/(dashboard)/desa/gallery/lib/youtube-utils';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip
|
||||||
|
} from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
function EditPencegahanKriminalitas() {
|
function EditPencegahanKriminalitas() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams()
|
const params = useParams();
|
||||||
const kriminalitasState = useProxy(pencegahanKriminalitasState)
|
const kriminalitasState = useProxy(pencegahanKriminalitasState);
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
pencegahanKriminalitas: {
|
judul: '',
|
||||||
programKeamanan: {
|
deskripsi: '',
|
||||||
nama: kriminalitasState.update.form.pencegahanKriminalitas.programKeamanan.nama,
|
deskripsiSingkat: '',
|
||||||
deskripsi: kriminalitasState.update.form.pencegahanKriminalitas.programKeamanan.deskripsi,
|
linkVideo: '',
|
||||||
slug: kriminalitasState.update.form.pencegahanKriminalitas.programKeamanan.slug,
|
});
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: kriminalitasState.update.form.pencegahanKriminalitas.tipsKeamanan.judul,
|
|
||||||
konten: kriminalitasState.update.form.pencegahanKriminalitas.tipsKeamanan.konten,
|
|
||||||
slug: kriminalitasState.update.form.pencegahanKriminalitas.tipsKeamanan.slug,
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: kriminalitasState.update.form.pencegahanKriminalitas.videoKeamanan.judul,
|
|
||||||
deskripsi: kriminalitasState.update.form.pencegahanKriminalitas.videoKeamanan.deskripsi,
|
|
||||||
videoUrl: kriminalitasState.update.form.pencegahanKriminalitas.videoKeamanan.videoUrl,
|
|
||||||
slug: kriminalitasState.update.form.pencegahanKriminalitas.videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadKriminalitas = async () => {
|
const loadKriminalitas = async () => {
|
||||||
@@ -43,167 +39,154 @@ function EditPencegahanKriminalitas() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await kriminalitasState.update.load(id);
|
const data = await kriminalitasState.update.load(id);
|
||||||
if (data && data.pencegahanKriminalitas) {
|
if (data) {
|
||||||
const { programKeamanan, tipsKeamanan, videoKeamanan } = data.pencegahanKriminalitas;
|
|
||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
pencegahanKriminalitas: {
|
judul: data.judul || '',
|
||||||
programKeamanan: {
|
deskripsi: data.deskripsi || '',
|
||||||
nama: programKeamanan?.nama || "",
|
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||||
deskripsi: programKeamanan?.deskripsi || "",
|
linkVideo: data.linkVideo || '',
|
||||||
slug: programKeamanan?.slug || "",
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: tipsKeamanan?.judul || "",
|
|
||||||
konten: tipsKeamanan?.konten || "",
|
|
||||||
slug: tipsKeamanan?.slug || "",
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: videoKeamanan?.judul || "",
|
|
||||||
deskripsi: videoKeamanan?.deskripsi || "",
|
|
||||||
videoUrl: videoKeamanan?.videoUrl || "",
|
|
||||||
slug: videoKeamanan?.slug || "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading pencegahan kriminalitas:", error);
|
console.error('Error loading pencegahan kriminalitas:', error);
|
||||||
toast.error("Gagal memuat data pencegahan kriminalitas");
|
toast.error('Gagal memuat data pencegahan kriminalitas');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
loadKriminalitas();
|
loadKriminalitas();
|
||||||
}, [params.id]);
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo);
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
const converted = convertYoutubeUrlToEmbed(formData.linkVideo);
|
||||||
|
if (!converted) {
|
||||||
|
toast.error('Link YouTube tidak valid. Pastikan formatnya benar.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Update the form data first
|
||||||
kriminalitasState.update.form = {
|
kriminalitasState.update.form = {
|
||||||
...kriminalitasState.update.form,
|
...kriminalitasState.update.form,
|
||||||
pencegahanKriminalitas: {
|
judul: formData.judul,
|
||||||
programKeamanan: {
|
deskripsi: formData.deskripsi,
|
||||||
nama: formData.pencegahanKriminalitas.programKeamanan.nama,
|
deskripsiSingkat: formData.deskripsiSingkat,
|
||||||
deskripsi: formData.pencegahanKriminalitas.programKeamanan.deskripsi,
|
linkVideo: formData.linkVideo,
|
||||||
slug: formData.pencegahanKriminalitas.programKeamanan.slug,
|
};
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: formData.pencegahanKriminalitas.tipsKeamanan.judul,
|
|
||||||
konten: formData.pencegahanKriminalitas.tipsKeamanan.konten,
|
|
||||||
slug: formData.pencegahanKriminalitas.tipsKeamanan.slug,
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: formData.pencegahanKriminalitas.videoKeamanan.judul,
|
|
||||||
deskripsi: formData.pencegahanKriminalitas.videoKeamanan.deskripsi,
|
|
||||||
videoUrl: formData.pencegahanKriminalitas.videoKeamanan.videoUrl,
|
|
||||||
slug: formData.pencegahanKriminalitas.videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await kriminalitasState.update.update();
|
|
||||||
toast.success("Pencegahan Kriminalitas berhasil diperbarui!");
|
|
||||||
router.push("/admin/keamanan/pencegahan-kriminalitas");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating pencegahan kriminalitas:", error);
|
|
||||||
toast.error("Gagal memuat data pencegahan kriminalitas");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box mb={10}>
|
|
||||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
// Set the ID and then call update
|
||||||
<Stack gap={"xs"}>
|
kriminalitasState.update.id = params?.id as string;
|
||||||
<Title order={4}>Edit Pencegahan Kriminalitas</Title>
|
|
||||||
|
await kriminalitasState.update.update();
|
||||||
|
toast.success('Pencegahan Kriminalitas berhasil diperbarui!');
|
||||||
|
router.push('/admin/keamanan/pencegahan-kriminalitas');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating pencegahan kriminalitas:', error);
|
||||||
|
toast.error('Terjadi kesalahan saat memperbarui data');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
|
{/* Back button + Title */}
|
||||||
|
<Group mb="md">
|
||||||
|
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
p="xs"
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Edit Pencegahan Kriminalitas
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Form container */}
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
value={formData.pencegahanKriminalitas.programKeamanan.nama}
|
label="Judul"
|
||||||
onChange={(val) => {
|
placeholder="Masukkan judul"
|
||||||
formData.pencegahanKriminalitas.programKeamanan.nama = val.target.value;
|
value={formData.judul}
|
||||||
}}
|
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Program Keamanan</Text>}
|
required
|
||||||
placeholder='Masukkan judul Program Keamanan'
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
value={formData.pencegahanKriminalitas.programKeamanan.slug}
|
label="Deskripsi Singkat"
|
||||||
onChange={(val) => {
|
placeholder="Masukkan deskripsi singkat"
|
||||||
formData.pencegahanKriminalitas.programKeamanan.slug = val.target.value;
|
value={formData.deskripsiSingkat}
|
||||||
}}
|
onChange={(e) =>
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Slug Program Keamanan</Text>}
|
setFormData({ ...formData, deskripsiSingkat: e.target.value })
|
||||||
placeholder='Masukkan slug Program Keamanan'
|
}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Program Keamanan</Text>
|
<Title order={6} fw="bold" fz="sm" mb={6}>
|
||||||
|
Deskripsi Lengkap
|
||||||
|
</Title>
|
||||||
<EditEditor
|
<EditEditor
|
||||||
value={formData.pencegahanKriminalitas.programKeamanan.deskripsi}
|
value={formData.deskripsi}
|
||||||
onChange={(val) => {
|
onChange={(val) =>
|
||||||
formData.pencegahanKriminalitas.programKeamanan.deskripsi = val;
|
setFormData({ ...formData, deskripsi: val })
|
||||||
}}
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<TextInput
|
|
||||||
value={formData.pencegahanKriminalitas.tipsKeamanan.judul}
|
|
||||||
onChange={(val) => {
|
|
||||||
formData.pencegahanKriminalitas.tipsKeamanan.judul = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Tips Keamanan</Text>}
|
|
||||||
placeholder='Masukkan judul Tips Keamanan'
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
value={formData.pencegahanKriminalitas.tipsKeamanan.slug}
|
|
||||||
onChange={(val) => {
|
|
||||||
formData.pencegahanKriminalitas.tipsKeamanan.slug = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Slug Tips Keamanan</Text>}
|
|
||||||
placeholder='Masukkan slug Tips Keamanan'
|
|
||||||
/>
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Tips Keamanan</Text>
|
<TextInput
|
||||||
<EditEditor
|
label="Link Video YouTube"
|
||||||
value={formData.pencegahanKriminalitas.tipsKeamanan.konten}
|
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||||
onChange={(val) => {
|
value={formData.linkVideo}
|
||||||
formData.pencegahanKriminalitas.tipsKeamanan.konten = val;
|
onChange={(e) =>
|
||||||
}}
|
setFormData({ ...formData, linkVideo: e.currentTarget.value })
|
||||||
|
}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
{embedLink && (
|
||||||
|
<Box
|
||||||
|
mt="sm"
|
||||||
|
style={{ display: 'flex', justifyContent: 'center' }}
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
className="rounded"
|
||||||
|
width="100%"
|
||||||
|
height="220"
|
||||||
|
src={embedLink}
|
||||||
|
title="Preview Video"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<TextInput
|
|
||||||
value={formData.pencegahanKriminalitas.videoKeamanan.judul}
|
{/* Action button */}
|
||||||
onChange={(val) => {
|
<Group justify="right">
|
||||||
formData.pencegahanKriminalitas.videoKeamanan.judul = val.target.value;
|
<Button
|
||||||
}}
|
onClick={handleSubmit}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Video Keamanan</Text>}
|
radius="md"
|
||||||
placeholder='Masukkan judul Video Keamanan'
|
size="md"
|
||||||
/>
|
style={{
|
||||||
<TextInput
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
value={formData.pencegahanKriminalitas.videoKeamanan.slug}
|
color: '#fff',
|
||||||
onChange={(val) => {
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
formData.pencegahanKriminalitas.videoKeamanan.slug = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Slug Video Keamanan</Text>}
|
|
||||||
placeholder='Masukkan slug Video Keamanan'
|
|
||||||
/>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Tips Keamanan</Text>
|
|
||||||
<EditEditor
|
|
||||||
value={formData.pencegahanKriminalitas.videoKeamanan.deskripsi}
|
|
||||||
onChange={(val) => {
|
|
||||||
formData.pencegahanKriminalitas.videoKeamanan.deskripsi = val;
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</Box>
|
Simpan
|
||||||
<TextInput
|
</Button>
|
||||||
value={formData.pencegahanKriminalitas.videoKeamanan.videoUrl}
|
|
||||||
onChange={(val) => {
|
|
||||||
formData.pencegahanKriminalitas.videoKeamanan.videoUrl = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Video URL</Text>}
|
|
||||||
placeholder='Masukkan video URL'
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -1,113 +1,146 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
import { Box, Button, Flex, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
|
||||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter, useParams } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { useParams } from 'next/navigation';
|
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
import pencegahanKriminalitasState from '../../../_state/keamanan/pencegahan-kriminalitas';
|
import pencegahanKriminalitasState from '../../../_state/keamanan/pencegahan-kriminalitas';
|
||||||
|
|
||||||
function DetailPencegahanKriminalitas() {
|
function DetailPencegahanKriminalitas() {
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams()
|
const params = useParams();
|
||||||
const kriminalitasState = useProxy(pencegahanKriminalitasState)
|
const kriminalitasState = useProxy(pencegahanKriminalitasState);
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
kriminalitasState.findUnique.load(params?.id as string)
|
kriminalitasState.findUnique.load(params?.id as string);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
kriminalitasState.delete.byId(selectedId)
|
kriminalitasState.delete.byId(selectedId);
|
||||||
setModalHapus(false)
|
setModalHapus(false);
|
||||||
setSelectedId(null)
|
setSelectedId(null);
|
||||||
router.push("/admin/keamanan/pencegahan-kriminalitas")
|
router.push("/admin/keamanan/pencegahan-kriminalitas");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (!kriminalitasState.findUnique.data) {
|
if (!kriminalitasState.findUnique.data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton h={40} />
|
<Skeleton height={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = kriminalitasState.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box py={10}>
|
||||||
<Box mb={10}>
|
{/* Tombol Kembali */}
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Button
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
variant="subtle"
|
||||||
</Button>
|
onClick={() => router.back()}
|
||||||
</Box>
|
leftSection={<IconArrowBack size={24} color={colors['blue-button']} />}
|
||||||
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
mb={15}
|
||||||
<Stack>
|
>
|
||||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
Kembali
|
||||||
<Text fz={"xl"} fw={"bold"}>Detail Pencegahan Kriminalitas</Text>
|
</Button>
|
||||||
{kriminalitasState.findUnique.data ? (
|
|
||||||
<Paper key={kriminalitasState.findUnique.data.id} bg={colors['BG-trans']}>
|
{/* Detail */}
|
||||||
<Stack gap={"xs"} py={'md'}>
|
<Paper
|
||||||
<Box>
|
withBorder
|
||||||
<Text fw={"bold"} fz={"lg"}>Judul Program Keamanan</Text>
|
w={{ base: "100%", md: "50%" }}
|
||||||
<Text fz={"lg"}>{kriminalitasState.findUnique.data?.programKeamanan.nama}</Text>
|
bg={colors['white-1']}
|
||||||
</Box>
|
p="lg"
|
||||||
<Box>
|
radius="md"
|
||||||
<Text fw={"bold"} fz={"lg"}>Slug</Text>
|
shadow="sm"
|
||||||
<Text fz={"lg"}>{kriminalitasState.findUnique.data?.programKeamanan.slug}</Text>
|
>
|
||||||
</Box>
|
<Stack gap="md">
|
||||||
<Box>
|
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
|
||||||
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
Detail Pencegahan Kriminalitas
|
||||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: kriminalitasState.findUnique.data?.programKeamanan.deskripsi || '' }} />
|
</Text>
|
||||||
</Box>
|
|
||||||
<Box>
|
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
|
||||||
<Text fw={"bold"} fz={"lg"}>Judul Tips Keamanan</Text>
|
<Stack gap="sm">
|
||||||
<Text fz={"lg"}>{kriminalitasState.findUnique.data?.tipsKeamanan.judul}</Text>
|
<Box>
|
||||||
</Box>
|
<Text fz="lg" fw="bold">Judul</Text>
|
||||||
<Box>
|
<Text fz="md" c="dimmed">{data?.judul || '-'}</Text>
|
||||||
<Text fw={"bold"} fz={"lg"}>Slug Tips Keamanan</Text>
|
</Box>
|
||||||
<Text fz={"lg"}>{kriminalitasState.findUnique.data?.tipsKeamanan.slug}</Text>
|
|
||||||
</Box>
|
<Box>
|
||||||
<Box>
|
<Text fz="lg" fw="bold">Deskripsi Singkat</Text>
|
||||||
<Text fw={"bold"} fz={"lg"}>Deskripsi Tips Keamanan</Text>
|
{data?.deskripsiSingkat ? (
|
||||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: kriminalitasState.findUnique.data?.tipsKeamanan.konten || '' }} />
|
<Text fz="md" c="dimmed" dangerouslySetInnerHTML={{ __html: data.deskripsiSingkat }} />
|
||||||
</Box>
|
) : (
|
||||||
<Flex gap={"xs"} mt={10}>
|
<Text fz="sm" c="dimmed">Tidak ada deskripsi singkat</Text>
|
||||||
<Button
|
)}
|
||||||
onClick={() => {
|
</Box>
|
||||||
if (kriminalitasState.findUnique.data) {
|
|
||||||
setSelectedId(kriminalitasState.findUnique.data.id);
|
<Box>
|
||||||
setModalHapus(true);
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
}
|
{data?.deskripsi ? (
|
||||||
}}
|
<Text fz="md" c="dimmed" dangerouslySetInnerHTML={{ __html: data.deskripsi }} />
|
||||||
disabled={kriminalitasState.delete.loading || !kriminalitasState.findUnique.data}
|
) : (
|
||||||
color={"red"}
|
<Text fz="sm" c="dimmed">Tidak ada deskripsi</Text>
|
||||||
>
|
)}
|
||||||
<IconX size={20} />
|
</Box>
|
||||||
</Button>
|
|
||||||
<Button
|
<Box>
|
||||||
onClick={() => {
|
<Text fz="lg" fw="bold">Video</Text>
|
||||||
if (kriminalitasState.findUnique.data) {
|
{data?.linkVideo ? (
|
||||||
router.push(`/admin/keamanan/pencegahan-kriminalitas/${kriminalitasState.findUnique.data.id}/edit`);
|
<Box
|
||||||
}
|
component="iframe"
|
||||||
}}
|
src={convertToEmbedUrl(data.linkVideo)}
|
||||||
disabled={!kriminalitasState.findUnique.data}
|
width="100%"
|
||||||
color={"green"}
|
height={300}
|
||||||
>
|
allowFullScreen
|
||||||
<IconEdit size={20} />
|
style={{ borderRadius: 8 }}
|
||||||
</Button>
|
/>
|
||||||
</Flex>
|
) : (
|
||||||
</Stack>
|
<Text fz="sm" c="dimmed">Tidak ada video</Text>
|
||||||
</Paper>
|
)}
|
||||||
) : null}
|
</Box>
|
||||||
|
|
||||||
|
{/* Tombol Aksi */}
|
||||||
|
<Flex gap="sm" mt="sm">
|
||||||
|
<Tooltip label="Hapus" withArrow position="top">
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<IconTrash size={20} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label="Edit" withArrow position="top">
|
||||||
|
<Button
|
||||||
|
color="green"
|
||||||
|
onClick={() => router.push(`/admin/keamanan/pencegahan-kriminalitas/${data.id}/edit`)}
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Modal Hapus */}
|
{/* Modal Konfirmasi Hapus */}
|
||||||
<ModalKonfirmasiHapus
|
<ModalKonfirmasiHapus
|
||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
onClose={() => setModalHapus(false)}
|
onClose={() => setModalHapus(false)}
|
||||||
@@ -116,6 +149,18 @@ function DetailPencegahanKriminalitas() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</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 DetailPencegahanKriminalitas;
|
export default DetailPencegahanKriminalitas;
|
||||||
|
|||||||
@@ -1,141 +1,158 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
} from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import CreateEditor from '../../../_com/createEditor';
|
import CreateEditor from '../../../_com/createEditor';
|
||||||
import pencegahanKriminalitasState from '../../../_state/keamanan/pencegahan-kriminalitas';
|
import pencegahanKriminalitasState from '../../../_state/keamanan/pencegahan-kriminalitas';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { convertYoutubeUrlToEmbed } from '../../../desa/gallery/lib/youtube-utils';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
function CreatePencegahanKriminalitas() {
|
function CreatePencegahanKriminalitas() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const kriminalitasState = useProxy(pencegahanKriminalitasState)
|
const kriminalitasState = useProxy(pencegahanKriminalitasState);
|
||||||
|
const [link, setLink] = useState('');
|
||||||
|
const embedLink = convertYoutubeUrlToEmbed(link);
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
kriminalitasState.create.form = {
|
kriminalitasState.create.form = {
|
||||||
pencegahanKriminalitas: {
|
judul: "",
|
||||||
programKeamanan: {
|
deskripsi: "",
|
||||||
nama: "",
|
deskripsiSingkat: "",
|
||||||
deskripsi: "",
|
linkVideo: "",
|
||||||
slug: "",
|
};
|
||||||
},
|
setLink('');
|
||||||
tipsKeamanan: {
|
};
|
||||||
judul: "",
|
|
||||||
konten: "",
|
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: "",
|
|
||||||
deskripsi: "",
|
|
||||||
videoUrl: "",
|
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
if (!embedLink) {
|
||||||
|
toast.error('Link YouTube tidak valid. Pastikan formatnya benar.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kriminalitasState.create.form.linkVideo = embedLink;
|
||||||
await kriminalitasState.create.create();
|
await kriminalitasState.create.create();
|
||||||
resetForm();
|
resetForm();
|
||||||
router.push('/admin/keamanan/pencegahan-kriminalitas');
|
router.push('/admin/keamanan/pencegahan-kriminalitas');
|
||||||
}
|
};
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box mb={10}>
|
|
||||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
return (
|
||||||
<Stack gap={"xs"}>
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
<Title order={4}>Create Pencegahan Kriminalitas</Title>
|
{/* Header Back Button + Title */}
|
||||||
|
<Group mb="md">
|
||||||
|
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
p="xs"
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Tambah Pencegahan Kriminalitas
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Card Form */}
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
{/* Judul */}
|
||||||
<TextInput
|
<TextInput
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.programKeamanan.nama}
|
label="Judul Pencegahan Kriminalitas"
|
||||||
onChange={(val) => {
|
placeholder="Masukkan judul Pencegahan Kriminalitas"
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.programKeamanan.nama = val.target.value;
|
value={kriminalitasState.create.form.judul}
|
||||||
|
onChange={(e) => {
|
||||||
|
kriminalitasState.create.form.judul = e.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Program Keamanan</Text>}
|
required
|
||||||
placeholder='Masukkan judul Program Keamanan'
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Deskripsi Singkat */}
|
||||||
<TextInput
|
<TextInput
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.programKeamanan.slug}
|
label="Deskripsi Singkat"
|
||||||
onChange={(val) => {
|
placeholder="Masukkan deskripsi singkat"
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.programKeamanan.slug = val.target.value;
|
value={kriminalitasState.create.form.deskripsiSingkat}
|
||||||
|
onChange={(e) => {
|
||||||
|
kriminalitasState.create.form.deskripsiSingkat = e.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Slug Program Keamanan</Text>}
|
required
|
||||||
placeholder='Masukkan slug Program Keamanan'
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Deskripsi Panjang */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Program Keamanan</Text>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
|
Deskripsi
|
||||||
|
</Text>
|
||||||
<CreateEditor
|
<CreateEditor
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.programKeamanan.deskripsi}
|
value={kriminalitasState.create.form.deskripsi}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.programKeamanan.deskripsi = val;
|
kriminalitasState.create.form.deskripsi = val;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Link YouTube */}
|
||||||
<TextInput
|
<TextInput
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.tipsKeamanan.judul}
|
label="Link Video YouTube"
|
||||||
onChange={(val) => {
|
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.tipsKeamanan.judul = val.target.value;
|
value={link}
|
||||||
}}
|
onChange={(e) => setLink(e.currentTarget.value)}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Tips Keamanan</Text>}
|
required
|
||||||
placeholder='Masukkan judul Tips Keamanan'
|
|
||||||
/>
|
/>
|
||||||
<TextInput
|
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.tipsKeamanan.slug}
|
{/* Preview Video */}
|
||||||
onChange={(val) => {
|
{embedLink && (
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.tipsKeamanan.slug = val.target.value;
|
<Box mt="sm">
|
||||||
}}
|
<iframe
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Slug Tips Keamanan</Text>}
|
style={{
|
||||||
placeholder='Masukkan slug Tips Keamanan'
|
borderRadius: 10,
|
||||||
/>
|
width: '100%',
|
||||||
<Box>
|
height: 400,
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Tips Keamanan</Text>
|
border: '1px solid #ddd',
|
||||||
<CreateEditor
|
}}
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.tipsKeamanan.konten}
|
src={embedLink}
|
||||||
onChange={(val) => {
|
title="Preview Video"
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.tipsKeamanan.konten = val;
|
allowFullScreen
|
||||||
|
></iframe>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Button Submit */}
|
||||||
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</Box>
|
Simpan
|
||||||
<TextInput
|
</Button>
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.judul}
|
|
||||||
onChange={(val) => {
|
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.judul = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Judul Video Keamanan</Text>}
|
|
||||||
placeholder='Masukkan judul Video Keamanan'
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.slug}
|
|
||||||
onChange={(val) => {
|
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.slug = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Slug Video Keamanan</Text>}
|
|
||||||
placeholder='Masukkan slug Video Keamanan'
|
|
||||||
/>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Tips Keamanan</Text>
|
|
||||||
<CreateEditor
|
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.deskripsi}
|
|
||||||
onChange={(val) => {
|
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.deskripsi = val;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<TextInput
|
|
||||||
value={kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.videoUrl}
|
|
||||||
onChange={(val) => {
|
|
||||||
kriminalitasState.create.form.pencegahanKriminalitas.videoKeamanan.videoUrl = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Video URL</Text>}
|
|
||||||
placeholder='Masukkan video URL'
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -62,14 +62,6 @@ function ListPencegahanKriminalitas({ search }: { search: string }) {
|
|||||||
load(page, 10, search);
|
load(page, 10, search);
|
||||||
}, [page, search]);
|
}, [page, search]);
|
||||||
|
|
||||||
const filteredData = (data || []).filter(item => {
|
|
||||||
const keyword = search.toLowerCase();
|
|
||||||
return (
|
|
||||||
item.programKeamanan.nama.toLowerCase().includes(keyword) ||
|
|
||||||
item.programKeamanan.slug.toLowerCase().includes(keyword) ||
|
|
||||||
item.programKeamanan.deskripsi?.toLowerCase().includes(keyword)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
@@ -103,35 +95,43 @@ function ListPencegahanKriminalitas({ search }: { search: string }) {
|
|||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Nama Pencegahan</TableTh>
|
<TableTh>Nama Pencegahan</TableTh>
|
||||||
<TableTh>Slug</TableTh>
|
|
||||||
<TableTh>Deskripsi</TableTh>
|
<TableTh>Deskripsi</TableTh>
|
||||||
|
<TableTh>Deskripsi Singkat</TableTh>
|
||||||
<TableTh>Aksi</TableTh>
|
<TableTh>Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.length > 0 ? (
|
{data.length > 0 ? (
|
||||||
filteredData.map((item) => (
|
data.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
{item.programKeamanan.nama}
|
{item.judul}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm" c="dimmed" truncate lineClamp={1}>
|
<Box w={200}>
|
||||||
{item.programKeamanan.slug}
|
<Text
|
||||||
</Text>
|
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||||
|
fz="sm"
|
||||||
|
c="dimmed"
|
||||||
|
truncate
|
||||||
|
lineClamp={1}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text
|
<Box w={200}>
|
||||||
fz="sm"
|
<Text
|
||||||
c="dimmed"
|
fz="sm"
|
||||||
truncate
|
c="dimmed"
|
||||||
lineClamp={1}
|
truncate
|
||||||
dangerouslySetInnerHTML={{
|
lineClamp={1}
|
||||||
__html: item.programKeamanan.deskripsi || ''
|
dangerouslySetInnerHTML={{
|
||||||
}}
|
__html: item.deskripsiSingkat || ''
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,81 +1,35 @@
|
|||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
import { Context } from "elysia";
|
import { Context } from "elysia";
|
||||||
|
|
||||||
type ProgramKeamananInput = {
|
type FormCreate = Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
nama: string;
|
select: {
|
||||||
deskripsi: string;
|
judul: true;
|
||||||
slug: string;
|
deskripsi: true;
|
||||||
};
|
deskripsiSingkat: true;
|
||||||
|
linkVideo: true;
|
||||||
type TipsKeamanan = {
|
};
|
||||||
judul: string;
|
}>;
|
||||||
konten: string;
|
|
||||||
slug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type VideoKeamanan = {
|
|
||||||
judul: string;
|
|
||||||
deskripsi: string;
|
|
||||||
videoUrl: string;
|
|
||||||
slug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type createdPencegahanKriminalitas = {
|
|
||||||
programKeamanan: ProgramKeamananInput;
|
|
||||||
tipsKeamanan: TipsKeamanan;
|
|
||||||
videoKeamanan: VideoKeamanan;
|
|
||||||
};
|
|
||||||
|
|
||||||
const pencegahanKriminalitasCreate = async (context: Context) => {
|
const pencegahanKriminalitasCreate = async (context: Context) => {
|
||||||
const { programKeamanan, tipsKeamanan, videoKeamanan } =
|
const body = context.body as FormCreate;
|
||||||
(await context.body) as createdPencegahanKriminalitas;
|
|
||||||
|
|
||||||
const createdProgram = await prisma.programKeamanan.create({
|
await prisma.pencegahanKriminalitas.create({
|
||||||
data: {
|
data: {
|
||||||
nama: programKeamanan.nama,
|
judul: body.judul,
|
||||||
deskripsi: programKeamanan.deskripsi,
|
deskripsi: body.deskripsi,
|
||||||
slug: programKeamanan.slug,
|
deskripsiSingkat: body.deskripsiSingkat,
|
||||||
|
linkVideo: body.linkVideo,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const createdTips = await prisma.tipsKeamanan.create({
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success create pencegahan kriminalitas",
|
||||||
data: {
|
data: {
|
||||||
judul: tipsKeamanan.judul,
|
...body,
|
||||||
konten: tipsKeamanan.konten,
|
|
||||||
slug: tipsKeamanan.slug,
|
|
||||||
},
|
},
|
||||||
});
|
status: 200,
|
||||||
|
};
|
||||||
const createdVideo = await prisma.videoKeamanan.create({
|
|
||||||
data: {
|
|
||||||
judul: videoKeamanan.judul,
|
|
||||||
deskripsi: videoKeamanan.deskripsi,
|
|
||||||
videoUrl: videoKeamanan.videoUrl,
|
|
||||||
slug: videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const createdPencegahanKriminalitas =
|
|
||||||
await prisma.pencegahanKriminalitas.create({
|
|
||||||
data: {
|
|
||||||
programKeamananId: createdProgram.id,
|
|
||||||
tipsKeamananId: createdTips.id,
|
|
||||||
videoKeamananId: createdVideo.id,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
programKeamanan: true,
|
|
||||||
tipsKeamanan: true,
|
|
||||||
videoKeamanan: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
success: true,
|
|
||||||
message: "Success create program keamanan",
|
|
||||||
data: createdPencegahanKriminalitas,
|
|
||||||
},
|
|
||||||
{ status: 200 }
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
export default pencegahanKriminalitasCreate;
|
export default pencegahanKriminalitasCreate;
|
||||||
|
|||||||
@@ -13,11 +13,6 @@ const pencegahanKriminalitasDelete = async (context: Context) => {
|
|||||||
|
|
||||||
const pencegahanKriminalitas = await prisma.pencegahanKriminalitas.findUnique({
|
const pencegahanKriminalitas = await prisma.pencegahanKriminalitas.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: {
|
|
||||||
programKeamanan: true,
|
|
||||||
tipsKeamanan: true,
|
|
||||||
videoKeamanan: true,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!pencegahanKriminalitas) {
|
if (!pencegahanKriminalitas) {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
export default async function pencegahanKriminalitasFindFirst(context: Context) {
|
||||||
|
// Ambil parameter query
|
||||||
|
const search = (context.query.search as string) || "";
|
||||||
|
|
||||||
|
// Buat where clause
|
||||||
|
const where: Prisma.PencegahanKriminalitasWhereInput = { isActive: true };
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
where.OR = [
|
||||||
|
{
|
||||||
|
judul: {
|
||||||
|
contains: search,
|
||||||
|
mode: "insensitive" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deskripsi: {
|
||||||
|
contains: search,
|
||||||
|
mode: "insensitive" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deskripsiSingkat: {
|
||||||
|
contains: search,
|
||||||
|
mode: "insensitive" as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await prisma.pencegahanKriminalitas.findFirst({
|
||||||
|
where,
|
||||||
|
orderBy: { createdAt: "desc" }, // ambil yang terbaru
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Data pencegahan kriminalitas tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch first pencegahan kriminalitas",
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Find first error:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Failed fetch first pencegahan kriminalitas",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,18 +16,21 @@ export default async function pencegahanKriminalitasFindMany(context: Context) {
|
|||||||
if (search) {
|
if (search) {
|
||||||
where.OR = [
|
where.OR = [
|
||||||
{
|
{
|
||||||
programKeamanan: {
|
judul: {
|
||||||
nama: { contains: search, mode: "insensitive" as const },
|
contains: search,
|
||||||
|
mode: "insensitive" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tipsKeamanan: {
|
deskripsi: {
|
||||||
judul: { contains: search, mode: "insensitive" as const },
|
contains: search,
|
||||||
|
mode: "insensitive" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
videoKeamanan: {
|
deskripsiSingkat: {
|
||||||
judul: { contains: search, mode: "insensitive" as const },
|
contains: search,
|
||||||
|
mode: "insensitive" as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -36,11 +39,6 @@ export default async function pencegahanKriminalitasFindMany(context: Context) {
|
|||||||
const [data, total] = await Promise.all([
|
const [data, total] = await Promise.all([
|
||||||
prisma.pencegahanKriminalitas.findMany({
|
prisma.pencegahanKriminalitas.findMany({
|
||||||
where,
|
where,
|
||||||
include: {
|
|
||||||
programKeamanan: true,
|
|
||||||
tipsKeamanan: true,
|
|
||||||
videoKeamanan: true,
|
|
||||||
},
|
|
||||||
skip,
|
skip,
|
||||||
take: limit,
|
take: limit,
|
||||||
orderBy: { createdAt: "desc" },
|
orderBy: { createdAt: "desc" },
|
||||||
|
|||||||
@@ -22,11 +22,6 @@ export default async function pencegahanKriminalitasFindUnique(request: Request)
|
|||||||
|
|
||||||
const data = await prisma.pencegahanKriminalitas.findUnique({
|
const data = await prisma.pencegahanKriminalitas.findUnique({
|
||||||
where: {id},
|
where: {id},
|
||||||
include: {
|
|
||||||
programKeamanan: true,
|
|
||||||
tipsKeamanan: true,
|
|
||||||
videoKeamanan: true,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import pencegahanKriminalitasFindUnique from "./findUnique";
|
|||||||
import pencegahanKriminalitasFindMany from "./findMany";
|
import pencegahanKriminalitasFindMany from "./findMany";
|
||||||
import pencegahanKriminalitasDelete from "./del";
|
import pencegahanKriminalitasDelete from "./del";
|
||||||
import pencegahanKriminalitasUpdate from "./updt";
|
import pencegahanKriminalitasUpdate from "./updt";
|
||||||
|
import pencegahanKriminalitasFindFirst from "./findFirst";
|
||||||
|
|
||||||
const PencegahanKriminalitas = new Elysia({
|
const PencegahanKriminalitas = new Elysia({
|
||||||
prefix: "pencegahankriminalitas",
|
prefix: "pencegahankriminalitas",
|
||||||
@@ -11,22 +12,10 @@ const PencegahanKriminalitas = new Elysia({
|
|||||||
})
|
})
|
||||||
.post("/create", pencegahanKriminalitasCreate, {
|
.post("/create", pencegahanKriminalitasCreate, {
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
programKeamanan: t.Object({
|
judul: t.String(),
|
||||||
nama: t.String(),
|
deskripsi: t.String(),
|
||||||
deskripsi: t.String(),
|
deskripsiSingkat: t.String(),
|
||||||
slug: t.String(),
|
linkVideo: t.String(),
|
||||||
}),
|
|
||||||
tipsKeamanan: t.Object({
|
|
||||||
judul: t.String(),
|
|
||||||
konten: t.String(),
|
|
||||||
slug: t.String(),
|
|
||||||
}),
|
|
||||||
videoKeamanan: t.Object({
|
|
||||||
judul: t.String(),
|
|
||||||
deskripsi: t.String(),
|
|
||||||
videoUrl: t.String(),
|
|
||||||
slug: t.String(),
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.get("/find-many", pencegahanKriminalitasFindMany)
|
.get("/find-many", pencegahanKriminalitasFindMany)
|
||||||
@@ -36,31 +25,20 @@ const PencegahanKriminalitas = new Elysia({
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
|
.get("/find-first", pencegahanKriminalitasFindFirst)
|
||||||
.delete("/del/:id", pencegahanKriminalitasDelete)
|
.delete("/del/:id", pencegahanKriminalitasDelete)
|
||||||
.put(
|
.put(
|
||||||
":/id",
|
"/:id",
|
||||||
async (context) => {
|
async (context) => {
|
||||||
const response = await pencegahanKriminalitasUpdate(context);
|
const response = await pencegahanKriminalitasUpdate(context);
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
programKeamanan: t.Object({
|
judul: t.String(),
|
||||||
nama: t.String(),
|
deskripsi: t.String(),
|
||||||
deskripsi: t.String(),
|
deskripsiSingkat: t.String(),
|
||||||
slug: t.String(),
|
linkVideo: t.String(),
|
||||||
}),
|
|
||||||
tipsKeamanan: t.Object({
|
|
||||||
judul: t.String(),
|
|
||||||
konten: t.String(),
|
|
||||||
slug: t.String(),
|
|
||||||
}),
|
|
||||||
videoKeamanan: t.Object({
|
|
||||||
judul: t.String(),
|
|
||||||
deskripsi: t.String(),
|
|
||||||
videoUrl: t.String(),
|
|
||||||
slug: t.String(),
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,110 +3,91 @@ import { Prisma } from "@prisma/client";
|
|||||||
import { Context } from "elysia";
|
import { Context } from "elysia";
|
||||||
|
|
||||||
type FormUpdate = Prisma.PencegahanKriminalitasGetPayload<{
|
type FormUpdate = Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
id: true;
|
id: true;
|
||||||
programKeamanan: true;
|
judul: true;
|
||||||
tipsKeamanan: true;
|
deskripsi: true;
|
||||||
videoKeamanan: true;
|
deskripsiSingkat: true;
|
||||||
}
|
linkVideo: true;
|
||||||
}>
|
};
|
||||||
|
}>;
|
||||||
export default async function pencegahanKriminalitasUpdate(context: Context) {
|
export default async function pencegahanKriminalitasUpdate(context: Context) {
|
||||||
try {
|
try {
|
||||||
const id = context.params?.id as string;
|
const id = context.params?.id as string;
|
||||||
const body = (await context.body) as Omit<FormUpdate, "id">;
|
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||||
|
|
||||||
const {programKeamanan, tipsKeamanan, videoKeamanan} = body;
|
const { judul, deskripsi, deskripsiSingkat, linkVideo } = body;
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
success: false,
|
JSON.stringify({
|
||||||
message: "ID tidak diberikan",
|
success: false,
|
||||||
}), {
|
message: "ID tidak diberikan",
|
||||||
status: 400,
|
}),
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
status: 400,
|
||||||
},
|
headers: {
|
||||||
});
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
);
|
||||||
const existing = await prisma.pencegahanKriminalitas.findUnique({
|
|
||||||
where: { id },
|
|
||||||
include: {
|
|
||||||
programKeamanan: true,
|
|
||||||
tipsKeamanan: true,
|
|
||||||
videoKeamanan: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existing) {
|
|
||||||
return new Response(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
message: "Pencegahan kriminalitas tidak ditemukan",
|
|
||||||
}), {
|
|
||||||
status: 404,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.programKeamanan.update({
|
|
||||||
where: {id: existing.programKeamananId},
|
|
||||||
data: {
|
|
||||||
nama: programKeamanan.nama,
|
|
||||||
deskripsi: programKeamanan.deskripsi,
|
|
||||||
slug: programKeamanan.slug,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await prisma.tipsKeamanan.update({
|
|
||||||
where: {id: existing.tipsKeamananId},
|
|
||||||
data: {
|
|
||||||
judul: tipsKeamanan.judul,
|
|
||||||
konten: tipsKeamanan.konten,
|
|
||||||
slug: tipsKeamanan.slug,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await prisma.videoKeamanan.update({
|
|
||||||
where: {id: existing.videoKeamananId},
|
|
||||||
data: {
|
|
||||||
judul: videoKeamanan.judul,
|
|
||||||
deskripsi: videoKeamanan.deskripsi,
|
|
||||||
videoUrl: videoKeamanan.videoUrl,
|
|
||||||
slug: videoKeamanan.slug,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const updated = await prisma.pencegahanKriminalitas.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
programKeamananId: programKeamanan.id,
|
|
||||||
tipsKeamananId: tipsKeamanan.id,
|
|
||||||
videoKeamananId: videoKeamanan.id,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: "Success update pencegahan kriminalitas",
|
|
||||||
data: updated,
|
|
||||||
}), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Update error:", error);
|
|
||||||
return new Response(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
message: "Failed update pencegahan kriminalitas",
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existing = await prisma.pencegahanKriminalitas.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Pencegahan kriminalitas tidak ditemukan",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 404,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.pencegahanKriminalitas.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
judul,
|
||||||
|
deskripsi,
|
||||||
|
deskripsiSingkat,
|
||||||
|
linkVideo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Success update pencegahan kriminalitas",
|
||||||
|
data: updated,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Update error:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
message: "Failed update pencegahan kriminalitas",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Page
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -1,141 +1,138 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
'use client'
|
||||||
|
import pencegahanKriminalitasState from '@/app/admin/(dashboard)/_state/keamanan/pencegahan-kriminalitas';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Flex, Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
import { Box, Button, Center, Flex, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowRight } from '@tabler/icons-react';
|
import { IconArrowRight } from '@tabler/icons-react';
|
||||||
|
import { useTransitionRouter } from 'next-view-transitions';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
|
const kriminalitasState = useProxy(pencegahanKriminalitasState);
|
||||||
|
const findFirst = useProxy(pencegahanKriminalitasState.findFirst);
|
||||||
|
const router = useTransitionRouter();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
page,
|
||||||
|
load,
|
||||||
|
} = kriminalitasState.findMany;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!findFirst.data && !findFirst.loading) {
|
||||||
|
kriminalitasState.findFirst.load();
|
||||||
|
}
|
||||||
|
}, [findFirst.data, findFirst.loading]);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
const LIMIT = 3;
|
||||||
|
load(page, LIMIT);
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
if (loading || !data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton height={600} radius="md" />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
|
Pencegahan Kriminalitas
|
||||||
|
</Text>
|
||||||
|
<Text c={colors['blue-button']} fz={{ base: 'h4', md: 'h3' }}>
|
||||||
|
Keamanan Komunitas & Pencegahan Kriminal
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
px={{ base: 20, md: 100 }}
|
px={{ base: 20, md: 100 }}
|
||||||
cols={{ base: 1, md: 2 }}
|
cols={{ base: 1, md: 2 }}
|
||||||
spacing="xl"
|
spacing="xl"
|
||||||
>
|
>
|
||||||
<Box pt={{ base: 0, md: 35 }}>
|
<Paper p="xl" radius="xl" shadow="lg" >
|
||||||
<Text c={colors['blue-button']} fz={{ base: 'h4', md: 'h3' }}>
|
<Text fz={{ base: 'h3', md: 'h2' }} c={colors['blue-button']} fw="bold">
|
||||||
Community Safety & Crime Prevention
|
Program Keamanan Berjalan
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
<Stack pt={30} gap="lg">
|
||||||
Emergency Contacts
|
{data.length > 0 ? (
|
||||||
</Text>
|
data.map((item) => (
|
||||||
<Group>
|
<Paper key={item.id} p="md" bg={colors['blue-button']} radius="md" shadow="sm" onClick={() => router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`)}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Flex align="center" justify="space-between">
|
||||||
|
<Text fz="h3" c={colors['white-1']}>
|
||||||
|
{item.judul}
|
||||||
|
</Text>
|
||||||
|
<IconArrowRight size={28} color={colors['white-1']} />
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text color="dimmed">
|
||||||
|
Tidak ada data pencegahan kriminalitas yang cocok
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
rightSection={<IconArrowRight />}
|
|
||||||
mt={20}
|
mt={20}
|
||||||
|
fullWidth
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size="md"
|
size="md"
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
c={colors['white-1']}
|
rightSection={<IconArrowRight size={20} color={colors['white-1']} />}
|
||||||
|
onClick={() => router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/program-lainnya`)}
|
||||||
>
|
>
|
||||||
View Details
|
Jelajahi Program Lainnya
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
|
||||||
</Box>
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }} spacing="lg">
|
|
||||||
<Paper p="lg" radius="xl" shadow="md">
|
|
||||||
<Text c={colors['blue-button']} fw="bold" fz={{ base: 'h4', md: 'h3' }}>
|
|
||||||
How to Keep Your Neighborhood Safe
|
|
||||||
</Text>
|
|
||||||
<Text fz={{ base: 'h5', md: 'h4' }} c={colors['blue-button']} mt="sm">
|
|
||||||
Practical safety habits everyone can apply daily to reduce risks.
|
|
||||||
</Text>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
rightSection={<IconArrowRight />}
|
|
||||||
mt={20}
|
|
||||||
radius="xl"
|
|
||||||
size="md"
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
c={colors['white-1']}
|
|
||||||
>
|
|
||||||
Learn More
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Paper>
|
|
||||||
<Paper p="lg" radius="xl" shadow="md">
|
|
||||||
<Text c={colors['blue-button']} fw="bold" fz={{ base: 'h4', md: 'h3' }}>
|
|
||||||
Recognizing Criminal Activities
|
|
||||||
</Text>
|
|
||||||
<Text fz={{ base: 'h5', md: 'h4' }} c={colors['blue-button']} mt="sm">
|
|
||||||
Key warning signs and behavior patterns you should stay aware of.
|
|
||||||
</Text>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
rightSection={<IconArrowRight />}
|
|
||||||
mt={20}
|
|
||||||
radius="xl"
|
|
||||||
size="md"
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
c={colors['white-1']}
|
|
||||||
>
|
|
||||||
Learn More
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Paper>
|
|
||||||
</SimpleGrid>
|
|
||||||
<Paper p="xl" radius="xl" shadow="lg">
|
|
||||||
<Text fz={{ base: 'h3', md: 'h2' }} c={colors['blue-button']} fw="bold">
|
|
||||||
Ongoing Security Programs
|
|
||||||
</Text>
|
|
||||||
<Stack pt={30} gap="lg">
|
|
||||||
{['Night Patrol', 'Neighborhood Watch', 'Emergency Preparedness'].map((program, i) => (
|
|
||||||
<Paper key={i} p="md" bg={colors['blue-button']} radius="md" shadow="sm">
|
|
||||||
<Flex align="center" justify="space-between">
|
|
||||||
<Text fz="h3" c={colors['white-1']}>
|
|
||||||
{program}
|
|
||||||
</Text>
|
|
||||||
<IconArrowRight size={28} color={colors['white-1']} />
|
|
||||||
</Flex>
|
|
||||||
</Paper>
|
|
||||||
))}
|
|
||||||
<Box pt={25}>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
radius="xl"
|
|
||||||
size="md"
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
rightSection={<IconArrowRight size={20} color={colors['white-1']} />}
|
|
||||||
>
|
|
||||||
Explore More Programs
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box>
|
<Box>
|
||||||
<Paper p="xl" radius="xl" shadow="lg">
|
{findFirst.loading ? (
|
||||||
<Box style={{ maxWidth: 560, width: '100%', aspectRatio: '16/9' }}>
|
<Center><Skeleton h={400} /></Center>
|
||||||
<iframe
|
) : findFirst.data ? (
|
||||||
width="100%"
|
<Paper p="xl" radius="xl" shadow="lg">
|
||||||
height="100%"
|
{findFirst.data?.linkVideo ? (
|
||||||
src="https://www.youtube.com/embed/p5OkdBgasWA?si=6lFKXeE9LN5zcJQq"
|
<Box
|
||||||
title="Community Safety Patrol"
|
component="iframe"
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
src={convertToEmbedUrl(findFirst.data.linkVideo)}
|
||||||
/>
|
width="100%"
|
||||||
</Box>
|
height={300}
|
||||||
<Text py={10} fz={{ base: 'h3', md: 'h2' }} fw="bold" c={colors['blue-button']}>
|
allowFullScreen
|
||||||
Darmasaba Night Patrol
|
style={{ borderRadius: 8 }}
|
||||||
</Text>
|
/>
|
||||||
<Text fz="h4" c={colors['blue-button']}>
|
) : (
|
||||||
A closer look at how the community works together to maintain safety.
|
<Text fz="sm" c="dimmed">Tidak ada video</Text>
|
||||||
</Text>
|
)}
|
||||||
<Box pt={10}>
|
<Text py={10} fz={{ base: 'h3', md: 'h2' }} fw="bold" c={colors['blue-button']}>
|
||||||
<Button
|
{findFirst.data?.judul}
|
||||||
radius="xl"
|
</Text>
|
||||||
size="md"
|
<Text fz="h4" c={colors['blue-button']}>
|
||||||
bg={colors['blue-button']}
|
{findFirst.data?.deskripsiSingkat}
|
||||||
rightSection={<IconArrowRight size={20} color={colors['white-1']} />}
|
</Text>
|
||||||
>
|
</Paper>
|
||||||
Watch More Videos
|
) : null}
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
</Box>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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 Page;
|
export default Page;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Page
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
Reference in New Issue
Block a user