Membuat database menu desa: Berita & Pengummuman
This commit is contained in:
20
package.json
20
package.json
@@ -20,15 +20,25 @@
|
|||||||
"@elysiajs/swagger": "^1.2.0",
|
"@elysiajs/swagger": "^1.2.0",
|
||||||
"@mantine/carousel": "^7.16.2",
|
"@mantine/carousel": "^7.16.2",
|
||||||
"@mantine/charts": "^7.17.1",
|
"@mantine/charts": "^7.17.1",
|
||||||
"@mantine/core": "^7.16.2",
|
"@mantine/core": "^7.17.4",
|
||||||
"@mantine/dates": "^7.17.4",
|
"@mantine/dates": "^7.17.4",
|
||||||
"@mantine/dropzone": "^7.17.0",
|
"@mantine/dropzone": "^7.17.0",
|
||||||
"@mantine/hooks": "^7.16.2",
|
"@mantine/hooks": "^7.17.4",
|
||||||
|
"@mantine/tiptap": "^7.17.4",
|
||||||
"@paljs/types": "^8.1.0",
|
"@paljs/types": "^8.1.0",
|
||||||
"@prisma/client": "^6.3.1",
|
"@prisma/client": "^6.3.1",
|
||||||
"@tabler/icons-react": "^3.30.0",
|
"@tabler/icons-react": "^3.30.0",
|
||||||
|
"@tiptap/extension-highlight": "^2.11.7",
|
||||||
|
"@tiptap/extension-link": "^2.11.7",
|
||||||
|
"@tiptap/extension-subscript": "^2.11.7",
|
||||||
|
"@tiptap/extension-superscript": "^2.11.7",
|
||||||
|
"@tiptap/extension-text-align": "^2.11.7",
|
||||||
|
"@tiptap/extension-underline": "^2.11.7",
|
||||||
|
"@tiptap/pm": "^2.11.7",
|
||||||
|
"@tiptap/react": "^2.11.7",
|
||||||
|
"@tiptap/starter-kit": "^2.11.7",
|
||||||
"@types/bun": "^1.2.2",
|
"@types/bun": "^1.2.2",
|
||||||
"@types/lodash": "^4.17.15",
|
"@types/lodash": "^4.17.16",
|
||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"bun": "^1.2.2",
|
"bun": "^1.2.2",
|
||||||
@@ -50,10 +60,12 @@
|
|||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-simple-toasts": "^6.1.0",
|
"react-simple-toasts": "^6.1.0",
|
||||||
|
"react-toastify": "^11.0.5",
|
||||||
"readdirp": "^4.1.1",
|
"readdirp": "^4.1.1",
|
||||||
"recharts": "2",
|
"recharts": "2",
|
||||||
"swr": "^2.3.2",
|
"swr": "^2.3.2",
|
||||||
"valtio": "^2.1.3"
|
"valtio": "^2.1.3",
|
||||||
|
"zod": "^3.24.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
|
|||||||
8
prisma/data/category-pengumuman.json
Normal file
8
prisma/data/category-pengumuman.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{ "name": "Sosial & Kesehatan" },
|
||||||
|
{ "name": "Ekonomi & UMKM" },
|
||||||
|
{ "name": "Pendidikan & Kepemudaan" },
|
||||||
|
{ "name": "Lingkungan & Bencana" },
|
||||||
|
{ "name": "Adat & Budaya" },
|
||||||
|
{ "name": "Digitalisasi Desa" }
|
||||||
|
]
|
||||||
9
prisma/data/katagory-berita.json
Normal file
9
prisma/data/katagory-berita.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[
|
||||||
|
{ "name": "Semua" },
|
||||||
|
{ "name": "Pemerintahan" },
|
||||||
|
{ "name": "Pembangunan" },
|
||||||
|
{ "name": "Ekonomi" },
|
||||||
|
{ "name": "Sosial" },
|
||||||
|
{ "name": "Budaya" },
|
||||||
|
{ "name": "Teknologi" }
|
||||||
|
]
|
||||||
@@ -18,7 +18,80 @@ model Potensi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model LandingPage_Layanan {
|
model LandingPage_Layanan {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
deksripsi String
|
deksripsi String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================= APPMENU ========================================= //
|
||||||
|
model AppMenu {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
link String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
AppMenuChild AppMenuChild[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= APPMENUCHILD ========================================= //
|
||||||
|
model AppMenuChild {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
link String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
AppMenu AppMenu? @relation(fields: [appMenuId], references: [id])
|
||||||
|
appMenuId String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= BERITA ========================================= //
|
||||||
|
model Berita {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
judul String
|
||||||
|
deskripsi String
|
||||||
|
image String
|
||||||
|
content String @db.Text
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
KatagoryBerita KatagoryBerita? @relation(fields: [katagoryBeritaId], references: [id])
|
||||||
|
katagoryBeritaId String?
|
||||||
|
}
|
||||||
|
|
||||||
|
model KatagoryBerita {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
beritas Berita[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PENGUMUMAN ========================================= //
|
||||||
|
model Pengumuman {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
judul String
|
||||||
|
deskripsi String
|
||||||
|
content String @db.Text
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
CategoryPengumuman CategoryPengumuman? @relation(fields: [categoryPengumumanId], references: [id])
|
||||||
|
categoryPengumumanId String?
|
||||||
|
}
|
||||||
|
|
||||||
|
model CategoryPengumuman {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
pengumumans Pengumuman[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import layanan from './data/list-layanan.json'
|
import layanan from './data/list-layanan.json'
|
||||||
import potensi from './data/list-potensi.json'
|
import potensi from './data/list-potensi.json'
|
||||||
|
import katagoryBerita from './data/katagory-berita.json'
|
||||||
|
import categoryPengumuman from './data/category-pengumuman.json'
|
||||||
import prisma from '@/lib/prisma';
|
import prisma from '@/lib/prisma';
|
||||||
; (async () => {
|
(async () => {
|
||||||
for (const l of layanan) {
|
for (const l of layanan) {
|
||||||
await prisma.layanan.upsert({
|
await prisma.layanan.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -34,21 +36,37 @@ import prisma from '@/lib/prisma';
|
|||||||
|
|
||||||
console.log("potensi success ...")
|
console.log("potensi success ...")
|
||||||
|
|
||||||
// for (const lpl of ) {
|
for (const k of katagoryBerita) {
|
||||||
// await prisma.landingPage_Layanan.upsert({
|
await prisma.katagoryBerita.upsert({
|
||||||
// where: {
|
where: {
|
||||||
// id: lpl.id
|
name: k.name
|
||||||
// },
|
},
|
||||||
// update: {
|
update: {
|
||||||
// deksripsi: lpl.deksripsi
|
name: k.name
|
||||||
// },
|
},
|
||||||
// create: {
|
create: {
|
||||||
// deksripsi: lpl.deksripsi
|
name: k.name
|
||||||
// }
|
}
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
|
|
||||||
// console.log("landing page success ...")
|
console.log("katagory berita success ...")
|
||||||
|
|
||||||
|
for (const c of categoryPengumuman) {
|
||||||
|
await prisma.categoryPengumuman.upsert({
|
||||||
|
where: {
|
||||||
|
name: c.name
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: c.name
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: c.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("category pengumuman success ...")
|
||||||
})().then(() => prisma.$disconnect()).catch((e) => {
|
})().then(() => prisma.$disconnect()).catch((e) => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
prisma.$disconnect()
|
prisma.$disconnect()
|
||||||
|
|||||||
91
src/app/admin/(dashboard)/_state/berita.ts
Normal file
91
src/app/admin/(dashboard)/_state/berita.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { proxy } from "valtio";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const templateForm = z.object({
|
||||||
|
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||||
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
|
image: z.string().url().min(3, "Image minimal 3 karakter"),
|
||||||
|
content: z.string().min(3, "Content minimal 3 karakter"),
|
||||||
|
katagoryBeritaId: z.string().nonempty(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const category = proxy({
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| null
|
||||||
|
| Prisma.KatagoryBeritaGetPayload<{ omit: { isActive: true } }>[],
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
category.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
type BeritaForm = Prisma.BeritaGetPayload<{
|
||||||
|
select: {
|
||||||
|
judul: true;
|
||||||
|
deskripsi: true;
|
||||||
|
image: true;
|
||||||
|
content: true;
|
||||||
|
katagoryBeritaId: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const berita = proxy({
|
||||||
|
create: {
|
||||||
|
form: {} as BeritaForm,
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
berita.create.form.image =
|
||||||
|
"https://www.shutterstock.com/image-vector/lower-news-live-streaming-breaking-600nw-2535984111.jpg";
|
||||||
|
const cek = templateForm.safeParse(berita.create.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
berita.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.desa.berita["create"].post(
|
||||||
|
berita.create.form
|
||||||
|
);
|
||||||
|
if (res.status === 200) {
|
||||||
|
berita.findMany.load();
|
||||||
|
return toast.success("succes create");
|
||||||
|
}
|
||||||
|
|
||||||
|
return toast.error("failed create");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
berita.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.BeritaGetPayload<{ omit: { isActive: true } }>[]
|
||||||
|
| null,
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.berita["find-many"].get();
|
||||||
|
console.log(res)
|
||||||
|
if (res.status === 200) {
|
||||||
|
berita.findMany.data = (res.data?.data as any) ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const stateDashboardBerita = proxy({
|
||||||
|
category,
|
||||||
|
berita,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default stateDashboardBerita;
|
||||||
83
src/app/admin/(dashboard)/_state/pengumuman.ts
Normal file
83
src/app/admin/(dashboard)/_state/pengumuman.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import ApiFetch from "@/lib/api-fetch"
|
||||||
|
import { Prisma } from "@prisma/client"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
import { proxy } from "valtio"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
const templateFormPengumuman = z.object({
|
||||||
|
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||||
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
|
content: z.string().min(3, "Content minimal 3 karakter"),
|
||||||
|
categoryPengumumanId: z.string().nonempty(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const category = proxy ({
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| null
|
||||||
|
| Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[],
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.pengumuman.category["find-many"].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
category.findMany.data = res.data?.data as any ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
type PengumumanForm = Prisma.PengumumanGetPayload<{
|
||||||
|
select: {
|
||||||
|
judul: true;
|
||||||
|
deskripsi: true;
|
||||||
|
content: true;
|
||||||
|
categoryPengumumanId: true;
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
const pengumuman = proxy({
|
||||||
|
create: {
|
||||||
|
form: {} as PengumumanForm,
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateFormPengumuman.safeParse(pengumuman.create.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
pengumuman.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.desa.pengumuman["create"].post(pengumuman.create.form)
|
||||||
|
if (res.status === 200) {
|
||||||
|
pengumuman.findMany.load();
|
||||||
|
return toast.success("success create");
|
||||||
|
}
|
||||||
|
console.log(res)
|
||||||
|
return toast.error("failed create");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally{
|
||||||
|
pengumuman.create.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.PengumumanGetPayload<{omit: {isActive: true}}>[]
|
||||||
|
| null,
|
||||||
|
async load () {
|
||||||
|
const res = await ApiFetch.api.desa.pengumuman["find-many"].get();
|
||||||
|
console.log(res)
|
||||||
|
if (res.status === 200) {
|
||||||
|
pengumuman.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const stateDesaPengumuman = proxy({
|
||||||
|
category,
|
||||||
|
pengumuman
|
||||||
|
})
|
||||||
|
export default stateDesaPengumuman
|
||||||
85
src/app/admin/(dashboard)/desa/berita/_com/BeritaEditor.tsx
Normal file
85
src/app/admin/(dashboard)/desa/berita/_com/BeritaEditor.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
'use client'
|
||||||
|
import { RichTextEditor, Link } from '@mantine/tiptap';
|
||||||
|
import { useEditor } from '@tiptap/react';
|
||||||
|
import Highlight from '@tiptap/extension-highlight';
|
||||||
|
import StarterKit from '@tiptap/starter-kit';
|
||||||
|
import Underline from '@tiptap/extension-underline';
|
||||||
|
import TextAlign from '@tiptap/extension-text-align';
|
||||||
|
import Superscript from '@tiptap/extension-superscript';
|
||||||
|
import SubScript from '@tiptap/extension-subscript';
|
||||||
|
import { Button, Stack } from '@mantine/core';
|
||||||
|
|
||||||
|
const content =
|
||||||
|
'<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup><sup /></sup> and <sub><sub /></sub> tags)</li><li>Ordered and bullet lists</li><li>Text align </li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
|
||||||
|
|
||||||
|
export function BeritaEditor({ onSubmit }: { onSubmit: (val: string) => void }) {
|
||||||
|
const editor = useEditor({
|
||||||
|
extensions: [
|
||||||
|
StarterKit,
|
||||||
|
Underline,
|
||||||
|
Link,
|
||||||
|
Superscript,
|
||||||
|
SubScript,
|
||||||
|
Highlight,
|
||||||
|
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||||
|
],
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<RichTextEditor editor={editor}>
|
||||||
|
<RichTextEditor.Toolbar sticky stickyOffset={60}>
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Bold />
|
||||||
|
<RichTextEditor.Italic />
|
||||||
|
<RichTextEditor.Underline />
|
||||||
|
<RichTextEditor.Strikethrough />
|
||||||
|
<RichTextEditor.ClearFormatting />
|
||||||
|
<RichTextEditor.Highlight />
|
||||||
|
<RichTextEditor.Code />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.H1 />
|
||||||
|
<RichTextEditor.H2 />
|
||||||
|
<RichTextEditor.H3 />
|
||||||
|
<RichTextEditor.H4 />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Blockquote />
|
||||||
|
<RichTextEditor.Hr />
|
||||||
|
<RichTextEditor.BulletList />
|
||||||
|
<RichTextEditor.OrderedList />
|
||||||
|
<RichTextEditor.Subscript />
|
||||||
|
<RichTextEditor.Superscript />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Link />
|
||||||
|
<RichTextEditor.Unlink />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.AlignLeft />
|
||||||
|
<RichTextEditor.AlignCenter />
|
||||||
|
<RichTextEditor.AlignJustify />
|
||||||
|
<RichTextEditor.AlignRight />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Undo />
|
||||||
|
<RichTextEditor.Redo />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
</RichTextEditor.Toolbar>
|
||||||
|
|
||||||
|
<RichTextEditor.Content />
|
||||||
|
</RichTextEditor>
|
||||||
|
<Button onClick={() => {
|
||||||
|
if (!editor) return
|
||||||
|
onSubmit(editor?.getHTML())
|
||||||
|
}}>Submit</Button>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,11 +1,88 @@
|
|||||||
import React from 'react';
|
'use client'
|
||||||
|
import { Center, Group, Select, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import stateDashboardBerita from '../../_state/berita';
|
||||||
|
import { BeritaEditor } from './_com/BeritaEditor';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack>
|
||||||
Berita
|
<SimpleGrid cols={2}>
|
||||||
</div>
|
<BeritaList />
|
||||||
|
<BeritaCreate />
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BeritaList() {
|
||||||
|
const beritaState = useProxy(stateDashboardBerita)
|
||||||
|
useShallowEffect(() => {
|
||||||
|
beritaState.berita.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!beritaState.berita.findMany.data) return <Stack>
|
||||||
|
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
|
||||||
|
</Stack>
|
||||||
|
return <Stack>
|
||||||
|
<Text>News List</Text>
|
||||||
|
{beritaState.berita.findMany.data?.map((item) => (
|
||||||
|
<Text key={item.id}>{item.judul}</Text>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
|
||||||
|
function BeritaCreate() {
|
||||||
|
const beritaState = useProxy(stateDashboardBerita)
|
||||||
|
return <Stack gap={"md"}>
|
||||||
|
<Text>Create Some News</Text>
|
||||||
|
<SelectCategory onChange={(val) => {
|
||||||
|
beritaState.berita.create.form.katagoryBeritaId = val.id
|
||||||
|
}} />
|
||||||
|
<TextInput onChange={(val) => {
|
||||||
|
beritaState.berita.create.form.judul = val.target.value
|
||||||
|
}} label={"Judul"} placeholder='masukkan judul' />
|
||||||
|
<TextInput onChange={(val) => {
|
||||||
|
beritaState.berita.create.form.deskripsi = val.target.value
|
||||||
|
}} label={"Deskripsi"} placeholder='masukkan deskripsi' />
|
||||||
|
<Center w={200} h={200} bg={"gray"}>
|
||||||
|
<IconImageInPicture />
|
||||||
|
</Center>
|
||||||
|
<BeritaEditor onSubmit={(val) => {
|
||||||
|
|
||||||
|
beritaState.berita.create.form.content = val
|
||||||
|
beritaState.berita.create.create()
|
||||||
|
}} />
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectCategory({ onChange }: {
|
||||||
|
onChange: (value: Prisma.KatagoryBeritaGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true
|
||||||
|
}
|
||||||
|
}>) => void
|
||||||
|
}) {
|
||||||
|
const beritaState = useProxy(stateDashboardBerita)
|
||||||
|
useShallowEffect(() => {
|
||||||
|
beritaState.category.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!beritaState.category.findMany.data) return <Skeleton h={40} />
|
||||||
|
return <Group>
|
||||||
|
<Select placeholder='pilih katagori' label={"select katagori"} data={beritaState.category.findMany.data.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))} onChange={(v) => {
|
||||||
|
const data = beritaState.category.findMany.data?.find((item) => item.id === v)
|
||||||
|
if (!data) return
|
||||||
|
onChange(data)
|
||||||
|
}} />
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
@@ -1,11 +1,89 @@
|
|||||||
|
'use client'
|
||||||
|
import { Group, Select, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import stateDesaPengumuman from '../../_state/pengumuman';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { BeritaEditor } from '../berita/_com/BeritaEditor';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack>
|
||||||
Pengumuman
|
<SimpleGrid cols={{
|
||||||
</div>
|
base: 1, md: 2
|
||||||
|
}}>
|
||||||
|
<PengumumanList />
|
||||||
|
<PengumumanCreate />
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PengumumanList() {
|
||||||
|
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||||
|
useShallowEffect(() => {
|
||||||
|
pengumumanState.pengumuman.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!pengumumanState.pengumuman.findMany.data) return <Stack>
|
||||||
|
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
|
||||||
|
</Stack>
|
||||||
|
return <Stack>
|
||||||
|
<Text>Announcement List</Text>
|
||||||
|
{pengumumanState.pengumuman.findMany.data?.map((item) => (
|
||||||
|
<Text key={item.id}>{item.judul}</Text>
|
||||||
|
))}
|
||||||
|
</Stack>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PengumumanCreate() {
|
||||||
|
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||||
|
|
||||||
|
|
||||||
|
return <Stack gap={"md"}>
|
||||||
|
<Text>Create Some Announcement</Text>
|
||||||
|
<SelectCategory onChange={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.categoryPengumumanId = val.id
|
||||||
|
}} />
|
||||||
|
<TextInput onChange={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.judul = val.target.value
|
||||||
|
}} label={"Judul"} placeholder='masukkan judul' />
|
||||||
|
<TextInput onChange={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.deskripsi = val.target.value
|
||||||
|
}} label={"Deskripsi"} placeholder='masukkan deskripsi' />
|
||||||
|
<BeritaEditor onSubmit={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.content = val
|
||||||
|
pengumumanState.pengumuman.create.create()
|
||||||
|
}} />
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectCategory({ onChange }: {
|
||||||
|
onChange: (value: Prisma.CategoryPengumumanGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true
|
||||||
|
}
|
||||||
|
}>) => void
|
||||||
|
}) {
|
||||||
|
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||||
|
useShallowEffect(() => {
|
||||||
|
pengumumanState.category.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!pengumumanState.category.findMany.data) return <Skeleton h={40} />
|
||||||
|
return <Group>
|
||||||
|
{/* {JSON.stringify(pengumumanState.category.findMany.data)} */}
|
||||||
|
<Select placeholder='pilih kategori' label={"select katagori"} data={pengumumanState.category.findMany.data.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
}))} onChange={(v) => {
|
||||||
|
const data = pengumumanState.category.findMany.data?.find((item) => item.id === v)
|
||||||
|
if (!data) return
|
||||||
|
onChange(data)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Text } from '@mantine/core';
|
|
||||||
|
|
||||||
function CompTextLengh({input, maxLength}: {input: string, maxLength: number}) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Text fz={"xs"}>{input.length}/{maxLength}</Text>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CompTextLengh;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export const maxLength = 2000
|
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Stack, Text, Textarea, Title } from '@mantine/core';
|
import { Box, Button, Group, Stack, Text, Textarea, Title } from '@mantine/core';
|
||||||
import { useState } from 'react';
|
|
||||||
import CompTextLengh from './_comp_textLengh';
|
|
||||||
import { maxLength } from './gs_maxLength';
|
|
||||||
|
|
||||||
function Page(
|
function Page() {
|
||||||
) {
|
|
||||||
const [value, setValue] = useState("")
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
@@ -15,13 +10,10 @@ function Page(
|
|||||||
<Textarea
|
<Textarea
|
||||||
label={<Text>Deskripsi</Text>}
|
label={<Text>Deskripsi</Text>}
|
||||||
placeholder='tambah deskripsi'
|
placeholder='tambah deskripsi'
|
||||||
maxLength={maxLength}
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => setValue(e.target.value)}
|
|
||||||
/>
|
/>
|
||||||
<CompTextLengh input={value} maxLength={maxLength}/>
|
|
||||||
<Group>
|
<Group>
|
||||||
<Button bg={colors['blue-button']} fz={'md'}>Submit</Button>
|
<Button
|
||||||
|
bg={colors['blue-button']} fz={'md'}>Submit</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,67 +1,87 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { ActionIcon, AppShell, AppShellHeader, AppShellMain, AppShellNavbar, Burger, Group, Image, NavLink, ScrollArea, Stack, Text } from "@mantine/core";
|
import { ActionIcon, AppShell, AppShellHeader, AppShellMain, AppShellNavbar, Burger, Group, Image, NavLink, ScrollArea, Text } from "@mantine/core";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
|
import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
|
||||||
|
import _ from 'lodash';
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useSelectedLayoutSegments } from "next/navigation";
|
||||||
import { navBar } from "./_com/list_PageAdmin";
|
import { navBar } from "./_com/list_PageAdmin";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
const [opened, { toggle }] = useDisclosure();
|
const [opened, { toggle }] = useDisclosure();
|
||||||
const [active, setActive] = useState(navBar[0]?.id || 0);
|
const [desktopOpened, { toggle: toggleDesktop }] =
|
||||||
const isClient = typeof window !== 'undefined';
|
useDisclosure(true);
|
||||||
|
|
||||||
|
|
||||||
|
const segments = useSelectedLayoutSegments()
|
||||||
return (
|
return (
|
||||||
<Stack h={"100%"}>
|
<AppShell
|
||||||
<AppShell
|
suppressHydrationWarning
|
||||||
header={{ height: 60 }}
|
header={{ height: 60 }}
|
||||||
navbar={{
|
navbar={{
|
||||||
width: 300, breakpoint: 'sm', collapsed: { mobile: !opened }
|
width: 300, breakpoint: 'sm', collapsed: { mobile: !opened, desktop: !desktopOpened }
|
||||||
}}
|
}}
|
||||||
padding={'md'}
|
padding={'md'}
|
||||||
>
|
>
|
||||||
<AppShellHeader bg={colors["white-trans-1"]}>
|
<AppShellHeader bg={colors["white-trans-1"]}>
|
||||||
<Group px={10} align="center">
|
<Group px={10} align="center">
|
||||||
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size={'sm'} />
|
{!desktopOpened && <ActionIcon variant="light" onClick={toggleDesktop}><IconChevronRight /></ActionIcon>}
|
||||||
<ActionIcon w={50} h={50} variant="transparent" component={Link} href="/admin">
|
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size={'sm'} />
|
||||||
|
<ActionIcon w={50} h={50} variant="transparent" component={Link} href="/admin">
|
||||||
<Image py={5} src={'/assets/images/darmasaba-icon.png'} alt="" width={50} height={50} />
|
<Image py={5} src={'/assets/images/darmasaba-icon.png'} alt="" width={50} height={50} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
<Text fw={'bold'} c={colors["blue-button"]} fz={'lg'}>Dashboard Admin</Text>
|
<Text fw={'bold'} c={colors["blue-button"]} fz={'lg'}>Dashboard Admin</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</AppShellHeader>
|
</AppShellHeader>
|
||||||
<AppShellNavbar
|
<AppShellNavbar
|
||||||
c={colors["blue-button"]}
|
|
||||||
component={ScrollArea}
|
c={colors["blue-button"]}
|
||||||
>
|
component={ScrollArea}
|
||||||
{navBar.map((v,k) => {
|
>
|
||||||
|
<AppShell.Section h={100} bg={"gray.1"}>
|
||||||
|
<Text c={colors["blue-button"]}>Heder Navbar</Text>
|
||||||
|
</AppShell.Section>
|
||||||
|
<AppShell.Section >
|
||||||
|
{navBar.map((v, k) => {
|
||||||
return (
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
c={colors["blue-button"]}
|
c={_.lowerCase(v.name) == segments[1] ? colors["blue-button"] : "grey"}
|
||||||
key={k}
|
key={k}
|
||||||
active={isClient && k === active}
|
defaultOpened={_.lowerCase(v.name) == segments[1]}
|
||||||
onClick={() => setActive(k)}
|
// onClick={() => setActive(k)}
|
||||||
label={<Text>{v.name}</Text>}
|
label={<Text
|
||||||
|
style={{ fontWeight: _.lowerCase(v.name) == segments[1] ? "bold" : "normal" }}
|
||||||
|
>{v.name}</Text>}
|
||||||
>
|
>
|
||||||
{v.children.map((child) => {
|
{v.children.map((child, key) => {
|
||||||
return (
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
|
c={_.lowerCase(child.name) == _.lowerCase(segments[2]) ? colors["blue-button"] : "grey"}
|
||||||
|
key={key}
|
||||||
href={child.path}
|
href={child.path}
|
||||||
key={child.id}
|
// active={isClient && Number(child.id) === active}
|
||||||
active={isClient && Number(child.id) === active}
|
// onClick={() => setActive(Number(child.id))}
|
||||||
onClick={() => setActive(Number(child.id))}
|
label={<Text
|
||||||
label={<Text>{child.name}</Text>}
|
style={{ fontWeight: _.lowerCase(child.name) == _.lowerCase(segments[2]) ? "bold" : "normal" }}
|
||||||
|
>{child.name}</Text>}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</AppShellNavbar>
|
</AppShell.Section>
|
||||||
<AppShellMain bg={colors.Bg}>
|
|
||||||
{children}
|
<AppShell.Section>
|
||||||
</AppShellMain>
|
<Group justify="end">
|
||||||
</AppShell>
|
<ActionIcon variant="light" onClick={toggleDesktop}><IconChevronLeft /></ActionIcon>
|
||||||
</Stack>
|
</Group>
|
||||||
|
</AppShell.Section>
|
||||||
|
</AppShellNavbar>
|
||||||
|
<AppShellMain bg={colors.Bg}>
|
||||||
|
{children}
|
||||||
|
</AppShellMain>
|
||||||
|
</AppShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
9
src/app/api/[[...slugs]]/_lib/desa/berita/category.ts
Normal file
9
src/app/api/[[...slugs]]/_lib/desa/berita/category.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
async function katagoryBeritaFindMany() {
|
||||||
|
const data = await prisma.katagoryBerita.findMany();
|
||||||
|
return { data };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default katagoryBeritaFindMany
|
||||||
|
|
||||||
39
src/app/api/[[...slugs]]/_lib/desa/berita/create.ts
Normal file
39
src/app/api/[[...slugs]]/_lib/desa/berita/create.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormCreate = Prisma.BeritaGetPayload<{
|
||||||
|
select: {
|
||||||
|
judul: true;
|
||||||
|
deskripsi: true;
|
||||||
|
image: true;
|
||||||
|
content: true;
|
||||||
|
katagoryBeritaId: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
async function beritaCreate(context: Context) {
|
||||||
|
const body = context.body as FormCreate;
|
||||||
|
|
||||||
|
// console.log(body)
|
||||||
|
|
||||||
|
await prisma.berita.create({
|
||||||
|
data: {
|
||||||
|
content: body.content,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
image: body.image,
|
||||||
|
judul: body.judul,
|
||||||
|
katagoryBeritaId: body.katagoryBeritaId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success create berita",
|
||||||
|
data: {
|
||||||
|
...body,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default beritaCreate
|
||||||
|
|
||||||
8
src/app/api/[[...slugs]]/_lib/desa/berita/find-many.ts
Normal file
8
src/app/api/[[...slugs]]/_lib/desa/berita/find-many.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function beritaFindMany() {
|
||||||
|
const res = await prisma.berita.findMany();
|
||||||
|
return {
|
||||||
|
data: res,
|
||||||
|
};
|
||||||
|
}
|
||||||
19
src/app/api/[[...slugs]]/_lib/desa/berita/index.ts
Normal file
19
src/app/api/[[...slugs]]/_lib/desa/berita/index.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import katagoryBeritaFindMany from "./category";
|
||||||
|
import beritaFindMany from "./find-many";
|
||||||
|
import beritaCreate from "./create";
|
||||||
|
|
||||||
|
const Berita = new Elysia({ prefix: "/berita", tags: ["Desa/Berita"] })
|
||||||
|
.get("/category/find-many", katagoryBeritaFindMany)
|
||||||
|
.get("/find-many", beritaFindMany)
|
||||||
|
.post("/create", beritaCreate, {
|
||||||
|
body: t.Object({
|
||||||
|
judul: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
image: t.String(),
|
||||||
|
content: t.String(),
|
||||||
|
katagoryBeritaId: t.Union([t.String(), t.Null()]),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Berita;
|
||||||
9
src/app/api/[[...slugs]]/_lib/desa/index.ts
Normal file
9
src/app/api/[[...slugs]]/_lib/desa/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Elysia from "elysia";
|
||||||
|
import Berita from "./berita";
|
||||||
|
import Pengumuman from "./pengumuman";
|
||||||
|
|
||||||
|
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
||||||
|
.use(Berita)
|
||||||
|
.use(Pengumuman)
|
||||||
|
|
||||||
|
export default Desa;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
async function pengumumanCategoryFindMany() {
|
||||||
|
const data = await prisma.categoryPengumuman.findMany();
|
||||||
|
return { data };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default pengumumanCategoryFindMany
|
||||||
43
src/app/api/[[...slugs]]/_lib/desa/pengumuman/create.ts
Normal file
43
src/app/api/[[...slugs]]/_lib/desa/pengumuman/create.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormCreate = Prisma.PengumumanGetPayload<{
|
||||||
|
select: {
|
||||||
|
judul: true;
|
||||||
|
deskripsi: true;
|
||||||
|
content: true;
|
||||||
|
categoryPengumumanId: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export async function pengumumanCreate(context: Context) {
|
||||||
|
const body = context.body as FormCreate;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prisma.pengumuman.create({
|
||||||
|
data: {
|
||||||
|
content: body.content,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
judul: body.judul,
|
||||||
|
categoryPengumumanId: body.categoryPengumumanId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
console.error(body)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Failed create pengumuman",
|
||||||
|
error: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success create pengumuman",
|
||||||
|
data: {
|
||||||
|
...body,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function pengumumanFindMany() {
|
||||||
|
const res = await prisma.pengumuman.findMany();
|
||||||
|
return {
|
||||||
|
data: res,
|
||||||
|
};
|
||||||
|
}
|
||||||
19
src/app/api/[[...slugs]]/_lib/desa/pengumuman/index.ts
Normal file
19
src/app/api/[[...slugs]]/_lib/desa/pengumuman/index.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Elysia from "elysia";
|
||||||
|
import { pengumumanCreate } from "./create";
|
||||||
|
import pengumumanFindMany from "./find-many";
|
||||||
|
import { t } from "elysia";
|
||||||
|
import pengumumanCategoryFindMany from "./category";
|
||||||
|
|
||||||
|
const Pengumuman = new Elysia({ prefix: "/pengumuman", tags: ["Desa/Pengumuman"] })
|
||||||
|
.get("/category/find-many", pengumumanCategoryFindMany)
|
||||||
|
.get("/find-many", pengumumanFindMany)
|
||||||
|
.post("/create", pengumumanCreate, {
|
||||||
|
body: t.Object({
|
||||||
|
judul: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
content: t.String(),
|
||||||
|
categoryPengumumanId: t.Union([t.String(), t.Null()]),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Pengumuman;
|
||||||
36
src/app/api/[[...slugs]]/_lib/landing_page/layanan.ts
Normal file
36
src/app/api/[[...slugs]]/_lib/landing_page/layanan.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { MODEL_LANDING_PAGE_LAYANAN } from "@/app/admin/(dashboard)/landing-page/layanan/lib/interface";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function layanan_landingpage({req} : {req: MODEL_LANDING_PAGE_LAYANAN}) {
|
||||||
|
try {
|
||||||
|
const data = await prisma.landingPage_Layanan.create({
|
||||||
|
data: {
|
||||||
|
id: req.id,
|
||||||
|
deksripsi: req.deskripsi
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
deksripsi: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: true,
|
||||||
|
message: "Success get collaboration",
|
||||||
|
data: data,
|
||||||
|
},
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error create layanan", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "Error create layanan",
|
||||||
|
reason: (error as Error).message,
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,17 @@ import prisma from "@/lib/prisma";
|
|||||||
import cors, { HTTPMethod } from "@elysiajs/cors";
|
import cors, { HTTPMethod } from "@elysiajs/cors";
|
||||||
import swagger from "@elysiajs/swagger";
|
import swagger from "@elysiajs/swagger";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import getPotensi from "./_lib/get-potensi";
|
|
||||||
import img from "./_lib/img";
|
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import uplImg from "./_lib/upl-img";
|
import getPotensi from "./_lib/get-potensi";
|
||||||
|
import img from "./_lib/img";
|
||||||
|
import imgDel from "./_lib/img-del";
|
||||||
import imgs from "./_lib/imgs";
|
import imgs from "./_lib/imgs";
|
||||||
import uplCsv from "./_lib/upl-csv";
|
import uplCsv from "./_lib/upl-csv";
|
||||||
import imgDel from "./_lib/img-del";
|
|
||||||
import { uplImgSingle } from "./_lib/upl-img-single";
|
|
||||||
import { uplCsvSingle } from "./_lib/upl-csv-single";
|
import { uplCsvSingle } from "./_lib/upl-csv-single";
|
||||||
|
import uplImg from "./_lib/upl-img";
|
||||||
|
import { uplImgSingle } from "./_lib/upl-img-single";
|
||||||
|
import Desa from "./_lib/desa";
|
||||||
const ROOT = process.cwd();
|
const ROOT = process.cwd();
|
||||||
|
|
||||||
if (!process.env.WIBU_UPLOAD_DIR)
|
if (!process.env.WIBU_UPLOAD_DIR)
|
||||||
@@ -46,6 +47,7 @@ async function layanan() {
|
|||||||
const ApiServer = new Elysia()
|
const ApiServer = new Elysia()
|
||||||
.use(swagger({ path: "/api/docs" }))
|
.use(swagger({ path: "/api/docs" }))
|
||||||
.use(cors(corsConfig))
|
.use(cors(corsConfig))
|
||||||
|
.use(Desa)
|
||||||
.onError(({ code }) => {
|
.onError(({ code }) => {
|
||||||
if (code === "NOT_FOUND") {
|
if (code === "NOT_FOUND") {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import 'react-simple-toasts/dist/theme/dark.css';
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import '@mantine/charts/styles.css';
|
import '@mantine/charts/styles.css';
|
||||||
import '@mantine/dates/styles.css';
|
import '@mantine/dates/styles.css';
|
||||||
|
import '@mantine/tiptap/styles.css';
|
||||||
|
|
||||||
|
|
||||||
import LoadDataFirstClient from "@/app/darmasaba/_com/LoadDataFirstClient";
|
import LoadDataFirstClient from "@/app/darmasaba/_com/LoadDataFirstClient";
|
||||||
import {
|
import {
|
||||||
@@ -20,6 +19,7 @@ import {
|
|||||||
mantineHtmlProps,
|
mantineHtmlProps,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { ViewTransitions } from "next-view-transitions";
|
import { ViewTransitions } from "next-view-transitions";
|
||||||
|
import { ToastContainer } from "react-toastify";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Desa Darmasaba",
|
title: "Desa Darmasaba",
|
||||||
@@ -53,7 +53,11 @@ export default function RootLayout({
|
|||||||
<body>
|
<body>
|
||||||
<MantineProvider theme={theme}>
|
<MantineProvider theme={theme}>
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
|
<ToastContainer position="bottom-center" hideProgressBar style={{
|
||||||
|
zIndex: 9999
|
||||||
|
}} />
|
||||||
</body>
|
</body>
|
||||||
<LoadDataFirstClient />
|
<LoadDataFirstClient />
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user