Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Dec 4, 2023
1 parent 59a9dd6 commit 291b729
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 57 deletions.
1 change: 1 addition & 0 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"react-hook-form": "^7.48.2",
"recharts": "^2.10.3",
"sharp": "^0.33.0",
"tus-js-client": "^3.1.1",
"zod": "^3.22.4"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ConnectedAccounts } from "@/components/connected-accounts";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Connected Accounts | Midday",
title: "Bank Accounts | Midday",
};

export default function Connected() {
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/select-category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function SelectCategory({ id, name, selectedId, isLoading }) {
if (transactions?.data?.length) {
toast({
duration: 6000,
description: `Do you want to mark ${
title: `Do you want to mark ${
transactions?.data?.length
} similar transactions form ${name} as ${t(
`categories.${value}`
Expand Down
7 changes: 4 additions & 3 deletions apps/dashboard/src/components/tables/vault/data-table-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ export function DataTableRow({ data, addOptimisticData }) {
setTimeout(() => {
toast({
duration: 4000,
description: "Successfully deleted file",
title: "Successfully deleted file",
variant: "success",
});
}, 100);
},
Expand All @@ -94,7 +95,7 @@ export function DataTableRow({ data, addOptimisticData }) {

toast({
duration: 4000,
description: `Copied URL for ${data.name} to clipboard.`,
title: `Copied URL for ${data.name} to clipboard.`,
});
} catch (err) {}
},
Expand All @@ -104,7 +105,7 @@ export function DataTableRow({ data, addOptimisticData }) {
onError: () => {
toast({
duration: 4000,
description:
title:
"The folder already exists in the current directory. Please use a different name.",
});
},
Expand Down
5 changes: 4 additions & 1 deletion apps/dashboard/src/components/tables/vault/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ export function DataTable({ data }) {

return (
<Table>
<TableHeader className="border-0">
<TableHeader
className="border-0 sticky top-0 backdrop-blur backdrop-filter bg-opacity-50"
style={{ background: "var(--sticky)" }}
>
<TableRow>
<TableHead className="w-[60%]">Name</TableHead>
<TableHead className="w-[15%]">Created at</TableHead>
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/tables/vault/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function Table({ path }) {
});

return (
<div className="mt-6 h-[calc(100vh-180px)] border">
<div className="mt-6 h-[calc(100vh-180px)] border overflow-scroll relative">
<UploadZone>
<DataTable data={data} />
{data.length === 0 && <EmptyTable type={path} />}
Expand Down
94 changes: 70 additions & 24 deletions apps/dashboard/src/components/tables/vault/upload-zone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,46 @@ import { createFolderAction } from "@/actions/create-folder-action";
import { invalidateCacheAction } from "@/actions/invalidate-cache-action";
import { createClient } from "@midday/supabase/client";
import { getCurrentUserTeamQuery } from "@midday/supabase/queries";
import { upload } from "@midday/supabase/storage";
import { resumableUpload } from "@midday/supabase/storage";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@midday/ui/context-menu";
import { useToast } from "@midday/ui/use-toast";
import { cn } from "@midday/ui/utils";
import { useAction } from "next-safe-action/hook";
import { useParams } from "next/navigation";
import { useCallback, useEffect, useRef, useState } from "react";
import { useDropzone } from "react-dropzone";

export function UploadZone({ children }) {
const supabase = createClient();
const [progress, setProgress] = useState(0);
const [showProgress, setShowProgress] = useState(false);
const [toastId, setToastId] = useState(null);
const uploadProgress = useRef([]);
const params = useParams();
const folders = params?.folders ?? [];
const folderPath = folders.join("/");
const { toast } = useToast();
const { toast, dismiss, update } = useToast();

useEffect(() => {
if (!toastId && showProgress) {
const { id } = toast({
title: `Uploading ${uploadProgress.current.length} files`,
progress,
variant: "progress",
description: "Please do not close browser until completed",
duration: Infinity,
});

setToastId(id);
} else {
update(toastId, { progress });
}
}, [showProgress, progress, toastId]);

const isDefaultFolder = ["inbox", "exports", "transactions"].includes(
folders.at(0)
Expand All @@ -35,32 +53,60 @@ export function UploadZone({ children }) {
onError: () => {
toast({
duration: 4000,
description:
// position: "bottom-right",
title:
"The folder already exists in the current directory. Please use a different name.",
});
},
});

const onDrop = async (files) => {
const onDrop = useCallback(async (files) => {
setShowProgress(true);

const { data: userData } = await getCurrentUserTeamQuery(supabase);
console.log(`${userData?.team_id}/${folderPath}`);

try {
await Promise.all(
files.map(async (file, idx) => {
await resumableUpload(supabase, {
bucket: "vault",
path: `${userData?.team_id}/${folderPath}`,
file,
onProgress: (bytesUploaded, bytesTotal) => {
uploadProgress.current[idx] = (bytesUploaded / bytesTotal) * 100;

const _progress = uploadProgress.current.reduce(
(acc, currentValue) => {
return acc + currentValue;
},
0
);

await Promise.all(
files.map(async (file) => {
await upload(supabase, {
bucket: "vault",
path: `${userData?.team_id}/${folderPath}`,
file,
});
})
);

invalidateCacheAction([`vault_${userData.team_id}`]);

toast({
duration: 4000,
description: "Upload successfull.",
});
};
setProgress(Math.round(_progress / files.length));
},
});
})
);

setProgress(0);
toast({
title: "Upload successfull.",
variant: "success",
duration: 2000,
});

setShowProgress(false);
setToastId(null);
dismiss(toastId);
invalidateCacheAction([`vault_${userData.team_id}`]);
} catch {
toast({
duration: 2500,
title: "Something went wrong please try again.",
});
}
}, []);

const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

Expand Down
Binary file modified bun.lockb
Binary file not shown.
53 changes: 53 additions & 0 deletions packages/supabase/src/utils/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SupabaseClient } from "@supabase/supabase-js";
import * as tus from "tus-js-client";

export const EMPTY_FOLDER_PLACEHOLDER_FILE_NAME = ".emptyFolderPlaceholder";

Expand Down Expand Up @@ -27,6 +28,58 @@ export async function upload(
throw result.error;
}

export async function resumableUpload(
client: SupabaseClient,
{ file, path, bucket, onProgress }: UploadParams
) {
const {
data: { session },
} = await client.auth.getSession();

const fullPath = `${path}/${file.name}`;

console.log(fullPath);

return new Promise((resolve, reject) => {
const upload = new tus.Upload(file, {
endpoint: `https://pytddvqiozwrhfbwqazp.supabase.co/storage/v1/upload/resumable`,
retryDelays: [0, 3000, 5000, 10000, 20000],
headers: {
authorization: `Bearer ${session?.access_token}`,
"x-upsert": "true", // optionally set upsert to true to overwrite existing files
},
uploadDataDuringCreation: true,
// Important if you want to allow re-uploading the same file https://github.com/tus/tus-js-client/blob/main/docs/api.md#removefingerprintonsuccess
removeFingerprintOnSuccess: true,
metadata: {
bucketName: bucket,
objectName: fullPath,
contentType: file.type,
cacheControl: "3600",
},
// NOTE: it must be set to 6MB (for now) do not change it
chunkSize: 6 * 1024 * 1024,
onError: (error) => {
reject(error);
},
onProgress,
onSuccess: () => {
resolve(upload);
},
});

// Check if there are any previous uploads to continue.
return upload.findPreviousUploads().then((previousUploads) => {
// Found previous uploads so we select the first one.
if (previousUploads.length) {
upload.resumeFromPreviousUpload(previousUploads[0]);
}

upload.start();
});
});
}

type RemoveParams = {
path: string;
bucket: string;
Expand Down
23 changes: 13 additions & 10 deletions packages/ui/src/components/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type VariantProps, cva } from "class-variance-authority";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "../utils";
import { Icons } from "./icons";

const ToastProvider = ToastPrimitives.Provider;

Expand All @@ -13,28 +14,30 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:left-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className,
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
)}
{...props}
/>
));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;

const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-left-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
"group pointer-events-auto relative flex w-full items-center space-x-4 overflow-hidden rounded-xl border p-5 pr-7 transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
default: "border bg-secondary text-foreground",
success: "border bg-secondary text-foreground",
progress: "border bg-secondary text-foreground",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
}
);

const Toast = React.forwardRef<
Expand All @@ -59,8 +62,8 @@ const ToastAction = React.forwardRef<
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className,
"inline-flex h-8 shrink-0 items-center justify-center rounded-xl border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
)}
{...props}
/>
Expand All @@ -75,7 +78,7 @@ const ToastClose = React.forwardRef<
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className,
className
)}
toast-close=""
{...props}
Expand All @@ -91,7 +94,7 @@ const ToastTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
className={cn("text-sm", className)}
{...props}
/>
));
Expand All @@ -103,7 +106,7 @@ const ToastDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
className={cn("text-xs text-[#878787]", className)}
{...props}
/>
));
Expand Down
Loading

0 comments on commit 291b729

Please sign in to comment.