From 29fe0cb6bfd552d55767463cdaa8f862b1d4a2c4 Mon Sep 17 00:00:00 2001 From: Michael Moyo Date: Mon, 10 Jun 2024 17:59:44 -0400 Subject: [PATCH 1/3] Adding Files Page to Sidebar --- .../components/files/file-uploader.tsx | 2 +- .../components/files/files-dialog.tsx | 94 +++++++++++++++++++ .../components/search/search-results.tsx | 19 ++-- packages/ocular-ui/components/side-nav.tsx | 15 ++- packages/ocular-ui/data/data.tsx | 14 +-- .../ocular-ui/pages/dashboard/files/index.tsx | 2 +- 6 files changed, 126 insertions(+), 20 deletions(-) create mode 100644 packages/ocular-ui/components/files/files-dialog.tsx diff --git a/packages/ocular-ui/components/files/file-uploader.tsx b/packages/ocular-ui/components/files/file-uploader.tsx index 734e00be..af5fff1f 100644 --- a/packages/ocular-ui/components/files/file-uploader.tsx +++ b/packages/ocular-ui/components/files/file-uploader.tsx @@ -141,7 +141,7 @@ export function FileUploader(props: FileUploaderProps) {

- Drag {'n'} drop files here, or click to select files + Drag & drop files here, or click to select files

You can upload diff --git a/packages/ocular-ui/components/files/files-dialog.tsx b/packages/ocular-ui/components/files/files-dialog.tsx new file mode 100644 index 00000000..f77c5f27 --- /dev/null +++ b/packages/ocular-ui/components/files/files-dialog.tsx @@ -0,0 +1,94 @@ +"use client"; + +import Link from "next/link"; +import { useRouter } from 'next/router'; +import { buttonVariants, Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, + TooltipProvider +} from "@/components/ui/tooltip"; +import { + File +} from "lucide-react" +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +export default function FilesDialog({ link }) { + const router = useRouter(); + const isActive = (href) => router.pathname === href; + + return ( +

+ + +
+ +
+ + {link.title} +
+
+ + {link.title} + {link.label && ( + + {link.label} + + )} + +
+
+
+ + + Share link + + Anyone who has this link will be able to view this. + + +
+
+ + +
+ +
+ + + + + +
+
+ ); +} diff --git a/packages/ocular-ui/components/search/search-results.tsx b/packages/ocular-ui/components/search/search-results.tsx index b39f6cd2..bdf4283b 100644 --- a/packages/ocular-ui/components/search/search-results.tsx +++ b/packages/ocular-ui/components/search/search-results.tsx @@ -69,16 +69,15 @@ const Results = ({ results }) => ( className="group mb-4 flex max-w-4xl px-3 py-4 text-xs sm:text-base" > { - result && result.documentMetadata.source === 'ocular-api' - ?
- : {result.documentMetadata.title} -} - + result && result.documentMetadata.source === 'ocular-api' + ?
+ : {result.documentMetadata.title} + }
diff --git a/packages/ocular-ui/components/side-nav.tsx b/packages/ocular-ui/components/side-nav.tsx index a78ad65b..6f69e6dd 100644 --- a/packages/ocular-ui/components/side-nav.tsx +++ b/packages/ocular-ui/components/side-nav.tsx @@ -8,6 +8,7 @@ import { cn } from "@/lib/utils" import { buttonVariants } from "@/components/ui/button" import { UserNav } from "./user-nav" import { ThemeToggle } from "@/components/theme-toggle" +import FilesDialog from "@/components/files/files-dialog" import { NavProps } from "@/types/types" import { Tooltip, @@ -25,6 +26,16 @@ import { Users, File, } from "lucide-react" + +// Corrected the filesLink object +const filesLink = { + title: "Files", + label: "", + icon: "File", + variant: "ghost", + link: "/dashboard/files" +}; + const iconMapping = { Search: Search, Bot: Bot, @@ -85,12 +96,14 @@ export function SideNav({ links }: NavProps) { ); })} +
+
    -
    +
    diff --git a/packages/ocular-ui/data/data.tsx b/packages/ocular-ui/data/data.tsx index bd4347e4..4e97a469 100644 --- a/packages/ocular-ui/data/data.tsx +++ b/packages/ocular-ui/data/data.tsx @@ -16,13 +16,13 @@ export const links: LinkProps[] =[ variant: "ghost", link: "/dashboard/chat" }, - { - title: "Files", - label: "", - icon: "File", - variant: "ghost", - link: "/dashboard/files" - }, + // { + // title: "Files", + // label: "", + // icon: "File", + // variant: "ghost", + // link: "/dashboard/files" + // }, { title: "Marketplace", label: "", diff --git a/packages/ocular-ui/pages/dashboard/files/index.tsx b/packages/ocular-ui/pages/dashboard/files/index.tsx index 75a78bef..cde8cbcc 100644 --- a/packages/ocular-ui/pages/dashboard/files/index.tsx +++ b/packages/ocular-ui/pages/dashboard/files/index.tsx @@ -12,7 +12,7 @@ export default function Files() { return ( Date: Mon, 10 Jun 2024 18:39:49 -0400 Subject: [PATCH 2/3] Adding details to dialog --- .../components/files/files-dialog.tsx | 106 +++++++++--------- packages/ocular-ui/components/ui/dialog.tsx | 4 +- 2 files changed, 57 insertions(+), 53 deletions(-) diff --git a/packages/ocular-ui/components/files/files-dialog.tsx b/packages/ocular-ui/components/files/files-dialog.tsx index f77c5f27..c5d27bd4 100644 --- a/packages/ocular-ui/components/files/files-dialog.tsx +++ b/packages/ocular-ui/components/files/files-dialog.tsx @@ -1,6 +1,5 @@ "use client"; -import Link from "next/link"; import { useRouter } from 'next/router'; import { buttonVariants, Button } from "@/components/ui/button"; import { @@ -22,72 +21,77 @@ import { import { File } from "lucide-react" -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; + + +import SectionContainer from "@/components/section-container" +import { UploadedFilesCard } from "@/components/files/uploaded-files-card" +import { useUploadFile } from "@/lib/hooks/files/use-upload-file" +import { FileUploader } from "@/components/files/file-uploader" export default function FilesDialog({ link }) { const router = useRouter(); const isActive = (href) => router.pathname === href; + const { uploadFiles, progresses, uploadedFiles, isUploading } = useUploadFile( + "fileUploader", + { defaultUploadedFiles: [] } + ) + return ( - - -
    - -
    - - {link.title} + +
    + + +
    +
    + + {link.title} +
    -
    - - {link.title} - {link.label && ( - - {link.label} - - )} - -
    -
    - - + + + + {link.title} + {link.label && ( + + {link.label} + + )} + +
    + + + - Share link - - Anyone who has this link will be able to view this. - -
    -
    - - -
    - + +
    + +
    - + + {/* - + */}
    ); diff --git a/packages/ocular-ui/components/ui/dialog.tsx b/packages/ocular-ui/components/ui/dialog.tsx index 45a18a01..517d112a 100644 --- a/packages/ocular-ui/components/ui/dialog.tsx +++ b/packages/ocular-ui/components/ui/dialog.tsx @@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef< Date: Mon, 10 Jun 2024 20:38:26 -0400 Subject: [PATCH 3/3] Files Dialog --- .../components/files/file-uploader.tsx | 152 +++++++----------- .../components/files/files-dialog.tsx | 21 +-- .../components/files/uploaded-files-card.tsx | 77 ++++----- packages/ocular-ui/types/types.ts | 11 ++ 4 files changed, 115 insertions(+), 146 deletions(-) diff --git a/packages/ocular-ui/components/files/file-uploader.tsx b/packages/ocular-ui/components/files/file-uploader.tsx index af5fff1f..602b6841 100644 --- a/packages/ocular-ui/components/files/file-uploader.tsx +++ b/packages/ocular-ui/components/files/file-uploader.tsx @@ -4,7 +4,6 @@ import * as React from "react"; import Image from "next/image"; import { Cross2Icon, UploadIcon } from "@radix-ui/react-icons"; -import Dropzone, { type DropzoneProps, type FileRejection } from "react-dropzone"; import { toast } from "sonner"; import { cn, formatBytes } from "@/lib/utils"; import { useControllableState } from "@/lib/hooks/files/use-controllable-state"; @@ -17,9 +16,9 @@ interface FileUploaderProps extends React.HTMLAttributes { onValueChange?: React.Dispatch>; onUpload?: (files: File[]) => Promise; progresses?: Record; - accept?: DropzoneProps["accept"]; - maxSize?: DropzoneProps["maxSize"]; - maxFiles?: DropzoneProps["maxFiles"]; + accept?: string; + maxSize?: number; + maxFiles?: number; multiple?: boolean; disabled?: boolean; } @@ -30,13 +29,13 @@ export function FileUploader(props: FileUploaderProps) { onValueChange, onUpload, progresses, - accept = undefined, - maxSize = Infinity, + accept = undefined, + maxSize = Infinity, maxFiles = Infinity, multiple = true, disabled = false, className, - ...dropzoneProps + ...otherProps } = props; const [files, setFiles] = useControllableState({ @@ -44,45 +43,41 @@ export function FileUploader(props: FileUploaderProps) { onChange: onValueChange, }); - const onDrop = React.useCallback( - (acceptedFiles: File[], rejectedFiles: FileRejection[]) => { - - if ((files?.length ?? 0) + acceptedFiles.length > maxFiles) { - toast.error(`Cannot upload more than ${maxFiles} files`); - return; - } - - const newFiles = acceptedFiles.map((file) => - Object.assign(file, { - preview: URL.createObjectURL(file), - }) - ); - - const updatedFiles = files ? [...files, ...newFiles] : newFiles; - - setFiles(updatedFiles); - - if (rejectedFiles.length > 0) { - rejectedFiles.forEach(({ file }) => { - toast.error(`File ${file.name} was rejected`); - }); - } - - if (onUpload && updatedFiles.length > 0 && updatedFiles.length <= maxFiles) { - const target = updatedFiles.length > 0 ? `${updatedFiles.length} files` : `file`; - - toast.promise(onUpload(updatedFiles), { - loading: `Uploading ${target}...`, - success: () => { - setFiles([]); - return `${target} uploaded`; - }, - error: `Failed to upload ${target}`, - }); - } - }, - [files, maxFiles, multiple, onUpload, setFiles] - ); + const handleFiles = (selectedFiles: FileList) => { + const acceptedFiles = Array.from(selectedFiles); + if ((files?.length ?? 0) + acceptedFiles.length > maxFiles) { + toast.error(`Cannot upload more than ${maxFiles} files`); + return; + } + + const newFiles = acceptedFiles.map((file) => + Object.assign(file, { + preview: URL.createObjectURL(file), + }) + ); + + const updatedFiles = files ? [...files, ...newFiles] : newFiles; + setFiles(updatedFiles); + + if (onUpload && updatedFiles.length > 0 && updatedFiles.length <= maxFiles) { + const target = updatedFiles.length > 0 ? `${updatedFiles.length} files` : `file`; + + toast.promise(onUpload(updatedFiles), { + loading: `Uploading ${target}...`, + success: () => { + setFiles([]); + return `${target} uploaded`; + }, + error: `Failed to upload ${target}`, + }); + } + }; + + const handleFileChange = (event: React.ChangeEvent) => { + if (event.target.files) { + handleFiles(event.target.files); + } + }; function onRemove(index: number) { if (!files) return; @@ -106,55 +101,26 @@ export function FileUploader(props: FileUploaderProps) { return (
    - 1 || multiple} + multiple={multiple} + onChange={handleFileChange} + className="hidden" disabled={isDisabled} - > - {({ getRootProps, getInputProps, isDragActive }) => ( -
    - - {isDragActive ? ( -
    -
    -
    -

    Drop the files here

    -
    - ) : ( -
    -
    -
    -
    -

    - Drag & drop files here, or click to select files -

    -

    - You can upload - {maxFiles > 1 - ? ` ${maxFiles === Infinity ? "multiple" : maxFiles} files of any size` - : ` a file of any size`} -

    -
    -
    - )} -
    - )} -
    + id="file-upload" + /> +
    + +
    {files?.length ? (
    diff --git a/packages/ocular-ui/components/files/files-dialog.tsx b/packages/ocular-ui/components/files/files-dialog.tsx index c5d27bd4..e3b45650 100644 --- a/packages/ocular-ui/components/files/files-dialog.tsx +++ b/packages/ocular-ui/components/files/files-dialog.tsx @@ -70,11 +70,11 @@ export default function FilesDialog({ link }) {
    - - - - -
    + + + + + - -
    - - {/* - - - - */} +
    ); diff --git a/packages/ocular-ui/components/files/uploaded-files-card.tsx b/packages/ocular-ui/components/files/uploaded-files-card.tsx index 426d1c73..b91e2c72 100644 --- a/packages/ocular-ui/components/files/uploaded-files-card.tsx +++ b/packages/ocular-ui/components/files/uploaded-files-card.tsx @@ -1,54 +1,55 @@ import Image from "next/image" -import type { UploadedFile } from "@/types/types" - -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card" +import type { Files } from "@/types/types" import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area" import { EmptyCard } from "@/components/files/empty-card" interface UploadedFilesCardProps { - uploadedFiles: UploadedFile[] + uploadedFiles: Files[] } export function UploadedFilesCard({ uploadedFiles }: UploadedFilesCardProps) { + + console.log("File Object: ", uploadedFiles) return ( - - - Uploaded files - View the uploaded files here - - - {uploadedFiles.length > 0 ? ( - -
    - {uploadedFiles.map((file) => ( -
    - {file.name} + {uploadedFiles.length > 0 ? ( + +
    + {uploadedFiles.map((file, key) => ( +
    +
    + {/* {file.title} + /> */} +
    +
    +

    {file.title}

    +

    + {new Date(file.created_at).toLocaleDateString(undefined, { + day: 'numeric', + month: 'long', + hour: '2-digit', + minute: '2-digit' + })} +

    - ))} -
    - - - ) : ( - - )} - - +
    + ))} +
    + + + ) : ( + + )} +
    ) } diff --git a/packages/ocular-ui/types/types.ts b/packages/ocular-ui/types/types.ts index 29e01088..eba97720 100644 --- a/packages/ocular-ui/types/types.ts +++ b/packages/ocular-ui/types/types.ts @@ -119,3 +119,14 @@ import { type ClientUploadedFileData } from "uploadthing/types" export interface UploadedFile extends ClientUploadedFileData {} +export interface Files { + id: string, + link: string, + title: string, + type: string, + source: string, + organisation_id: string, + updated_at: string, + created_at: string +} +