# Forum Created

## feat
- CRUD Forum
- CRD Koment
- Tampilan forum profile
### No issue
This commit is contained in:
2024-03-15 10:27:06 +08:00
parent 7baceafa80
commit a77d728a5f
40 changed files with 1999 additions and 512 deletions

View File

@@ -32,6 +32,10 @@ model User {
Voting Voting[] Voting Voting[]
Voting_Kontributor Voting_Kontributor[] Voting_Kontributor Voting_Kontributor[]
Job Job[] Job Job[]
Forum_Posting Forum_Posting[]
Forum_Komentar Forum_Komentar[]
Forum_ReportPosting Forum_ReportPosting[]
Forum_ReportKomentar Forum_ReportKomentar[]
} }
model MasterUserRole { model MasterUserRole {
@@ -113,6 +117,7 @@ model ImagesBackground {
model Portofolio { model Portofolio {
id String @id @default(cuid()) id String @id @default(cuid())
id_Portofolio String @unique
namaBisnis String namaBisnis String
alamatKantor String alamatKantor String
tlpn String tlpn String
@@ -634,3 +639,70 @@ model Job {
MasterStatus MasterStatus? @relation(fields: [masterStatusId], references: [id]) MasterStatus MasterStatus? @relation(fields: [masterStatusId], references: [id])
masterStatusId String? @default("2") masterStatusId String? @default("2")
} }
model Forum_Posting {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
publishAt DateTime?
diskusi String @db.Text
Forum_Komentar Forum_Komentar[]
Forum_ReportPosting Forum_ReportPosting[]
Author User? @relation(fields: [authorId], references: [id])
authorId String?
}
model Forum_Komentar {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
komentar String @db.Text
Forum_Posting Forum_Posting? @relation(fields: [forum_PostingId], references: [id])
forum_PostingId String?
Forum_ReportKomentar Forum_ReportKomentar[]
Author User? @relation(fields: [authorId], references: [id])
authorId String?
}
model ForumMaster_KategoriReport {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
kategori String
Forum_ReportPosting Forum_ReportPosting[]
Forum_ReportKomentar Forum_ReportKomentar[]
}
model Forum_ReportPosting {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ForumMaster_KategoriReport ForumMaster_KategoriReport? @relation(fields: [forumMaster_KategoriReportId], references: [id])
forumMaster_KategoriReportId String?
Forum_Posting Forum_Posting? @relation(fields: [forum_PostingId], references: [id])
forum_PostingId String?
deskripsi String?
User User? @relation(fields: [userId], references: [id])
userId String?
}
model Forum_ReportKomentar {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ForumMaster_KategoriReport ForumMaster_KategoriReport? @relation(fields: [forumMaster_KategoriReportId], references: [id])
forumMaster_KategoriReportId String?
Forum_Komentar Forum_Komentar? @relation(fields: [forum_KomentarId], references: [id])
forum_KomentarId String?
deskripsi String?
User User? @relation(fields: [userId], references: [id])
userId String?
}

View File

@@ -1,11 +1,22 @@
import Forum_Detail from "@/app_modules/forum/detail"; import Forum_Detail from "@/app_modules/forum/detail";
import { forum_getKomentarById } from "@/app_modules/forum/fun/get/get_komentar_by_id";
import { forum_getOnePostingById } from "@/app_modules/forum/fun/get/get_one_posting_by_id";
import { forum_countOneTotalKomentarById } from "@/app_modules/forum/fun/count/count_one_total_komentar_by_id";
export default async function Page({ params }: { params: { id: string } }) {
let postingId = params.id;
const dataPosting = await forum_getOnePostingById(postingId);
const listKomentar = await forum_getKomentarById(postingId);
const totalKomentar = await forum_countOneTotalKomentarById(postingId)
export default async function Page({params}: {params: {id: string}}) {
let forumId = params.id
return ( return (
<> <>
<Forum_Detail forumId={forumId} /> <Forum_Detail
dataPosting={dataPosting as any}
listKomentar={listKomentar as any}
totalKomentar={totalKomentar}
/>
</> </>
); );
} }

View File

@@ -1,9 +1,13 @@
import { Forum_EditPosting } from "@/app_modules/forum"; import { Forum_EditPosting } from "@/app_modules/forum";
import { forum_getOnePostingById } from "@/app_modules/forum/fun/get/get_one_posting_by_id";
export default async function Page({ params }: { params: { id: string } }) {
let postingId = params.id;
const dataPosting = await forum_getOnePostingById(postingId)
export default async function Page() {
return ( return (
<> <>
<Forum_EditPosting /> <Forum_EditPosting dataPosting={dataPosting as any} />
</> </>
); );
} }

View File

@@ -5,10 +5,12 @@ import React from "react";
export default async function Layout({ export default async function Layout({
children, children,
params,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
params: { id: string };
}) { }) {
const authorId = await User_getUserId(); const authorId = params.id;
const dataAuthor = await user_getOneById(authorId); const dataAuthor = await user_getOneById(authorId);
return ( return (

View File

@@ -1,10 +1,14 @@
import { Forum_Forumku } from "@/app_modules/forum"; import { Forum_Forumku } from "@/app_modules/forum";
import { forum_getListPostingByAuhtorId } from "@/app_modules/forum/fun/get/get_list_posting_by_author_id";
import { forum_countOneTotalKomentarById } from "@/app_modules/forum/fun/count/count_one_total_komentar_by_id";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token"; import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import { user_getOneById } from "@/app_modules/home/fun/get/get_one_user_by_id"; import { user_getOneById } from "@/app_modules/home/fun/get/get_one_user_by_id";
import _ from "lodash"; import _ from "lodash";
import { forum_countPostingByAuthorId } from "@/app_modules/forum/fun/count/count_posting_by_author_id";
export default async function Page({ params }: { params: { id: string } }) { export default async function Page({ params }: { params: { id: string } }) {
const authorId = params.id; const authorId = params.id;
const userLoginId = await User_getUserId()
const dataAuthor = await user_getOneById(authorId); const dataAuthor = await user_getOneById(authorId);
const auhtorSelectedData = _.omit(dataAuthor, [ const auhtorSelectedData = _.omit(dataAuthor, [
"Profile.email", "Profile.email",
@@ -21,9 +25,20 @@ export default async function Page({ params }: { params: { id: string } }) {
// setTimeout(a, 1000); // setTimeout(a, 1000);
// }); // });
const dataPosting = await forum_getListPostingByAuhtorId(authorId);
const totalPosting = await forum_countPostingByAuthorId(authorId);
return ( return (
<> <>
<Forum_Forumku auhtorSelectedData={auhtorSelectedData as any} />
<Forum_Forumku
auhtorSelectedData={auhtorSelectedData as any}
dataPosting={dataPosting as any}
totalPosting={totalPosting}
userLoginId={userLoginId as any}
/>
</> </>
); );
} }

View File

@@ -1,12 +1,13 @@
import { Forum_Komentar } from "@/app_modules/forum"; import { Forum_Komentar } from "@/app_modules/forum";
import { forum_getOnePostingById } from "@/app_modules/forum/fun/get/get_one_posting_by_id";
export default async function Page({params}: {params: {id: string}}) { export default async function Page({ params }: { params: { id: string } }) {
let forumId = params.id let postingId = params.id;
const dataPosting = await forum_getOnePostingById(postingId);
return ( return (
<> <>
<Forum_Komentar forumId={forumId} /> <Forum_Komentar dataPosting={dataPosting as any} />
</> </>
); );
} }

View File

@@ -1,9 +1,14 @@
import { Forum_Beranda } from "@/app_modules/forum"; import { Forum_Beranda } from "@/app_modules/forum";
import { forum_getListAllPosting } from "@/app_modules/forum/fun/get/get_list_all_posting";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
export default async function Page() { export default async function Page() {
const listForum = await forum_getListAllPosting();
// console.log(listForum);
return ( return (
<> <>
<Forum_Beranda /> <Forum_Beranda listForum={listForum as any} />
</> </>
); );
} }

View File

@@ -1,172 +0,0 @@
"use client";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import {
Drawer,
Stack,
Grid,
Button,
Modal,
Title,
Group,
ActionIcon,
Text,
Box,
Center,
Loader,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconTrash, IconEdit, IconFlag3, IconDots } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { createStyles } from "@mantine/core";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { useAtom } from "jotai";
import { gs_forum_loading_edit_posting } from "../global_state";
import ComponentForum_LoadingDrawer from "./loading_drawer";
const useStyles = createStyles((theme) => ({
myCustomButton: {
...theme.fn.focusStyles(),
},
radiusCustom: {
// borderRadius: "10px, 10px, 0px,0px",
borderTopRightRadius: "10px",
borderTopLeftRadius: "10px",
},
}));
export default function ButtonMore({ id, tipe }: { id: any; tipe: string }) {
const router = useRouter();
const { classes } = useStyles();
const [opened, { open, close }] = useDisclosure(false);
const [openDel, setOpenDel] = useState(false);
// loading
const [loadingEdit, setLoadingEdit] = useState(false);
const [loadingReport, setLoadingReport] = useState(false);
// if (loadingEdit) return <ComponentGlobal_V2_LoadingPage />;
return (
<>
<Drawer
// className={classes.radiusCustom}
opened={opened}
onClose={close}
withCloseButton={false}
overlayProps={{ opacity: 0.1, blur: 1 }}
position="bottom"
size={"auto"}
>
<Stack>
<Grid
onClick={() => {
close();
setOpenDel(true);
}}
>
<Grid.Col span={"content"}>
<IconTrash color="red" />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text c={"red"}>Hapus</Text>
</Grid.Col>
</Grid>
<Grid
onClick={() => {
setLoadingEdit(true);
if (tipe === "posting") {
router.push(RouterForum.edit_posting + id);
} else {
router.push(RouterForum.edit_komentar + id);
}
}}
>
<Grid.Col span={"content"}>
<IconEdit color={loadingEdit ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingEdit ? "gray" : "black"}>Edit {tipe}</Text>{" "}
{loadingEdit ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
<Grid
onClick={() => {
setLoadingReport(true);
if (tipe === "posting") {
router.push(RouterForum.report_posting + id);
} else {
router.push(RouterForum.report_komentar + id);
}
}}
>
<Grid.Col span={"content"}>
<IconFlag3 color={loadingReport ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingReport ? "gray" : "black"}>
Laporkan {tipe}
</Text>{" "}
{loadingReport ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
<Button variant="outline" radius={"xl"} onClick={close}>
Batal
</Button>
</Stack>
</Drawer>
<Modal
opened={openDel}
onClose={() => {
setOpenDel(false);
}}
centered
withCloseButton={false}
>
<Stack>
<Title order={6}>Yakin menghapus {tipe} ini ?</Title>
<Group position="center">
<Button radius={"xl"} onClick={() => setOpenDel(false)}>
Batal
</Button>
<Button
color="red"
radius={"xl"}
onClick={() => {
setOpenDel(false);
if (tipe === "posting") {
ComponentGlobal_NotifikasiBerhasil(
"Postingan Terhapus ",
2000
);
} else {
ComponentGlobal_NotifikasiBerhasil(
"Berhasil Hapus Komentar",
2000
);
}
}}
>
Hapus
</Button>
</Group>
</Stack>
</Modal>
<ActionIcon variant="transparent" onClick={() => open()}>
<IconDots size={20} />
</ActionIcon>
</>
);
}

View File

@@ -21,27 +21,27 @@ import {
IconMessageCircle, IconMessageCircle,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { IconCircle } from "@tabler/icons-react"; import { IconCircle } from "@tabler/icons-react";
import ButtonMore from "./button_more"; import ComponentForum_PostingButtonMore from "../more_button/posting_button_more";
import ComponentForum_DetailMoreButton from "../more_button/detail_more_button";
export default function ComponentForum_AuthorNameOnDetail({ export default function ComponentForum_DetailOnHeaderAuthorName({
forumId, authorId,
postingId,
imagesId, imagesId,
authorName, authorName,
username, username,
tglPublish,
isPembatas, isPembatas,
tipe,
}: { }: {
forumId?: string; authorId?: string
postingId?: string;
imagesId?: string; imagesId?: string;
authorName?: string; authorName?: string;
username?: string; username?: string;
tglPublish?: Date; tglPublish?: Date;
isPembatas?: boolean; isPembatas?: boolean;
tipe: string
}) { }) {
const router = useRouter(); const router = useRouter();
const skrng = new Date();
return ( return (
<> <>
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
@@ -49,8 +49,8 @@ export default function ComponentForum_AuthorNameOnDetail({
<Grid.Col <Grid.Col
span={"content"} span={"content"}
onClick={() => { onClick={() => {
if (forumId) { if (authorId) {
router.push(RouterForum.forumku + forumId); router.push(RouterForum.forumku + authorId);
} else { } else {
ComponentGlobal_NotifikasiPeringatan("Id tidak ditemukan"); ComponentGlobal_NotifikasiPeringatan("Id tidak ditemukan");
} }
@@ -74,7 +74,7 @@ export default function ComponentForum_AuthorNameOnDetail({
{authorName ? authorName : "Nama author "} {authorName ? authorName : "Nama author "}
</Text> </Text>
<Text lineClamp={1} fz={"xs"} c={"gray"}> <Text lineClamp={1} fz={"xs"} c={"gray"}>
{username ? username : "@username "} {username ? `@${username}` : "@username "}
</Text> </Text>
</Stack> </Stack>
@@ -104,7 +104,10 @@ export default function ComponentForum_AuthorNameOnDetail({
</Stack> */} </Stack> */}
</Grid.Col> </Grid.Col>
<Grid.Col span={"content"}> <Grid.Col span={"content"}>
<ButtonMore id={forumId} tipe="posting"/> <ComponentForum_DetailMoreButton
postingId={postingId}
authorId={authorId}
/>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
{isPembatas ? <Divider /> : ""} {isPembatas ? <Divider /> : ""}

View File

@@ -1,6 +1,15 @@
"use client"; "use client";
import { Header, Group, ActionIcon, Text, Title } from "@mantine/core"; import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import {
Header,
Group,
ActionIcon,
Text,
Title,
Center,
Loader,
} from "@mantine/core";
import { IconArrowLeft, IconChevronLeft } from "@tabler/icons-react"; import { IconArrowLeft, IconChevronLeft } from "@tabler/icons-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@@ -23,6 +32,9 @@ export default function ComponentForum_HeaderTamplate({
bg?: any; bg?: any;
}) { }) {
const router = useRouter(); const router = useRouter();
const [loading, setLoading] = useState(false);
if (loading) return <ComponentGlobal_V2_LoadingPage />;
return ( return (
<> <>
<Header <Header
@@ -37,6 +49,7 @@ export default function ComponentForum_HeaderTamplate({
<ActionIcon <ActionIcon
variant="transparent" variant="transparent"
onClick={() => { onClick={() => {
setLoading(true);
if (route === null || route === undefined) { if (route === null || route === undefined) {
return router.back(); return router.back();
} else { } else {

View File

@@ -0,0 +1,132 @@
"use client";
import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog";
import { Stack, Grid, Avatar, Divider, Text, Group } from "@mantine/core";
import { useRouter } from "next/navigation";
import moment from "moment";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/component_global/notif_global/notifikasi_peringatan";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import {
IconCircleFilled,
IconDots,
IconEdit,
IconFlag3,
IconMessageCircle,
IconTrash,
} from "@tabler/icons-react";
import { IconCircle } from "@tabler/icons-react";
import { IoIosMore } from "react-icons/io";
import { useDisclosure } from "@mantine/hooks";
import { useState } from "react";
import ComponentForum_PostingButtonMore from "../more_button/posting_button_more";
import ComponentForum_KomentarButtonMore from "../more_button/komentar_button_more";
export default function ComponentForum_KomentarAuthorNameOnHeader({
userId,
komentarId,
imagesId,
authorName,
tglPublish,
isPembatas,
isMoreButton,
setKomentar,
postingId,
}: {
userId?: string;
komentarId?: string;
imagesId?: string;
authorName?: string;
tglPublish?: Date;
isPembatas?: boolean;
isMoreButton?: boolean;
setKomentar?: any;
postingId?: string
}) {
const router = useRouter();
return (
<>
<Stack spacing={"xs"}>
<Grid>
<Grid.Col
span={"content"}
onClick={() => {
if (userId) {
router.push(RouterForum.forumku + userId);
} else {
ComponentGlobal_NotifikasiPeringatan("Id tidak ditemukan");
}
}}
>
<Avatar
size={30}
sx={{ borderStyle: "solid", borderWidth: "0.5px" }}
radius={"xl"}
bg={"gray.1"}
src={
imagesId
? RouterProfile.api_foto_profile + imagesId
: "/aset/global/avatar.png"
}
/>
</Grid.Col>
<Grid.Col span={"auto"}>
<Stack justify="center" h={"100%"}>
<Grid>
<Grid.Col span={"auto"}>
<Text lineClamp={1} fz={"sm"} fw={"bold"}>
{authorName
? authorName
: "Nama author coba di berikan panjang "}
</Text>
</Grid.Col>
{/* <Grid.Col span={"auto"}>
<Text lineClamp={1} fz={"sm"} c={"gray"}>
{username ? username : "@username "}
</Text>
</Grid.Col> */}
<Grid.Col span={"content"}></Grid.Col>
</Grid>
</Stack>
</Grid.Col>
<Grid.Col span={"content"}>
<Group position="center" spacing={"xs"}>
<Group spacing={3}>
<Text c={"gray"} fz={"sm"}>
{tglPublish
? tglPublish.toLocaleDateString(["id-ID"], {
day: "numeric",
month: "short",
})
: new Date().toLocaleDateString(["id-ID"], {
day: "numeric",
month: "short",
})}
<IconCircle
size={5}
color="gray"
style={{ marginLeft: "5px" }}
/>
</Text>
</Group>
{isMoreButton ? (
<Group position="right">
<ComponentForum_KomentarButtonMore
userId={userId}
komentarId={komentarId}
setKomentar={setKomentar}
postingId={postingId}
/>
</Group>
) : (
""
)}
</Group>
</Grid.Col>
</Grid>
{isPembatas ? <Divider /> : ""}
</Stack>
</>
);
}

View File

@@ -1,21 +1,7 @@
"use client"; "use client";
import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog";
import { import { Stack, Grid, Avatar, Divider, Text, Group } from "@mantine/core";
Stack,
Grid,
Avatar,
Divider,
Text,
Group,
ThemeIcon,
ActionIcon,
Drawer,
Paper,
Button,
Modal,
Title,
} from "@mantine/core";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import moment from "moment"; import moment from "moment";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/component_global/notif_global/notifikasi_peringatan"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/component_global/notif_global/notifikasi_peringatan";
@@ -32,29 +18,26 @@ import { IconCircle } from "@tabler/icons-react";
import { IoIosMore } from "react-icons/io"; import { IoIosMore } from "react-icons/io";
import { useDisclosure } from "@mantine/hooks"; import { useDisclosure } from "@mantine/hooks";
import { useState } from "react"; import { useState } from "react";
import ButtonMore from "./button_more"; import ComponentForum_PostingButtonMore from "../more_button/posting_button_more";
export default function ComponentForum_AuthorNameOnHeader({ export default function ComponentForum_PostingAuthorNameOnHeader({
forumId, authorId,
postingId,
imagesId, imagesId,
authorName, authorName,
username,
tglPublish, tglPublish,
isPembatas, isPembatas,
tipe,
isMoreButton, isMoreButton,
}: { }: {
forumId?: string; authorId?: string;
postingId?: string;
imagesId?: string; imagesId?: string;
authorName?: string; authorName?: string;
username?: string;
tglPublish?: Date; tglPublish?: Date;
isPembatas?: boolean; isPembatas?: boolean;
tipe: string;
isMoreButton?: boolean; isMoreButton?: boolean;
}) { }) {
const router = useRouter(); const router = useRouter();
const skrng = new Date();
return ( return (
<> <>
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
@@ -62,8 +45,8 @@ export default function ComponentForum_AuthorNameOnHeader({
<Grid.Col <Grid.Col
span={"content"} span={"content"}
onClick={() => { onClick={() => {
if (forumId) { if (authorId) {
router.push(RouterForum.forumku + forumId); router.push(RouterForum.forumku + authorId);
} else { } else {
ComponentGlobal_NotifikasiPeringatan("Id tidak ditemukan"); ComponentGlobal_NotifikasiPeringatan("Id tidak ditemukan");
} }
@@ -104,8 +87,12 @@ export default function ComponentForum_AuthorNameOnHeader({
<Group position="center" spacing={"xs"}> <Group position="center" spacing={"xs"}>
<Group spacing={3}> <Group spacing={3}>
<Text c={"gray"} fz={"sm"}> <Text c={"gray"} fz={"sm"}>
{skrng.toLocaleDateString(["id-ID"], { {tglPublish
// dateStyle: "medium", ? tglPublish.toLocaleDateString(["id-ID"], {
day: "numeric",
month: "short",
})
: new Date().toLocaleDateString(["id-ID"], {
day: "numeric", day: "numeric",
month: "short", month: "short",
})} })}
@@ -118,7 +105,10 @@ export default function ComponentForum_AuthorNameOnHeader({
</Group> </Group>
{isMoreButton ? ( {isMoreButton ? (
<Group position="right"> <Group position="right">
<ButtonMore id={forumId} tipe={tipe} /> <ComponentForum_PostingButtonMore
authorId={authorId}
postingId={postingId as any}
/>
</Group> </Group>
) : ( ) : (
"" ""

View File

@@ -0,0 +1,98 @@
"use client";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { Stack, Card, Group, ActionIcon, Divider, Text } from "@mantine/core";
import { IconMessageCircle } from "@tabler/icons-react";
import ComponentForum_PostingAuthorNameOnHeader from "./header/posting_author_header_name";
import { MODEL_FORUM_POSTING } from "../model/interface";
import { useState } from "react";
import { useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation";
import { useAtom } from "jotai";
import { gs_forum_total_komentar } from "../global_state";
import { forum_countOneTotalKomentarById } from "../fun/count/count_one_total_komentar_by_id";
export default function ComponentForum_MainCardView({
data,
setLoadingKomen,
setLoadingDetail,
}: {
data: MODEL_FORUM_POSTING[];
setLoadingKomen: any;
setLoadingDetail: any;
}) {
const router = useRouter();
return (
<>
<Stack>
{data.map((e, i) => (
<Card key={i}>
<Card.Section>
<ComponentForum_PostingAuthorNameOnHeader
authorName={e?.Author?.Profile?.name}
imagesId={e?.Author?.Profile?.imagesId}
tglPublish={e?.createdAt}
isMoreButton={true}
authorId={e?.Author?.id}
postingId={e?.id}
/>
</Card.Section>
<Card.Section
sx={{ zIndex: 0 }}
p={"sm"}
onClick={() => {
// console.log("halaman forum");
setLoadingDetail(true);
router.push(RouterForum.main_detail + e.id);
}}
>
<Stack spacing={"xs"}>
<Text fz={"sm"} lineClamp={4}>
<div dangerouslySetInnerHTML={{ __html: e.diskusi }} />
</Text>
</Stack>
</Card.Section>
<Card.Section>
<Stack>
<Group spacing={"xs"} px={"sm"}>
<ActionIcon
// loading={loadingKomen ? true : false}
variant="transparent"
sx={{ zIndex: 1 }}
onClick={() => {
setLoadingKomen(true);
router.push(RouterForum.komentar + e.id);
}}
>
<IconMessageCircle color="gray" size={25} />
</ActionIcon>
{/* <TotalKomentar postingId={e?.id} /> */}
<Text c={"gray"}>{e?._count}</Text>
</Group>
<Divider />
</Stack>
</Card.Section>
</Card>
))}
</Stack>
</>
);
}
function TotalKomentar({ postingId }: { postingId: string }) {
const [value, setValue] = useState(0)
useShallowEffect(() => {
forum_countOneTotalKomentarById(postingId).then((res) => {
setValue(res);
});
}, [postingId]);
return (
<>
<Text c={"gray"}>{value}</Text>
</>
);
}

View File

@@ -0,0 +1,203 @@
"use client";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import {
Drawer,
Stack,
Grid,
Button,
Modal,
Title,
Group,
ActionIcon,
Text,
Box,
Center,
Loader,
} from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconTrash, IconEdit, IconFlag3, IconDots } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { createStyles } from "@mantine/core";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { useAtom } from "jotai";
import { gs_forum_loading_edit_posting } from "../../global_state";
import ComponentForum_LoadingDrawer from "../loading_drawer";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import { forum_funDeletePostingById } from "../../fun/delete/fun_delete_posting_by_id";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
export default function ComponentForum_DetailMoreButton({
authorId,
postingId,
}: {
authorId: any;
postingId?: any;
}) {
const router = useRouter();
const [opened, { open, close }] = useDisclosure(false);
const [openDel, setOpenDel] = useState(false);
const [userLoginId, setUserLoginId] = useState("");
// loading
const [loadingEdit, setLoadingEdit] = useState(false);
const [loadingReport, setLoadingReport] = useState(false);
// if (loadingEdit) return <ComponentGlobal_V2_LoadingPage />;
useShallowEffect(() => {
getUserLoginId();
}, []);
async function getUserLoginId() {
const getUserLoginId = await User_getUserId();
setUserLoginId(getUserLoginId);
}
return (
<>
<Drawer
// className={classes.radiusCustom}
opened={opened}
onClose={close}
withCloseButton={false}
overlayProps={{ opacity: 0.1, blur: 1 }}
position="bottom"
size={"auto"}
>
<Stack>
{userLoginId != authorId ? (
""
) : (
<Stack>
<Grid
onClick={() => {
close();
setOpenDel(true);
}}
>
<Grid.Col span={"content"}>
<IconTrash color="red" />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text c={"red"}>Hapus</Text>
</Grid.Col>
</Grid>
<Grid
onClick={() => {
setLoadingEdit(true);
router.push(RouterForum.edit_posting + postingId);
}}
>
<Grid.Col span={"content"}>
<IconEdit color={loadingEdit ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingEdit ? "gray" : "black"}>Edit posting</Text>{" "}
{loadingEdit ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
</Stack>
)}
{userLoginId == authorId ? (
""
) : (
<Grid
onClick={() => {
setLoadingReport(true);
router.push(RouterForum.report_posting + postingId);
}}
>
<Grid.Col span={"content"}>
<IconFlag3 color={loadingReport ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingReport ? "gray" : "black"}>
Laporkan posting
</Text>{" "}
{loadingReport ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
)}
<Button variant="outline" radius={"xl"} onClick={close}>
Batal
</Button>
</Stack>
</Drawer>
<Modal
opened={openDel}
onClose={() => {
setOpenDel(false);
}}
centered
withCloseButton={false}
>
<ButtonDelete postingId={postingId} setOpenDel={setOpenDel} />
</Modal>
<ActionIcon variant="transparent" onClick={() => open()}>
<IconDots size={20} />
</ActionIcon>
</>
);
}
function ButtonDelete({
postingId,
setOpenDel,
}: {
postingId?: string;
setOpenDel: any;
}) {
const router = useRouter();
const [loading, setLoading] = useState(false);
if (loading) return <ComponentGlobal_V2_LoadingPage />;
async function onDelete() {
setOpenDel(false);
await forum_funDeletePostingById(postingId as any).then((res) => {
if (res.status === 200) {
ComponentGlobal_NotifikasiBerhasil(`Postingan Terhapus`, 2000);
setLoading(true);
router.back();
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
}
return (
<>
<Stack>
<Title order={6}>Yakin menghapus posting ini ?</Title>
<Group position="center">
<Button radius={"xl"} onClick={() => setOpenDel(false)}>
Batal
</Button>
<Button
loaderPosition="center"
loading={loading ? true : false}
color="red"
radius={"xl"}
onClick={() => {
onDelete();
}}
>
Hapus
</Button>
</Group>
</Stack>
</>
);
}

View File

@@ -0,0 +1,229 @@
"use client";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import {
Drawer,
Stack,
Grid,
Button,
Modal,
Title,
Group,
ActionIcon,
Text,
Box,
Center,
Loader,
} from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconTrash, IconEdit, IconFlag3, IconDots } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { createStyles } from "@mantine/core";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { useAtom } from "jotai";
import { gs_forum_loading_edit_posting } from "../../global_state";
import ComponentForum_LoadingDrawer from "../loading_drawer";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import { forum_funDeletePostingById } from "../../fun/delete/fun_delete_posting_by_id";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
import { forum_funDeleteKomentarById } from "../../fun/delete/fun_delete_komentar_by_id";
import { forum_getKomentarById } from "../../fun/get/get_komentar_by_id";
export default function ComponentForum_KomentarButtonMore({
userId,
komentarId,
setKomentar,
postingId,
}: {
userId: any;
komentarId: any;
setKomentar?: any;
postingId?: string;
}) {
const router = useRouter();
const [opened, { open, close }] = useDisclosure(false);
const [openDel, setOpenDel] = useState(false);
const [userLoginId, setUserLoginId] = useState("");
// loading
const [loadingEdit, setLoadingEdit] = useState(false);
const [loadingReport, setLoadingReport] = useState(false);
// if (loadingEdit) return <ComponentGlobal_V2_LoadingPage />;
useShallowEffect(() => {
getUserLoginId();
}, []);
async function getUserLoginId() {
const getUserLoginId = await User_getUserId();
setUserLoginId(getUserLoginId);
}
return (
<>
<Drawer
// className={classes.radiusCustom}
opened={opened}
onClose={close}
withCloseButton={false}
overlayProps={{ opacity: 0.1, blur: 1 }}
position="bottom"
size={"auto"}
>
<Stack>
{userLoginId != userId ? (
""
) : (
<Stack>
<Grid
onClick={() => {
close();
setOpenDel(true);
}}
>
<Grid.Col span={"content"}>
<IconTrash color="red" />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text c={"red"}>Hapus</Text>
</Grid.Col>
</Grid>
{/* <Grid
onClick={() => {
setLoadingEdit(true);
router.push(RouterForum.edit_komentar + komentarId);
}}
>
<Grid.Col span={"content"}>
<IconEdit color={loadingEdit ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingEdit ? "gray" : "black"}>
Edit komentar
</Text>{" "}
{loadingEdit ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid> */}
</Stack>
)}
{userLoginId == userId ? (
""
) : (
<Grid
onClick={() => {
setLoadingReport(true);
router.push(RouterForum.report_komentar + komentarId);
}}
>
<Grid.Col span={"content"}>
<IconFlag3 color={loadingReport ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingReport ? "gray" : "black"}>
Laporkan komentar
</Text>{" "}
{loadingReport ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
)}
<Button variant="outline" radius={"xl"} onClick={close}>
Batal
</Button>
</Stack>
</Drawer>
<Modal
opened={openDel}
onClose={() => {
setOpenDel(false);
}}
centered
withCloseButton={false}
>
<ButtonDelete
komentarId={komentarId}
setOpenDel={setOpenDel}
setKomentar={setKomentar}
postingId={postingId}
/>
</Modal>
<ActionIcon variant="transparent" onClick={() => open()}>
<IconDots size={20} />
</ActionIcon>
</>
);
}
function ButtonDelete({
komentarId,
setOpenDel,
setKomentar,
postingId,
}: {
komentarId?: string;
setOpenDel: any;
setKomentar?: any;
postingId?: string;
}) {
const [loading, setLoading] = useState(false);
if (loading) return <ComponentGlobal_V2_LoadingPage />;
async function onDelete() {
await forum_funDeleteKomentarById(komentarId as any).then(async (res) => {
if (res.status === 200) {
await forum_getKomentarById(postingId as any).then((val) => {
setKomentar(val);
setOpenDel(false);
setLoading(true);
ComponentGlobal_NotifikasiBerhasil(res.message, 2000);
});
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
// await forum_funDeletePostingById(komentarId as any).then((res) => {
// if (res.status === 200) {
// ComponentGlobal_NotifikasiBerhasil(`Postingan Terhapus`, 2000);
// } else {
// ComponentGlobal_NotifikasiGagal(res.message);
// }
// });
}
return (
<>
<Stack>
<Title order={6}>Yakin menghapus komentar ini ?</Title>
<Group position="center">
<Button radius={"xl"} onClick={() => setOpenDel(false)}>
Batal
</Button>
<Button
loaderPosition="center"
loading={loading ? true : false}
color="red"
radius={"xl"}
onClick={() => {
onDelete();
}}
>
Hapus
</Button>
</Group>
</Stack>
</>
);
}

View File

@@ -0,0 +1,199 @@
"use client";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import {
Drawer,
Stack,
Grid,
Button,
Modal,
Title,
Group,
ActionIcon,
Text,
Box,
Center,
Loader,
} from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconTrash, IconEdit, IconFlag3, IconDots } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { createStyles } from "@mantine/core";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { useAtom } from "jotai";
import { gs_forum_loading_edit_posting } from "../../global_state";
import ComponentForum_LoadingDrawer from "../loading_drawer";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import { forum_funDeletePostingById } from "../../fun/delete/fun_delete_posting_by_id";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
export default function ComponentForum_PostingButtonMore({
authorId,
postingId,
}: {
authorId: any;
postingId?: any;
}) {
const router = useRouter();
const [opened, { open, close }] = useDisclosure(false);
const [openDel, setOpenDel] = useState(false);
const [userLoginId, setUserLoginId] = useState("");
// loading
const [loadingEdit, setLoadingEdit] = useState(false);
const [loadingReport, setLoadingReport] = useState(false);
// if (loadingEdit) return <ComponentGlobal_V2_LoadingPage />;
useShallowEffect(() => {
getUserLoginId();
}, []);
async function getUserLoginId() {
const getUserLoginId = await User_getUserId();
setUserLoginId(getUserLoginId);
}
return (
<>
<Drawer
// className={classes.radiusCustom}
opened={opened}
onClose={close}
withCloseButton={false}
overlayProps={{ opacity: 0.1, blur: 1 }}
position="bottom"
size={"auto"}
>
<Stack>
{userLoginId != authorId ? (
""
) : (
<Stack>
<Grid
onClick={() => {
close();
setOpenDel(true);
}}
>
<Grid.Col span={"content"}>
<IconTrash color="red" />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text c={"red"}>Hapus</Text>
</Grid.Col>
</Grid>
<Grid
onClick={() => {
setLoadingEdit(true);
router.push(RouterForum.edit_posting + postingId);
}}
>
<Grid.Col span={"content"}>
<IconEdit color={loadingEdit ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingEdit ? "gray" : "black"}>Edit posting</Text>{" "}
{loadingEdit ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
</Stack>
)}
{userLoginId == authorId ? (
""
) : (
<Grid
onClick={() => {
setLoadingReport(true);
router.push(RouterForum.report_posting + postingId);
}}
>
<Grid.Col span={"content"}>
<IconFlag3 color={loadingReport ? "gray" : "black"} />
</Grid.Col>
<Grid.Col span={"auto"}>
<Group>
<Text c={loadingReport ? "gray" : "black"}>
Laporkan posting
</Text>{" "}
{loadingReport ? <Loader size={"sm"} /> : ""}
</Group>
</Grid.Col>
</Grid>
)}
<Button variant="outline" radius={"xl"} onClick={close}>
Batal
</Button>
</Stack>
</Drawer>
<Modal
opened={openDel}
onClose={() => {
setOpenDel(false);
}}
centered
withCloseButton={false}
>
<ButtonDelete postingId={postingId} setOpenDel={setOpenDel} />
</Modal>
<ActionIcon variant="transparent" onClick={() => open()}>
<IconDots size={20} />
</ActionIcon>
</>
);
}
function ButtonDelete({
postingId,
setOpenDel,
}: {
postingId?: string;
setOpenDel: any;
}) {
const [loading, setLoading] = useState(false);
async function onDelete() {
setOpenDel(false);
await forum_funDeletePostingById(postingId as any).then((res) => {
if (res.status === 200) {
ComponentGlobal_NotifikasiBerhasil(`Postingan Terhapus`, 2000);
setLoading(true)
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
}
return (
<>
<Stack>
<Title order={6}>Yakin menghapus posting ini ?</Title>
<Group position="center">
<Button radius={"xl"} onClick={() => setOpenDel(false)}>
Batal
</Button>
<Button
loaderPosition="center"
loading={loading ? true : false}
color="red"
radius={"xl"}
onClick={() => {
onDelete();
}}
>
Hapus
</Button>
</Group>
</Stack>
</>
);
}

View File

@@ -18,6 +18,11 @@ import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loadi
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import React, { useState } from "react"; import React, { useState } from "react";
import { forum_funCreate } from "../fun/create/fun_create";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/component_global/notif_global/notifikasi_peringatan";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
const ReactQuill = dynamic( const ReactQuill = dynamic(
() => { () => {
return import("react-quill"); return import("react-quill");
@@ -27,6 +32,7 @@ const ReactQuill = dynamic(
export default function Forum_Create() { export default function Forum_Create() {
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const [maxForum, setMaxForum] = useState(0);
const [reload, setReload] = useState(false); const [reload, setReload] = useState(false);
useShallowEffect(() => { useShallowEffect(() => {
if (window && window.document) setReload(true); if (window && window.document) setReload(true);
@@ -48,6 +54,9 @@ export default function Forum_Create() {
placeholder="Apa yang sedang hangat dibicarakan ?" placeholder="Apa yang sedang hangat dibicarakan ?"
style={{ height: 150 }} style={{ height: 150 }}
onChange={(val) => { onChange={(val) => {
// if (val.length > 300) {
// setMaxForum(val.length);
// }
setValue(val); setValue(val);
}} }}
/> />
@@ -56,19 +65,37 @@ export default function Forum_Create() {
{/* <ActionIcon> {/* <ActionIcon>
<IconPhotoUp /> <IconPhotoUp />
</ActionIcon> */} </ActionIcon> */}
<ButtonAction /> <ButtonAction value={value} />
</Group> </Group>
</Stack> </Stack>
<div dangerouslySetInnerHTML={{ __html: value }} /> {/* <div dangerouslySetInnerHTML={{ __html: value }} /> */}
</> </>
); );
} }
function ButtonAction() { function ButtonAction({ value }: { value: string }) {
const router = useRouter(); const router = useRouter();
const [loading, setLoading] = useState(false);
async function onCreate() {
await forum_funCreate(value).then((res) => {
if (res.status === 201) {
setLoading(true);
ComponentGlobal_NotifikasiBerhasil(res.message);
setTimeout(() => router.back(), 1000);
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
}
return ( return (
<> <>
<Button radius={"xl"} onClick={() => router.back()}> <Button
radius={"xl"}
loading={loading ? true : false}
loaderPosition="center"
onClick={() => onCreate()}
>
Posting Posting
</Button> </Button>
</> </>

View File

@@ -4,43 +4,94 @@ import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { import {
ActionIcon, ActionIcon,
Box, Box,
Button,
Card, Card,
Center,
Divider, Divider,
Group, Group,
Paper,
Stack, Stack,
Text, Text,
} from "@mantine/core"; } from "@mantine/core";
import { IconMessageCircle } from "@tabler/icons-react"; import { IconMessageCircle } from "@tabler/icons-react";
import ComponentForum_AuthorNameOnHeader from "../component/author_header_name"; import ComponentForum_PostingAuthorNameOnHeader from "../component/header/posting_author_header_name";
import ComponentForum_AuthorNameOnDetail from "../component/author_name_on_detail"; import ComponentForum_DetailOnHeaderAuthorName from "../component/header/detail_author_header_name";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { MODEL_FORUM_KOMENTAR, MODEL_FORUM_POSTING } from "../model/interface";
import ComponentForum_KomentarAuthorNameOnHeader from "../component/header/komentar_author_header_name";
import _ from "lodash";
import dynamic from "next/dynamic";
import React, { useState } from "react";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
import { forum_funCreateKomentar } from "../fun/create/fun_create_komentra";
const ReactQuill = dynamic(
() => {
return import("react-quill");
},
{ ssr: false }
);
import "react-quill/dist/quill.bubble.css";
import { forum_getKomentarById } from "../fun/get/get_komentar_by_id";
export default function Forum_Detail({
dataPosting,
listKomentar,
totalKomentar,
}: {
dataPosting: MODEL_FORUM_POSTING;
listKomentar: MODEL_FORUM_KOMENTAR[];
totalKomentar: number
}) {
const [komentar, setKomentar] = useState(listKomentar);
export default function Forum_Detail({ forumId }: { forumId: string }) {
return ( return (
<> <>
<Stack px={"xs"}> <Stack px={"xs"}>
<ForumView forumId={forumId} /> <ForumView dataPosting={dataPosting} totalKomentar={totalKomentar} />
<DiskusiView /> <CreateKomentar postingId={dataPosting?.id} setKomentar={setKomentar} />
<KomentarView
listKomentar={komentar}
setKomentar={setKomentar}
postingId={dataPosting?.id}
/>
</Stack> </Stack>
</> </>
); );
} }
function ForumView({ forumId }: { forumId: string }) { function ForumView({
const router = useRouter(); dataPosting,
totalKomentar,
}: {
dataPosting: MODEL_FORUM_POSTING;
totalKomentar: number
}) {
return ( return (
<> <>
<Card style={{ position: "relative", width: "100%" }}> <Card style={{ position: "relative", width: "100%" }}>
<Card.Section> <Card.Section>
<ComponentForum_AuthorNameOnDetail forumId={forumId} tipe="posting" /> <ComponentForum_DetailOnHeaderAuthorName
authorId={dataPosting?.Author?.id}
authorName={dataPosting?.Author?.Profile?.name}
username={dataPosting?.Author?.username}
imagesId={dataPosting?.Author?.Profile?.imagesId}
postingId={dataPosting?.id}
tglPublish={dataPosting?.createdAt}
/>
</Card.Section> </Card.Section>
<Card.Section sx={{ zIndex: 0 }} py={"sm"}> <Card.Section sx={{ zIndex: 0 }} py={"sm"}>
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
<Text fz={"sm"}> <Text fz={"sm"}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad, {dataPosting?.diskusi ? (
vitae. Quisquam aspernatur, eius consequatur dicta repellendus <div
facere vero recusandae deleniti voluptas quod architecto, tenetur dangerouslySetInnerHTML={{ __html: dataPosting?.diskusi }}
totam excepturi rem nam iusto earum. />
) : (
""
)}
</Text> </Text>
</Stack> </Stack>
</Card.Section> </Card.Section>
@@ -48,33 +99,34 @@ function ForumView({ forumId }: { forumId: string }) {
<Stack> <Stack>
<Group position="apart"> <Group position="apart">
<Group spacing={"xs"}> <Group spacing={"xs"}>
<ActionIcon {/* <ActionIcon
variant="transparent" variant="transparent"
sx={{ zIndex: 1 }} sx={{ zIndex: 1 }}
onClick={() => { onClick={() => {
router.push(RouterForum.komentar + forumId); router.push(RouterForum.komentar + dataPosting.id);
}} }}
> >
</ActionIcon> */}
<IconMessageCircle color="gray" size={25} /> <IconMessageCircle color="gray" size={25} />
</ActionIcon> <Text c={"gray"}>{totalKomentar}</Text>
<Text c={"gray"}>1</Text>
</Group> </Group>
<Group> <Group>
<Text c={"gray"} fz={"sm"}> <Text c={"gray"} fz={"sm"}>
{new Date( {new Date(dataPosting?.createdAt).toLocaleTimeString()}
"August 19, 1975 23:15:30 GMT+00:00" {/* {new Intl.RelativeTimeFormat("id", {style: "short"}).format(-1,"day")} */}
).toLocaleTimeString()}
</Text> </Text>
<Text c={"gray"} fz={"sm"}> <Text c={"gray"} fz={"sm"}>
{new Date().toLocaleDateString(["id-ID"], { {dataPosting?.createdAt
? dataPosting?.createdAt.toLocaleDateString(["id-ID"], {
dateStyle: "medium",
})
: new Date().toLocaleDateString(["id-ID"], {
dateStyle: "medium", dateStyle: "medium",
})} })}
</Text> </Text>
</Group> </Group>
</Group> </Group>
<Box>
<Divider /> <Divider />
</Box>
</Stack> </Stack>
</Card.Section> </Card.Section>
</Card> </Card>
@@ -82,51 +134,114 @@ function ForumView({ forumId }: { forumId: string }) {
); );
} }
function DiskusiView() { function CreateKomentar({
postingId,
setKomentar,
}: {
postingId: string;
setKomentar: any;
}) {
const router = useRouter();
const [value, setValue] = useState("");
const [loading, setLoading] = useState(false);
async function onComment() {
await forum_funCreateKomentar(postingId, value).then(async (res) => {
if (res.status === 201) {
await forum_getKomentarById(postingId).then((val) => {
setKomentar(val);
// setLoading(true);
setValue("");
ComponentGlobal_NotifikasiBerhasil(res.message, 2000);
});
// router.replace(RouterForum.main_detail + postingId, { scroll: false });
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
}
return ( return (
<> <>
<Stack> <Stack>
{Array(5) <Paper withBorder shadow="lg">
.fill(0) <ReactQuill
.map((e, i) => ( value={value}
theme="bubble"
placeholder="Ketik komentar anda?"
onChange={(val) => {
setValue(val);
}}
/>
</Paper>
<Group position="right">
<Button
loaderPosition="center"
loading={loading ? true : false}
radius={"xl"}
onClick={() => onComment()}
>
Balas
</Button>
</Group>
<Divider />
</Stack>
</>
);
}
function KomentarView({
listKomentar,
setKomentar,
postingId,
}: {
listKomentar: MODEL_FORUM_KOMENTAR[];
setKomentar: any;
postingId: string
}) {
return (
<>
<Stack>
{_.isEmpty(listKomentar) ? (
<Center>
<Text fw={"bold"} fz={"xs"} c={"gray"}>
Belum ada komentar
</Text>
</Center>
) : (
listKomentar.map((e, i) => (
<Card key={i} mt={"xs"}> <Card key={i} mt={"xs"}>
<Card.Section> <Card.Section>
<ComponentForum_AuthorNameOnHeader <ComponentForum_KomentarAuthorNameOnHeader
forumId={i as any} authorName={e?.Author?.Profile?.name}
tipe="komentar" imagesId={e?.Author?.Profile?.imagesId}
tglPublish={e?.createdAt}
userId={e?.Author?.id}
komentarId={e?.id}
isMoreButton={true} isMoreButton={true}
setKomentar={setKomentar}
postingId={postingId}
/> />
</Card.Section> </Card.Section>
<Card.Section sx={{ zIndex: 0 }} p={"sm"}> <Card.Section sx={{ zIndex: 0 }} p={"sm"}>
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
<Text fz={"sm"} lineClamp={4}> <Text fz={"sm"} lineClamp={4}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. {e.komentar ? (
Ad, vitae. Quisquam aspernatur, eius consequatur dicta <div dangerouslySetInnerHTML={{ __html: e.komentar }} />
repellendus facere vero recusandae deleniti voluptas quod ) : (
architecto, tenetur totam excepturi rem nam iusto earum. ""
)}
</Text> </Text>
</Stack> </Stack>
</Card.Section> </Card.Section>
<Card.Section> <Card.Section>
<Stack> <Stack>
{/* <Group>
<Text c={"gray"} fz={"sm"}>
{new Date(
"August 19, 1975 23:15:30 GMT+00:00"
).toLocaleTimeString()}
</Text>
<Text c={"gray"} fz={"sm"}>
{new Date().toLocaleDateString(["id-ID"], {
dateStyle: "medium",
})}
</Text>
</Group> */}
<Divider /> <Divider />
</Stack> </Stack>
</Card.Section> </Card.Section>
</Card> </Card>
))} ))
)}
</Stack> </Stack>
</> </>
); );

View File

@@ -1,17 +1,24 @@
"use client"; "use client";
import { ActionIcon, AppShell, Group, Header, Title } from "@mantine/core"; import { ActionIcon, AppShell, Group, Header, Title } from "@mantine/core";
import React from "react"; import React, { useState } from "react";
import ComponentForum_HeaderTamplate from "../component/header/header_tamplate"; import ComponentForum_HeaderTamplate from "../component/header/header_tamplate";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { IconChevronLeft } from "@tabler/icons-react"; import { IconChevronLeft } from "@tabler/icons-react";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { revalidatePath } from "next/cache";
export const dynamic = "force-dynamic";
export default function LayoutForum_Detail({ export default function LayoutForum_Detail({
children, children,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const router = useRouter(); const router = useRouter();
const [loading, setLoading] = useState(false);
if (loading) return <ComponentGlobal_V2_LoadingPage />;
return ( return (
<> <>
<AppShell <AppShell
@@ -19,8 +26,11 @@ export default function LayoutForum_Detail({
<Header height={50} sx={{ borderStyle: "none" }}> <Header height={50} sx={{ borderStyle: "none" }}>
<Group h={50} px={"md"}> <Group h={50} px={"md"}>
<ActionIcon <ActionIcon
loading={loading ? true : false}
variant="transparent" variant="transparent"
onClick={() => { onClick={() => {
setLoading(true);
// revalidatePath("/dev/forum/main");
router.back(); router.back();
}} }}
> >

View File

@@ -1,4 +1,4 @@
"use client" "use client";
import { import {
ActionIcon, ActionIcon,
@@ -16,11 +16,14 @@ import { useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2"; import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import React, { useState } from "react"; import React, { useState } from "react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { gs_forum_loading_edit_posting } from "../../global_state"; import { gs_forum_loading_edit_posting } from "../../global_state";
import { MODEL_FORUM_POSTING } from "../../model/interface";
import { forum_funEditPostingById } from "../../fun/edit/fun_edit_posting_by_id";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
const ReactQuill = dynamic( const ReactQuill = dynamic(
() => { () => {
return import("react-quill"); return import("react-quill");
@@ -28,8 +31,12 @@ const ReactQuill = dynamic(
{ ssr: false } { ssr: false }
); );
export default function Forum_EditPosting() { export default function Forum_EditPosting({
const [value, setValue] = useState(""); dataPosting,
}: {
dataPosting: MODEL_FORUM_POSTING;
}) {
const [value, setValue] = useState(dataPosting);
const [reload, setReload] = useState(false); const [reload, setReload] = useState(false);
useShallowEffect(() => { useShallowEffect(() => {
if (window && window.document) setReload(true); if (window && window.document) setReload(true);
@@ -50,8 +57,12 @@ export default function Forum_EditPosting() {
theme="bubble" theme="bubble"
placeholder="Apa yang sedang hangat dibicarakan ?" placeholder="Apa yang sedang hangat dibicarakan ?"
style={{ height: 150 }} style={{ height: 150 }}
value={value.diskusi}
onChange={(val) => { onChange={(val) => {
setValue(val); setValue({
...value,
diskusi: val,
});
}} }}
/> />
</Paper> </Paper>
@@ -59,24 +70,46 @@ export default function Forum_EditPosting() {
{/* <ActionIcon> {/* <ActionIcon>
<IconPhotoUp /> <IconPhotoUp />
</ActionIcon> */} </ActionIcon> */}
<ButtonAction /> <ButtonAction diskusi={value.diskusi as any} postingId={value.id} />
</Group> </Group>
</Stack> </Stack>
<div dangerouslySetInnerHTML={{ __html: value }} /> {/* <div dangerouslySetInnerHTML={{ __html: value }} /> */}
</> </>
); );
} }
function ButtonAction() { function ButtonAction({
postingId,
diskusi,
}: {
postingId: string;
diskusi: string;
}) {
const router = useRouter(); const router = useRouter();
const [loadingEdit, setLoadingEdit] = useAtom(gs_forum_loading_edit_posting); const [loading, setLoading] = useState(false);
async function onUpdate() {
await forum_funEditPostingById(postingId, diskusi).then((res) => {
if (res.status === 200) {
setLoading(true);
ComponentGlobal_NotifikasiBerhasil(res.message);
setTimeout(() => router.back(), 1000)
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
}
return ( return (
<> <>
<Button radius={"xl"} onClick={() => { <Button
router.back() loaderPosition="center"
loading={loading ? true : false}
}}> radius={"xl"}
onClick={() => {
onUpdate();
}}
>
Update Update
</Button> </Button>
</> </>

View File

@@ -5,36 +5,98 @@ import ComponentGlobal_AuthorNameOnHeader from "@/app_modules/component_global/a
import { MODEL_USER } from "@/app_modules/home/model/interface"; import { MODEL_USER } from "@/app_modules/home/model/interface";
import { import {
ActionIcon, ActionIcon,
Affix,
Avatar, Avatar,
Button, Button,
Card, Card,
Center, Center,
Divider, Divider,
Grid,
Group, Group,
Stack, Stack,
Text, Text,
rem,
} from "@mantine/core"; } from "@mantine/core";
import { IconCircleFilled, IconMessageCircle } from "@tabler/icons-react"; import {
IconCircleFilled,
IconMessageCircle,
IconPencilPlus,
} from "@tabler/icons-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import ComponentForum_AuthorNameOnHeader from "../component/author_header_name"; import ComponentForum_PostingAuthorNameOnHeader from "../component/header/posting_author_header_name";
import { RouterForum } from "@/app/lib/router_hipmi/router_forum"; import { RouterForum } from "@/app/lib/router_hipmi/router_forum";
import { useState } from "react"; import { useState } from "react";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2"; import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { MODEL_FORUM_POSTING } from "../model/interface";
import ComponentForum_MainCardView from "../component/main_card_view";
import { useWindowScroll } from "@mantine/hooks";
export default function Forum_Forumku({ export default function Forum_Forumku({
auhtorSelectedData, auhtorSelectedData,
dataPosting,
totalPosting,
userLoginId,
}: { }: {
auhtorSelectedData: MODEL_USER; auhtorSelectedData: MODEL_USER;
dataPosting: MODEL_FORUM_POSTING[];
totalPosting: number;
userLoginId: string;
}) { }) {
const router = useRouter(); const router = useRouter();
const [loadingDetail, setLoaduingDetail] = useState(false); const [scroll, scrollTo] = useWindowScroll();
const [loadingKomen, setLoadingKomen] = useState(false); const [loadingCreate, setLoadingCreate] = useState(false);
if (loadingDetail) return <ComponentGlobal_V2_LoadingPage />;
if (loadingKomen) return <ComponentGlobal_V2_LoadingPage />;
return ( return (
<> <>
{userLoginId === auhtorSelectedData.id ? (
<Affix position={{ bottom: rem(100), right: rem(30) }}>
<ActionIcon
loading={loadingCreate ? true : false}
opacity={scroll.y > 0 ? 0.5 : ""}
style={{
transition: "0.5s",
}}
size={"xl"}
radius={"xl"}
variant="transparent"
bg={"blue"}
onClick={() => {
setLoadingCreate(true);
router.push(RouterForum.create);
}}
>
<IconPencilPlus color="white" />
</ActionIcon>
</Affix>
) : (
""
)}
<Stack spacing={"xl"} px={"sm"}> <Stack spacing={"xl"} px={"sm"}>
<ForumProfile
auhtorSelectedData={auhtorSelectedData}
totalPosting={totalPosting}
/>
<ForumPosting dataPosting={dataPosting} />
</Stack>
</>
);
}
function ForumProfile({
auhtorSelectedData,
totalPosting,
}: {
auhtorSelectedData: MODEL_USER;
totalPosting: number;
}) {
const router = useRouter();
const [loading, setLoading] = useState(false);
// if (loading) return <ComponentGlobal_V2_LoadingPage />;
return (
<>
<Center> <Center>
<Avatar <Avatar
radius={"100%"} radius={"100%"}
@@ -51,60 +113,90 @@ export default function Forum_Forumku({
} }
/> />
</Center> </Center>
<Group position="apart"> <Grid>
<Grid.Col span={"auto"}>
<Stack spacing={0}> <Stack spacing={0}>
<Text lineClamp={1} fw={"bold"}> <Text lineClamp={1} fw={"bold"}>
{auhtorSelectedData?.Profile?.name} {auhtorSelectedData?.Profile?.name}
</Text> </Text>
<Grid gutter={"xs"}>
<Grid.Col span={"content"}>
<Text lineClamp={1} c={"gray"} fz={"sm"}> <Text lineClamp={1} c={"gray"} fz={"sm"}>
@{auhtorSelectedData?.username}{" "} {totalPosting} Posting <IconCircleFilled size={5} />
<Text inherit span>
<IconCircleFilled size={5} /> 5 Posting
</Text> </Text>
</Grid.Col>
<Grid.Col span={"auto"}>
<Text lineClamp={1} c={"gray"} fz={"sm"}>
@{auhtorSelectedData?.username}
{""}
</Text> </Text>
</Grid.Col>
</Grid>
</Stack> </Stack>
</Grid.Col>
<Grid.Col span={5}>
<Stack align="center" justify="center" h={"100%"}>
<Button <Button
compact compact
loaderPosition="center"
loading={loading ? true : false}
radius={"xl"} radius={"xl"}
variant="outline" variant="outline"
onClick={() => onClick={() => {
setLoading(true);
router.push( router.push(
RouterProfile.katalog + auhtorSelectedData?.Profile?.id RouterProfile.katalog + auhtorSelectedData?.Profile?.id
) );
} }}
> >
Kunjungi Profile Kunjungi Profile
</Button> </Button>
</Group> </Stack>
<Divider /> </Grid.Col>
</Grid>
<Stack> <Divider />
{Array(5) </>
.fill(0) );
.map((e, i) => ( }
function ForumPosting({ dataPosting }: { dataPosting: MODEL_FORUM_POSTING[] }) {
const router = useRouter();
const [loadingDetail, setLoadingDetail] = useState(false);
const [loadingKomen, setLoadingKomen] = useState(false);
if (loadingDetail) return <ComponentGlobal_V2_LoadingPage />;
if (loadingKomen) return <ComponentGlobal_V2_LoadingPage />;
return (
<>
<ComponentForum_MainCardView
data={dataPosting}
setLoadingKomen={setLoadingKomen}
setLoadingDetail={setLoadingDetail}
/>
{/* <Stack>
{dataPosting.map((e, i) => (
<Card key={i}> <Card key={i}>
<Card.Section> <Card.Section>
<ComponentForum_AuthorNameOnHeader <ComponentForum_PostingAuthorNameOnHeader isMoreButton={true} />
forumId={i as any}
tipe="posting"
isMoreButton={true}
/>
</Card.Section> </Card.Section>
<Card.Section <Card.Section
sx={{ zIndex: 0 }} sx={{ zIndex: 0 }}
p={"sm"} p={"sm"}
onClick={() => { onClick={() => {
// console.log("halaman forum"); // console.log("halaman forum");
setLoaduingDetail(true); setLoadingDetail(true);
router.push(RouterForum.main_detail + i); router.push(RouterForum.main_detail + i);
}} }}
> >
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
<Text fz={"sm"} lineClamp={4}> <Text fz={"sm"} lineClamp={4}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad,
Ad, vitae. Quisquam aspernatur, eius consequatur dicta vitae. Quisquam aspernatur, eius consequatur dicta repellendus
repellendus facere vero recusandae deleniti voluptas quod facere vero recusandae deleniti voluptas quod architecto,
architecto, tenetur totam excepturi rem nam iusto earum. tenetur totam excepturi rem nam iusto earum.
</Text> </Text>
</Stack> </Stack>
</Card.Section> </Card.Section>
@@ -129,8 +221,7 @@ export default function Forum_Forumku({
</Card.Section> </Card.Section>
</Card> </Card>
))} ))}
</Stack> </Stack> */}
</Stack>
</> </>
); );
} }

View File

@@ -4,6 +4,7 @@ import { AppShell } from "@mantine/core";
import React from "react"; import React from "react";
import ComponentForum_HeaderTamplate from "../component/header/header_tamplate"; import ComponentForum_HeaderTamplate from "../component/header/header_tamplate";
import { MODEL_USER } from "@/app_modules/home/model/interface"; import { MODEL_USER } from "@/app_modules/home/model/interface";
import { IconX } from "@tabler/icons-react";
export default function LayoutForum_Forumku({ export default function LayoutForum_Forumku({
children, children,
@@ -15,7 +16,7 @@ export default function LayoutForum_Forumku({
return ( return (
<> <>
<AppShell <AppShell
header={<ComponentForum_HeaderTamplate title={`${username}`} />} header={<ComponentForum_HeaderTamplate title={`${username}`} changeIconBack={<IconX/>}/>}
> >
{children} {children}
</AppShell> </AppShell>

View File

@@ -0,0 +1,15 @@
"use server";
import prisma from "@/app/lib/prisma";
import { revalidatePath } from "next/cache";
export async function forum_countOneTotalKomentarById(postingId: any) {
const data = await prisma.forum_Komentar.count({
where: {
forum_PostingId: postingId,
isActive: true,
},
});
return data
}

View File

@@ -0,0 +1,17 @@
"use server";
import prisma from "@/app/lib/prisma";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
export async function forum_countPostingByAuthorId(authorId: string) {
const data = await prisma.forum_Posting.count({
where: {
authorId: authorId,
isActive: true
},
});
return data
}

View File

@@ -0,0 +1,32 @@
"use server";
import prisma from "@/app/lib/prisma";
// PERCOBAAN
export async function forum_countTotalKomenById(postingId: any[]) {
// console.log(postingId)
const data = postingId.map(async (e) => {
const get = await prisma.forum_Komentar.count({
where: {
forum_PostingId: e,
isActive: true,
},
select: {
forum_PostingId: true,
},
});
console.log(get);
});
// const data = await prisma.forum_Komentar.count({
// where: {
// forum_PostingId: postingId,
// isActive: true,
// },
// });
// return data;
}

View File

@@ -0,0 +1,20 @@
"use server";
import prisma from "@/app/lib/prisma";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import { revalidatePath } from "next/cache";
export async function forum_funCreate(value: string) {
const AuthorId = await User_getUserId();
const create = await prisma.forum_Posting.create({
data: {
diskusi: value,
authorId: AuthorId,
},
});
if (!create) return { status: 400, message: "Gagal menambahkan postingan" };
revalidatePath("/dev/forum/main");
return { status: 201, message: "Berhasil menambahkan postingan" };
}

View File

@@ -0,0 +1,24 @@
"use server";
import prisma from "@/app/lib/prisma";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import { revalidatePath } from "next/cache";
export async function forum_funCreateKomentar(
postingId: string,
komentar: string
) {
const authorId = await User_getUserId();
const create = await prisma.forum_Komentar.create({
data: {
komentar: komentar,
forum_PostingId: postingId,
authorId: authorId,
},
});
if (!create) return { status: 400, message: "Gagal menambahkan komentar" };
revalidatePath("/dev/forum/detail");
return { status: 201, message: "Berhasil menambahkan komentar" };
}

View File

@@ -0,0 +1,19 @@
"use server";
import prisma from "@/app/lib/prisma";
import { revalidatePath } from "next/cache";
export async function forum_funDeleteKomentarById(komentarId: string) {
const del = await prisma.forum_Komentar.update({
where: {
id: komentarId,
},
data: {
isActive: false,
},
});
if (!del) return { status: 400, message: "Gagal Dihapus" };
revalidatePath("/dev/forum/detail");
return { status: 200, message: "Berhasil Dihapus" };
}

View File

@@ -0,0 +1,19 @@
"use server";
import prisma from "@/app/lib/prisma";
import { revalidatePath } from "next/cache";
export async function forum_funDeletePostingById(forumId: string) {
const del = await prisma.forum_Posting.update({
where: {
id: forumId,
},
data: {
isActive: false,
},
});
if (!del) return { status: 400, message: "Gagal dihapus" };
revalidatePath("/dev/forum/main");
return { status: 200, message: "Berhasil dihapus" };
}

View File

@@ -0,0 +1,22 @@
"use server";
import prisma from "@/app/lib/prisma";
import { revalidatePath } from "next/cache";
export async function forum_funEditPostingById(
postingId: string,
diskusi: string
) {
const updt = await prisma.forum_Posting.update({
where: {
id: postingId,
},
data: {
diskusi: diskusi,
},
});
if (!updt) return { status: 400, message: "Gagal update" };
revalidatePath("/dev/forum/main");
return { status: 200, message: "Berhasil update" };
}

View File

@@ -0,0 +1,35 @@
"use server";
import prisma from "@/app/lib/prisma";
export async function forum_getKomentarById(postingId: string) {
const data = await prisma.forum_Komentar.findMany({
orderBy: {
createdAt: "desc",
},
where: {
forum_PostingId: postingId,
isActive: true,
},
select: {
id: true,
isActive: true,
komentar: true,
createdAt: true,
Author: {
select: {
id: true,
Profile: {
select: {
name: true,
imagesId: true,
},
},
},
},
authorId: true,
},
});
return data;
}

View File

@@ -0,0 +1,54 @@
"use server";
import _ from "lodash";
import prisma from "@/app/lib/prisma";
import { forum_countOneTotalKomentarById } from "../count/count_one_total_komentar_by_id";
import { forum_countTotalKomenById } from "../count/count_total_komentar_by_id";
export async function forum_getListAllPosting() {
const get = await prisma.forum_Posting.findMany({
orderBy: {
createdAt: "desc",
},
where: {
isActive: true,
},
select: {
id: true,
diskusi: true,
createdAt: true,
isActive: true,
authorId: true,
Author: {
select: {
id: true,
Profile: true,
},
},
Forum_Komentar: {
where: {
isActive: true,
},
},
// _count: {
// select: {
// Forum_Komentar: true,
// },
// },
},
});
// const data = get.map((v) => ({
// ..._.omit(v, ['Forum_Komentar']),
// total_coment: v.Forum_Komentar.filter((v) => v.isActive).length,
// }));
const data = get.map((val) => ({
..._.omit(val, ["Forum_Komentar"]),
_count: val.Forum_Komentar.length,
}));
// console.log(JSON.stringify(data, null, 2));
return data;
}

View File

@@ -0,0 +1,42 @@
"use server";
import prisma from "@/app/lib/prisma";
import { User_getUserId } from "@/app_modules/fun_global/get_user_token";
import _ from "lodash";
export async function forum_getListPostingByAuhtorId(authorId: string) {
const get = await prisma.forum_Posting.findMany({
orderBy: {
createdAt: "desc",
},
where: {
authorId: authorId,
isActive: true,
},
select: {
id: true,
diskusi: true,
createdAt: true,
isActive: true,
authorId: true,
Author: {
select: {
id: true,
Profile: true,
},
},
Forum_Komentar: {
where: {
isActive: true,
},
},
},
});
const data = get.map((val) => ({
..._.omit(val, ["Forum_Komentar"]),
_count: val.Forum_Komentar.length,
}));
return data;
}

View File

@@ -0,0 +1,32 @@
"use server";
import prisma from "@/app/lib/prisma";
export async function forum_getOnePostingById(postingId: string) {
const data = await prisma.forum_Posting.findFirst({
where: {
id: postingId,
},
select: {
id: true,
diskusi: true,
isActive: true,
createdAt: true,
authorId: true,
Author: {
select: {
id: true,
username: true,
Profile: true,
},
},
_count: {
select: {
Forum_Komentar: true,
},
},
},
});
return data;
}

View File

@@ -1,3 +1,4 @@
import { atomWithStorage } from "jotai/utils"; import { atomWithStorage } from "jotai/utils";
export const gs_forum_loading_edit_posting = atomWithStorage("gs_forum_loading_edit_posting", false) export const gs_forum_loading_edit_posting = atomWithStorage("gs_forum_loading_edit_posting", false)
export const gs_forum_total_komentar = atomWithStorage("gs_forum_total_komentar", 0);

View File

@@ -10,7 +10,7 @@ import {
Stack, Stack,
Text, Text,
} from "@mantine/core"; } from "@mantine/core";
import ComponentForum_AuthorNameOnHeader from "../component/author_header_name"; import ComponentForum_PostingAuthorNameOnHeader from "../component/header/posting_author_header_name";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -23,36 +23,74 @@ const ReactQuill = dynamic(
import "react-quill/dist/quill.bubble.css"; import "react-quill/dist/quill.bubble.css";
import { IconPhotoUp } from "@tabler/icons-react"; import { IconPhotoUp } from "@tabler/icons-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { MODEL_FORUM_POSTING } from "../model/interface";
import { forum_funCreateKomentar } from "../fun/create/fun_create_komentra";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
export default function Forum_Komentar({ forumId }: { forumId: string }) { export default function Forum_Komentar({
const [value, setValue] = useState(""); dataPosting,
}: {
dataPosting: MODEL_FORUM_POSTING;
}) {
return ( return (
<> <>
<Stack px={"sm"}> <Stack px={"sm"}>
<Card> <Card>
<Card.Section> <Card.Section>
<ComponentForum_AuthorNameOnHeader <ComponentForum_PostingAuthorNameOnHeader
forumId={forumId} authorId={dataPosting?.Author?.id}
tipe="komentar" authorName={dataPosting?.Author?.Profile?.name}
imagesId={dataPosting?.Author?.Profile?.imagesId}
postingId={dataPosting?.id}
tglPublish={dataPosting?.createdAt}
/> />
</Card.Section> </Card.Section>
<Card.Section sx={{ zIndex: 0 }} p={"sm"}> <Card.Section sx={{ zIndex: 0 }} p={"sm"}>
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
<Text fz={"sm"}> <Text fz={"sm"}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad, {dataPosting?.diskusi ? (
vitae. Quisquam aspernatur, eius consequatur dicta repellendus <div
facere vero recusandae deleniti voluptas quod architecto, dangerouslySetInnerHTML={{ __html: dataPosting?.diskusi }}
tenetur totam excepturi rem nam iusto earum. />
) : (
""
)}
</Text> </Text>
</Stack> </Stack>
</Card.Section> </Card.Section>
</Card> </Card>
<CreateKomentar postingId={dataPosting?.id} />
</Stack>
</>
);
}
function CreateKomentar({ postingId }: { postingId: string }) {
const router = useRouter();
const [value, setValue] = useState("");
const [loading, setLoading] = useState(false);
async function onComment() {
await forum_funCreateKomentar(postingId, value).then((res) => {
if (res.status === 201) {
setLoading(true);
ComponentGlobal_NotifikasiBerhasil(res.message);
router.replace(RouterForum.main_detail + postingId, {scroll: false});
router.refresh()
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
});
}
return (
<>
<Stack> <Stack>
<Paper withBorder shadow="lg"> <Paper withBorder shadow="lg">
<ReactQuill <ReactQuill
theme="bubble" theme="bubble"
placeholder="Posting balasan anda?" placeholder="Ketik komentar anda?"
// style={{ height: 150 }} // style={{ height: 150 }}
onChange={(val) => { onChange={(val) => {
setValue(val); setValue(val);
@@ -60,27 +98,16 @@ export default function Forum_Komentar({ forumId }: { forumId: string }) {
/> />
</Paper> </Paper>
<Group position="right"> <Group position="right">
{/* <ActionIcon>
<IconPhotoUp />
</ActionIcon> */}
<ButtonAction forumId={forumId} />
</Group>
</Stack>
</Stack>
</>
);
}
function ButtonAction({ forumId }: { forumId: string }) {
const router = useRouter();
return (
<>
<Button <Button
loaderPosition="center"
loading={loading ? true : false}
radius={"xl"} radius={"xl"}
onClick={() => router.replace(RouterForum.main_detail + forumId)} onClick={() => onComment()}
> >
Balas Balas
</Button> </Button>
</Group>
</Stack>
</> </>
); );
} }

View File

@@ -13,33 +13,41 @@ import {
Stack, Stack,
Divider, Divider,
Group, Group,
Box,
} from "@mantine/core"; } from "@mantine/core";
import { useTimeout, useWindowScroll } from "@mantine/hooks"; import { useShallowEffect, useTimeout, useWindowScroll } from "@mantine/hooks";
import { import {
IconCirclePlus, IconCirclePlus,
IconMessageCircle, IconMessageCircle,
IconPencilPlus, IconPencilPlus,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import ComponentForum_AuthorNameOnHeader from "../component/author_header_name"; import ComponentForum_PostingAuthorNameOnHeader from "../component/header/posting_author_header_name";
import { useState } from "react"; import { useState } from "react";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2"; import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { gs_forum_loading_edit_posting } from "../global_state"; import { gs_forum_loading_edit_posting } from "../global_state";
import { MODEL_FORUM_POSTING } from "../model/interface";
import ComponentForum_MainCardView from "../component/main_card_view";
export default function Forum_Beranda() { export default function Forum_Beranda({
listForum,
}: {
listForum: MODEL_FORUM_POSTING[];
}) {
const router = useRouter(); const router = useRouter();
const skrng = Date.now();
const [scroll, scrollTo] = useWindowScroll(); const [scroll, scrollTo] = useWindowScroll();
const [loadingCreate, setLoadingCreate] = useState(false); const [loadingCreate, setLoadingCreate] = useState(false);
const [loadingKomen, setLoadingKomen] = useState(false); const [loadingKomen, setLoadingKomen] = useState(false);
const [loadingDetail, setLoaduingDetail] = useState(false); const [loadingDetail, setLoadingDetail] = useState(false);
if (loadingDetail) return <ComponentGlobal_V2_LoadingPage />; if (loadingDetail) return <ComponentGlobal_V2_LoadingPage />;
if (loadingKomen) return <ComponentGlobal_V2_LoadingPage />; if (loadingKomen) return <ComponentGlobal_V2_LoadingPage />;
return ( return (
<> <>
{/* <pre>{JSON.stringify(listForum, null, 2)}</pre> */}
<Affix position={{ bottom: rem(100), right: rem(30) }}> <Affix position={{ bottom: rem(100), right: rem(30) }}>
<ActionIcon <ActionIcon
loading={loadingCreate ? true : false} loading={loadingCreate ? true : false}
@@ -60,16 +68,25 @@ export default function Forum_Beranda() {
</ActionIcon> </ActionIcon>
</Affix> </Affix>
<Stack px={"sm"}> <Box px={"sm"}>
{Array(5) <ComponentForum_MainCardView
.fill(0) data={listForum}
.map((e, i) => ( setLoadingKomen={setLoadingKomen}
setLoadingDetail={setLoadingDetail}
/>
</Box>
{/* <Stack px={"sm"}>
{listForum.map((e, i) => (
<Card key={i}> <Card key={i}>
<Card.Section> <Card.Section>
<ComponentForum_AuthorNameOnHeader <ComponentForum_PostingAuthorNameOnHeader
forumId={i as any} authorName={e?.Author?.Profile?.name}
tipe="posting" imagesId={e?.Author?.Profile?.imagesId}
tglPublish={e?.createdAt}
isMoreButton={true} isMoreButton={true}
authorId={e?.Author?.id}
postingId={e?.id}
/> />
</Card.Section> </Card.Section>
<Card.Section <Card.Section
@@ -77,16 +94,13 @@ export default function Forum_Beranda() {
p={"sm"} p={"sm"}
onClick={() => { onClick={() => {
// console.log("halaman forum"); // console.log("halaman forum");
setLoaduingDetail(true); setLoadingDetail(true);
router.push(RouterForum.main_detail + i); router.push(RouterForum.main_detail + e.id);
}} }}
> >
<Stack spacing={"xs"}> <Stack spacing={"xs"}>
<Text fz={"sm"} lineClamp={4}> <Text fz={"sm"} lineClamp={4}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. <div dangerouslySetInnerHTML={{ __html: e.diskusi }} />
Ad, vitae. Quisquam aspernatur, eius consequatur dicta
repellendus facere vero recusandae deleniti voluptas quod
architecto, tenetur totam excepturi rem nam iusto earum.
</Text> </Text>
</Stack> </Stack>
</Card.Section> </Card.Section>
@@ -99,19 +113,22 @@ export default function Forum_Beranda() {
sx={{ zIndex: 1 }} sx={{ zIndex: 1 }}
onClick={() => { onClick={() => {
setLoadingKomen(true); setLoadingKomen(true);
router.push(RouterForum.komentar + i); router.push(RouterForum.komentar + e.id);
}} }}
> >
<IconMessageCircle color="gray" size={25} /> <IconMessageCircle color="gray" size={25} />
</ActionIcon> </ActionIcon>
<Text c={"gray"}>1</Text>
<TotalKomentar postingId={e?.id} />
</Group> </Group>
<Divider /> <Divider />
</Stack> </Stack>
</Card.Section> </Card.Section>
</Card> </Card>
))} ))}
</Stack> </Stack> */}
</> </>
); );
} }

View File

@@ -23,6 +23,7 @@ import { useRouter } from "next/navigation";
import { title } from "process"; import { title } from "process";
import { MODEL_USER } from "@/app_modules/home/model/interface"; import { MODEL_USER } from "@/app_modules/home/model/interface";
import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
export default function LayoutForum_Main({ export default function LayoutForum_Main({
children, children,
@@ -35,6 +36,8 @@ export default function LayoutForum_Main({
const [hotMenu, setHotMenu] = useState(1); const [hotMenu, setHotMenu] = useState(1);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
if (loading) return <ComponentGlobal_V2_LoadingPage />;
const listFooter = [ const listFooter = [
{ {
id: 1, id: 1,
@@ -60,6 +63,7 @@ export default function LayoutForum_Main({
<ActionIcon <ActionIcon
variant="transparent" variant="transparent"
onClick={() => { onClick={() => {
setLoading(true);
return router.push(RouterHome.main_home); return router.push(RouterHome.main_home);
}} }}
> >

View File

@@ -0,0 +1,24 @@
import { MODEL_USER } from "@/app_modules/home/model/interface";
export interface MODEL_FORUM_POSTING {
id: string;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
publishAt: Date;
diskusi: string;
authorId: string;
Author: MODEL_USER;
_count: number
}
export interface MODEL_FORUM_KOMENTAR {
id: string;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
komentar: string;
forum_PostingId: string;
authorId: string;
Author: MODEL_USER
}

View File

@@ -37,6 +37,7 @@ export default async function funCreatePortofolio(
const createProto = await prisma.portofolio.create({ const createProto = await prisma.portofolio.create({
data: { data: {
profileId: profileId, profileId: profileId,
id_Portofolio: "Porto" + Date.now().toString(),
namaBisnis: data.namaBisnis, namaBisnis: data.namaBisnis,
deskripsi: data.deskripsi, deskripsi: data.deskripsi,
tlpn: data.tlpn, tlpn: data.tlpn,