mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-18 21:44:06 +01:00
feat: add dataroom manage page for business account
This commit is contained in:
parent
d0d9c7a0f8
commit
3bc6159618
104
src/app/dataroom/manage/AccessRequestsManagement.tsx
Normal file
104
src/app/dataroom/manage/AccessRequestsManagement.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
|
||||
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
|
||||
import { updateAccessRequestStatus } from "@/lib/data/dataroomMutate";
|
||||
import { getAccessRequests } from "@/lib/data/dataroomQuery";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
|
||||
interface AccessRequest {
|
||||
id: number;
|
||||
dataroom_id: number;
|
||||
user_id: string;
|
||||
status: "pending" | "approve" | "reject";
|
||||
requested_at: string;
|
||||
}
|
||||
|
||||
export default function AccessRequestsManagement({ dataroomId }: { dataroomId: number }) {
|
||||
const supabase = createSupabaseClient();
|
||||
|
||||
const { data, error, isLoading } = useQuery(getAccessRequests(supabase, { dataroomId }));
|
||||
const [accessRequests, setAccessRequests] = useState<AccessRequest[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setAccessRequests(data);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
const handleStatusChange = async (requestId: number, newStatus: "approve" | "reject" | "pending") => {
|
||||
await updateAccessRequestStatus(supabase, requestId, newStatus);
|
||||
setAccessRequests((prevRequests) =>
|
||||
prevRequests.map((request) => (request.id === requestId ? { ...request, status: newStatus } : request))
|
||||
);
|
||||
};
|
||||
|
||||
if (isLoading) return <p>Loading access requests...</p>;
|
||||
if (error) return <p>Error loading access requests: {error.message}</p>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="text-lg font-medium mb-2">Manage Access Requests</h3>
|
||||
<Separator className="my-2" />
|
||||
<div className="overflow-y-auto max-h-60">
|
||||
<Table>
|
||||
<TableCaption>A list of access requests for the selected project/dataroom.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>User</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{accessRequests.map((request) => (
|
||||
<TableRow key={request.id}>
|
||||
<TableCell>{request.user_id}</TableCell>
|
||||
<TableCell>{request.status}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{request.status === "pending" ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="bg-green-500 text-white mr-2"
|
||||
onClick={() => handleStatusChange(request.id, "approve")}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="bg-red-500 text-white"
|
||||
onClick={() => handleStatusChange(request.id, "reject")}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</>
|
||||
) : request.status === "approve" ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="bg-red-500 text-white"
|
||||
onClick={() => handleStatusChange(request.id, "pending")}
|
||||
>
|
||||
Revoke Access
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="bg-green-500 text-white"
|
||||
onClick={() => handleStatusChange(request.id, "approve")}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
186
src/app/dataroom/manage/FileManagement.tsx
Normal file
186
src/app/dataroom/manage/FileManagement.tsx
Normal file
@ -0,0 +1,186 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { uploadFileToDataRoom } from "@/lib/data/bucket/uploadFile";
|
||||
import { getFilesByDataroomId } from "@/lib/data/dataroomQuery";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import Link from "next/link";
|
||||
import { deleteFileFromDataRoom } from "@/lib/data/bucket/deleteFile";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
interface FileManagementInterface {
|
||||
dataroomId: number;
|
||||
}
|
||||
|
||||
interface Files {
|
||||
id: number;
|
||||
dataroom_id: number;
|
||||
file_url: string;
|
||||
file_type: {
|
||||
id: number;
|
||||
value: string;
|
||||
};
|
||||
uploaded_at: string;
|
||||
}
|
||||
|
||||
export default function FileManagement({ dataroomId }: FileManagementInterface) {
|
||||
const supabase = createSupabaseClient();
|
||||
const [fileToDelete, setFileToDelete] = useState<Files | null>(null);
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [uploadError, setUploadError] = useState<string | null>(null);
|
||||
const [deleteError, setDeleteError] = useState<string | null>(null);
|
||||
|
||||
const { data: files, isLoading, error, refetch } = useQuery(getFilesByDataroomId(supabase, dataroomId));
|
||||
|
||||
function getFileNameFromUrl(fileUrl: string): string {
|
||||
const fullFileName = fileUrl.split("/").pop() || "";
|
||||
return decodeURIComponent(fullFileName.split("?")[0]);
|
||||
}
|
||||
|
||||
const handleDeleteClick = (file: any) => {
|
||||
setFileToDelete(file);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
if (!fileToDelete) return;
|
||||
|
||||
try {
|
||||
await deleteFileFromDataRoom(
|
||||
supabase,
|
||||
fileToDelete.dataroom_id,
|
||||
getFileNameFromUrl(fileToDelete.file_url),
|
||||
fileToDelete.id
|
||||
);
|
||||
setFileToDelete(null);
|
||||
refetch();
|
||||
toast.success("Delete successfully!");
|
||||
} catch (error) {
|
||||
toast.error("Error occur while deleting file!");
|
||||
setDeleteError("Error occur while deleting file!");
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
setSelectedFile(event.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUploadFile = async () => {
|
||||
if (!selectedFile) return;
|
||||
|
||||
setUploading(true);
|
||||
setUploadError(null);
|
||||
|
||||
try {
|
||||
await uploadFileToDataRoom(supabase, selectedFile, dataroomId);
|
||||
refetch();
|
||||
toast.success("Upload successfully!");
|
||||
} catch (error) {
|
||||
toast.error("Error occur while uploading!");
|
||||
setUploadError("Error occur while uploading!");
|
||||
} finally {
|
||||
setUploading(false);
|
||||
setSelectedFile(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) return <p>Loading files...</p>;
|
||||
if (error) return <p>Error loading files: {error.message}</p>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="text-lg font-medium mb-2">Manage Files</h3>
|
||||
<Separator className="my-2" />
|
||||
<Input type="file" className="mb-2" onChange={handleFileChange} />
|
||||
<Button className="mb-4" onClick={handleUploadFile} disabled={uploading}>
|
||||
{uploading ? "Uploading..." : "Upload File"}
|
||||
</Button>
|
||||
{uploadError && <div className="text-red-500">{uploadError}</div>}
|
||||
{deleteError && <div className="text-red-500">{deleteError}</div>}
|
||||
|
||||
<div className="overflow-y-auto max-h-60 mb-4">
|
||||
<Table>
|
||||
<TableCaption>A list of files in the selected data room.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>File Name</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{files?.map((file) => (
|
||||
<TableRow key={file.id}>
|
||||
<TableCell className="font-medium">
|
||||
<Link href={file.file_url} rel="noopener noreferrer" target="_blank">
|
||||
<p className="text-blue-600 dark:text-blue-400 hover:text-blue-800">
|
||||
{getFileNameFromUrl(file.file_url)}
|
||||
</p>
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>Uploaded</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="outline" className="text-red-500" onClick={() => handleDeleteClick(file)}>
|
||||
Delete
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently delete the file "
|
||||
{getFileNameFromUrl(file.file_url)}
|
||||
".
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={() => setFileToDelete(null)}>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleDeleteConfirm}>Continue</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={2}>Total Files</TableCell>
|
||||
<TableCell className="text-right">{files?.length}</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
110
src/app/dataroom/manage/page.tsx
Normal file
110
src/app/dataroom/manage/page.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import FileManagement from "./FileManagement";
|
||||
import AccessRequestsManagement from "./AccessRequestsManagement";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import { getProjectByUserId } from "@/lib/data/projectQuery";
|
||||
import { getUserRole } from "@/lib/data/userQuery";
|
||||
import { useRouter } from "next/navigation";
|
||||
import toast from "react-hot-toast";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
export default function ManageDataroomPage() {
|
||||
const supabase = createSupabaseClient();
|
||||
const router = useRouter();
|
||||
|
||||
const [userRole, setUserRole] = useState<string | null>(null);
|
||||
const [projects, setProjects] = useState<any[]>([]);
|
||||
const [selectedProject, setSelectedProject] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserData = async () => {
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
if (!user) {
|
||||
router.push("/");
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: roleData, error: roleError } = await getUserRole(supabase, user.id);
|
||||
if (roleError) {
|
||||
toast.error("Error loading user role.");
|
||||
router.push("/");
|
||||
return;
|
||||
}
|
||||
setUserRole(roleData.role);
|
||||
|
||||
const { data: projectData, error: projectError } = await getProjectByUserId(supabase, user.id);
|
||||
if (projectError) {
|
||||
toast.error("Error loading projects.");
|
||||
router.push("/");
|
||||
return;
|
||||
}
|
||||
setProjects(projectData);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
fetchUserData();
|
||||
}, [supabase, router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userRole && userRole !== "business") {
|
||||
router.push("/");
|
||||
}
|
||||
}, [userRole, router]);
|
||||
|
||||
if (loading) {
|
||||
return <p className="flex items-center justify-center h-screen">Loading...</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-screen-xl p-4">
|
||||
<h2 className="text-xl font-semibold mb-4">Manage Data Room / Projects</h2>
|
||||
|
||||
<div className="mb-4">
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
const selected = projects.find((project) => project.id.toString() === value);
|
||||
setSelectedProject(selected);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select a project" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Projects</SelectLabel>
|
||||
{projects.map((project) => (
|
||||
<SelectItem key={project.id} value={project.id.toString()}>
|
||||
{project.project_name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{selectedProject && (
|
||||
<>
|
||||
<div className="border border-border p-4 rounded-md my-3">
|
||||
<FileManagement dataroomId={selectedProject.dataroom_id} />
|
||||
</div>
|
||||
<div className="border border-border p-4 rounded-md">
|
||||
<AccessRequestsManagement dataroomId={selectedProject.dataroom_id} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
141
src/components/ui/alert-dialog.tsx
Normal file
141
src/components/ui/alert-dialog.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root
|
||||
|
||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
||||
|
||||
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
||||
|
||||
const AlertDialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
))
|
||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
||||
|
||||
const AlertDialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
))
|
||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
||||
|
||||
const AlertDialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
AlertDialogHeader.displayName = "AlertDialogHeader"
|
||||
|
||||
const AlertDialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
AlertDialogFooter.displayName = "AlertDialogFooter"
|
||||
|
||||
const AlertDialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
||||
|
||||
const AlertDialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogDescription.displayName =
|
||||
AlertDialogPrimitive.Description.displayName
|
||||
|
||||
const AlertDialogAction = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Action
|
||||
ref={ref}
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
||||
|
||||
const AlertDialogCancel = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
ref={ref}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"mt-2 sm:mt-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
}
|
||||
29
src/lib/data/bucket/deleteFile.ts
Normal file
29
src/lib/data/bucket/deleteFile.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { Database } from "@/types/database.types";
|
||||
|
||||
export async function deleteFileFromDataRoom(
|
||||
supabase: SupabaseClient<Database>,
|
||||
dataroomId: number,
|
||||
fileName: string,
|
||||
dataroomMaterialId: number
|
||||
) {
|
||||
const filePath = `${dataroomId}/${fileName}`;
|
||||
|
||||
if (!filePath) {
|
||||
throw new Error("Invalid filepath: Unable to extract file path for deletion.");
|
||||
}
|
||||
|
||||
const { error: storageError } = await supabase.storage.from("dataroom-material").remove([filePath]);
|
||||
|
||||
if (storageError) {
|
||||
throw new Error(`Error deleting file from storage: ${storageError.message}`);
|
||||
}
|
||||
|
||||
const { error: dbError } = await supabase.from("dataroom_material").delete().eq("id", dataroomMaterialId);
|
||||
|
||||
if (dbError) {
|
||||
throw new Error(`Error deleting file from database: ${dbError.message}`);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
54
src/lib/data/bucket/uploadFile.ts
Normal file
54
src/lib/data/bucket/uploadFile.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
export async function uploadFileToDataRoom(supabase: SupabaseClient, file: File, dataRoomId: number) {
|
||||
const allowedExtensions = ["pdf", "docx", "xlsx", "pptx", "txt"];
|
||||
const fileExtension = file.name.split(".").pop()?.toLowerCase();
|
||||
|
||||
if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
|
||||
throw new Error("Invalid file format. Only pdf, docx, xlsx, pptx, and txt are allowed.");
|
||||
}
|
||||
const { data: fileTypeData, error: fileTypeError } = await supabase
|
||||
.from("material_file_type")
|
||||
.select("id")
|
||||
.eq("value", fileExtension)
|
||||
.single();
|
||||
|
||||
if (fileTypeError || !fileTypeData) {
|
||||
throw new Error("File type not supported or not found in material_file_type table.");
|
||||
}
|
||||
|
||||
const fileTypeId = fileTypeData.id;
|
||||
|
||||
const fileName = `${dataRoomId}/${file.name}`;
|
||||
const { data: uploadData, error: uploadError } = await supabase.storage
|
||||
.from("dataroom-material")
|
||||
.upload(fileName, file, {
|
||||
upsert: true,
|
||||
contentType: file.type,
|
||||
});
|
||||
|
||||
if (uploadError) {
|
||||
throw uploadError;
|
||||
}
|
||||
const { data: signedUrlData, error: signedUrlError } = await supabase.storage
|
||||
.from("dataroom-material")
|
||||
.createSignedUrl(fileName, 2628000);
|
||||
|
||||
if (signedUrlError) {
|
||||
throw signedUrlError;
|
||||
}
|
||||
|
||||
const { error: insertError } = await supabase.from("dataroom_material").insert([
|
||||
{
|
||||
dataroom_id: dataRoomId,
|
||||
file_url: signedUrlData.signedUrl,
|
||||
file_type_id: fileTypeId,
|
||||
},
|
||||
]);
|
||||
|
||||
if (insertError) {
|
||||
throw insertError;
|
||||
}
|
||||
|
||||
return uploadData;
|
||||
}
|
||||
19
src/lib/data/dataroomMutate.ts
Normal file
19
src/lib/data/dataroomMutate.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
export const requestAccessToDataRoom = (client: SupabaseClient, dataRoomId: number, userId: number) => {
|
||||
return client.from("access_request").insert([
|
||||
{
|
||||
dataroom_id: dataRoomId,
|
||||
user_id: userId,
|
||||
status: "pending",
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export const updateAccessRequestStatus = (
|
||||
client: SupabaseClient,
|
||||
requestId: number,
|
||||
status: "approve" | "reject" | "pending"
|
||||
) => {
|
||||
return client.from("access_request").update({ status: status }).eq("id", requestId);
|
||||
};
|
||||
@ -44,10 +44,14 @@ export const getDataRoomsByProjectId = (client: SupabaseClient, projectId: numbe
|
||||
.eq("project_id", projectId);
|
||||
};
|
||||
|
||||
export const getAccessRequests = (client: SupabaseClient, filters: { dataroomId?: number; userId?: string }) => {
|
||||
export const getAccessRequests = (
|
||||
client: SupabaseClient<Database>,
|
||||
filters: { dataroomId?: number; userId?: string }
|
||||
) => {
|
||||
let query = client.from("access_request").select(
|
||||
`
|
||||
id,
|
||||
dataroom_id,
|
||||
user_id,
|
||||
status,
|
||||
requested_at
|
||||
@ -55,7 +59,7 @@ export const getAccessRequests = (client: SupabaseClient, filters: { dataroomId?
|
||||
);
|
||||
|
||||
if (filters.dataroomId !== undefined) {
|
||||
query = query.eq("data_room_id", filters.dataroomId);
|
||||
query = query.eq("dataroom_id", filters.dataroomId);
|
||||
}
|
||||
|
||||
if (filters.userId !== undefined) {
|
||||
|
||||
@ -222,4 +222,27 @@ const getProjectByBusinessId = (client: SupabaseClient, businessIds: string[]) =
|
||||
.in("business_id", businessIds);
|
||||
};
|
||||
|
||||
export { getProjectData, getProjectDataQuery, getTopProjects, searchProjectsQuery, getProjectByBusinessId };
|
||||
const getProjectByUserId = (client: SupabaseClient, userId: string) => {
|
||||
return client
|
||||
.from("project")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
project_name,
|
||||
business_id:business!inner (
|
||||
user_id
|
||||
),
|
||||
dataroom_id
|
||||
`
|
||||
)
|
||||
.eq("business.user_id", userId);
|
||||
};
|
||||
|
||||
export {
|
||||
getProjectData,
|
||||
getProjectDataQuery,
|
||||
getTopProjects,
|
||||
searchProjectsQuery,
|
||||
getProjectByBusinessId,
|
||||
getProjectByUserId,
|
||||
};
|
||||
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user