Skip to content

Commit

Permalink
feat(web): download file feature on admin
Browse files Browse the repository at this point in the history
  • Loading branch information
mrevanzak committed May 26, 2024
1 parent d098633 commit 2dcd6c0
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 39 deletions.
12 changes: 12 additions & 0 deletions apps/web/src/app/api/documents/[fileId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { env } from "@/env";
import { auth } from "@/server/auth";

export async function GET({ params }: { params: { fileId: string } }) {
const session = await auth();
if (session?.user.role !== "admin") {
return new Response("Unauthorized", { status: 401 });
}

const res = await fetch(env.BACKEND_URL + "/files/download/" + params.fileId);
return res;
}
52 changes: 35 additions & 17 deletions apps/web/src/components/documents-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ import { MdOutlineDownloadForOffline } from "react-icons/md";
import { RiDeleteBin2Line } from "react-icons/ri";

import { Button } from "@tanya.in/ui/button";
import { toast } from "@tanya.in/ui/toast";

const columns = [
{
key: "name" as const,
label: "NAME",
},
{
key: "createdAt" as const,
key: "created" as const,
label: "CREATED AT",
},
{
Expand All @@ -48,24 +49,18 @@ export function DocumentsTable() {
const searchParams = useSearchParams();
const showModal = searchParams.get("delete") === "true";

const [document, setDocument] = React.useState<Document>();
const [selectedDocument, setSelectedDocument] = React.useState<Document>();

const { data, isLoading } = api.documents.get.useQuery();

const renderCell = React.useCallback(
(document: Document, columnKey: ColumnKey) => {
(file: Document, columnKey: ColumnKey) => {
switch (columnKey) {
case "createdAt":
case "created":
return (
<Chip
size="sm"
variant="flat"
color={!document[columnKey] ? "default" : "success"}
>
<Chip size="sm" variant="flat" color="warning">
<span className="text-xs capitalize">
{document[columnKey]
? moment(document[columnKey]).fromNow()
: "Never"}
{moment(file[columnKey]).fromNow()}
</span>
</Chip>
);
Expand All @@ -77,14 +72,37 @@ export function DocumentsTable() {
color="primary"
className="p-2"
>
<button className="pointer-events-auto text-primary">
<button
className="pointer-events-auto text-primary"
onClick={() => {
toast.promise(
fetch(`/api/documents/${file.id}`)
.then((res) => res.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", file.filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}),
{
loading: "Downloading...",
success: "Downloaded!",
error: "Failed to download",
},
);
}}
>
<MdOutlineDownloadForOffline size={20} />
</button>
</Tooltip>
<Tooltip content="Delete document" color="danger" className="p-2">
<Link
href="?delete=true"
onClick={() => setDocument(document)}
onClick={() => setSelectedDocument(file)}
className="pointer-events-auto"
>
<RiDeleteBin2Line size={20} color="#FF0080" />
Expand All @@ -93,7 +111,7 @@ export function DocumentsTable() {
</div>
);
default:
return document[columnKey];
return file[columnKey];
}
},
[],
Expand Down Expand Up @@ -123,7 +141,7 @@ export function DocumentsTable() {
emptyContent={
!isLoading && (
<p>
No files found <br /> Drag and drop a file to upload
No documents found <br /> Drag and drop a document to upload
</p>
)
}
Expand All @@ -148,7 +166,7 @@ export function DocumentsTable() {
<>
<ModalHeader>Delete Document</ModalHeader>
<ModalBody>
<p>Are you sure you want to delete {document?.name}?</p>
<p>Are you sure you want to delete {selectedDocument?.name}?</p>
</ModalBody>
<ModalFooter>
<Button variant="light" onClick={onClose}>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/dropzone-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function DropzoneContainer({
/>
{/* //TODO: fix full reload when clicking this button */}
<Button color="primary" onClick={() => openRef.current?.()}>
Upload New Files
Upload New Document
</Button>
</div>
<div className="mx-auto w-full">
Expand Down
30 changes: 13 additions & 17 deletions apps/web/src/server/api/routers/documents/documents.procedure.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import type { Document } from "@/server/api/routers/documents/documents.schema";
import { env } from "@/env";
import { adminProcedure, createTRPCRouter } from "@/server/api/trpc";
import { z } from "zod";

import { documentSchema } from "./documents.schema";

export const documentsRouter = createTRPCRouter({
get: adminProcedure.query(() => {
const documents: Document[] = [
{
id: "1",
name: "Document 1.pdf",
createdAt: new Date(),
uploadedBy: "3152ffe5-6496-4214-8fcd-b69fb4f70fd5",
},
get: adminProcedure.query(async () => {
const res = await fetch(env.BACKEND_URL + "/files");

{
id: "2",
name: "Document 2.pdf",
createdAt: new Date(),
uploadedBy: "3152ffe5-6496-4214-8fcd-b69fb4f70fd5",
},
];
const validate = z
.object({
status: z.string(),
files: documentSchema.array(),
})
.parse(await res.json());

return documents;
return validate.files;
}),
});
13 changes: 9 additions & 4 deletions apps/web/src/server/api/routers/documents/documents.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { z } from "zod";
import { documents } from "@/server/db/schema";
import { createSelectSchema } from "drizzle-zod";
import { z } from "zod";

export const documentSchema = z.object({
id: z.number(),
name: z.string(),
filename: z.string(),
created: z.coerce.date(),
lastModified: z.coerce.date(),
});

export const documentSchema = createSelectSchema(documents);
export type Document = z.infer<typeof documentSchema>;

0 comments on commit 2dcd6c0

Please sign in to comment.