fix-admin-menu-desa-potensi
This commit is contained in:
@@ -633,25 +633,25 @@ model KategoriBerita {
|
|||||||
// ========================================= POTENSI DESA ========================================= //
|
// ========================================= POTENSI DESA ========================================= //
|
||||||
model PotensiDesa {
|
model PotensiDesa {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String @unique @db.VarChar(255)
|
||||||
deskripsi String
|
deskripsi String @db.Text
|
||||||
kategori KategoriPotensi? @relation(fields: [kategoriId], references: [id])
|
kategori KategoriPotensi? @relation(fields: [kategoriId], references: [id])
|
||||||
kategoriId String?
|
kategoriId String @db.VarChar(36)
|
||||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
imageId String?
|
imageId String?
|
||||||
content String @db.Text
|
content String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
model KategoriPotensi {
|
model KategoriPotensi {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
nama String
|
nama String @unique @db.VarChar(100)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
PotensiDesa PotensiDesa[]
|
PotensiDesa PotensiDesa[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
|
|||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
export default function DetailPotensi() {
|
export default function DetailPotensi() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -77,7 +78,17 @@ export default function DetailPotensi() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Deskripsi</Text>
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}></Text>
|
<Text
|
||||||
|
fz="md"
|
||||||
|
c="dimmed"
|
||||||
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(data.deskripsi || '-', {
|
||||||
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ul', 'ol', 'li'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
></Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
@@ -102,7 +113,12 @@ export default function DetailPotensi() {
|
|||||||
<Text
|
<Text
|
||||||
fz="md"
|
fz="md"
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
dangerouslySetInnerHTML={{ __html: data.content || '-' }}
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(data.content || '-', {
|
||||||
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ul', 'ol', 'li'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
})
|
||||||
|
}}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { useProxy } from 'valtio/utils';
|
|||||||
import HeaderSearch from '../../../_com/header';
|
import HeaderSearch from '../../../_com/header';
|
||||||
import potensiDesaState from '../../../_state/desa/potensi';
|
import potensiDesaState from '../../../_state/desa/potensi';
|
||||||
import { useDebouncedValue } from '@mantine/hooks';
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
function Potensi() {
|
function Potensi() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
@@ -137,7 +138,12 @@ function ListPotensi({ search }: { search: string }) {
|
|||||||
fz="sm"
|
fz="sm"
|
||||||
lh={1.5}
|
lh={1.5}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(item.deskripsi, {
|
||||||
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ul', 'ol', 'li'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
})
|
||||||
|
}}
|
||||||
style={{ wordBreak: 'break-word' }}
|
style={{ wordBreak: 'break-word' }}
|
||||||
/>
|
/>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
@@ -199,7 +205,12 @@ function ListPotensi({ search }: { search: string }) {
|
|||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="sm"
|
||||||
lh={1.5}
|
lh={1.5}
|
||||||
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(item.deskripsi, {
|
||||||
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ul', 'ol', 'li'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
})
|
||||||
|
}}
|
||||||
style={{ wordBreak: 'break-word' }}
|
style={{ wordBreak: 'break-word' }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -21,8 +21,13 @@ export default async function findUnique(
|
|||||||
}, { status: 400 });
|
}, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.potensiDesa.findUnique({
|
// ✅ Filter by isActive and deletedAt
|
||||||
where: { id },
|
const data = await prisma.potensiDesa.findFirst({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
isActive: true,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
include: {
|
include: {
|
||||||
image: true,
|
image: true,
|
||||||
kategori: true
|
kategori: true
|
||||||
|
|||||||
@@ -2,15 +2,50 @@ import prisma from "@/lib/prisma";
|
|||||||
import { Context } from "elysia";
|
import { Context } from "elysia";
|
||||||
|
|
||||||
export default async function kategoriPotensiDelete(context: Context) {
|
export default async function kategoriPotensiDelete(context: Context) {
|
||||||
const id = context.params.id as string;
|
try {
|
||||||
|
const id = context.params?.id as string;
|
||||||
|
|
||||||
await prisma.kategoriPotensi.delete({
|
if (!id) {
|
||||||
where: { id },
|
return Response.json({
|
||||||
});
|
success: false,
|
||||||
|
message: "ID tidak boleh kosong",
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
// ✅ Cek apakah kategori masih digunakan oleh potensi desa
|
||||||
status: 200,
|
const existingPotensi = await prisma.potensiDesa.findFirst({
|
||||||
success: true,
|
where: {
|
||||||
message: "Sukses Menghapus kategori potensi",
|
kategoriId: id,
|
||||||
};
|
isActive: true,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingPotensi) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Kategori masih digunakan oleh potensi desa. Tidak dapat dihapus.",
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft delete
|
||||||
|
await prisma.kategoriPotensi.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
deletedAt: new Date(),
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Kategori potensi berhasil dihapus",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Delete kategori error:", error);
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Gagal menghapus kategori: " + (error instanceof Error ? error.message : 'Unknown error'),
|
||||||
|
}, { status: 500 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user