@betterinternship/components 1.4.7 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,12 @@
1
- type ProcessStatus = "pending" | "handled" | "failed";
2
1
  export interface ProcessCallbackDto {
3
2
  processId: string;
4
3
  processName: string;
4
+ processCallbackUrl: string;
5
+ }
6
+ export interface ClientProcessOptions {
7
+ loadingMessage?: string;
8
+ successMessage?: string;
9
+ failureMessage?: string;
5
10
  }
6
11
  export interface ProcessResponse {
7
12
  processId: string;
@@ -10,24 +15,35 @@ export interface ProcessResponse {
10
15
  success: boolean;
11
16
  message?: string;
12
17
  }
18
+ /**
19
+ * This should be at the root layout.
20
+ *
21
+ * @context
22
+ */
23
+ export declare const ClientProcessesProvider: ({ children }: {
24
+ children: React.ReactNode;
25
+ }) => import("react/jsx-runtime").JSX.Element;
13
26
  /**
14
27
  * A simpler version, where u just pass the async function that returns process data.
15
28
  *
16
29
  * @param caller
17
30
  * @returns
18
31
  */
19
- export declare function useClientProcess<T, R>({ caller, loadingMessage, successMessage, failureMessage }: {
32
+ export declare function useClientProcess<T, R>({ caller, loadingMessage, successMessage, failureMessage, }: {
20
33
  caller: (inputs: T) => Promise<ProcessResponse>;
21
34
  loadingMessage?: string;
22
35
  successMessage?: string;
23
36
  failureMessage?: string;
24
37
  }): {
25
38
  run: (inputs: T) => Promise<{
39
+ id: string;
26
40
  success: boolean;
27
41
  message: string | undefined;
28
42
  }>;
29
- attempts: number;
30
- status: ProcessStatus;
31
- result: unknown;
43
+ get: (id: string) => {
44
+ id: string;
45
+ name: string;
46
+ result?: unknown;
47
+ success: boolean;
48
+ } | null;
32
49
  };
33
- export {};
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- import require$$0, { useState, useEffect, useRef } from 'react';
1
+ import require$$0, { createContext, useState, useRef, useContext } from 'react';
2
2
  import { Slot } from '@radix-ui/react-slot';
3
- import { useQuery } from '@tanstack/react-query';
3
+ import { useQueries } from '@tanstack/react-query';
4
4
  import { toast } from 'sonner';
5
5
 
6
6
  function styleInject(css, ref) {
@@ -3630,7 +3630,7 @@ const processStatusChecker = (processId) => fetch(`${config.orchestratorApi}/${e
3630
3630
  * @param processId
3631
3631
  * @returns
3632
3632
  */
3633
- const processCallbackFetcher = (processId, processName, processCallbackUrl) => fetch(`${processCallbackUrl}`, {
3633
+ const processCallbackResultFetcher = (processId, processName, processCallbackUrl) => fetch(`${processCallbackUrl}`, {
3634
3634
  method: "POST",
3635
3635
  headers: {
3636
3636
  "Content-Type": "application/json",
@@ -3641,114 +3641,121 @@ const processCallbackFetcher = (processId, processName, processCallbackUrl) => f
3641
3641
  })
3642
3642
  }).then((response) => response.json());
3643
3643
  /**
3644
- * Hook for handling async processes on client.
3644
+ * We need a context to keep track of all client processes.
3645
+ * This helps us manage the state of the toasts as well.
3646
+ */
3647
+ const clientProcessesContext = createContext({});
3648
+ /**
3649
+ * This should be at the root layout.
3645
3650
  *
3646
- * @hook
3647
- * @param UseAsyncProcessOptions
3648
- * @returns
3651
+ * @context
3649
3652
  */
3650
- function useClientProcessBase({ processId, processName, processCallbackUrl, baseInterval = 1000, maxInterval = 30000, }) {
3651
- const [status, setStatus] = useState("pending");
3652
- const attemptRef = useRef(0);
3653
- const query = useQuery({
3654
- queryKey: ["process", processName, processId],
3655
- queryFn: async () => {
3656
- if (!processId)
3657
- throw new Error("No processId");
3658
- if (!processName)
3659
- throw new Error("No processName");
3660
- const { process } = await processStatusChecker(processId);
3661
- // Handled! Return the proper response too
3662
- if (process.status === "handled") {
3663
- const response = await processCallbackFetcher(processId, processName, processCallbackUrl);
3664
- setStatus("handled");
3665
- return response;
3666
- }
3667
- // Pending or failed
3668
- if (process.status === "pending") {
3669
- setStatus("pending");
3670
- }
3671
- else if (process.status === "failed") {
3672
- setStatus("failed");
3673
- }
3674
- // Return null since no response
3675
- return null;
3676
- },
3677
- // Fetching only works if the task hasn't been resolved
3678
- enabled: !!processId && !!processName,
3679
- // Returns the next interval length to use to wait for fetch
3680
- refetchInterval: (query) => {
3681
- const data = query.state.data;
3682
- if (data !== null)
3683
- return false;
3684
- const interval = Math.min(baseInterval * 2 ** attemptRef.current, maxInterval);
3685
- attemptRef.current += 1;
3686
- return interval;
3687
- },
3653
+ const ClientProcessesProvider = ({ children }) => {
3654
+ const [clientProcessDtos, setClientProcessDtos] = useState({});
3655
+ const clientProcessAttempts = useRef({});
3656
+ const clientProcesses = useQueries({ queries: Object.entries(clientProcessDtos).map(([processId, { dto, options }]) => ({
3657
+ queryKey: ['__process', processId],
3658
+ queryFn: async () => {
3659
+ if (!processId)
3660
+ throw new Error("No processId");
3661
+ const { process } = await processStatusChecker(processId);
3662
+ // Handled! Return the proper response too
3663
+ if (process.status === "handled") {
3664
+ const result = await processCallbackResultFetcher(process.id, process.name, dto.processCallbackUrl);
3665
+ console.warn(process.name, options?.successMessage ?? "Success!");
3666
+ toast.success(options?.successMessage ?? "Success!", {
3667
+ id: process.id
3668
+ });
3669
+ setTimeout(() => toast.dismiss(process.id), 1500);
3670
+ return { id: process.id, name: process.name, success: true, result };
3671
+ }
3672
+ // Pending or failed
3673
+ if (process.status === "pending") {
3674
+ console.log(process.name, options?.loadingMessage ?? "Loading...");
3675
+ toast.loading(options?.loadingMessage ?? "Loading...", {
3676
+ id: process.id,
3677
+ duration: Infinity,
3678
+ });
3679
+ return null;
3680
+ }
3681
+ else if (process.status === "failed") {
3682
+ console.error(process.name, options?.failureMessage ?? "Something went wrong.");
3683
+ toast.error(options?.failureMessage ?? "Something went wrong.", {
3684
+ id: process.id
3685
+ });
3686
+ setTimeout(() => toast.dismiss(process.id), 1500);
3687
+ return { id: process.id, name: process.name, success: false };
3688
+ }
3689
+ // Return null since no response
3690
+ return null;
3691
+ },
3692
+ // Returns the next interval length to use to wait for fetch
3693
+ refetchInterval: (query) => {
3694
+ const data = query.state.data;
3695
+ if (data !== null)
3696
+ return false;
3697
+ // If not defined, init to 0
3698
+ if (!clientProcessAttempts.current[processId])
3699
+ clientProcessAttempts.current[processId] = 0;
3700
+ const interval = Math.min(500 * 2 ** clientProcessAttempts.current[processId], 10000);
3701
+ clientProcessAttempts.current[processId] += 1;
3702
+ return interval;
3703
+ },
3704
+ }))
3688
3705
  });
3689
- return {
3690
- status,
3691
- attempts: attemptRef.current,
3692
- result: query.data,
3693
- error: query.error,
3694
- isFetching: query.isFetching,
3706
+ /**
3707
+ * Registers a new process to handle
3708
+ *
3709
+ * @param processId
3710
+ * @param processCallbackDto
3711
+ * @param options
3712
+ */
3713
+ const registerProcess = (processId, processCallbackDto, options) => {
3714
+ setClientProcessDtos({ ...clientProcessDtos, [processId]: { dto: processCallbackDto, options } });
3695
3715
  };
3696
- }
3716
+ /**
3717
+ * Retrieves a process
3718
+ *
3719
+ * @param processId
3720
+ * @returns
3721
+ */
3722
+ const getProcess = (processId) => {
3723
+ return clientProcesses.find(clientProcess => clientProcess.data?.id === processId)?.data ?? null;
3724
+ };
3725
+ return (jsxRuntimeExports.jsx(clientProcessesContext.Provider, { value: {
3726
+ registerProcess,
3727
+ getProcess
3728
+ }, children: children }));
3729
+ };
3697
3730
  /**
3698
3731
  * A simpler version, where u just pass the async function that returns process data.
3699
3732
  *
3700
3733
  * @param caller
3701
3734
  * @returns
3702
3735
  */
3703
- function useClientProcess({ caller, loadingMessage, successMessage, failureMessage }) {
3704
- const [response, setResponse] = useState();
3705
- const clientProcess = useClientProcessBase({
3706
- processId: response?.processId,
3707
- processName: response?.processName,
3708
- processCallbackUrl: response?.processCallbackUrl,
3709
- });
3710
- useEffect(() => {
3711
- if (!response?.processId)
3712
- return;
3713
- // Update toast to show current state
3714
- if (clientProcess.status === 'pending') {
3715
- console.log(response.processName, loadingMessage ?? "Loading...");
3716
- toast.loading(loadingMessage ?? "Loading...", {
3717
- id: response.processId,
3718
- duration: Infinity,
3719
- });
3720
- }
3721
- else if (clientProcess.status === 'failed') {
3722
- console.error(response.processName, failureMessage ?? "Something went wrong.");
3723
- toast.error(failureMessage ?? "Something went wrong.", {
3724
- id: response.processId
3725
- });
3726
- }
3727
- else if (clientProcess.status === 'handled') {
3728
- console.warn(response.processName, successMessage ?? "Success!");
3729
- toast.success(successMessage ?? "Success!", {
3730
- id: response.processId
3731
- });
3732
- }
3733
- }, [response, clientProcess]);
3736
+ function useClientProcess({ caller, loadingMessage, successMessage, failureMessage, }) {
3737
+ const clientProcessesApi = useContext(clientProcessesContext);
3734
3738
  // Note that the exposed function doesn't return process ids
3735
3739
  // Client should be oblivious to those
3736
3740
  // Abstraction BABY!
3737
3741
  const wrappedCaller = async (inputs) => {
3738
3742
  const response = await caller(inputs);
3739
- setResponse(response);
3743
+ clientProcessesApi.registerProcess(response.processId, response, {
3744
+ loadingMessage,
3745
+ successMessage,
3746
+ failureMessage
3747
+ });
3740
3748
  return {
3749
+ id: response.processId,
3741
3750
  success: response.success,
3742
3751
  message: response.message,
3743
3752
  };
3744
3753
  };
3745
3754
  return {
3746
3755
  run: wrappedCaller,
3747
- attempts: clientProcess.attempts,
3748
- status: clientProcess.status,
3749
- result: clientProcess.result
3756
+ get: (id) => clientProcessesApi.getProcess(id)
3750
3757
  };
3751
3758
  }
3752
3759
 
3753
- export { Button, buttonVariants, config, configure, useClientProcess };
3760
+ export { Button, ClientProcessesProvider, buttonVariants, config, configure, useClientProcess };
3754
3761
  //# sourceMappingURL=index.esm.js.map