From 56405fcc5d309313d0f9286831a9af9b43219576 Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Mon, 18 Nov 2024 15:36:06 +0100 Subject: [PATCH] wip --- apps/dashboard/jobs/invoice/scheduler.ts | 8 -- .../src/actions/create-tag-action.tsx | 27 +++++ .../src/actions/create-tags-action.ts | 23 ---- .../create-tracker-project-tag-action.ts | 32 +++++ .../actions/create-transaction-tag-action.ts | 32 +++++ .../actions/delete-transaction-tag-action.ts | 32 +++++ apps/dashboard/src/actions/schema.ts | 16 ++- .../(app)/(sidebar)/transactions/page.tsx | 2 + .../(sidebar)/transactions/search-params.ts | 1 + .../components/forms/tracker-project-form.tsx | 29 ++++- apps/dashboard/src/components/select-tags.tsx | 58 +++++---- .../tables/tracker/data-table-header.tsx | 13 +++ .../tables/tracker/data-table-row.tsx | 70 +++++------ .../tables/transactions/columns.tsx | 20 ++++ .../tables/transactions/data-table-header.tsx | 16 ++- .../src/components/transaction-details.tsx | 24 +++- packages/events/src/events.ts | 12 ++ .../supabase/src/queries/cached-queries.ts | 10 +- packages/supabase/src/queries/index.ts | 28 +++-- packages/supabase/src/types/db.ts | 110 +++++++++++++++++- .../ui/src/components/multiple-selector.tsx | 10 +- 21 files changed, 465 insertions(+), 108 deletions(-) create mode 100644 apps/dashboard/src/actions/create-tag-action.tsx delete mode 100644 apps/dashboard/src/actions/create-tags-action.ts create mode 100644 apps/dashboard/src/actions/create-tracker-project-tag-action.ts create mode 100644 apps/dashboard/src/actions/create-transaction-tag-action.ts create mode 100644 apps/dashboard/src/actions/delete-transaction-tag-action.ts diff --git a/apps/dashboard/jobs/invoice/scheduler.ts b/apps/dashboard/jobs/invoice/scheduler.ts index b3e9e46397..3bad65a0ad 100644 --- a/apps/dashboard/jobs/invoice/scheduler.ts +++ b/apps/dashboard/jobs/invoice/scheduler.ts @@ -15,14 +15,6 @@ export const invoiceScheduler = schedules.task({ if (!invoices) return; - await checkInvoiceStatus.batchTrigger( - invoices.map((invoice) => ({ - payload: { - invoiceId: invoice.id, - }, - })), - ); - logger.info("Invoice status check jobs started", { count: invoices.length, }); diff --git a/apps/dashboard/src/actions/create-tag-action.tsx b/apps/dashboard/src/actions/create-tag-action.tsx new file mode 100644 index 0000000000..81cf743e76 --- /dev/null +++ b/apps/dashboard/src/actions/create-tag-action.tsx @@ -0,0 +1,27 @@ +"use server"; + +import { LogEvents } from "@midday/events/events"; +import { authActionClient } from "./safe-action"; +import { createTagSchema } from "./schema"; + +export const createTagAction = authActionClient + .schema(createTagSchema) + .metadata({ + name: "create-tag", + track: { + event: LogEvents.CreateTag.name, + channel: LogEvents.CreateTag.channel, + }, + }) + .action(async ({ parsedInput: { name }, ctx: { user, supabase } }) => { + const { data } = await supabase + .from("tags") + .insert({ + name, + team_id: user.team_id!, + }) + .select("id, name") + .single(); + + return data; + }); diff --git a/apps/dashboard/src/actions/create-tags-action.ts b/apps/dashboard/src/actions/create-tags-action.ts deleted file mode 100644 index 6e39db339b..0000000000 --- a/apps/dashboard/src/actions/create-tags-action.ts +++ /dev/null @@ -1,23 +0,0 @@ -"use server"; - -import { LogEvents } from "@midday/events/events"; -import { authActionClient } from "./safe-action"; -import { createTagsSchema } from "./schema"; - -export const createTagsAction = authActionClient - .schema(createTagsSchema) - .metadata({ - name: "create-tags", - track: { - event: LogEvents.CreateTag.name, - channel: LogEvents.CreateTag.channel, - }, - }) - .action(async ({ parsedInput: tags, ctx: { user, supabase } }) => { - const { data, error } = await supabase - .from("transaction_tags") - .insert(tags.map((tag) => ({ name: tag.name, team_id: user.team_id }))); - - console.log(error); - return data; - }); diff --git a/apps/dashboard/src/actions/create-tracker-project-tag-action.ts b/apps/dashboard/src/actions/create-tracker-project-tag-action.ts new file mode 100644 index 0000000000..ff584afe31 --- /dev/null +++ b/apps/dashboard/src/actions/create-tracker-project-tag-action.ts @@ -0,0 +1,32 @@ +"use server"; + +import { LogEvents } from "@midday/events/events"; +import { revalidateTag } from "next/cache"; +import { authActionClient } from "./safe-action"; +import { createTrackerProjectTagSchema } from "./schema"; + +export const createTrackerProjectTagAction = authActionClient + .schema(createTrackerProjectTagSchema) + .metadata({ + name: "create-tracker-project-tag", + track: { + event: LogEvents.CreateTrackerProjectTag.name, + channel: LogEvents.CreateTrackerProjectTag.channel, + }, + }) + .action( + async ({ + parsedInput: { tagId, trackerProjectId }, + ctx: { user, supabase }, + }) => { + const { data } = await supabase.from("tracker_project_tags").insert({ + tag_id: tagId, + tracker_project_id: trackerProjectId, + team_id: user.team_id!, + }); + + revalidateTag(`tracker_projects_${user.team_id}`); + + return data; + }, + ); diff --git a/apps/dashboard/src/actions/create-transaction-tag-action.ts b/apps/dashboard/src/actions/create-transaction-tag-action.ts new file mode 100644 index 0000000000..b414102a5c --- /dev/null +++ b/apps/dashboard/src/actions/create-transaction-tag-action.ts @@ -0,0 +1,32 @@ +"use server"; + +import { LogEvents } from "@midday/events/events"; +import { revalidateTag } from "next/cache"; +import { authActionClient } from "./safe-action"; +import { createTransactionTagSchema } from "./schema"; + +export const createTransactionTagAction = authActionClient + .schema(createTransactionTagSchema) + .metadata({ + name: "create-transaction-tag", + track: { + event: LogEvents.CreateTransactionTag.name, + channel: LogEvents.CreateTransactionTag.channel, + }, + }) + .action( + async ({ + parsedInput: { tagId, transactionId }, + ctx: { user, supabase }, + }) => { + const { data } = await supabase.from("transaction_tags").insert({ + tag_id: tagId, + transaction_id: transactionId, + team_id: user.team_id!, + }); + + revalidateTag(`transactions_${user.team_id}`); + + return data; + }, + ); diff --git a/apps/dashboard/src/actions/delete-transaction-tag-action.ts b/apps/dashboard/src/actions/delete-transaction-tag-action.ts new file mode 100644 index 0000000000..095e8c7662 --- /dev/null +++ b/apps/dashboard/src/actions/delete-transaction-tag-action.ts @@ -0,0 +1,32 @@ +"use server"; + +import { LogEvents } from "@midday/events/events"; +import { revalidateTag } from "next/cache"; +import { authActionClient } from "./safe-action"; +import { deleteTransactionTagSchema } from "./schema"; + +export const deleteTransactionTagAction = authActionClient + .schema(deleteTransactionTagSchema) + .metadata({ + name: "delete-transaction-tag", + track: { + event: LogEvents.DeleteTransactionTag.name, + channel: LogEvents.DeleteTransactionTag.channel, + }, + }) + .action( + async ({ + parsedInput: { tagId, transactionId }, + ctx: { user, supabase }, + }) => { + const { data } = await supabase + .from("transaction_tags") + .delete() + .eq("transaction_id", transactionId) + .eq("tag_id", tagId); + + revalidateTag(`transactions_${user.team_id}`); + + return data; + }, + ); diff --git a/apps/dashboard/src/actions/schema.ts b/apps/dashboard/src/actions/schema.ts index af70ae266b..d63c46f3e4 100644 --- a/apps/dashboard/src/actions/schema.ts +++ b/apps/dashboard/src/actions/schema.ts @@ -13,7 +13,21 @@ export const updateUserSchema = z.object({ revalidatePath: z.string().optional(), }); -export const createTagsSchema = z.array(z.object({ name: z.string() })); +export const createTagSchema = z.object({ name: z.string() }); +export const createTransactionTagSchema = z.object({ + tagId: z.string(), + transactionId: z.string(), +}); + +export const deleteTransactionTagSchema = z.object({ + tagId: z.string(), + transactionId: z.string(), +}); + +export const createTrackerProjectTagSchema = z.object({ + tagId: z.string(), + trackerProjectId: z.string(), +}); export type UpdateUserFormValues = z.infer; diff --git a/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/page.tsx b/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/page.tsx index 8cf414aadc..51262b3d88 100644 --- a/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/page.tsx +++ b/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/page.tsx @@ -39,6 +39,7 @@ export default async function Transactions({ statuses, recurring, accounts, + tags, } = searchParamsCache.parse(searchParams); // Move this in a suspense @@ -59,6 +60,7 @@ export default async function Transactions({ statuses, recurring, accounts, + tags, }; const sort = searchParams?.sort?.split(":"); diff --git a/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/search-params.ts b/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/search-params.ts index 6dfd036e22..06084cbb5a 100644 --- a/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/search-params.ts +++ b/apps/dashboard/src/app/[locale]/(app)/(sidebar)/transactions/search-params.ts @@ -13,6 +13,7 @@ export const searchParamsCache = createSearchParamsCache({ start: parseAsString, end: parseAsString, categories: parseAsArrayOf(parseAsString), + tags: parseAsArrayOf(parseAsString), accounts: parseAsArrayOf(parseAsString), assignees: parseAsArrayOf(parseAsString), recurring: parseAsArrayOf( diff --git a/apps/dashboard/src/components/forms/tracker-project-form.tsx b/apps/dashboard/src/components/forms/tracker-project-form.tsx index e52826fde4..e35715998d 100644 --- a/apps/dashboard/src/components/forms/tracker-project-form.tsx +++ b/apps/dashboard/src/components/forms/tracker-project-form.tsx @@ -1,5 +1,6 @@ "use client"; +import { createTrackerProjectTagAction } from "@/actions/create-tracker-project-tag-action"; import type { Customer } from "@/components/invoice/customer-details"; import { uniqueCurrencies } from "@midday/location/currencies"; import { Button } from "@midday/ui/button"; @@ -26,6 +27,7 @@ import { import { Switch } from "@midday/ui/switch"; import { Textarea } from "@midday/ui/textarea"; import { Loader2 } from "lucide-react"; +import { useAction } from "next-safe-action/hooks"; import { useEffect, useState } from "react"; import { SearchCustomer } from "../search-customer"; import { SelectTags } from "../select-tags"; @@ -44,6 +46,7 @@ export function TrackerProjectForm({ customers, }: Props) { const [isOpen, setIsOpen] = useState(false); + const createTrackerProjectTag = useAction(createTrackerProjectTagAction); useEffect(() => { setIsOpen(Boolean(form.getValues("billable"))); @@ -101,7 +104,31 @@ export function TrackerProjectForm({ Expense Tags - + + // createTrackerProjectTag.execute({ + // tagId, + // trackerProjectId: form.getValues("id"), + // }) + // } + // tags={data?.tags?.map((tag) => ({ + // label: tag.tag.name, + // value: tag.tag.name, + // id: tag.tag.id, + // }))} + onSelect={(tag) => { + createTrackerProjectTag.execute({ + tagId: tag.id, + trackerProjectId: form.getValues("id"), + }); + }} + onRemove={(tag) => { + createTrackerProjectTag.execute({ + tagId: tag.id, + trackerProjectId: form.getValues("id"), + }); + }} + /> Tags help categorize and track project expenses. diff --git a/apps/dashboard/src/components/select-tags.tsx b/apps/dashboard/src/components/select-tags.tsx index baf94b7bdd..b02c8c2e6c 100644 --- a/apps/dashboard/src/components/select-tags.tsx +++ b/apps/dashboard/src/components/select-tags.tsx @@ -1,27 +1,33 @@ -import { createTagsAction } from "@/actions/create-tags-action"; +import { createTagAction } from "@/actions/create-tag-action"; import { useUserContext } from "@/store/user/hook"; import { createClient } from "@midday/supabase/client"; -import { getTransactionTagsQuery } from "@midday/supabase/queries"; +import { getTagsQuery } from "@midday/supabase/queries"; import MultipleSelector, { type Option } from "@midday/ui/multiple-selector"; import { useAction } from "next-safe-action/hooks"; import React, { useEffect, useState } from "react"; type Props = { tags?: Option[]; + onSelect?: (tag: Option) => void; + onRemove?: (tag: Option & { id: string }) => void; }; -export function SelectTags({ tags }: Props) { - const [isLoading, setIsLoading] = useState(false); - const [data, setData] = useState(tags ?? []); - const createTags = useAction(createTagsAction); +export function SelectTags({ tags, onSelect, onRemove }: Props) { const supabase = createClient(); + + const [data, setData] = useState(tags ?? []); + const [selected, setSelected] = useState(tags ?? []); + const createTag = useAction(createTagAction, { + onSuccess: ({ data }) => { + onSelect(data); + }, + }); + const { team_id: teamId } = useUserContext((state) => state.data); useEffect(() => { async function fetchData() { - setIsLoading(true); - - const { data } = await getTransactionTagsQuery(supabase, teamId); + const { data } = await getTagsQuery(supabase, teamId); if (data?.length) { setData( @@ -32,30 +38,42 @@ export function SelectTags({ tags }: Props) { })), ); } - - setIsLoading(false); } - if (!data?.length) { - fetchData(); - } + fetchData(); }, [teamId]); return (
no results found.

} + emptyIndicator={

No results found.

} + onCreate={(option) => { + createTag.execute({ name: option.value }); + }} onChange={(options) => { - const newTags = options.filter((option) => option.create); + setSelected(options); + + const newTag = options.find( + (tag) => !selected.find((opt) => opt.value === tag.value), + ); + + if (newTag) { + onSelect?.(newTag); + return; + } - console.log(newTags); + if (options.length < selected.length) { + const removedTag = selected.find( + (tag) => !options.find((opt) => opt.value === tag.value), + ); - if (newTags.length > 0) { - createTags.execute(newTags.map((tag) => ({ name: tag.value }))); + if (removedTag) { + onRemove?.(removedTag); + } } }} /> diff --git a/apps/dashboard/src/components/tables/tracker/data-table-header.tsx b/apps/dashboard/src/components/tables/tracker/data-table-header.tsx index b869616e19..6630e51665 100644 --- a/apps/dashboard/src/components/tables/tracker/data-table-header.tsx +++ b/apps/dashboard/src/components/tables/tracker/data-table-header.tsx @@ -97,6 +97,19 @@ export function DataTableHeader() { )} + + + + + + + )} + {isVisible("bank_account") && (
diff --git a/packages/events/src/events.ts b/packages/events/src/events.ts index 6a6bdf9f75..c5740f2d67 100644 --- a/packages/events/src/events.ts +++ b/packages/events/src/events.ts @@ -219,4 +219,16 @@ export const LogEvents = { name: "Create Tag", channel: "tag", }, + CreateTransactionTag: { + name: "Create Transaction Tag", + channel: "tag", + }, + DeleteTransactionTag: { + name: "Delete Transaction Tag", + channel: "tag", + }, + CreateTrackerProjectTag: { + name: "Create Tracker Project Tag", + channel: "tag", + }, }; diff --git a/packages/supabase/src/queries/cached-queries.ts b/packages/supabase/src/queries/cached-queries.ts index efd5221863..136199948b 100644 --- a/packages/supabase/src/queries/cached-queries.ts +++ b/packages/supabase/src/queries/cached-queries.ts @@ -30,6 +30,7 @@ import { getPaymentStatusQuery, getRunwayQuery, getSpendingQuery, + getTagsQuery, getTeamBankAccountsQuery, getTeamInvitesQuery, getTeamMembersQuery, @@ -38,7 +39,6 @@ import { getTeamsByUserIdQuery, getTrackerProjectsQuery, getTrackerRecordsByRangeQuery, - getTransactionTagsQuery, getTransactionsQuery, getUserInvitesQuery, getUserQuery, @@ -581,7 +581,7 @@ export const getLastInvoiceNumber = async () => { )(); }; -export const getTransactionTags = async () => { +export const getTags = async () => { const supabase = createClient(); const user = await getUser(); const teamId = user?.data?.team_id; @@ -592,11 +592,11 @@ export const getTransactionTags = async () => { return unstable_cache( async () => { - return getTransactionTagsQuery(supabase, teamId); + return getTagsQuery(supabase, teamId); }, - ["transaction_tags", teamId], + ["tags", teamId], { - tags: [`transaction_tags_${teamId}`], + tags: [`tags_${teamId}`], revalidate: 180, }, )(); diff --git a/packages/supabase/src/queries/index.ts b/packages/supabase/src/queries/index.ts index eb2690596a..98978dfb29 100644 --- a/packages/supabase/src/queries/index.ts +++ b/packages/supabase/src/queries/index.ts @@ -164,6 +164,7 @@ export type GetTransactionsParams = { statuses?: string[]; attachments?: "include" | "exclude"; categories?: string[]; + tags?: string[]; accounts?: string[]; assignees?: string[]; type?: "income" | "expense"; @@ -183,6 +184,7 @@ export async function getTransactionsQuery( statuses, attachments, categories, + tags, type, accounts, start, @@ -208,6 +210,7 @@ export async function getTransactionsQuery( "category:transaction_categories(id, name, color, slug)", "bank_account:bank_accounts(id, name, currency, bank_connection:bank_connections(id, logo_url))", "attachments:transaction_attachments(id, name, size, path, type)", + "tags:transaction_tags(id, tag_id, tag:tags(id, name))", "vat:calculated_vat", ]; @@ -228,6 +231,8 @@ export async function getTransactionsQuery( query.order("bank_account(name)", { ascending }); } else if (column === "category") { query.order("category(name)", { ascending }); + } else if (column === "tags") { + query.order("is_transaction_tagged", { ascending }); } else { query.order(column, { ascending }); } @@ -280,6 +285,15 @@ export async function getTransactionsQuery( query.or(matchCategory); } + if (tags) { + query + .select( + [...columns, "temp_filter_tags:transaction_tags!inner()"].join(","), + ) + .eq("team_id", teamId) + .in("temp_filter_tags.tag_id", tags); + } + if (recurring) { if (recurring.includes("all")) { query.eq("recurring", true); @@ -338,6 +352,7 @@ export async function getTransactionQuery(supabase: Client, id: string) { "assigned:assigned_id(*)", "category:category_slug(id, name, vat)", "attachments:transaction_attachments(*)", + "tags:transaction_tags(id, tag:tags(id, name))", "bank_account:bank_accounts(id, name, currency, bank_connection:bank_connections(id, logo_url))", "vat:calculated_vat", ]; @@ -870,7 +885,7 @@ export async function getTrackerProjectQuery( ) { return supabase .from("tracker_projects") - .select("*") + .select("*, tags:tracker_project_tags(id, tag:tags(id, name))") .eq("id", params.projectId) .eq("team_id", params.teamId) .single(); @@ -912,7 +927,7 @@ export async function getTrackerProjectsQuery( const query = supabase .from("tracker_projects") .select( - "*, total_duration, users:get_assigned_users_for_project, total_amount:get_project_total_amount, customer:customer_id(id, name, website)", + "*, total_duration, users:get_assigned_users_for_project, total_amount:get_project_total_amount, customer:customer_id(id, name, website), tags:tracker_project_tags(id, tag:tags(id, name))", { count: "exact", }, @@ -946,6 +961,8 @@ export async function getTrackerProjectsQuery( // query.order("assigned_id", { ascending: value === "asc" }); } else if (column === "customer") { query.order("customer(name)", { ascending: value === "asc" }); + } else if (column === "tags") { + query.order("is_project_tagged", { ascending: value === "asc" }); } else { query.order(column, { ascending: value === "asc" }); } @@ -1304,12 +1321,9 @@ export async function getLastInvoiceNumberQuery( return { data }; } -export async function getTransactionTagsQuery( - supabase: Client, - teamId: string, -) { +export async function getTagsQuery(supabase: Client, teamId: string) { return supabase - .from("transaction_tags") + .from("tags") .select("*") .eq("team_id", teamId) .order("created_at", { ascending: false }); diff --git a/packages/supabase/src/types/db.ts b/packages/supabase/src/types/db.ts index d10ecdcab4..9e4a414f84 100644 --- a/packages/supabase/src/types/db.ts +++ b/packages/supabase/src/types/db.ts @@ -781,6 +781,35 @@ export type Database = { }, ] } + tags: { + Row: { + created_at: string + id: string + name: string + team_id: string + } + Insert: { + created_at?: string + id?: string + name: string + team_id: string + } + Update: { + created_at?: string + id?: string + name?: string + team_id?: string + } + Relationships: [ + { + foreignKeyName: "tags_team_id_fkey" + columns: ["team_id"] + isOneToOne: false + referencedRelation: "teams" + referencedColumns: ["id"] + }, + ] + } teams: { Row: { base_currency: string | null @@ -894,6 +923,52 @@ export type Database = { }, ] } + tracker_project_tags: { + Row: { + created_at: string + id: string + tag_id: string + team_id: string + tracker_project_id: string + } + Insert: { + created_at?: string + id?: string + tag_id: string + team_id: string + tracker_project_id: string + } + Update: { + created_at?: string + id?: string + tag_id?: string + team_id?: string + tracker_project_id?: string + } + Relationships: [ + { + foreignKeyName: "project_tags_tag_id_fkey" + columns: ["tag_id"] + isOneToOne: false + referencedRelation: "tags" + referencedColumns: ["id"] + }, + { + foreignKeyName: "project_tags_tracker_project_id_fkey" + columns: ["tracker_project_id"] + isOneToOne: false + referencedRelation: "tracker_projects" + referencedColumns: ["id"] + }, + { + foreignKeyName: "tracker_project_tags_team_id_fkey" + columns: ["team_id"] + isOneToOne: false + referencedRelation: "teams" + referencedColumns: ["id"] + }, + ] + } tracker_projects: { Row: { billable: boolean | null @@ -1148,22 +1223,32 @@ export type Database = { Row: { created_at: string id: string - name: string + tag_id: string team_id: string + transaction_id: string } Insert: { created_at?: string id?: string - name: string + tag_id: string team_id: string + transaction_id: string } Update: { created_at?: string id?: string - name?: string + tag_id?: string team_id?: string + transaction_id?: string } Relationships: [ + { + foreignKeyName: "transaction_tags_tag_id_fkey" + columns: ["tag_id"] + isOneToOne: false + referencedRelation: "tags" + referencedColumns: ["id"] + }, { foreignKeyName: "transaction_tags_team_id_fkey" columns: ["team_id"] @@ -1171,6 +1256,13 @@ export type Database = { referencedRelation: "teams" referencedColumns: ["id"] }, + { + foreignKeyName: "transaction_tags_transaction_id_fkey" + columns: ["transaction_id"] + isOneToOne: false + referencedRelation: "transactions" + referencedColumns: ["id"] + }, ] } transactions: { @@ -2069,6 +2161,18 @@ export type Database = { } Returns: boolean } + is_project_tagged: { + Args: { + project: unknown + } + Returns: boolean + } + is_transaction_tagged: { + Args: { + transaction: unknown + } + Returns: boolean + } match_transaction_with_inbox: { Args: { p_transaction_id: string diff --git a/packages/ui/src/components/multiple-selector.tsx b/packages/ui/src/components/multiple-selector.tsx index 58c0a811d8..b82e86a413 100644 --- a/packages/ui/src/components/multiple-selector.tsx +++ b/packages/ui/src/components/multiple-selector.tsx @@ -49,6 +49,7 @@ interface MultipleSelectorProps { **/ onSearchSync?: (value: string) => Option[]; onChange?: (options: Option[]) => void; + onCreate?: (option: Option) => void; /** Limit the maximum number of selected options. */ maxSelected?: number; /** When the number of selected options exceeds the limit, the onMaxSelected will be called. */ @@ -179,6 +180,7 @@ const MultipleSelector = React.forwardRef< { value, onChange, + onCreate, placeholder, defaultOptions: arrayDefaultOptions = [], options: arrayOptions, @@ -375,12 +377,12 @@ const MultipleSelector = React.forwardRef< return; } setInputValue(""); - const newOptions = [ - ...selected, - { value: inputValue, label: inputValue, create: true }, - ]; + const newOption = { value: inputValue, label: inputValue }; + const newOptions = [...selected, newOption]; + setSelected(newOptions); onChange?.(newOptions); + onCreate?.(newOption); }} > {`Create "${inputValue}"`}