perbaikan(kolaborasi): memperbaiki bug modul kolaborasi dan tingkatkan komponen UI

Deskripsi:

- Perbaikan endpoint API dan routing kolaborasi
- Pembaruan tampilan grup kolaborasi dan partisipasi proyek
- Peningkatan komponen skeleton loading
- Perbaikan tampilan komponen avatar dan username
- Refaktor layout pembuatan dan detail kolaborasi
This commit is contained in:
2025-01-02 14:42:49 +08:00
parent ea2e52937c
commit d0def08ff6
26 changed files with 981 additions and 459 deletions

View File

@@ -19,15 +19,15 @@ export default function ComponentColab_DetailData({
<Stack spacing={"sm"}>
<Grid>
<Grid.Col span={2}>
<Text fw={"bold"} fz={"sm"}>
<Text fw={"bold"} >
Industri
</Text>
</Grid.Col>
<Grid.Col span={1}>
<Text fz={"sm"}>:</Text>
<Text >:</Text>
</Grid.Col>
<Grid.Col span={"auto"}>
<Text fz={"sm"}>
<Text >
{data?.ProjectCollaborationMaster_Industri.name
? data.ProjectCollaborationMaster_Industri.name
: "Industri"}
@@ -37,31 +37,31 @@ export default function ComponentColab_DetailData({
<Grid>
<Grid.Col span={2}>
<Text fw={"bold"} fz={"sm"}>
<Text fw={"bold"} >
Lokasi
</Text>
</Grid.Col>
<Grid.Col span={1}>
<Text fz={"sm"}>:</Text>
<Text >:</Text>
</Grid.Col>
<Grid.Col span={"auto"}>
<Text fz={"sm"} lineClamp={1}>
<Text lineClamp={1}>
{data?.lokasi ? data.lokasi : " Lokasi dari proyek"}
</Text>
</Grid.Col>
</Grid>
<Stack spacing={5}>
<Text fw={"bold"} fz={"sm"}>
<Text fw={"bold"} >
Tujuan proyek
</Text>
<Text fz={"sm"}>{data?.purpose ? data?.purpose : "-"}</Text>
<Text >{data?.purpose ? data?.purpose : "-"}</Text>
</Stack>
<Stack spacing={5}>
<Text fw={"bold"} fz={"sm"}>
<Text fw={"bold"} >
Keuntungan
</Text>
<Text fz={"sm"}>{data?.benefit ? data?.benefit : "-"}</Text>
<Text >{data?.benefit ? data?.benefit : "-"}</Text>
</Stack>
</Stack>
</Box>

View File

@@ -4,9 +4,14 @@ import {
AccentColor,
MainColor,
} from "@/app_modules/_global/color/color_pallet";
import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import ComponentGlobal_InputCountDown from "@/app_modules/_global/component/input_countdown";
import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal";
import notifikasiToUser_funCreate from "@/app_modules/notifikasi/fun/create/create_notif_to_user";
import { clientLogger } from "@/util/clientLogger";
import mqtt_client from "@/util/mqtt_client";
import {
ActionIcon,
Box,
@@ -14,86 +19,219 @@ import {
Center,
Drawer,
Group,
Paper,
ScrollArea,
Loader,
Stack,
Text,
Textarea,
Title,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconX } from "@tabler/icons-react";
import _ from "lodash";
import { ScrollOnly } from "next-scroll-loader";
import { useParams } from "next/navigation";
import { useState } from "react";
import { apiGetOneCollaborationById } from "../../_lib/api_collaboration";
import colab_funCreatePartisipan from "../../fun/create/fun_create_partisipan_by_user_id";
import colab_getListPartisipanByColabId from "../../fun/get/get_list_partisipan_by_id";
import { MODEL_COLLABORATION_PARTISIPASI } from "../../model/interface";
import { Collaboration_SkeletonListPrtisipanIsUser } from "../skeleton_view";
import ComponentColab_AuthorNameOnListPartisipan from "./header_author_list_partisipan";
import notifikasiToUser_funCreate from "@/app_modules/notifikasi/fun/create/create_notif_to_user";
import mqtt_client from "@/util/mqtt_client";
import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
export default function ComponentColab_DetailListPartisipasiUser({
listPartisipan,
userLoginId,
authorId,
colabId,
cekPartisipan,
}: {
listPartisipan?: MODEL_COLLABORATION_PARTISIPASI[];
userLoginId?: string;
authorId?: string;
colabId?: string;
cekPartisipan?: boolean;
}) {
const [apply, setApply] = useState(false);
const [data, setData] = useState(listPartisipan);
const params = useParams<{ id: string }>();
const [data, setData] = useState<MODEL_COLLABORATION_PARTISIPASI[] | null>(
null
);
const [isPartisipan, setCekPartisipan] = useState<boolean | null>(null);
const [activePage, setActivePage] = useState(1);
const [opened, { open, close }] = useDisclosure(false);
const [deskripsi, setDeskripsi] = useState("");
const [isLoading, setIsLoading] = useState(false);
useShallowEffect(() => {
onLoadDataPartisipan();
}, []);
async function onJoin() {
const res = await colab_funCreatePartisipan(
colabId as any,
userLoginId as any,
deskripsi
);
if (res.status === 201) {
const dataNotif = {
appId: res?.data?.ProjectCollaboration?.id,
userId: res?.data?.ProjectCollaboration?.userId,
pesan: res?.data?.ProjectCollaboration?.title,
status: "Partisipan Project",
kategoriApp: "COLLABORATION",
title: "Partisipan baru telah bergabung !",
};
const createNotifikasi = await notifikasiToUser_funCreate({
data: dataNotif as any,
async function onLoadDataPartisipan() {
try {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "list_partisipan",
page: `${activePage}`,
});
if (createNotifikasi.status === 201) {
mqtt_client.publish(
"USER",
JSON.stringify({
userId: dataNotif.userId,
count: 1,
})
);
if (respone) {
setData(respone.data);
}
const resList = await colab_getListPartisipanByColabId(colabId as any);
setApply(true);
close();
setData(resList as any);
ComponentGlobal_NotifikasiBerhasil(res.message);
} else {
ComponentGlobal_NotifikasiGagal(res.message);
} catch (error) {
clientLogger.error("Error get list partisipan", error);
}
}
useShallowEffect(() => {
onCheckPartisipasi();
}, []);
async function onCheckPartisipasi() {
try {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "cek_partisipasi",
});
if (respone) {
setCekPartisipan(respone.data);
}
} catch (error) {
clientLogger.error("Error cek partisipasi", error);
}
}
async function onJoin() {
try {
setIsLoading(true);
const res = await colab_funCreatePartisipan({
id: params.id,
deskripsi: deskripsi,
});
if (res.status === 201) {
// const dataNotif = {
// appId: res?.data?.ProjectCollaboration?.id,
// userId: res?.data?.ProjectCollaboration?.userId,
// pesan: res?.data?.ProjectCollaboration?.title,
// status: "Partisipan Project",
// kategoriApp: "COLLABORATION",
// title: "Partisipan baru telah bergabung !",
// };
// const createNotifikasi = await notifikasiToUser_funCreate({
// data: dataNotif as any,
// });
// if (createNotifikasi.status === 201) {
// mqtt_client.publish(
// "USER",
// JSON.stringify({
// userId: dataNotif.userId,
// count: 1,
// })
// );
// }
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "list_partisipan",
page: `${activePage}`,
});
if (respone) {
setData(respone.data);
}
const cekPartisipan = await apiGetOneCollaborationById({
id: params.id,
kategori: "cek_partisipasi",
});
if (cekPartisipan) {
setCekPartisipan(cekPartisipan);
}
close();
ComponentGlobal_NotifikasiBerhasil(res.message);
} else {
ComponentGlobal_NotifikasiGagal(res.message);
}
} catch (error) {
clientLogger.error("Error create partisipan", error);
} finally {
setIsLoading(false);
}
}
if (_.isNull(data) || _.isNull(isPartisipan)) {
return <Collaboration_SkeletonListPrtisipanIsUser />;
}
return (
<>
<Stack>
{userLoginId !== authorId && (
<Center>
<Button
radius={"xl"}
disabled={isPartisipan}
color={isPartisipan ? "green" : "yellow"}
onClick={open}
// bg={MainColor.yellow}
>
{isPartisipan ? "Telah Berpartisipasi" : "Partisipasi"}
</Button>
</Center>
)}
{_.isEmpty(data) ? (
<ComponentGlobal_CardStyles>
<Stack>
<Center>
<Title order={5}>Partispasi User ({data?.length})</Title>
</Center>
<ComponentGlobal_IsEmptyData
height={10}
text=" Tidak ada partisipan"
/>
</Stack>
</ComponentGlobal_CardStyles>
) : (
<ComponentGlobal_CardStyles>
<Stack spacing={"xl"}>
<Center>
<Title order={5}>Partispasi User ({data?.length})</Title>
</Center>{" "}
<Box>
<ScrollOnly
height="50vh"
renderLoading={() => (
<Center mt={"lg"}>
<Loader color={"yellow"} />
</Center>
)}
data={data}
setData={setData as any}
moreData={async () => {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "list_partisipan",
page: `${activePage + 1}`,
});
setActivePage((val) => val + 1);
return respone.data;
}}
>
{(item) => (
<ComponentColab_AuthorNameOnListPartisipan
isPembatas={true}
author={item.User}
deskripsi={item.deskripsi_diri}
/>
)}
</ScrollOnly>
</Box>
</Stack>
</ComponentGlobal_CardStyles>
)}
</Stack>
<Drawer
opened={opened}
onClose={close}
@@ -149,6 +287,8 @@ export default function ComponentColab_DetailListPartisipasiUser({
maxInput={300}
/>
<Button
loaderPosition="center"
loading={isLoading}
disabled={!deskripsi}
radius={"xl"}
color="yellow"
@@ -163,55 +303,6 @@ export default function ComponentColab_DetailListPartisipasiUser({
</Group>
</Stack>
</Drawer>
<Stack>
{userLoginId !== authorId ? (
<Center>
<Button
radius={"xl"}
disabled={cekPartisipan ? true : false}
color={cekPartisipan ? "green" : "yellow"}
onClick={open}
// bg={MainColor.yellow}
>
{cekPartisipan ? "Telah Berpartisipasi" : "Partisipasi"}
</Button>
</Center>
) : (
""
)}
<ComponentGlobal_CardStyles>
<Stack spacing={"xl"}>
<Center>
<Title order={5}>Partispasi User ({data?.length})</Title>
</Center>{" "}
<ScrollArea h={data?.length === 0 ? 30 : 400}>
<Box>
<Stack>
{data?.length === 0 ? (
<Center>
<Text fz={"xs"} fw={"bold"} c={"gray"}>
Tidak ada partisipan
</Text>
</Center>
) : (
data?.map((e, i) => (
<Box key={i}>
<ComponentColab_AuthorNameOnListPartisipan
isPembatas={true}
author={e.User}
deskripsi={e.deskripsi_diri}
/>
</Box>
))
)}
</Stack>
</Box>
</ScrollArea>
</Stack>
</ComponentGlobal_CardStyles>
</Stack>
</>
);
}

View File

@@ -3,6 +3,8 @@
import { ComponentGlobal_AvatarAndUsername } from "@/app_modules/_global/component";
import { Group, Stack, Text } from "@mantine/core";
import { useRouter } from "next/navigation";
import moment from "moment";
import "moment/locale/id";
export default function ComponentColab_AuthorNameOnHeader({
tglPublish,
@@ -19,10 +21,8 @@ export default function ComponentColab_AuthorNameOnHeader({
component={
<Group position="right">
{tglPublish ? (
<Text fz={"xs"}>
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "medium",
}).format(tglPublish)}
<Text lineClamp={1}>
{moment(tglPublish).locale("id").format("ll")}
</Text>
) : (
""
@@ -65,7 +65,7 @@ export default function ComponentColab_AuthorNameOnHeader({
<Grid.Col span={"content"}>
<Stack justify="center" h={"100%"}>
{tglPublish ? (
<Text fz={"xs"}>
<Text >
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "medium"

View File

@@ -1,30 +1,38 @@
import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import { Center, Grid, Skeleton, Stack } from "@mantine/core";
import { Center, Grid, Group, Skeleton, Stack } from "@mantine/core";
export {
Collaboration_SkeletonCreate,
Collaboration_SkeletonBeranda,
Collaboration_SkeletonCreate,
Collaboration_SkeletonDetail,
Collaboration_SkeletonDetailInfoGroup,
Collaboration_SkeletonGrup,
Collaboration_SkeletonListPartisipan,
Collaboration_SkeletonListPrtisipanIsUser,
};
function Collaboration_SkeletonCreate() {
return (
<>
<Stack px={"xl"} spacing={"lg"}>
<Stack px={"xl"} spacing={"md"}>
<Stack spacing={"xs"}>
<Skeleton height={10} width={50} />
<Skeleton height={15} width={50} />
<Skeleton height={40} />
</Stack>
<Stack spacing={"xs"}>
<Skeleton height={10} width={50} />
<Skeleton height={15} width={50} />
<Skeleton height={40} />
</Stack>
<Stack spacing={"xs"}>
<Skeleton height={10} width={50} />
<Skeleton height={15} width={50} />
<Skeleton height={40} />
</Stack>
<Stack spacing={"xs"}>
<Skeleton height={15} width={50} />
<Skeleton height={130} />
</Stack>
<Stack spacing={"xs"}>
<Skeleton height={10} width={50} />
<Skeleton height={15} width={50} />
<Skeleton height={130} />
</Stack>
@@ -94,9 +102,176 @@ function Collaboration_SkeletonGrup() {
<>
{Array.from(new Array(2)).map((e, i) => (
<ComponentGlobal_CardStyles marginBottom={"15px"} key={i}>
<Skeleton h={40} />
<Group position="apart">
<Stack>
<Skeleton h={15} w={100} />
<Skeleton h={15} w={50} />
</Stack>
<Skeleton circle height={20} />
</Group>
</ComponentGlobal_CardStyles>
))}
</>
);
}
function Collaboration_SkeletonDetail() {
return (
<>
<ComponentGlobal_CardStyles marginBottom={"0"}>
<Stack spacing={"xl"}>
<Grid align="center" gutter={"md"}>
<Grid.Col span={"content"}>
<Skeleton circle height={40} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={150} />
</Grid.Col>
</Grid>
<Center>
<Skeleton height={20} w={200} />
</Center>
<Grid align="center" gutter={"md"}>
<Grid.Col span={"content"}>
<Skeleton h={20} w={70} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={200} />
</Grid.Col>
</Grid>
<Grid align="center" gutter={"md"}>
<Grid.Col span={"content"}>
<Skeleton h={20} w={70} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={200} />
</Grid.Col>
</Grid>
<Skeleton height={20} w={100} />
<Skeleton height={20} w={"100%"} />
<Skeleton height={20} w={100} />
<Skeleton height={20} w={"100%"} />
</Stack>
</ComponentGlobal_CardStyles>
</>
);
}
function Collaboration_SkeletonListPrtisipanIsUser() {
return (
<>
<Stack>
<ComponentGlobal_CardStyles marginBottom={"0"}>
<Stack>
<Center>
<Skeleton h={20} w={100} />
</Center>
{Array.from(new Array(2)).map((e, i) => (
<Grid align="center" gutter={"md"} key={i}>
<Grid.Col span={"content"}>
<Skeleton circle height={40} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={150} />
</Grid.Col>
</Grid>
))}
</Stack>
</ComponentGlobal_CardStyles>
</Stack>
</>
);
}
function Collaboration_SkeletonListPartisipan() {
return (
<>
<Stack>
<Center>
<Skeleton h={40} w={150} radius={"xl"} />
</Center>
<ComponentGlobal_CardStyles marginBottom={"0"}>
<Stack>
<Center>
<Skeleton h={20} w={100} />
</Center>
{Array.from(new Array(2)).map((e, i) => (
<Grid align="center" gutter={"md"} key={i}>
<Grid.Col span={"content"}>
<Skeleton circle height={40} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={150} />
</Grid.Col>
</Grid>
))}
</Stack>
</ComponentGlobal_CardStyles>
</Stack>
</>
);
}
function Collaboration_SkeletonDetailInfoGroup() {
return (
<>
<Stack>
<ComponentGlobal_CardStyles marginBottom={"0"}>
<Stack spacing={"xl"}>
<Center>
<Skeleton height={20} w={200} />
</Center>
<Grid align="center" gutter={"md"}>
<Grid.Col span={"content"}>
<Skeleton h={20} w={70} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={200} />
</Grid.Col>
</Grid>
<Grid align="center" gutter={"md"}>
<Grid.Col span={"content"}>
<Skeleton h={20} w={70} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={200} />
</Grid.Col>
</Grid>
<Skeleton height={20} w={100} />
<Skeleton height={20} w={"100%"} />
<Skeleton height={20} w={"100%"} />
<Skeleton height={20} w={100} />
<Skeleton height={20} w={"100%"} />
<Skeleton height={20} w={"100%"} />
</Stack>
</ComponentGlobal_CardStyles>
<ComponentGlobal_CardStyles marginBottom={"0"}>
<Stack>
<Skeleton h={20} w={100} />
{Array.from(new Array(2)).map((e, i) => (
<Grid align="center" gutter={"md"} key={i}>
<Grid.Col span={"content"}>
<Skeleton circle height={40} />
</Grid.Col>
<Grid.Col span={3}>
<Skeleton height={20} w={150} />
</Grid.Col>
</Grid>
))}
</Stack>
</ComponentGlobal_CardStyles>
</Stack>
</>
);
}