Merge pull request #392 from bipproduction/amalia/30-jan-25

Amalia/30 jan 25
This commit is contained in:
Amalia
2025-01-30 17:00:51 +08:00
committed by GitHub
10 changed files with 181 additions and 103 deletions

View File

@@ -133,7 +133,43 @@ export async function GET(request: Request, context: { params: { id: string } })
projectTitle: v.DivisionProject.title projectTitle: v.DivisionProject.title
})) }))
} else if (kategori == "new-file") { } else if (kategori == "new-file") {
allData = await prisma.divisionDocumentFolderFile.findMany({ const dataShare = await prisma.divisionDocumentShare.findMany({
skip: 0,
take: 5,
where: {
isActive: true,
idDivision: String(id),
DivisionDocumentFolderFile: {
isActive: true,
category: "FILE"
}
},
select: {
DivisionDocumentFolderFile: {
select: {
id: true,
name: true,
extension: true,
path: true,
}
},
createdAt: true
},
orderBy: {
createdAt: 'desc'
}
})
const fixShare = dataShare.map((v: any) => ({
..._.omit(v, ["DivisionDocumentFolderFile"]),
id: v.DivisionDocumentFolderFile.id,
name: v.DivisionDocumentFolderFile.name,
extension: v.DivisionDocumentFolderFile.extension,
path: 'home',
share: true
}))
const dataDokumen = await prisma.divisionDocumentFolderFile.findMany({
skip: 0, skip: 0,
take: 5, take: 5,
where: { where: {
@@ -146,11 +182,24 @@ export async function GET(request: Request, context: { params: { id: string } })
name: true, name: true,
extension: true, extension: true,
path: true, path: true,
createdAt: true
}, },
orderBy: { orderBy: {
createdAt: "desc" createdAt: "desc"
} }
}) })
const fixData = dataDokumen.map((v: any) => ({
..._.omit(v, [""]),
share: false
}))
if (fixShare.length > 0) {
fixData.push(...fixShare)
}
allData = _.orderBy(fixData, ['createdAt'], ['desc']);
} else if (kategori == "new-discussion") { } else if (kategori == "new-discussion") {
const diskusi = await prisma.divisionDisscussion.findMany({ const diskusi = await prisma.divisionDisscussion.findMany({
skip: 0, skip: 0,

View File

@@ -192,10 +192,10 @@ export async function DELETE(request: Request) {
data: omitData data: omitData
}) })
// create log user
const log = await createLogUser({ act: 'CREATE', desc: 'User membagikan item', table: 'divisionDocumentShare', data: dataItem[i].id })
} }
// create log user
const log = await createLogUser({ act: 'CREATE', desc: 'User membagikan item', table: 'divisionDocumentShare', data: '' })
return NextResponse.json({ success: true, message: "Berhasil membagikan item" }, { status: 200 }); return NextResponse.json({ success: true, message: "Berhasil membagikan item" }, { status: 200 });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@@ -214,6 +214,7 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const idItem = searchParams.get("item"); const idItem = searchParams.get("item");
const category = searchParams.get("cat");
const cekItem = await prisma.divisionDocumentFolderFile.count({ const cekItem = await prisma.divisionDocumentFolderFile.count({
where: { where: {
@@ -225,66 +226,72 @@ export async function GET(request: Request) {
return NextResponse.json({ success: false, message: "Gagal mendapatkan dokumen, data tidak ditemukan" }, { status: 404 }); return NextResponse.json({ success: false, message: "Gagal mendapatkan dokumen, data tidak ditemukan" }, { status: 404 });
} }
const data = await prisma.divisionDocumentFolderFile.findUnique({ let fixData
where: { if (category == 'share') {
id: String(idItem), const share = await prisma.divisionDocumentShare.findMany({
}, where: {
select: { idDocument: String(idItem),
category: true, isActive: true
name: true,
extension: true,
createdAt: true,
path: true,
Division: {
select: {
name: true
}
}, },
User: { select: {
select: { idDivision: true,
name: true Division: {
select: {
name: true
}
} }
} }
} })
})
const dataPath = await prisma.divisionDocumentFolderFile.findUnique({ fixData = share.map((v: any) => ({
where: { ..._.omit(v, ["idDivision", "Division"]),
id: data?.path id: v.idDivision,
} name: v.Division.name
}) }))
const share = await prisma.divisionDocumentShare.findMany({ } else {
where: { const data = await prisma.divisionDocumentFolderFile.findUnique({
idDocument: String(idItem), where: {
isActive: true id: String(idItem),
}, },
select: { select: {
Division: { category: true,
select: { name: true,
name: true extension: true,
createdAt: true,
path: true,
Division: {
select: {
name: true
}
},
User: {
select: {
name: true
}
} }
} }
})
const dataPath = await prisma.divisionDocumentFolderFile.findUnique({
where: {
id: data?.path
}
})
fixData = {
category: data?.category,
name: data?.name,
extension: data?.extension,
createdAt: moment(data?.createdAt).format('DD MMMM YYYY'),
path: (dataPath?.name !== undefined && dataPath?.name !== null && dataPath?.name !== '') ? dataPath.name : "home",
division: data?.Division?.name,
createdBy: data?.User?.name
} }
})
const dataUtama = {
category: data?.category,
name: data?.name,
extension: data?.extension,
createdAt: moment(data?.createdAt).format('DD MMMM YYYY'),
path: (dataPath?.name !== undefined && dataPath?.name !== null && dataPath?.name !== '') ? dataPath.name : "home",
division: data?.Division?.name,
createdBy: data?.User?.name
} }
const dataShare = share.map((v: any) => ({ return NextResponse.json({ success: true, message: "Berhasil mendapatkan item", data: fixData }, { status: 200 });
..._.omit(v, ["Division"]),
division: v.Division.name
}))
return NextResponse.json({ success: true, message: "Berhasil mendapatkan item", data: dataUtama, share: dataShare }, { status: 200 });
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
export async function GET(request: Request) { export async function GET(request: Request) {
try { try {
return NextResponse.json({ success: true, version: "1.1.4", tahap: "beta", update:"-resize profile image user (create anggota, edit anggota, edit profile) -menghilangkan image extensi heic" }, { status: 200 }); return NextResponse.json({ success: true, version: "1.2.0", tahap: "beta", update:"-unshare dokumen divisi -jumlah dokumen pada detail divisi -tampil dokumen share pada detail divisi -loguser pada saat share dokumen divisi -boleh unduh file share" }, { status: 200 });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 });

View File

@@ -42,7 +42,8 @@ export interface IDataKalenderOnDetailDivision {
id: string, id: string,
name: string, name: string,
extension: string, extension: string,
path: string path: string,
share:boolean
} }
export interface IDataDiscussionOnDetailDivision { export interface IDataDiscussionOnDetailDivision {

View File

@@ -2,7 +2,7 @@
import { TEMA, } from "@/module/_global"; import { TEMA, } from "@/module/_global";
import { useHookstate } from "@hookstate/core"; import { useHookstate } from "@hookstate/core";
import { Carousel } from "@mantine/carousel"; import { Carousel } from "@mantine/carousel";
import { Box, Center, Group, Image, Skeleton, Stack, Text, UnstyledButton } from "@mantine/core"; import { Box, Center, Flex, Group, Image, Indicator, Skeleton, Stack, Text, UnstyledButton } from "@mantine/core";
import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@@ -10,6 +10,7 @@ import toast from "react-hot-toast";
import { funGetDetailDivisionById } from "../lib/api_division"; import { funGetDetailDivisionById } from "../lib/api_division";
import * as ICON from '../lib/file_icon'; import * as ICON from '../lib/file_icon';
import { IDataKalenderOnDetailDivision } from "../lib/type_division"; import { IDataKalenderOnDetailDivision } from "../lib/type_division";
import { FaShare } from "react-icons/fa6";
const iconContainer = (icon: string) => 'data:image/svg+xml;base64,' + btoa(icon) const iconContainer = (icon: string) => 'data:image/svg+xml;base64,' + btoa(icon)
@@ -44,7 +45,7 @@ export default function ListDocumentOnDetailDivision() {
return ( return (
<Box pt={10}> <Box pt={10}>
<Text c={tema.get().utama} mb={10} fw={'bold'} fz={16}>Dokumen Terbaru</Text> <Text c={tema.get().utama} mb={10} fw={'bold'} fz={16}>Dokumen Terkini</Text>
<Group justify="center" grow> <Group justify="center" grow>
{ {
loading loading
@@ -70,11 +71,31 @@ export default function ListDocumentOnDetailDivision() {
<Carousel.Slide key={v.id}> <Carousel.Slide key={v.id}>
<UnstyledButton onClick={() => router.push(`/division/${param.id}/document?path=${v.path}`)}> <UnstyledButton onClick={() => router.push(`/division/${param.id}/document?path=${v.path}`)}>
<Stack gap={0} w={isMobile ? 100 : 170}> <Stack gap={0} w={isMobile ? 100 : 170}>
<Box bg={"white"} style={{ borderRadius: 10, border: `1px solid ${"#D6D8F6"}` }} > {
<Center p={"md"}> v.share ?
<Image w={isMobile ? 50 : 75} src={(v.extension == "pdf") ? iconContainer(ICON.PDF) : iconContainer(ICON.IMAGE)} alt="image" /> <Indicator
</Center> offset={15}
</Box> withBorder
inline
color={'#D6D8F6'}
position="bottom-end"
label={<FaShare />}
size={25}
>
<Box bg={"white"} style={{ borderRadius: 10, border: `1px solid ${"#D6D8F6"}` }} >
<Center p={"md"}>
<Image w={isMobile ? 50 : 75} src={(v.extension == "pdf") ? iconContainer(ICON.PDF) : iconContainer(ICON.IMAGE)} alt="image" />
</Center>
</Box>
</Indicator>
:
<Box bg={"white"} style={{ borderRadius: 10, border: `1px solid ${"#D6D8F6"}` }} >
<Center p={"md"}>
<Image w={isMobile ? 50 : 75} src={(v.extension == "pdf") ? iconContainer(ICON.PDF) : iconContainer(ICON.IMAGE)} alt="image" />
</Center>
</Box>
}
<Box> <Box>
<Text c={"dimmed"} ta={"center"} lineClamp={1} fz={isMobile ? 14 : 16}>{v.name + '.' + v.extension}</Text> <Text c={"dimmed"} ta={"center"} lineClamp={1} fz={isMobile ? 14 : 16}>{v.name + '.' + v.extension}</Text>
</Box> </Box>

View File

@@ -23,7 +23,8 @@ export interface IInfoDocument {
} }
export interface IInfoShare { export interface IInfoShare {
division: string id: string
name: string
} }
export interface IFormFolder { export interface IFormFolder {

View File

@@ -4,17 +4,13 @@ import { useMediaQuery, useShallowEffect } from "@mantine/hooks";
import _ from "lodash"; import _ from "lodash";
import { useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { CiCalendarDate } from "react-icons/ci";
import { FcDocument, FcFolder, FcImageFile } from "react-icons/fc"; import { FcDocument, FcFolder, FcImageFile } from "react-icons/fc";
import { GrLocationPin } from "react-icons/gr";
import { HiOutlineDocumentText } from "react-icons/hi2"; import { HiOutlineDocumentText } from "react-icons/hi2";
import { MdOutlineCategory } from "react-icons/md"; import { PiCalendarBlankLight, PiMapPinSimpleLight, PiShapesLight, PiShareNetworkLight, PiUsersThreeLight } from "react-icons/pi";
import { PiUsersThree } from "react-icons/pi";
import { TbLockAccess } from "react-icons/tb";
import { funGetInfoDocument } from "../lib/api_document"; import { funGetInfoDocument } from "../lib/api_document";
import { IFormDetailMoreItem, IInfoDocument, IInfoShare } from "../lib/type_document"; import { IFormDetailMoreItem, IInfoDocument, IInfoShare } from "../lib/type_document";
export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem[] }) { export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem }) {
const [dataInfo, setDataInfo] = useState<IInfoDocument>(); const [dataInfo, setDataInfo] = useState<IInfoDocument>();
const [dataShare, setDataShare] = useState<IInfoShare[]>([]) const [dataShare, setDataShare] = useState<IInfoShare[]>([])
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -24,16 +20,17 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
async function getOneData(loading: boolean) { async function getOneData(loading: boolean) {
try { try {
setLoading(loading); setLoading(loading);
const respon = await funGetInfoDocument("?item=" + data[0].id); const respon = await funGetInfoDocument(`?item=${data.id}&cat=utama`);
const responShare = await funGetInfoDocument(`?item=${data.id}&cat=share`);
if (respon.success) { if (respon.success) {
setDataInfo(respon.data); setDataInfo(respon.data);
setDataShare(respon.share) setDataShare(responShare.data)
} else { } else {
toast.error(respon.message); toast.error(respon.message);
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
toast.error("Gagal mendapatkan item, coba lagi nanti"); toast.error("Gagal mendapatkan data, coba lagi nanti");
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -61,7 +58,7 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
} }
</Stack> </Stack>
</Box> </Box>
<ScrollArea {/* <ScrollArea
h={{ h={{
base: "55vh", base: "55vh",
xl: "56vh", xl: "56vh",
@@ -72,7 +69,7 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
scrollbarSize={2} scrollbarSize={2}
scrollHideDelay={0} scrollHideDelay={0}
scrollbars="y" scrollbars="y"
> > */}
{loading ? ( {loading ? (
<SkeletonDetailProfile /> <SkeletonDetailProfile />
) : ( ) : (
@@ -89,7 +86,7 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
</Group> </Group>
</Grid.Col> </Grid.Col>
<Grid.Col span={6}> <Grid.Col span={6}>
<Text lineClamp={1} fz={15} ta={"right"}>{dataInfo?.category == "FOLDER" ? dataInfo?.name : dataInfo?.name + '.' + dataInfo?.extension}</Text> <Text truncate="end" ta={"right"} fz={15}>{dataInfo?.category == "FOLDER" ? dataInfo?.name : `${dataInfo?.name}.${dataInfo?.extension}`}</Text>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<Divider size="xs" /> <Divider size="xs" />
@@ -98,7 +95,7 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
<Group> <Group>
{ {
!isMobile ? !isMobile ?
<MdOutlineCategory size={25} /> <PiShapesLight size={25} />
: '' : ''
} }
<Text fz={15}>Tipe</Text> <Text fz={15}>Tipe</Text>
@@ -114,14 +111,14 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
<Group> <Group>
{ {
!isMobile ? !isMobile ?
<GrLocationPin size={25} /> <PiMapPinSimpleLight size={25} />
: '' : ''
} }
<Text fz={15}>Lokasi</Text> <Text fz={15}>Lokasi</Text>
</Group> </Group>
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8}>
<Text lineClamp={1} fz={15} ta={"right"}>{dataInfo?.path}</Text> <Text truncate="end" fz={15} ta={"right"}>{dataInfo?.path}</Text>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<Divider size="xs" /> <Divider size="xs" />
@@ -130,14 +127,14 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
<Group> <Group>
{ {
!isMobile ? !isMobile ?
<PiUsersThree size={25} /> <PiUsersThreeLight size={25} />
: '' : ''
} }
<Text fz={15}>Pemilik</Text> <Text fz={15}>Pemilik</Text>
</Group> </Group>
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8}>
<Text lineClamp={1} fz={15} ta={"right"}> {dataInfo?.division} </Text> <Text truncate="end" fz={15} ta={"right"}> {dataInfo?.division} </Text>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<Divider size="xs" /> <Divider size="xs" />
@@ -146,7 +143,7 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
<Group> <Group>
{ {
!isMobile ? !isMobile ?
<CiCalendarDate size={25} /> <PiCalendarBlankLight size={25} />
: '' : ''
} }
<Text fz={15}>Tanggal Dibuat</Text> <Text fz={15}>Tanggal Dibuat</Text>
@@ -163,25 +160,23 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
<Accordion.Item value="share" p={0}> <Accordion.Item value="share" p={0}>
<Accordion.Control p={0} icon={ <Accordion.Control p={0} icon={
!isMobile ? !isMobile ?
<TbLockAccess size={25} /> <PiShareNetworkLight size={25} />
: '' : ''
} fz={15}>Yang Memiliki Akses</Accordion.Control> } fz={15}>Telah dibagikan ke divisi</Accordion.Control>
<Accordion.Panel> <Accordion.Panel>
<List <List
spacing="xs" spacing="xs"
size="sm" size="sm"
center center
icon={ icon={dataShare.length > 0 ? <PiUsersThreeLight size={20} /> : <></>}
<PiUsersThree size={20} />
}
> >
<List.Item>{dataInfo?.division}</List.Item>
{ {
dataShare.map((i: any) => { dataShare.length === 0 ? <List.Item ta={"center"}>Tidak ada data</List.Item> :
return ( dataShare.map((i: any) => {
<List.Item key={i.id}>{i.division}</List.Item> return (
) <List.Item key={i.id}>{i.name}</List.Item>
}) )
})
} }
</List> </List>
</Accordion.Panel> </Accordion.Panel>
@@ -189,7 +184,7 @@ export default function DrawerInfoDocument({ data }: { data: IFormDetailMoreItem
</Accordion> </Accordion>
</Stack> </Stack>
)} )}
</ScrollArea> {/* </ScrollArea> */}
</Box> </Box>
</Box> </Box>
); );

View File

@@ -150,7 +150,7 @@ export default function DrawerMore({ data, share }: { data: IDataDocument[], sha
</LayoutDrawer> </LayoutDrawer>
<LayoutDrawer opened={isInfo} onClose={() => setIsInfo(false)} title={'Informasi Dokumen'} size="lg"> <LayoutDrawer opened={isInfo} onClose={() => setIsInfo(false)} title={'Informasi Dokumen'} size="lg">
<DrawerInfoDocument data={data} /> <DrawerInfoDocument data={data[0]} />
</LayoutDrawer> </LayoutDrawer>
</Box> </Box>
); );

View File

@@ -7,24 +7,23 @@ import { useParams } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { FaCheck, FaUsers } from "react-icons/fa6"; import { FaCheck, FaUsers } from "react-icons/fa6";
import { funShareDocument } from "../lib/api_document"; import { funGetInfoDocument, funShareDocument } from "../lib/api_document";
import { IShareDivision } from "../lib/type_document"; import { IShareDivision } from "../lib/type_document";
import { globalRefreshDocument } from "../lib/val_document"; import { globalRefreshDocument } from "../lib/val_document";
export default function DrawerShareDocument({ data, }: { data: IShareDivision[]; }) { export default function DrawerShareDocument({ data }: { data: IShareDivision[]; }) {
const [selectedFiles, setSelectedFiles] = useState<any>([]); const [selectedFiles, setSelectedFiles] = useState<any>([]);
const [isData, setData] = useState<IDataDivison[]>([]); const [isData, setData] = useState<IDataDivison[]>([]);
const param = useParams<{ id: string }>(); const param = useParams<{ id: string }>();
const refresh = useHookstate(globalRefreshDocument); const refresh = useHookstate(globalRefreshDocument);
const tema = useHookstate(TEMA); const tema = useHookstate(TEMA);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loadingBtn, setLoadingBtn] = useState(false)
const isMobile2 = useMediaQuery("(max-width: 438px)"); const isMobile2 = useMediaQuery("(max-width: 438px)");
async function onShare() { async function onShare() {
try { try {
if (selectedFiles.length == 0) { setLoadingBtn(true)
return toast.error("Pilih divisi terlebih dahulu");
}
const respon = await funShareDocument({ const respon = await funShareDocument({
dataDivision: selectedFiles, dataDivision: selectedFiles,
@@ -39,6 +38,8 @@ export default function DrawerShareDocument({ data, }: { data: IShareDivision[];
} catch (error) { } catch (error) {
console.error(error); console.error(error);
toast.error("Gagal membagikan item, coba lagi nanti"); toast.error("Gagal membagikan item, coba lagi nanti");
} finally {
setLoadingBtn(false)
} }
} }
@@ -48,8 +49,10 @@ export default function DrawerShareDocument({ data, }: { data: IShareDivision[];
const response = await funGetListDivisionByIdDivision( const response = await funGetListDivisionByIdDivision(
"?division=" + param.id "?division=" + param.id
); );
const shared = await funGetInfoDocument(`?item=${data[0].id}&cat=share`);
if (response.success) { if (response.success) {
setData(response.data.filter((i: any) => i.id != param.id)); setData(response.data.filter((i: any) => i.id != param.id));
setSelectedFiles(shared.data)
} else { } else {
toast.error(response.message); toast.error(response.message);
} }
@@ -202,6 +205,7 @@ export default function DrawerShareDocument({ data, }: { data: IShareDivision[];
radius={30} radius={30}
fullWidth fullWidth
onClick={() => onShare()} onClick={() => onShare()}
loading={loadingBtn}
> >
Simpan Simpan
</Button> </Button>

View File

@@ -434,16 +434,16 @@ export default function NavbarDocumentDivision() {
</Flex> </Flex>
<Flex justify={"center"} align={"center"} direction={"column"} <Flex justify={"center"} align={"center"} direction={"column"}
onClick={ onClick={
selectedFiles.length > 0 && !shareSelected selectedFiles.length == 1 && !shareSelected
? () => setShare(true) ? () => setShare(true)
: undefined : undefined
} }
> >
<ActionIcon variant="subtle" aria-label="share" > <ActionIcon variant="subtle" aria-label="share">
<LuShare2 <LuShare2
size={20} size={20}
color={ color={
selectedFiles.length > 0 && !shareSelected selectedFiles.length == 1 && !shareSelected
? "white" ? "white"
: "#656060" : "#656060"
} }
@@ -453,7 +453,7 @@ export default function NavbarDocumentDivision() {
fz={12} fz={12}
ta={"center"} ta={"center"}
c={ c={
selectedFiles.length > 0 && !shareSelected selectedFiles.length == 1 && !shareSelected
? "white" ? "white"
: "#656060" : "#656060"
} }