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

@@ -26,7 +26,7 @@ import {
IconSend,
} from "@tabler/icons-react";
import _ from "lodash";
import { useRouter } from "next/navigation";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import useInfiniteScroll, {
ScrollDirection,
@@ -43,6 +43,8 @@ import {
MainColor,
} from "@/app_modules/_global/color/color_pallet";
import ComponentGlobal_Loader from "@/app_modules/_global/component/loader";
import { apiGetDataGroupById } from "../../_lib/api_collaboration";
import { clientLogger } from "@/util/clientLogger";
export default function Colab_GroupChatView({
userLoginId,
@@ -55,6 +57,7 @@ export default function Colab_GroupChatView({
selectRoom: MODEL_COLLABORATION_ROOM_CHAT;
dataUserLogin: MODEL_USER;
}) {
const params = useParams<{ id: string }>();
const router = useRouter();
const [loadingBack, setLoadingBack] = useState(false);
const [loadingInfo, setLoadingInfo] = useState(false);
@@ -66,6 +69,30 @@ export default function Colab_GroupChatView({
const [isGet, setIsGet] = useState(true);
const [newMessageId, setIdMessage] = useState("");
// NEW
const [dataGroup, setDataGroup] =
useState<MODEL_COLLABORATION_ROOM_CHAT | null>(null);
useShallowEffect(() => {
onLoadDataGroup();
}, []);
async function onLoadDataGroup() {
try {
const respone = await apiGetDataGroupById({
id: params.id,
kategori: "detail",
});
if (respone) {
setDataGroup(respone.data);
}
} catch (error) {
clientLogger.error("Error get data group", error);
}
}
const next = async (direction: ScrollDirection) => {
try {
setIsLoading(true);
@@ -220,7 +247,9 @@ export default function Colab_GroupChatView({
radius={"xl"}
onClick={() => {
setLoadingInfo(true);
router.push(RouterColab.info_grup + selectRoom.id, {scroll: false});
router.push(RouterColab.info_grup + selectRoom.id, {
scroll: false,
});
}}
>
{loadingInfo ? (

View File

@@ -1,56 +1,90 @@
"use client";
import { AccentColor } from "@/app_modules/_global/color/color_pallet";
import { ComponentGlobal_AvatarAndUsername, ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import {
ComponentGlobal_AvatarAndUsername,
ComponentGlobal_CardStyles,
} from "@/app_modules/_global/component";
import UIGlobal_LayoutHeaderTamplate from "@/app_modules/_global/ui/ui_header_tamplate";
import UIGlobal_LayoutTamplate from "@/app_modules/_global/ui/ui_layout_tamplate";
import { Box, Paper, Stack, Title } from "@mantine/core";
import { clientLogger } from "@/util/clientLogger";
import { Box, Stack, Title } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import _ from "lodash";
import { useParams } from "next/navigation";
import { useState } from "react";
import {
apiGetDataGroupById
} from "../../_lib/api_collaboration";
import ComponentColab_DetailData from "../../component/detail/detail_data";
import { MODEL_COLLABORATION_ROOM_CHAT } from "../../model/interface";
import {
Collaboration_SkeletonDetailInfoGroup
} from "../../component/skeleton_view";
import {
MODEL_COLLABORATION_ROOM_CHAT
} from "../../model/interface";
export default function Colab_DetailInfoGrup({
dataRoom,
}: {
dataRoom: MODEL_COLLABORATION_ROOM_CHAT;
}) {
export default function Colab_DetailInfoGrup() {
return (
<>
<UIGlobal_LayoutTamplate
header={<UIGlobal_LayoutHeaderTamplate title="Info Grup" />}
>
<InfoGroup dataRoom={dataRoom} />
<InfoGroup />
</UIGlobal_LayoutTamplate>
</>
);
}
function InfoGroup({ dataRoom }: { dataRoom: MODEL_COLLABORATION_ROOM_CHAT }) {
function InfoGroup() {
const params = useParams<{ id: string }>();
const [data, setData] = useState<MODEL_COLLABORATION_ROOM_CHAT | null>(null);
useShallowEffect(() => {
onLoadData();
}, []);
async function onLoadData() {
try {
const respone = await apiGetDataGroupById({
id: params.id,
kategori: "info_group",
});
if (respone) {
setData(respone.data);
}
} catch (error) {
clientLogger.error("Error get data info group", error);
}
}
if (_.isNull(data)) {
return (
<>
<Collaboration_SkeletonDetailInfoGroup />
</>
);
}
return (
<>
<ComponentGlobal_CardStyles>
<Stack>
<ComponentColab_DetailData data={dataRoom.ProjectCollaboration} />
<Paper
style={{
border: `2px solid ${AccentColor.softblue}`,
backgroundColor: AccentColor.blue,
color: "white",
borderRadius: "10px",
padding: "15px",
}}
>
<Stack>
<Title order={6}>Anggota Grup</Title>
{dataRoom.ProjectCollaboration_AnggotaRoomChat.map((e, i) => (
<Box key={i}>
<ComponentGlobal_AvatarAndUsername profile={e.User.Profile as any}/>
</Box>
))}
</Stack>
</Paper>
</Stack>
</ComponentGlobal_CardStyles>
<Stack>
<ComponentGlobal_CardStyles marginBottom={"0px"}>
<ComponentColab_DetailData data={data?.ProjectCollaboration} />
</ComponentGlobal_CardStyles>
<ComponentGlobal_CardStyles>
<Stack>
<Title order={6}>Anggota Grup</Title>
{data?.ProjectCollaboration_AnggotaRoomChat.map((e, i) => (
<Box mb={"sm"} key={i}>
<ComponentGlobal_AvatarAndUsername
profile={e.User.Profile as any}
/>
</Box>
))}
</Stack>
</ComponentGlobal_CardStyles>
</Stack>
</>
);
}

View File

@@ -1,38 +1,26 @@
"use client";
import { AccentColor } from "@/app_modules/_global/color/color_pallet";
import { Center, Stack, Loader } from "@mantine/core";
import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import { clientLogger } from "@/util/clientLogger";
import { Stack } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import _ from "lodash";
import { useParams } from "next/navigation";
import { useState } from "react";
import { apiGetOneCollaborationById } from "../../_lib/api_collaboration";
import ComponentColab_DetailData from "../../component/detail/detail_data";
import ComponentColab_DetailListPartisipasiUser from "../../component/detail/list_partisipasi_user";
import ComponentColab_AuthorNameOnHeader from "../../component/header_author_name";
import { Collaboration_SkeletonDetail } from "../../component/skeleton_view";
import { MODEL_COLLABORATION } from "../../model/interface";
import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import { clientLogger } from "@/util/clientLogger";
import { useShallowEffect } from "@mantine/hooks";
import { useState } from "react";
import {
apiGetAllCollaboration,
apiGetOneCollaborationById,
} from "../../_lib/api_collaboration";
import { useParams } from "next/navigation";
import _ from "lodash";
export default function Colab_MainDetail({
dataColab,
userLoginId,
listPartisipan,
cekPartisipan,
}: {
dataColab?: MODEL_COLLABORATION;
userLoginId?: string;
listPartisipan?: any[];
cekPartisipan: boolean;
}) {
const params = useParams<{ id: string }>();
const [data, setData] = useState<MODEL_COLLABORATION | null>(null);
const [activePage, setActivePage] = useState(1);
const [isNewPost, setIsNewPost] = useState(false);
useShallowEffect(() => {
onLoadData();
@@ -42,6 +30,7 @@ export default function Colab_MainDetail({
try {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "detail",
});
if (respone) {
@@ -52,38 +41,27 @@ export default function Colab_MainDetail({
}
}
if (_.isNull(data)) {
return (
<>
<Center>
<Loader />
</Center>
</>
);
}
return (
<>
<Stack>
<ComponentGlobal_CardStyles>
<Stack>
<ComponentColab_AuthorNameOnHeader
tglPublish={new Date()}
profile={data?.Author?.Profile as any}
/>
<ComponentColab_DetailData data={data as any} />
</Stack>
</ComponentGlobal_CardStyles>
{_.isNull(data) ? (
<Collaboration_SkeletonDetail />
) : (
<ComponentGlobal_CardStyles>
<Stack>
<ComponentColab_AuthorNameOnHeader
tglPublish={data.createdAt}
profile={data?.Author?.Profile as any}
/>
<ComponentColab_DetailData data={data as any} />
</Stack>
</ComponentGlobal_CardStyles>
)}
<ComponentColab_DetailListPartisipasiUser
listPartisipan={listPartisipan}
userLoginId={userLoginId}
authorId={data?.Author.id}
colabId={data?.id}
cekPartisipan={cekPartisipan}
/>
{/* <ComponentGlobal_CardStyles>
</ComponentGlobal_CardStyles> */}
<ComponentColab_DetailListPartisipasiUser
userLoginId={userLoginId}
authorId={data?.Author.id}
/>
</Stack>
</>
);

View File

@@ -1,36 +1,60 @@
"use client";
import { AccentColor } from "@/app_modules/_global/color/color_pallet";
import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import { apiGetOneCollaborationById } from "@/app_modules/colab/_lib/api_collaboration";
import ComponentColab_DetailData from "@/app_modules/colab/component/detail/detail_data";
import ComponentColab_DetailListPartisipasiUser from "@/app_modules/colab/component/detail/list_partisipasi_user";
import ComponentColab_AuthorNameOnHeader from "@/app_modules/colab/component/header_author_name";
import {
MODEL_COLLABORATION,
MODEL_COLLABORATION_PARTISIPASI,
} from "@/app_modules/colab/model/interface";
import { Stack, Text } from "@mantine/core";
import { Collaboration_SkeletonDetail } from "@/app_modules/colab/component/skeleton_view";
import { MODEL_COLLABORATION } from "@/app_modules/colab/model/interface";
import { clientLogger } from "@/util/clientLogger";
import { Stack } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import _ from "lodash";
import { useParams } from "next/navigation";
import { useState } from "react";
export default function Colab_DetailPartisipasiProyek() {
const params = useParams<{ id: string }>();
const [data, setData] = useState<MODEL_COLLABORATION | null>(null);
useShallowEffect(() => {
onLoadData();
}, []);
async function onLoadData() {
try {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "detail",
});
if (respone) {
setData(respone.data);
}
} catch (error) {
clientLogger.error("Error get all collaboration", error);
}
}
export default function Colab_DetailPartisipasiProyek({
dataColab,
listPartisipan,
}: {
dataColab: MODEL_COLLABORATION;
listPartisipan: MODEL_COLLABORATION_PARTISIPASI[];
}) {
return (
<>
<ComponentGlobal_CardStyles>
{_.isNull(data) ? (
<Collaboration_SkeletonDetail />
) : (
<Stack>
<ComponentColab_AuthorNameOnHeader
profile={dataColab.Author.Profile}
/>
<ComponentColab_DetailData data={dataColab} />
<ComponentColab_DetailListPartisipasiUser
listPartisipan={listPartisipan}
/>
<ComponentGlobal_CardStyles>
<Stack>
<ComponentColab_AuthorNameOnHeader
profile={data.Author.Profile}
/>
<ComponentColab_DetailData data={data} />
</Stack>
</ComponentGlobal_CardStyles>
<ComponentColab_DetailListPartisipasiUser />
</Stack>
</ComponentGlobal_CardStyles>
)}
</>
);
}

View File

@@ -9,83 +9,120 @@ import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan";
import { apiGetOneCollaborationById } from "@/app_modules/colab/_lib/api_collaboration";
import ComponentColab_DetailData from "@/app_modules/colab/component/detail/detail_data";
import ComponentColab_AuthorNameOnListPartisipan from "@/app_modules/colab/component/detail/header_author_list_partisipan";
import ComponentColab_IsEmptyData from "@/app_modules/colab/component/is_empty_data";
import {
Collaboration_SkeletonDetail,
Collaboration_SkeletonListPrtisipanIsUser,
} from "@/app_modules/colab/component/skeleton_view";
import colab_funCreateRoomChat from "@/app_modules/colab/fun/create/fun_create_room_chat";
import { gs_colab_hot_menu } from "@/app_modules/colab/global_state";
import {
MODEL_COLLABORATION,
MODEL_COLLABORATION_PARTISIPASI,
} from "@/app_modules/colab/model/interface";
import mqtt_client from "@/util/mqtt_client";
import { clientLogger } from "@/util/clientLogger";
import {
ActionIcon,
Box,
Button,
Center,
Checkbox,
Drawer,
Grid,
Group,
Paper,
ScrollArea,
Loader,
Stack,
Text,
TextInput,
Title,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconX } from "@tabler/icons-react";
import { useAtom } from "jotai";
import _ from "lodash";
import { useRouter } from "next/navigation";
import { ScrollOnly } from "next-scroll-loader";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
export default function Colab_DetailProyekSaya({
dataColab,
listPartisipan,
}: {
dataColab: MODEL_COLLABORATION;
listPartisipan: MODEL_COLLABORATION_PARTISIPASI[];
}) {
return (
<>
<ComponentGlobal_CardStyles marginBottom={"15px"}>
<Stack>
<ComponentColab_DetailData data={dataColab} />
<CheckBoxPartisipan
listPartisipan={listPartisipan}
colabId={dataColab.id}
/>
</Stack>
</ComponentGlobal_CardStyles>
</>
);
}
export default function Colab_DetailProyekSaya() {
const params = useParams<{ id: string }>();
const [data, setData] = useState<MODEL_COLLABORATION | null>(null);
function CheckBoxPartisipan({
listPartisipan,
colabId,
}: {
listPartisipan: MODEL_COLLABORATION_PARTISIPASI[];
colabId: string;
}) {
const [value, setValue] = useState<string[]>([]);
useShallowEffect(() => {
onLoadData();
}, []);
async function onLoadData() {
try {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "detail",
});
if (respone) {
setData(respone.data);
}
} catch (error) {
clientLogger.error("Error get all collaboration", error);
}
}
return (
<>
<Stack>
{/* <pre>{JSON.stringify(listPartisipan,null,2)}</pre> */}
<Paper
style={{
border: `2px solid ${AccentColor.softblue}`,
backgroundColor: AccentColor.blue,
color: "white",
borderRadius: "10px",
marginBottom: "20px",
padding: "15px",
}}
>
{/* {JSON.stringify(value, null, 2)} */}
{_.isNull(data) ? (
<Collaboration_SkeletonDetail />
) : (
<ComponentGlobal_CardStyles marginBottom={"15px"}>
<ComponentColab_DetailData data={data} />
</ComponentGlobal_CardStyles>
)}
<CheckBoxPartisipan />
</Stack>
</>
);
}
function CheckBoxPartisipan() {
const params = useParams<{ id: string }>();
const [value, setValue] = useState<string[]>([]);
const [data, setData] = useState<MODEL_COLLABORATION_PARTISIPASI[] | null>(
null
);
const [activePage, setActivePage] = useState(1);
useShallowEffect(() => {
onLoadDataPartisipan();
}, []);
async function onLoadDataPartisipan() {
try {
const respone = await apiGetOneCollaborationById({
id: params.id,
kategori: "list_partisipan",
page: `${activePage}`,
});
if (respone) {
setData(respone.data);
}
} catch (error) {
clientLogger.error("Error get list partisipan", error);
}
}
if (_.isNull(data)) {
return <Collaboration_SkeletonListPrtisipanIsUser />;
}
return (
<>
<Stack mb={"lg"}>
<ComponentGlobal_CardStyles>
<Stack>
<Stack spacing={5}>
<Text c={"red"} fz={10}>
@@ -94,52 +131,63 @@ function CheckBoxPartisipan({
Pilih user yang akan menjadi tim proyek anda
</Text>
</Text>
{/* <Text c={"red"} fz={10}>
*
<Text px={"xs"} span inherit c={"white"}>
Room chat dapat dibentuk jika ada 2 user yang dipilih
</Text>
</Text> */}
</Stack>
<ScrollArea h={400} offsetScrollbars>
<Checkbox.Group value={value} onChange={setValue}>
<Stack mt="xs">
{_.isEmpty(listPartisipan) ? (
<ComponentColab_IsEmptyData text="Tidak Ada Pertisipan" />
) : (
listPartisipan.map((e, i) => (
<Grid key={i} align="center">
{_.isEmpty(data) ? (
<ComponentColab_IsEmptyData text="Tidak Ada Pertisipan" />
) : (
<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) => (
<Checkbox.Group value={value} onChange={setValue}>
<Grid key={item} align="center">
<Grid.Col span={2}>
<Checkbox color={"yellow"} value={e?.User?.id} />
<Checkbox color={"yellow"} value={item.User?.id} />
</Grid.Col>
<Grid.Col span={"auto"}>
<ComponentColab_AuthorNameOnListPartisipan
isPembatas={true}
author={e?.User}
deskripsi={e?.deskripsi_diri}
author={item.User}
deskripsi={item.deskripsi_diri}
/>
</Grid.Col>
</Grid>
))
</Checkbox.Group>
)}
</Stack>
</Checkbox.Group>
</ScrollArea>
</ScrollOnly>
</Box>
)}
</Stack>
</Paper>
<ButtonAction value={value} colabId={colabId} />
</ComponentGlobal_CardStyles>
<ButtonAction value={value} />
</Stack>
</>
);
}
function ButtonAction({
value,
colabId,
}: {
value: string[];
colabId: string;
}) {
function ButtonAction({ value }: { value: string[] }) {
const params = useParams<{ id: string }>();
const router = useRouter();
const [opened, { open, close }] = useDisclosure(false);
const [hotMenu, setHotMenu] = useAtom(gs_colab_hot_menu);
@@ -147,29 +195,35 @@ function ButtonAction({
const [loading, setLoading] = useState(false);
async function onSave() {
if (nameRoom === "")
return ComponentGlobal_NotifikasiPeringatan("Lengkapi Nama Grup");
try {
setLoading(true);
if (nameRoom === "")
return ComponentGlobal_NotifikasiPeringatan("Lengkapi Nama Grup");
// await notifikasiToUser_CreateGroupCollaboration({ colabId: colabId });
// await notifikasiToUser_CreateGroupCollaboration({ colabId: colabId });
const res = await colab_funCreateRoomChat(nameRoom, value, colabId);
if (res.status === 201) {
for (let a of value) {
mqtt_client.publish(
"USER",
JSON.stringify({
userId: a,
count: 1,
})
);
const res = await colab_funCreateRoomChat(nameRoom, value, params.id);
if (res.status === 201) {
// for (let a of value) {
// mqtt_client.publish(
// "USER",
// JSON.stringify({
// userId: a,
// count: 1,
// })
// );
// }
ComponentGlobal_NotifikasiBerhasil("Berhasil Membuat Grup");
setHotMenu(4);
router.replace(RouterColab.grup_diskusi);
} else {
ComponentGlobal_NotifikasiGagal("Gagal Membuat Grup");
}
setLoading(true);
ComponentGlobal_NotifikasiBerhasil("Berhasil Membuat Grup");
setHotMenu(4);
router.push(RouterColab.grup_diskusi);
} else {
ComponentGlobal_NotifikasiGagal("Gagal Membuat Grup");
} catch (error) {
clientLogger.error("Error create room chat", error);
} finally {
setLoading(false);
}
}
@@ -188,7 +242,7 @@ function ButtonAction({
transition: "0.5s",
}}
>
Buat Ruang Diskusi{" "}
Buat Ruang Diskusi
</Button>
<Drawer

View File

@@ -6,22 +6,22 @@ import UIGlobal_LayoutHeaderTamplate from "@/app_modules/_global/ui/ui_header_ta
import UIGlobal_LayoutTamplate from "@/app_modules/_global/ui/ui_layout_tamplate";
import { ActionIcon } from "@mantine/core";
import { IconDotsVertical, IconEdit } from "@tabler/icons-react";
import { useParams } from "next/navigation";
import React, { useState } from "react";
export default function LayoutColab_DetailProyekSaya({
children,
colabId,
}: {
children: React.ReactNode;
colabId: string;
}) {
const params = useParams<{id: string}>()
const [openDrawer, setOpenDrawer] = useState(false);
const listPage = [
{
id: "1",
name: "Edit Proyek",
icon: <IconEdit />,
path: RouterColab.edit + colabId,
path: RouterColab.edit + params.id,
},
];