diff --git a/apps/dashboard/src/app/[locale]/@dashboard/(root)/vault/[[...folders]]/page.tsx b/apps/dashboard/src/app/[locale]/@dashboard/(root)/vault/[[...folders]]/page.tsx
index e82facf51b..baadf33d90 100644
--- a/apps/dashboard/src/app/[locale]/@dashboard/(root)/vault/[[...folders]]/page.tsx
+++ b/apps/dashboard/src/app/[locale]/@dashboard/(root)/vault/[[...folders]]/page.tsx
@@ -1,3 +1,4 @@
+import { Breadcrumbs } from "@/components/breadcrumbs";
import { Table } from "@/components/tables/vault";
import { Metadata } from "next";
@@ -6,5 +7,10 @@ export const metadata: Metadata = {
};
export default function Vault({ params }) {
- return
;
+ return (
+
+ );
}
diff --git a/apps/dashboard/src/components/breadcrumbs.tsx b/apps/dashboard/src/components/breadcrumbs.tsx
new file mode 100644
index 0000000000..f98d792af1
--- /dev/null
+++ b/apps/dashboard/src/components/breadcrumbs.tsx
@@ -0,0 +1,3 @@
+export function Breadcrumbs({ folders }) {
+ return folders?.map((folder) => {folder});
+}
diff --git a/apps/dashboard/src/components/file-icon.tsx b/apps/dashboard/src/components/file-icon.tsx
new file mode 100644
index 0000000000..8cca63392c
--- /dev/null
+++ b/apps/dashboard/src/components/file-icon.tsx
@@ -0,0 +1,28 @@
+import { Icons } from "@midday/ui/icons";
+
+export function FileIcon({ mimetype, name }) {
+ if (name === "exports") {
+ return ;
+ }
+
+ if (name === "inbox") {
+ return ;
+ }
+
+ if (name === "transactions") {
+ return ;
+ }
+
+ if (mimetype?.startsWith("image")) {
+ return ;
+ }
+
+ switch (mimetype) {
+ case "application/pdf":
+ return ;
+ case "application/zip":
+ return ;
+ default:
+ 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 3f2851c158..72a4e33e34 100644
--- a/apps/dashboard/src/components/tables/vault/data-table-row.tsx
+++ b/apps/dashboard/src/components/tables/vault/data-table-row.tsx
@@ -1,18 +1,37 @@
"use client";
+import { FileIcon } from "@/components/file-icon";
+import { formatSize } from "@/utils/format";
+import { Icons } from "@midday/ui/icons";
import { TableCell, TableRow } from "@midday/ui/table";
+import { format } from "date-fns";
import { usePathname, useRouter } from "next/navigation";
export function DataTableRow({ data }) {
const router = useRouter();
const pathname = usePathname();
+
const handleNavigate = () => router.push(`${pathname}/${data.name}`);
return (
- {data.name}
- {data.created_at}
- wef
+
+
+
+ {data.name}
+ {data?.metadata?.size && (
+
+ {formatSize(data.metadata.size)}
+
+ )}
+
+
+
+ {data?.created_at && format(new Date(data.created_at), "MMM d, yyyy")}
+
+
+
+
);
}
diff --git a/apps/dashboard/src/components/tables/vault/data-table.tsx b/apps/dashboard/src/components/tables/vault/data-table.tsx
index 3e98fbac1f..f67f0f6589 100644
--- a/apps/dashboard/src/components/tables/vault/data-table.tsx
+++ b/apps/dashboard/src/components/tables/vault/data-table.tsx
@@ -13,8 +13,8 @@ export function DataTable({ data }) {
Name
- Uploaded
- Actions
+ Uploaded
+ Actions
diff --git a/packages/jobs/src/transactions/notification.ts b/packages/jobs/src/transactions/notification.ts
index 79f567cca3..75e52597a8 100644
--- a/packages/jobs/src/transactions/notification.ts
+++ b/packages/jobs/src/transactions/notification.ts
@@ -3,7 +3,6 @@ import { getI18n } from "@midday/email/locales";
import { TriggerEvents, triggerBulk } from "@midday/notification";
import { renderAsync } from "@react-email/components";
import { eventTrigger } from "@trigger.dev/sdk";
-import { revalidateTag } from "next/cache";
import { z } from "zod";
import { client, supabase } from "../client";
import { Events, Jobs } from "../constants";
@@ -36,83 +35,81 @@ client.defineJob({
.select("team_id, user:user_id(id, full_name, avatar_url, email, locale)")
.eq("team_id", teamId);
- if (transactions?.length && transactions.length > 0) {
- revalidateTag(`transactions_${teamId}`);
- revalidateTag(`spending_${teamId}`);
- revalidateTag(`metrics_${teamId}`);
+ const notificationEvents = await Promise.all(
+ usersData?.map(async ({ user, team_id }) => {
+ const { t } = getI18n({ locale: user.locale });
- const notificationEvents = await Promise.all(
- usersData?.map(async ({ user, team_id }) => {
- const { t } = getI18n({ locale: user.locale });
+ return transactions.map((transaction) => ({
+ name: TriggerEvents.TransactionNewInApp,
+ payload: {
+ transactionId: transaction.id,
+ description: t(
+ { id: "notifications.transaction" },
+ {
+ amount: Intl.NumberFormat(user.locale, {
+ style: "currency",
+ currency: transaction.currency,
+ }).format(transaction.amount),
+ from: transaction.name,
+ }
+ ),
+ },
+ user: {
+ subscriberId: user.id,
+ teamId: team_id,
+ email: user.email,
+ fullName: user.full_name,
+ avatarUrl: user.avatar_url,
+ },
+ }));
+ })
+ );
- return transactions.map((transaction) => ({
- name: TriggerEvents.TransactionNewInApp,
- payload: {
- transactionId: transaction.id,
- description: t(
- { id: "notifications.transaction" },
- {
- amount: Intl.NumberFormat(user.locale, {
- style: "currency",
- currency: transaction.currency,
- }).format(transaction.amount),
- from: transaction.name,
- }
- ),
- },
- user: {
- subscriberId: user.id,
- teamId: team_id,
- email: user.email,
- fullName: user.full_name,
- avatarUrl: user.avatar_url,
- },
- }));
- })
+ if (notificationEvents?.length) {
+ await io.logger.log(
+ `Sending notifications: ${notificationEvents.length}`
);
+ triggerBulk(notificationEvents.flat());
+ }
- if (notificationEvents?.length) {
- triggerBulk(notificationEvents.flat());
- }
-
- const emailEvents = await Promise.all(
- usersData?.map(async ({ user, team_id }) => {
- const { t } = getI18n({ locale: user.locale });
+ const emailEvents = await Promise.all(
+ usersData?.map(async ({ user, team_id }) => {
+ const { t } = getI18n({ locale: user.locale });
- const html = await renderAsync(
- TransactionsEmail({
- fullName: user.full_name,
- transactions: transactions.map((transaction) => ({
- id: transaction.id,
- date: transaction.date,
- amount: transaction.amount,
- name: transaction.name,
- currency: transaction.currency,
- })),
- locale: user.locale,
- })
- );
+ const html = await renderAsync(
+ TransactionsEmail({
+ fullName: user.full_name,
+ transactions: transactions.map((transaction) => ({
+ id: transaction.id,
+ date: transaction.date,
+ amount: transaction.amount,
+ name: transaction.name,
+ currency: transaction.currency,
+ })),
+ locale: user.locale,
+ })
+ );
- return {
- name: TriggerEvents.TransactionNewEmail,
- payload: {
- subject: t({ id: "transactions.subject" }),
- html,
- },
- user: {
- subscriberId: user.id,
- teamId: team_id,
- email: user.email,
- fullName: user.full_name,
- avatarUrl: user.avatar_url,
- },
- };
- })
- );
+ return {
+ name: TriggerEvents.TransactionNewEmail,
+ payload: {
+ subject: t({ id: "transactions.subject" }),
+ html,
+ },
+ user: {
+ subscriberId: user.id,
+ teamId: team_id,
+ email: user.email,
+ fullName: user.full_name,
+ avatarUrl: user.avatar_url,
+ },
+ };
+ })
+ );
- if (emailEvents?.length) {
- triggerBulk(emailEvents);
- }
+ if (emailEvents?.length) {
+ await io.logger.log(`Sending emails: ${emailEvents.length}`);
+ triggerBulk(emailEvents);
}
},
});
diff --git a/packages/jobs/src/transactions/sync.ts b/packages/jobs/src/transactions/sync.ts
index 454a94e9eb..e785627701 100644
--- a/packages/jobs/src/transactions/sync.ts
+++ b/packages/jobs/src/transactions/sync.ts
@@ -18,6 +18,8 @@ client.defineJob({
.eq("id", ctx.source.id)
.single();
+ const teamId = data?.team_id;
+
// Update bank account last_accessed
await io.supabase.client
.from("bank_accounts")
@@ -26,8 +28,8 @@ client.defineJob({
})
.eq("id", ctx.source.id);
- revalidateTag(`bank_accounts_${data?.team_id}`);
- await io.logger.info(`bank_accounts_${data?.team_id}`);
+ revalidateTag(`bank_accounts_${teamId}`);
+ await io.logger.info(`bank_accounts_${teamId}`);
if (!data) {
await io.logger.error(`Bank account not found: ${ctx.source.id}`);
@@ -42,7 +44,7 @@ client.defineJob({
.upsert(
transformTransactions(transactions?.booked, {
accountId: data?.id,
- teamId: data?.team_id,
+ teamId,
}),
{
onConflict: "internal_id",
@@ -51,20 +53,28 @@ client.defineJob({
)
.select();
- await io.sendEvent("🔔 Send notifications", {
- name: Events.TRANSACTIONS_NOTIFICATION,
- payload: {
- teamId: data?.team_id,
- transactions: transactionsData,
- },
- });
+ if (transactionsData && transactionsData.length > 0) {
+ await io.logger.log(`Sending notifications: ${transactionsData.length}`);
+
+ revalidateTag(`transactions_${teamId}`);
+ revalidateTag(`spending_${teamId}`);
+ revalidateTag(`metrics_${teamId}`);
- await io.sendEvent("💅 Enrich Transactions", {
- name: Events.TRANSACTIONS_ENCRICHMENT,
- payload: {
- teamId: data?.team_id,
- },
- });
+ await io.sendEvent("🔔 Send notifications", {
+ name: Events.TRANSACTIONS_NOTIFICATION,
+ payload: {
+ teamId,
+ transactions: transactionsData,
+ },
+ });
+
+ await io.sendEvent("💅 Enrich Transactions", {
+ name: Events.TRANSACTIONS_ENCRICHMENT,
+ payload: {
+ teamId,
+ },
+ });
+ }
if (error) {
await io.logger.error(JSON.stringify(error, null, 2));
diff --git a/packages/ui/src/components/icons.tsx b/packages/ui/src/components/icons.tsx
index a386b63af7..1d21a018ef 100644
--- a/packages/ui/src/components/icons.tsx
+++ b/packages/ui/src/components/icons.tsx
@@ -3,16 +3,22 @@ import { Settings } from "lucide-react";
import {
MdApartment,
MdBarChart,
+ MdBrokenImage,
MdCelebration,
+ MdDescription,
MdDesk,
MdDevices,
+ MdDriveFileMove,
MdDynamicForm,
MdExpandMore,
MdFastfood,
MdFence,
MdFlightTakeoff,
+ MdFolderOpen,
+ MdFolderZip,
MdHomeWork,
MdInventory2,
+ MdMoreHoriz,
MdOutlineAccountBalanceWallet,
MdOutlineCategory,
MdOutlineDescription,
@@ -25,9 +31,11 @@ import {
MdPayments,
MdPeople,
MdPerson,
+ MdPictureAsPdf,
MdRefresh,
MdSave,
MdSensors,
+ MdTopic,
MdTrendingDown,
MdTrendingUp,
} from "react-icons/md";
@@ -316,4 +324,12 @@ export const Icons = {
Apartment: MdApartment,
Sensors: MdSensors,
DynamicForm: MdDynamicForm,
+ MoreHoriz: MdMoreHoriz,
+ Pdf: MdPictureAsPdf,
+ DriveFileMove: MdDriveFileMove,
+ FolderOpen: MdFolderOpen,
+ Topic: MdTopic,
+ BrokenImage: MdBrokenImage,
+ Description: MdDescription,
+ FolderZip: MdFolderZip,
};