diff --git a/apps/resources/app/profile/DeleteAccountButton.tsx b/apps/resources/app/profile/DeleteAccountButton.tsx index c9a5748c..5ba4eac2 100644 --- a/apps/resources/app/profile/DeleteAccountButton.tsx +++ b/apps/resources/app/profile/DeleteAccountButton.tsx @@ -8,7 +8,7 @@ import { deleteAccount } from './actions'; export const DeleteAccountButton = () => { const { isRunning, isSuccess, error, runAction } = useAction(deleteAccount, { - onRunAction: (test) => {}, + onRunAction: () => {}, onSuccess: () => { signOut(); }, diff --git a/apps/resources/components/Comments/DeleteCommentButton.tsx b/apps/resources/components/Comments/DeleteCommentButton.tsx index 2f517ab6..2bf701ea 100644 --- a/apps/resources/components/Comments/DeleteCommentButton.tsx +++ b/apps/resources/components/Comments/DeleteCommentButton.tsx @@ -21,7 +21,7 @@ export const DeleteCommentButton = ({ }: Props) => { const { isRunning, runAction } = useAction(deleteComment, { onError: (error) => { - console.log(error); + console.error(error); }, }); const handleDeleteAccount = () => { diff --git a/apps/resources/lib/serverActions/client.ts b/apps/resources/lib/serverActions/client.ts index 0f0afd90..23a06c24 100644 --- a/apps/resources/lib/serverActions/client.ts +++ b/apps/resources/lib/serverActions/client.ts @@ -1,6 +1,6 @@ import { useCallback, useReducer, useTransition } from 'react'; import { z } from 'zod'; -import { InferInputType, InferValidationErrors, ServerAction } from './server'; +import { InferInputArgs, InferValidationErrors, ServerAction } from './server'; interface State { isIdle: boolean; @@ -75,7 +75,7 @@ export const useAction = < >( inputAction: ServerAction, options: { - onRunAction?: (input: InferInputType) => void; + onRunAction?: (...inputArgs: InferInputArgs) => void; onSuccess?: (data: TResponse | null) => void; onError?: ( error: string | null, @@ -91,16 +91,16 @@ export const useAction = < const [isRunning, startTransition] = useTransition(); const runAction = useCallback( - async (input: InferInputType) => { + async (...inputArgs: InferInputArgs) => { startTransition(async () => { dispatch({ type: 'RUN_ACTION', }); - options.onRunAction?.(input); + options.onRunAction?.(...inputArgs); try { - const result = await inputAction(input); + const result = await inputAction(...inputArgs); // If /next/navigation function (redirect() and notFound()) is called in the action, the result will be undefined // Skip processing because the page will be redirected diff --git a/apps/resources/lib/serverActions/server.ts b/apps/resources/lib/serverActions/server.ts index b4f9ffb5..25fa5779 100644 --- a/apps/resources/lib/serverActions/server.ts +++ b/apps/resources/lib/serverActions/server.ts @@ -7,8 +7,10 @@ import { type MaybePromise = Promise | T; -export type InferInputType = - z.ZodTypeAny extends TInputSchema ? void : z.infer; +// If TInputSchema is a more specific type than z.ZodTypeAny (e.g. z.ZodString), +// then we can infer the input type. Otherwise, no input is needed. +export type InferInputArgs = + z.ZodTypeAny extends TInputSchema ? [] : [input: z.infer]; export type InferValidationErrors = z.inferFlattenedErrors['fieldErrors']; @@ -23,25 +25,26 @@ export type ServerAction< TInputSchema extends z.ZodTypeAny, TResponse extends any, > = ( - input: InferInputType, + ...inputArgs: InferInputArgs ) => Promise> | void; export const createActionClient = (createClientOpts?: { middleware?: () => MaybePromise; }) => { - const actionBuilder = < + const createAction = < TInputSchema extends z.ZodTypeAny, TResponse extends any, >(actionBuilderOpts: { input?: TInputSchema; - action: (args: { + action: (actionArgs: { input: z.output; ctx: Context; }) => MaybePromise | MaybePromise; }) => { - const createAction: ServerAction = async ( - input, + const action: ServerAction = async ( + ...inputArgs ) => { + const [input] = inputArgs; try { let parsedInput = input; if (actionBuilderOpts.input) { @@ -89,8 +92,8 @@ export const createActionClient = (createClientOpts?: { } }; - return createAction; + return action; }; - return actionBuilder; + return createAction; }; diff --git a/apps/resources/lib/serverActions/tests.ts b/apps/resources/lib/serverActions/tests.ts new file mode 100644 index 00000000..066fd1d6 --- /dev/null +++ b/apps/resources/lib/serverActions/tests.ts @@ -0,0 +1,21 @@ +import { z } from 'zod'; +import { createActionClient } from './server'; + +const createAction = createActionClient(); + +const action = createAction({ + input: z.string(), + action: () => {}, +}); + +// @ts-expect-error +action(); +action('string'); + +const action2 = createAction({ + action: () => {}, +}); + +action2(); +// @ts-expect-error +action2('string'); diff --git a/apps/resources/lib/tracking.ts b/apps/resources/lib/tracking.ts index 301489c7..ef3afbe8 100644 --- a/apps/resources/lib/tracking.ts +++ b/apps/resources/lib/tracking.ts @@ -50,10 +50,10 @@ interface TrackingEvents { } export const track = ( - event: TEventKey, - ...data: TrackingEvents[TEventKey] extends null - ? [] - : [TrackingEvents[TEventKey]] + ...args: TrackingEvents[TEventKey] extends null + ? [event: TEventKey] + : [event: TEventKey, data: TrackingEvents[TEventKey]] ) => { + const [event, data] = args; splitbee.track(event, data); }; diff --git a/apps/resources/lib/users.ts b/apps/resources/lib/users.ts index 99765ef2..7e2e1046 100644 --- a/apps/resources/lib/users.ts +++ b/apps/resources/lib/users.ts @@ -22,7 +22,7 @@ export const withUser = async (data: TData) => { const user = users.find((user) => user.id === data.userId); if (!user) { - console.log(`User (${data.userId}) not found`); + console.error(`User (${data.userId}) not found`); } return { @@ -43,7 +43,7 @@ export const withUserCollection = async ( const user = users.find((user) => user.id === element.userId); if (!user) { - console.log(`User (${element.userId}) not found`); + console.error(`User (${element.userId}) not found`); } return {