diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index d42751b093..725519b49f 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -45,7 +45,7 @@ "devDependencies": { "@midday/tsconfig": "workspace:*", "@t3-oss/env-nextjs": "^0.7.1", - "@types/node": "^20.10.2", + "@types/node": "^20.10.3", "@types/react": "^18.2.41", "@types/react-dom": "^18.2.17", "typescript": "^5.3.2" diff --git a/apps/dashboard/src/app/[locale]/layout.tsx b/apps/dashboard/src/app/[locale]/layout.tsx index b32dee6b61..72bbe826d1 100644 --- a/apps/dashboard/src/app/[locale]/layout.tsx +++ b/apps/dashboard/src/app/[locale]/layout.tsx @@ -4,7 +4,6 @@ import { Provider } from "./provider"; export default async function Layout({ dashboard, login, - params: { locale }, }: { dashboard: React.ReactNode; diff --git a/apps/dashboard/src/app/api/auth/callback/route.ts b/apps/dashboard/src/app/api/auth/callback/route.ts index e8363e10ca..2253a80ecc 100644 --- a/apps/dashboard/src/app/api/auth/callback/route.ts +++ b/apps/dashboard/src/app/api/auth/callback/route.ts @@ -13,6 +13,7 @@ export async function GET(req: NextRequest) { const code = requestUrl.searchParams.get("code"); const returnTo = requestUrl.searchParams.get("return_to"); const provider = requestUrl.searchParams.get("provider"); + const onboardingVisited = cookieStore.get(Cookies.OnboardingVisited)?.value; if (provider) { cookieStore.set(Cookies.PrefferedSignInProvider, provider); @@ -23,6 +24,11 @@ export async function GET(req: NextRequest) { await supabase.auth.exchangeCodeForSession(code); } + if (!onboardingVisited) { + cookieStore.set(Cookies.OnboardingVisited, "true"); + return NextResponse.redirect(`${requestUrl.origin}/onboarding`); + } + if (returnTo) { return NextResponse.redirect(`${requestUrl.origin}/${returnTo}`); } diff --git a/apps/dashboard/src/app/api/download/zip/route.ts b/apps/dashboard/src/app/api/download/zip/route.ts index e95bb1335c..e69fb45b20 100644 --- a/apps/dashboard/src/app/api/download/zip/route.ts +++ b/apps/dashboard/src/app/api/download/zip/route.ts @@ -1,19 +1,33 @@ +import { getUser } from "@midday/supabase/cached-queries"; +import { getVaultRecursiveQuery } from "@midday/supabase/queries"; import { createClient } from "@midday/supabase/server"; +import { download } from "@midday/supabase/storage"; import { BlobReader, BlobWriter, ZipWriter } from "@zip.js/zip.js"; export const preferredRegion = "fra1"; export const runtime = "edge"; +export const dynamic = "force-dynamic"; export async function GET(req, res) { - const promises = []; - const supabase = createClient(); const requestUrl = new URL(req.url); + const supabase = createClient(); + const user = await getUser(); const path = requestUrl.searchParams.get("path"); const filename = requestUrl.searchParams.get("filename"); + const promises: any = []; + + const files = await getVaultRecursiveQuery(supabase, { + teamId: user.data.team_id, + path, + }); + files.forEach((file) => { promises.push( - supabaseClient.storage.from(bucket).download(`${folder}/${file.name}`) + download(supabase, { + bucket: "vault", + path: `${file.basePath}/${file.name}`, + }) ); }); @@ -32,20 +46,21 @@ export async function GET(req, res) { }); downloadedFiles.forEach((downloadedFile) => { - if (downloadedFile) { + if (downloadedFile?.blob) { zipWriter.add(downloadedFile.name, new BlobReader(downloadedFile.blob)); } }); - // const { data } = await supabase.storage.from("vault").download(path); - // const responseHeaders = new Headers(res.headers); + const responseHeaders = new Headers(res.headers); + + responseHeaders.set( + "Content-Disposition", + `attachment; filename="${filename}.zip"` + ); - // responseHeaders.set( - // "Content-Disposition", - // `attachment; filename="${filename}"` - // ); + const data = await zipWriter.close(); - // return new Response(data, { - // headers: responseHeaders, - // }); + return new Response(data, { + headers: responseHeaders, + }); } diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index b48e4a5c2b..82c071bc00 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -26,9 +26,15 @@ export const metadata: Metadata = { ], }; -export default function Layout({ children }: { children: ReactElement }) { +export default function Layout({ + children, + params, +}: { + children: ReactElement; + params: { locale: string }; +}) { return ( - + { return newState; - }, + } ); return ( diff --git a/apps/dashboard/src/components/tables/vault/data-table-row.tsx b/apps/dashboard/src/components/tables/vault/data-table-row.tsx index 22d17101f6..6842a7c029 100644 --- a/apps/dashboard/src/components/tables/vault/data-table-row.tsx +++ b/apps/dashboard/src/components/tables/vault/data-table-row.tsx @@ -29,11 +29,8 @@ import { import { DropdownMenu, DropdownMenuContent, - DropdownMenuGroup, DropdownMenuItem, DropdownMenuPortal, - DropdownMenuSeparator, - DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, @@ -209,7 +206,7 @@ export function DataTableRow({ data, addOptimisticData }) { {data.isFolder ? ( @@ -292,7 +289,7 @@ export function DataTableRow({ data, addOptimisticData }) { {data.isFolder ? ( diff --git a/apps/dashboard/src/locales/client.ts b/apps/dashboard/src/locales/client.ts index 497c60f0ce..df0399041b 100644 --- a/apps/dashboard/src/locales/client.ts +++ b/apps/dashboard/src/locales/client.ts @@ -1,6 +1,5 @@ "use client"; -import { createT } from "next-international"; import { createI18nClient } from "next-international/client"; export const languages = ["en", "sv"]; diff --git a/apps/dashboard/src/utils/constants.ts b/apps/dashboard/src/utils/constants.ts index 0daf638909..b1b294d521 100644 --- a/apps/dashboard/src/utils/constants.ts +++ b/apps/dashboard/src/utils/constants.ts @@ -3,4 +3,5 @@ export const Cookies = { SpendingPeriod: "spending-period", ChartType: "chart-type", TransactionsPeriod: "transactions-period", + OnboardingVisited: "onboarding-visited", }; diff --git a/apps/website/package.json b/apps/website/package.json index 79b6042b0b..cd3019d246 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@midday/tsconfig": "workspace:*", - "@types/node": "^20.10.2", + "@types/node": "^20.10.3", "@types/react": "^18.2.41", "@types/react-dom": "^18.2.17" } diff --git a/apps/website/src/app/layout.tsx b/apps/website/src/app/layout.tsx index f869f41354..902125e49b 100644 --- a/apps/website/src/app/layout.tsx +++ b/apps/website/src/app/layout.tsx @@ -25,9 +25,15 @@ export const metadata: Metadata = { ], }; -export default function Layout({ children }: { children: ReactElement }) { +export default function Layout({ + children, + params, +}: { + children: ReactElement; + params: { locale: string }; +}) { return ( - + +
-
+ +

{t("title")}

@@ -100,222 +101,220 @@ export function StartPage() { )}
- -
-
-
-
-
- -
- - {t("open")} - +
+
+
+
+
-
-
- -
- - {t("live")} - + + {t("open")} + +
+
+
+
-
-
- -
- - {t("document")} - + + {t("live")} + +
+
+
+
-
-
- -
- - {t("reciept")} - + + {t("document")} + +
+
+
+
-
-
- -
- - {t("time")} - + + {t("reciept")} + +
+
+
+
-
-
- -
- - {t("ai")} - + + {t("time")} + +
+
+
+
+ + {t("ai")} +
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- Midday | Mobile +
+
+
+
+
+
+
+
- Midday | Overview +
+ Midday | Mobile - Midday | Search + Midday | Overview - Midday | Tracking + Midday | Search - Midday | Transactions + Midday | Tracking - Midday | Mobile + Midday | Transactions - Midday | Overview + Midday | Mobile - Midday | Search + Midday | Overview - Midday | Tracking + Midday | Search - Midday | Transactions -
+ Midday | Tracking + + Midday | Transactions
); diff --git a/bun.lockb b/bun.lockb index 806a5a7a43..33ca712c15 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/email/package.json b/packages/email/package.json index cde95bf609..d4d0c3525a 100644 --- a/packages/email/package.json +++ b/packages/email/package.json @@ -12,10 +12,10 @@ }, "dependencies": { "@formatjs/intl": "^2.9.9", - "@react-email/render": "0.0.9", - "@react-email/components": "0.0.11", + "@react-email/render": "0.0.10", + "@react-email/components": "0.0.12", "date-fns": "^2.30.0", - "react-email": "1.9.5" + "react-email": "1.10.0" }, "devDependencies": { "typescript": "^5.3.2" diff --git a/packages/supabase/src/queries/index.ts b/packages/supabase/src/queries/index.ts index b130a6780c..7923134830 100644 --- a/packages/supabase/src/queries/index.ts +++ b/packages/supabase/src/queries/index.ts @@ -517,3 +517,61 @@ export async function getVaultQuery(supabase: Client, params: GetVaultParams) { data: mergedArray, }; } + +type GetVaultRecursiveParams = { + teamId: string; + path?: string; + folder?: string; + limit?: number; + offset?: number; +}; + +export async function getVaultRecursiveQuery( + supabase: Client, + params: GetVaultRecursiveParams +) { + const { teamId, path, folder, limit = 10000, offset = 0 } = params; + + let basePath = teamId; + + if (path) { + basePath = `${basePath}/${path}`; + } + + if (folder) { + basePath = `${basePath}/${folder}`; + } + + const items = []; + let folderContents: any = []; + + for (;;) { + const { data } = await supabase.storage.from("vault").list(basePath); + + folderContents = folderContents.concat(data); + // offset += limit; + if ((data || []).length < limit) { + break; + } + } + + const subfolders = folderContents?.filter((item) => item.id === null) ?? []; + const folderItems = folderContents?.filter((item) => item.id !== null) ?? []; + + folderItems.forEach((item) => items.push({ ...item, basePath })); + + const subFolderContents = await Promise.all( + subfolders.map((folder: any) => + getVaultRecursiveQuery(supabase, { + ...params, + folder: decodeURIComponent(folder.name), + }) + ) + ); + + subFolderContents.map((subfolderContent) => { + subfolderContent.map((item) => items.push(item)); + }); + + return items; +} diff --git a/packages/supabase/src/utils/storage.ts b/packages/supabase/src/utils/storage.ts index 196b67ad24..a6359939f6 100644 --- a/packages/supabase/src/utils/storage.ts +++ b/packages/supabase/src/utils/storage.ts @@ -103,61 +103,3 @@ export async function share( ) { return client.storage.from(bucket).createSignedUrl(path, expireIn, options); } - -// export async function getAllItemsAlongFolder(folder) { -// const items = []; - -// let formattedPathToFolder = ""; -// const { name, columnIndex, prefix } = folder; - -// if (prefix === undefined) { -// const pathToFolder = this.openedFolders -// .slice(0, columnIndex) -// .map((folder) => folder.name) -// .join("/"); -// formattedPathToFolder = -// pathToFolder.length > 0 ? `${pathToFolder}/${name}` : name; -// } else { -// formattedPathToFolder = `${prefix}/${name}`; -// } - -// const options = { -// limit: 10000, -// offset: OFFSET, -// sortBy: { column: this.sortBy, order: this.sortByOrder }, -// }; -// let folderContents = []; - -// for (;;) { -// const res = await post( -// `${this.endpoint}/buckets/${this.selectedBucket.name}/objects/list`, -// { -// path: formattedPathToFolder, -// options, -// } -// ); -// folderContents = folderContents.concat(res); -// options.offset += options.limit; -// if ((res || []).length < options.limit) { -// break; -// } -// } - -// const subfolders = folderContents?.filter((item) => item.id === null) ?? []; -// const folderItems = folderContents?.filter((item) => item.id !== null) ?? []; - -// folderItems.forEach((item) => -// items.push({ ...item, prefix: formattedPathToFolder }) -// ); - -// const subFolderContents = await Promise.all( -// subfolders.map((folder) => -// this.getAllItemsAlongFolder({ ...folder, prefix: formattedPathToFolder }) -// ) -// ); -// subFolderContents.map((subfolderContent) => { -// subfolderContent.map((item) => items.push(item)); -// }); - -// return items; -// } diff --git a/packages/ui/package.json b/packages/ui/package.json index cb9caefb38..99c218e047 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -85,7 +85,7 @@ "lucide-react": "^0.294.0", "react-day-picker": "^8.9.1", "react-icons": "^4.12.0", - "tailwind-merge": "^2.0.0", + "tailwind-merge": "2.0.0", "tailwindcss": "^3.3.5", "tailwindcss-animate": "^1.0.7" }