From 84fa9815be33d7256d3fca9272062bbaa07e1a5e Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Tue, 19 Nov 2024 14:45:18 +0100 Subject: [PATCH] Edit tag and use team_id --- .../src/actions/delete-tag-action.ts | 29 +++ apps/dashboard/src/actions/schema.ts | 9 + .../src/actions/update-tag-action.ts | 33 ++++ apps/dashboard/src/components/assign-user.tsx | 18 +- apps/dashboard/src/components/attachments.tsx | 10 +- .../components/modals/import-modal/index.tsx | 11 +- .../src/components/select-account.tsx | 35 ++-- .../src/components/select-category.tsx | 48 +++-- apps/dashboard/src/components/select-tags.tsx | 186 ++++++++++++++---- apps/dashboard/src/components/select-user.tsx | 21 +- .../tables/tracker/data-table-row.tsx | 3 +- .../components/tables/vault/upload-zone.tsx | 9 +- .../src/components/transaction-details.tsx | 14 +- packages/events/src/events.ts | 8 + .../ui/src/components/multiple-selector.tsx | 7 +- 15 files changed, 303 insertions(+), 138 deletions(-) create mode 100644 apps/dashboard/src/actions/delete-tag-action.ts create mode 100644 apps/dashboard/src/actions/update-tag-action.ts diff --git a/apps/dashboard/src/actions/delete-tag-action.ts b/apps/dashboard/src/actions/delete-tag-action.ts new file mode 100644 index 0000000000..2691f35290 --- /dev/null +++ b/apps/dashboard/src/actions/delete-tag-action.ts @@ -0,0 +1,29 @@ +"use server"; + +import { LogEvents } from "@midday/events/events"; +import { revalidateTag } from "next/cache"; +import { authActionClient } from "./safe-action"; +import { deleteTagSchema } from "./schema"; + +export const deleteTagAction = authActionClient + .schema(deleteTagSchema) + .metadata({ + name: "delete-tag", + track: { + event: LogEvents.DeleteTag.name, + channel: LogEvents.DeleteTag.channel, + }, + }) + .action(async ({ parsedInput: { id }, ctx: { supabase, user } }) => { + const { data } = await supabase + .from("tags") + .delete() + .eq("id", id) + .select("id, name") + .single(); + + revalidateTag(`tracker_projects_${user.team_id}`); + revalidateTag(`transactions_${user.team_id}`); + + return data; + }); diff --git a/apps/dashboard/src/actions/schema.ts b/apps/dashboard/src/actions/schema.ts index be805874ec..6b114bdb52 100644 --- a/apps/dashboard/src/actions/schema.ts +++ b/apps/dashboard/src/actions/schema.ts @@ -19,6 +19,15 @@ export const createTransactionTagSchema = z.object({ transactionId: z.string(), }); +export const deleteTagSchema = z.object({ + id: z.string(), +}); + +export const updateTagSchema = z.object({ + id: z.string(), + name: z.string(), +}); + export const deleteTransactionTagSchema = z.object({ tagId: z.string(), transactionId: z.string(), diff --git a/apps/dashboard/src/actions/update-tag-action.ts b/apps/dashboard/src/actions/update-tag-action.ts new file mode 100644 index 0000000000..1d4766da7e --- /dev/null +++ b/apps/dashboard/src/actions/update-tag-action.ts @@ -0,0 +1,33 @@ +"use server"; + +import { LogEvents } from "@midday/events/events"; +import { revalidateTag } from "next/cache"; +import { authActionClient } from "./safe-action"; +import { updateTagSchema } from "./schema"; + +export const updateTagAction = authActionClient + .schema(updateTagSchema) + .metadata({ + name: "update-tag", + track: { + event: LogEvents.UpdateTag.name, + channel: LogEvents.UpdateTag.channel, + }, + }) + .action(async ({ parsedInput: { name, id }, ctx: { supabase, user } }) => { + const { data } = await supabase + .from("tags") + .update({ + name, + }) + .eq("id", id) + .select("id, name") + .single(); + + revalidateTag(`tracker_projects_${user.team_id}`); + + // TODO: Fix transaction sheet rerendering + // revalidateTag(`transactions_${user.team_id}`); + + return data; + }); diff --git a/apps/dashboard/src/components/assign-user.tsx b/apps/dashboard/src/components/assign-user.tsx index 8c9d492905..62e4bf894a 100644 --- a/apps/dashboard/src/components/assign-user.tsx +++ b/apps/dashboard/src/components/assign-user.tsx @@ -1,8 +1,6 @@ +import { useUserContext } from "@/store/user/hook"; import { createClient } from "@midday/supabase/client"; -import { - getCurrentUserTeamQuery, - getTeamMembersQuery, -} from "@midday/supabase/queries"; +import { getTeamMembersQuery } from "@midday/supabase/queries"; import { Select, SelectContent, @@ -30,6 +28,7 @@ export function AssignUser({ selectedId, isLoading, onSelect }: Props) { const [value, setValue] = useState(); const supabase = createClient(); const [users, setUsers] = useState([]); + const { team_id: teamId } = useUserContext((state) => state.data); useEffect(() => { setValue(selectedId); @@ -37,16 +36,9 @@ export function AssignUser({ selectedId, isLoading, onSelect }: Props) { useEffect(() => { async function getUsers() { - const { data: userData } = await getCurrentUserTeamQuery(supabase); - - if (userData?.team_id) { - const { data: membersData } = await getTeamMembersQuery( - supabase, - userData.team_id, - ); + const { data: membersData } = await getTeamMembersQuery(supabase, teamId); - setUsers(membersData?.map(({ user }) => user)); - } + setUsers(membersData?.map(({ user }) => user)); } getUsers(); diff --git a/apps/dashboard/src/components/attachments.tsx b/apps/dashboard/src/components/attachments.tsx index dc0da54471..2caca02e1c 100644 --- a/apps/dashboard/src/components/attachments.tsx +++ b/apps/dashboard/src/components/attachments.tsx @@ -2,8 +2,7 @@ import { deleteAttachmentAction } from "@/actions/delete-attachment-action"; import { useUpload } from "@/hooks/use-upload"; -import { createClient } from "@midday/supabase/client"; -import { getCurrentUserTeamQuery } from "@midday/supabase/queries"; +import { useUserContext } from "@/store/user/hook"; import { cn } from "@midday/ui/cn"; import { useToast } from "@midday/ui/use-toast"; import { stripSpecialCharacters } from "@midday/utils"; @@ -33,11 +32,12 @@ type Props = { }; export function Attachments({ prefix, data, onUpload }: Props) { - const supabase = createClient(); const { toast } = useToast(); const [files, setFiles] = useState([]); const { uploadFile } = useUpload(); + const { team_id: teamId } = useUserContext((state) => state.data); + const handleOnDelete = async (id: string) => { setFiles((files) => files.filter((file) => file?.id !== id)); await deleteAttachmentAction(id); @@ -54,15 +54,13 @@ export function Attachments({ prefix, data, onUpload }: Props) { })), ]); - const { data: userData } = await getCurrentUserTeamQuery(supabase); - const uploadedFiles = await Promise.all( acceptedFiles.map(async (acceptedFile) => { const filename = stripSpecialCharacters(acceptedFile.name); const { path } = await uploadFile({ bucket: "vault", - path: [userData?.team_id, "transactions", prefix, filename], + path: [teamId, "transactions", prefix, filename], file: acceptedFile, }); diff --git a/apps/dashboard/src/components/modals/import-modal/index.tsx b/apps/dashboard/src/components/modals/import-modal/index.tsx index 2a2ef599f4..d9fa0f24ca 100644 --- a/apps/dashboard/src/components/modals/import-modal/index.tsx +++ b/apps/dashboard/src/components/modals/import-modal/index.tsx @@ -2,9 +2,8 @@ import { importTransactionsAction } from "@/actions/transactions/import-transactions"; import { useUpload } from "@/hooks/use-upload"; +import { useUserContext } from "@/store/user/hook"; import { zodResolver } from "@hookform/resolvers/zod"; -import { createClient } from "@midday/supabase/client"; -import { getCurrentUserTeamQuery } from "@midday/supabase/queries"; import { AnimatedSizeContainer } from "@midday/ui/animated-size-container"; import { Button } from "@midday/ui/button"; import { @@ -48,10 +47,11 @@ export function ImportModal({ currencies, defaultCurrency }: Props) { null, ); + const { team_id: teamId } = useUserContext((state) => state.data); + const [pageNumber, setPageNumber] = useState(0); const page = pages[pageNumber]; - const supabase = createClient(); const { uploadFile } = useUpload(); const { toast } = useToast(); @@ -218,15 +218,12 @@ export function ImportModal({ currencies, defaultCurrency }: Props) { setIsImporting(true); if (data.import_type === "csv") { - const { data: userData } = - await getCurrentUserTeamQuery(supabase); - const filename = stripSpecialCharacters( data.file.name, ); const { path } = await uploadFile({ bucket: "vault", - path: [userData?.team_id, "imports", filename], + path: [teamId, "imports", filename], file, }); diff --git a/apps/dashboard/src/components/select-account.tsx b/apps/dashboard/src/components/select-account.tsx index 0f29ce485e..b1508588e7 100644 --- a/apps/dashboard/src/components/select-account.tsx +++ b/apps/dashboard/src/components/select-account.tsx @@ -1,10 +1,8 @@ import { createBankAccountAction } from "@/actions/create-bank-account-action"; +import { useUserContext } from "@/store/user/hook"; import { formatAccountName } from "@/utils/format"; import { createClient } from "@midday/supabase/client"; -import { - getCurrentUserTeamQuery, - getTeamBankAccountsQuery, -} from "@midday/supabase/queries"; +import { getTeamBankAccountsQuery } from "@midday/supabase/queries"; import { ComboboxDropdown } from "@midday/ui/combobox-dropdown"; import { useAction } from "next-safe-action/hooks"; import { useEffect, useState } from "react"; @@ -27,6 +25,8 @@ export function SelectAccount({ placeholder, onChange, value }: Props) { const [data, setData] = useState([]); const supabase = createClient(); + const { team_id: teamId } = useUserContext((state) => state.data); + const createBankAccount = useAction(createBankAccountAction, { onSuccess: async ({ data: result }) => { if (result) { @@ -38,22 +38,19 @@ export function SelectAccount({ placeholder, onChange, value }: Props) { useEffect(() => { async function fetchData() { - const { data: userData } = await getCurrentUserTeamQuery(supabase); - if (userData?.team_id) { - const repsonse = await getTeamBankAccountsQuery(supabase, { - teamId: userData.team_id, - }); + const repsonse = await getTeamBankAccountsQuery(supabase, { + teamId, + }); - setData( - repsonse.data?.map((account) => ({ - id: account.id, - label: account.name, - logo: account?.logo_url, - currency: account.currency, - type: account.type, - })), - ); - } + setData( + repsonse.data?.map((account) => ({ + id: account.id, + label: account.name, + logo: account?.logo_url, + currency: account.currency, + type: account.type, + })), + ); } if (!data.length) { diff --git a/apps/dashboard/src/components/select-category.tsx b/apps/dashboard/src/components/select-category.tsx index 616694197f..7aecb9a3ef 100644 --- a/apps/dashboard/src/components/select-category.tsx +++ b/apps/dashboard/src/components/select-category.tsx @@ -1,10 +1,8 @@ import { createCategoriesAction } from "@/actions/create-categories-action"; +import { useUserContext } from "@/store/user/hook"; import { getColorFromName } from "@/utils/categories"; import { createClient } from "@midday/supabase/client"; -import { - getCategoriesQuery, - getCurrentUserTeamQuery, -} from "@midday/supabase/queries"; +import { getCategoriesQuery } from "@midday/supabase/queries"; import { ComboboxDropdown } from "@midday/ui/combobox-dropdown"; import { Spinner } from "@midday/ui/spinner"; import { useAction } from "next-safe-action/hooks"; @@ -46,31 +44,29 @@ export function SelectCategory({ const [isLoading, setIsLoading] = useState(true); const supabase = createClient(); + const { team_id: teamId } = useUserContext((state) => state.data); + useEffect(() => { async function fetchData() { - const { data: userData } = await getCurrentUserTeamQuery(supabase); - - if (userData?.team_id) { - const response = await getCategoriesQuery(supabase, { - teamId: userData.team_id, - limit: 1000, - }); + const response = await getCategoriesQuery(supabase, { + teamId, + limit: 1000, + }); - if (response.data) { - setData([ - ...response.data.map(transformCategory), - ...(uncategorized - ? [ - { - id: "uncategorized", - label: "Uncategorized", - color: "#606060", - slug: "uncategorized", - }, - ] - : []), - ]); - } + if (response.data) { + setData([ + ...response.data.map(transformCategory), + ...(uncategorized + ? [ + { + id: "uncategorized", + label: "Uncategorized", + color: "#606060", + slug: "uncategorized", + }, + ] + : []), + ]); } setIsLoading(false); diff --git a/apps/dashboard/src/components/select-tags.tsx b/apps/dashboard/src/components/select-tags.tsx index f9240fd123..949e389364 100644 --- a/apps/dashboard/src/components/select-tags.tsx +++ b/apps/dashboard/src/components/select-tags.tsx @@ -1,8 +1,21 @@ import { createTagAction } from "@/actions/create-tag-action"; +import { deleteTagAction } from "@/actions/delete-tag-action"; +import { updateTagAction } from "@/actions/update-tag-action"; import { useUserContext } from "@/store/user/hook"; import { createClient } from "@midday/supabase/client"; import { getTagsQuery } from "@midday/supabase/queries"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@midday/ui/dialog"; +import { Input } from "@midday/ui/input"; +import { Label } from "@midday/ui/label"; import MultipleSelector, { type Option } from "@midday/ui/multiple-selector"; +import { SubmitButton } from "@midday/ui/submit-button"; import { useAction } from "next-safe-action/hooks"; import React, { useEffect, useState } from "react"; @@ -22,18 +35,40 @@ export function SelectTags({ onCreate, }: Props) { const supabase = createClient(); - + const [isOpen, setIsOpen] = useState(false); const [data, setData] = useState(tags ?? []); const [selected, setSelected] = useState(tags ?? []); + const [editingTag, setEditingTag] = useState