diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx index 12ee8290ca..75adf898d6 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx @@ -1,4 +1,3 @@ -import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackProvider"; import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent"; import { EngineProvider } from "./contexts/EngineContext"; import { useActiveJobs } from "~/hooks/useActiveJobs"; @@ -28,12 +27,16 @@ import { useGenerationCompleteEvent } from "./TauriEvents/generations/useGenerat import { PageEditor } from "~/pages/PageEnigma/PageEditor"; import { GalleryDragComponent } from "@storyteller/ui-gallery-modal"; import { LoadingDots } from "@storyteller/ui-loading"; +import { + isActionReminderOpen, + actionReminderProps, + ActionReminderModal, +} from "@storyteller/ui-action-reminder-modal"; export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { useSignals(); useActiveJobs(); useBackgroundLoadingMedia(); - // implement the code to handle incoming messages from the Engine useQueueHandler(); const [validGpu, setValidGpu] = useState("unknown"); @@ -110,8 +113,9 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { return ; } - return ( + const currentReminderModalProps = actionReminderProps.value; + return ( @@ -127,7 +131,22 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { - + {currentReminderModalProps && ( + + )} + ); }; diff --git a/frontend/apps/editor3d/tsconfig.json b/frontend/apps/editor3d/tsconfig.json index e364cb141b..cb9ae01121 100644 --- a/frontend/apps/editor3d/tsconfig.json +++ b/frontend/apps/editor3d/tsconfig.json @@ -58,6 +58,9 @@ { "path": "../../libs/components/button-dropdown" }, + { + "path": "../../libs/components/action-reminder-modal" + }, { "path": "../../libs/components/login-modal" }, diff --git a/frontend/libs/components/action-reminder-modal/.babelrc b/frontend/libs/components/action-reminder-modal/.babelrc new file mode 100644 index 0000000000..1ea870ead4 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/frontend/libs/components/action-reminder-modal/README.md b/frontend/libs/components/action-reminder-modal/README.md new file mode 100644 index 0000000000..5a6c2ac9f6 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/README.md @@ -0,0 +1,7 @@ +# @storyteller/ui-action-reminder-modal + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test @storyteller/ui-action-reminder-modal` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/frontend/libs/components/action-reminder-modal/package.json b/frontend/libs/components/action-reminder-modal/package.json new file mode 100644 index 0000000000..0ddb51960f --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/package.json @@ -0,0 +1,17 @@ +{ + "name": "@storyteller/ui-action-reminder-modal", + "version": "0.0.1", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "development": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + } +} diff --git a/frontend/libs/components/action-reminder-modal/src/index.ts b/frontend/libs/components/action-reminder-modal/src/index.ts new file mode 100644 index 0000000000..ea40603be8 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/src/index.ts @@ -0,0 +1,2 @@ +export * from "./lib/action-reminder-modal-signals"; +export * from "./lib/action-reminder-modal"; diff --git a/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal-signals.ts b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal-signals.ts new file mode 100644 index 0000000000..075a2d32f4 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal-signals.ts @@ -0,0 +1,48 @@ +// frontend/apps/editor3d/app/src/signals/actionReminderSignals.ts +import { signal } from "@preact/signals-react"; +import { ReactNode } from "react"; +import { ReminderType } from "@storyteller/ui-action-reminder-modal"; + +export interface ShowActionReminderOptions { + reminderType: ReminderType; + onPrimaryAction: () => void; + title?: string; + message?: ReactNode; + primaryActionText?: string; + secondaryActionText?: string; + onSecondaryAction?: () => void; + isLoading?: boolean; + openAiLogo?: string; +} + +interface ActionReminderModalFullProps extends ShowActionReminderOptions { + onClose: () => void; +} + +export const isActionReminderOpen = signal(false); +export const actionReminderProps = signal( + null +); + +export function showActionReminder(options: ShowActionReminderOptions): void { + actionReminderProps.value = { + ...options, + onClose: () => { + isActionReminderOpen.value = false; + }, + }; + isActionReminderOpen.value = true; +} + +// Example of how to trigger it - BFlat: +// import { showGlobalActionReminder } from "@storyteller/ui-action-reminder-modal"; +// import openAiLogo from 'path/to/logo.svg'; +// +// function someAction() { +// showActionReminder({ +// reminderType: 'soraLogin', +// onPrimaryAction: () => console.log('Login with Sora!'), +// openAiLogo: openAiLogo, +// title: 'Sora Login Needed' +// }); +// } diff --git a/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.module.css b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.module.css new file mode 100644 index 0000000000..d0246c74b9 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.module.css @@ -0,0 +1,7 @@ +/* + * Replace this with your own classes + * + * e.g. + * .container { + * } +*/ \ No newline at end of file diff --git a/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.spec.tsx b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.spec.tsx new file mode 100644 index 0000000000..019db4d868 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.spec.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; + +import ActionReminderModal from './action-reminder-modal'; + +describe('ActionReminderModal', () => { + + it('should render successfully', () => { + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); + +}); diff --git a/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.tsx b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.tsx new file mode 100644 index 0000000000..643a3e90a2 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/src/lib/action-reminder-modal.tsx @@ -0,0 +1,140 @@ +import { Button } from "@storyteller/ui-button"; +import { Modal } from "@storyteller/ui-modal"; +import { ReactNode } from "react"; +import { faRightToBracket } from "@fortawesome/pro-solid-svg-icons"; + +export type ReminderType = "default" | "soraLogin" | "artcraftLogin"; + +interface ActionReminderModalProps { + isOpen: boolean; + onClose: () => void; + reminderType?: ReminderType; + onPrimaryAction: () => void; + title?: string; + hideTitle?: boolean; + message?: ReactNode; + children?: ReactNode; + primaryActionText?: string; + secondaryActionText?: string; + onSecondaryAction?: () => void; + isLoading?: boolean; + openAiLogo?: string; + modalClassName?: string; +} + +export function ActionReminderModal({ + isOpen, + onClose, + reminderType = "default", + onPrimaryAction, + title: customTitle, + hideTitle = false, + message: customMessage, + children: customChildren, + primaryActionText: customPrimaryActionText, + secondaryActionText: customSecondaryActionText, + onSecondaryAction, + isLoading = false, + openAiLogo, + modalClassName: customModalClassName = "", +}: ActionReminderModalProps) { + let titleText: string | undefined; + let messageContent: ReactNode; + let primaryActionBtnText: string; + let primaryActionIcon = faRightToBracket; + + if (!hideTitle) { + switch (reminderType) { + case "soraLogin": + titleText = customTitle || "Link Your OpenAI Sora Account"; + break; + case "artcraftLogin": + titleText = customTitle || "Login to ArtCraft"; + break; + default: + titleText = customTitle || "Action Required"; + break; + } + } else { + titleText = undefined; + } + + switch (reminderType) { + case "soraLogin": + titleText = customTitle || "Link Your OpenAI Account"; + messageContent = customMessage || ( +

+ To use this feature, please connect your OpenAI account. This allows + us to leverage your existing subscription. +

+ ); + primaryActionBtnText = customPrimaryActionText || "Login with OpenAI"; + break; + case "artcraftLogin": + messageContent = customMessage || ( +

+ Please log in or sign up to ArtCraft to proceed. This will allow you + to save your work and access all features. +

+ ); + primaryActionBtnText = customPrimaryActionText || "Login / Sign Up"; + break; + default: + messageContent = customMessage || ( +

+ Please complete the required action. +

+ ); + primaryActionBtnText = customPrimaryActionText || "Proceed"; + break; + } + + const effectiveSecondaryAction = onSecondaryAction || onClose; + const effectiveSecondaryActionText = customSecondaryActionText || "Cancel"; + + let modalSpecificClasses = ""; + + const finalModalClassName = + `${customModalClassName} ${modalSpecificClasses}`.trim(); + + return ( + +
+ {customChildren ? ( +
{customChildren}
+ ) : ( +
{messageContent}
+ )} + +
+ + {(onSecondaryAction || customSecondaryActionText) && ( + + )} +
+
+
+ ); +} + +export default ActionReminderModal; diff --git a/frontend/libs/components/action-reminder-modal/tsconfig.json b/frontend/libs/components/action-reminder-modal/tsconfig.json new file mode 100644 index 0000000000..d39936c57e --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/tsconfig.json @@ -0,0 +1,19 @@ +{ + "files": [], + "include": [], + "references": [ + { + "path": "../modal" + }, + { + "path": "../button" + }, + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/frontend/libs/components/action-reminder-modal/tsconfig.lib.json b/frontend/libs/components/action-reminder-modal/tsconfig.lib.json new file mode 100644 index 0000000000..36311b175d --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/tsconfig.lib.json @@ -0,0 +1,53 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts", + "vite/client" + ], + "rootDir": "src", + "jsx": "react-jsx", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo" + }, + "exclude": [ + "out-tsc", + "dist", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx" + ], + "include": [ + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx" + ], + "references": [ + { + "path": "../modal/tsconfig.lib.json" + }, + { + "path": "../button/tsconfig.lib.json" + } + ] +} diff --git a/frontend/libs/components/action-reminder-modal/tsconfig.spec.json b/frontend/libs/components/action-reminder-modal/tsconfig.spec.json new file mode 100644 index 0000000000..18b7de51b8 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/tsconfig.spec.json @@ -0,0 +1,34 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/vitest", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ], + "jsx": "react-jsx" + }, + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/frontend/libs/components/action-reminder-modal/vite.config.ts b/frontend/libs/components/action-reminder-modal/vite.config.ts new file mode 100644 index 0000000000..44ac23a151 --- /dev/null +++ b/frontend/libs/components/action-reminder-modal/vite.config.ts @@ -0,0 +1,49 @@ +/// +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import dts from 'vite-plugin-dts'; +import * as path from 'path'; + +export default defineConfig(() => ({ + root: __dirname, + cacheDir: '../../../node_modules/.vite/libs/components/action-reminder-modal', + plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })], + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + outDir: './dist', + emptyOutDir: true, + reportCompressedSize: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: '@storyteller/ui-action-reminder-modal', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forget to update your package.json as well. + formats: ['es' as const] + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: ['react','react-dom','react/jsx-runtime'] + }, + }, + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + coverage: { + reportsDirectory: './test-output/vitest/coverage', + provider: 'v8' as const, + } + }, +})); diff --git a/frontend/libs/components/login-modal/src/lib/ArtCraftSignUp.tsx b/frontend/libs/components/login-modal/src/lib/artcraft-signup.tsx similarity index 100% rename from frontend/libs/components/login-modal/src/lib/ArtCraftSignUp.tsx rename to frontend/libs/components/login-modal/src/lib/artcraft-signup.tsx diff --git a/frontend/libs/components/login-modal/src/lib/login-modal.tsx b/frontend/libs/components/login-modal/src/lib/login-modal.tsx index afe6613e85..8d6ca4a4ee 100644 --- a/frontend/libs/components/login-modal/src/lib/login-modal.tsx +++ b/frontend/libs/components/login-modal/src/lib/login-modal.tsx @@ -14,7 +14,7 @@ import { useSoraLoginListener, } from "@storyteller/tauri-api"; import { invoke } from "@tauri-apps/api/core"; -import { ArtCraftSignUp } from "./ArtCraftSignUp"; +import { ArtCraftSignUp } from "./artcraft-signup"; import { UsersApi } from "@storyteller/api"; interface LoginModalProps { onClose: () => void; @@ -42,11 +42,21 @@ export function LoginModal({ const artCraftFormRef = useRef(null); const [initialArtCraftLoginStatusForUI, setInitialArtCraftLoginStatusForUI] = useState(null); + const [initialSoraSessionStatusForUI, setInitialSoraSessionStatusForUI] = + useState(null); - const uiTotalSteps = initialArtCraftLoginStatusForUI ? 3 : 4; + // Determine total steps and current step for UI + let uiTotalSteps = 4; let uiCurrentStep = step; - - if (initialArtCraftLoginStatusForUI) { + if (initialSoraSessionStatusForUI) { + // If Sora session exists, remove the Sora step (step 3) + uiTotalSteps = 3; + if (step === 1) uiCurrentStep = 1; + else if (step === 2) uiCurrentStep = 2; + else if (step === 4) uiCurrentStep = 3; + } else if (initialArtCraftLoginStatusForUI) { + // If ArtCraft is logged in but not Sora, keep 4 steps + uiTotalSteps = 4; if (step === 1) uiCurrentStep = 1; else if (step === 3) uiCurrentStep = 2; else if (step === 4) uiCurrentStep = 3; @@ -67,11 +77,7 @@ export function LoginModal({ useSoraLoginListener((payload: any) => { console.log("Login success!", payload); - if (isLoggedInArtCraft) { - setStep(4); - } else { - setStep(2); - } + setStep(4); // Always go to the final step after Sora login }); // Check session on component mount @@ -80,8 +86,13 @@ export function LoginModal({ Promise.all([initSession(), checkArtCraftLogin()]).then( ([soraSessionExists, loggedIn]) => { setInitialArtCraftLoginStatusForUI(loggedIn || false); // Set initial status for UI step counting + setInitialSoraSessionStatusForUI(soraSessionExists || false); // Set initial Sora status for UI step counting - if (soraSessionExists && !loggedIn) { + if (soraSessionExists && loggedIn) { + setIsLoggedInArtCraft(true); + setIsOpen(false); // Close the modal if fully logged in + return; + } else if (soraSessionExists && !loggedIn) { // Sora session exists, but not logged in to ArtCraft setIsOpen(true); setStep(2); @@ -96,14 +107,10 @@ export function LoginModal({ setIsOpen(true); setStep(1); setIsLoggedInArtCraft(false); // Ensure this is also set - } else if (loggedIn) { - // Already logged in, no need to show modal - setIsLoggedInArtCraft(true); - // setIsOpen(false); // This would prevent modal from showing if fully logged in } } ); - }, []); // Empty dependency array ensures this runs once on mount + }, []); useEffect(() => { if (onOpenChange) onOpenChange(isOpen); @@ -124,14 +131,14 @@ export function LoginModal({ const result = await CheckSoraSession(); const sessionExists = result.state === SoraSessionState.Valid; if (sessionExists) { - setStep(step + 1); + setStep(4); // Always go to the final step after Sora login } } else { alert("Please open the desktop app to login"); await new Promise((resolve) => setTimeout(resolve, 3000)); const sessionExists = true; if (sessionExists) { - setStep(step + 1); + setStep(4); // Always go to the final step after Sora login } } } catch (error) { @@ -279,7 +286,13 @@ export function LoginModal({ const userInfo = session.data?.user; if (userInfo) onArtCraftAuthSuccess(userInfo); } - setStep(3); + // Check for Sora session and skip to step 4 if present + const soraSessionExists = await initSession(); + if (soraSessionExists) { + setStep(4); + } else { + setStep(3); + } } catch (e) { console.error(e); setErrorMessage( diff --git a/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx b/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx index ed1ab2e96c..5c9d7aadc8 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx @@ -37,8 +37,13 @@ import { SoraImageRemix, SoraImageRemixErrorType, SoraImageRemixAspectRatio, + CheckSoraSession, + SoraSessionState, + waitForSoraLogin, } from "@storyteller/tauri-api"; import { SoundRegistry } from "@storyteller/soundboard"; +import { showActionReminder } from "@storyteller/ui-action-reminder-modal"; +import { invoke } from "@tauri-apps/api/core"; interface ReferenceImage { id: string; @@ -243,7 +248,30 @@ export const PromptBox2D = ({ setPrompt(e.target.value); }; + // Helper to show Sora login reminder and wait for login + const handleSoraLoginReminder = async () => { + return new Promise((resolve) => { + showActionReminder({ + reminderType: "soraLogin", + onPrimaryAction: async () => { + await invoke("open_sora_login_command"); + await waitForSoraLogin(); + toast.success("Logged in to Sora!"); + resolve(); + }, + }); + }); + }; + const handleTauriEnqueue = async () => { + // Check if the Sora session is valid + const soraSession = await CheckSoraSession(); + if (soraSession.state !== SoraSessionState.Valid) { + setIsEnqueueing(false); + await handleSoraLoginReminder(); + return; + } + const api = new PromptsApi(); let image = getCanvasRenderBitmap(); if (image === undefined) { @@ -403,7 +431,7 @@ export const PromptBox2D = ({ const getCurrentAspectRatioIcon = () => { const selected = aspectRatioList.find((item) => item.selected); if (!selected || !selected.icon) return faRectangle; - const iconElement = selected.icon as React.ReactElement; + const iconElement = selected.icon as React.ReactElement; return iconElement.props.icon; }; diff --git a/frontend/libs/tauri-api/src/index.ts b/frontend/libs/tauri-api/src/index.ts index f3c5b1ea38..1478157b7d 100644 --- a/frontend/libs/tauri-api/src/index.ts +++ b/frontend/libs/tauri-api/src/index.ts @@ -12,3 +12,4 @@ export * from "./lib/sora/LogoutSoraSession.js"; export * from "./lib/sora/SoraImageRemix.js"; export * from "./lib/sora/useSoraLoginListener.js"; export * from "./lib/system/GetBuildInfo.js"; +export * from "./lib/sora/waitForSoraLogin.js"; diff --git a/frontend/libs/tauri-api/src/lib/sora/waitForSoraLogin.ts b/frontend/libs/tauri-api/src/lib/sora/waitForSoraLogin.ts new file mode 100644 index 0000000000..45eb58af52 --- /dev/null +++ b/frontend/libs/tauri-api/src/lib/sora/waitForSoraLogin.ts @@ -0,0 +1,13 @@ +import { listen } from "@tauri-apps/api/event"; + +export function waitForSoraLogin(): Promise { + return new Promise((resolve) => { + let unlisten: () => void; + listen("sora-login-success", () => { + if (unlisten) unlisten(); + resolve(); + }).then((stop) => { + unlisten = stop; + }); + }); +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e0c1aec879..3b50971b79 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1297,6 +1297,10 @@ "version": "0.0.1", "extraneous": true }, + "libs/components/action-reminder-modal": { + "name": "@storyteller/ui-action-reminder-modal", + "version": "0.0.1" + }, "libs/components/badge": { "name": "@storyteller/ui-badge", "version": "0.0.1" @@ -9171,6 +9175,10 @@ "resolved": "libs/tauri-utils", "link": true }, + "node_modules/@storyteller/ui-action-reminder-modal": { + "resolved": "libs/components/action-reminder-modal", + "link": true + }, "node_modules/@storyteller/ui-badge": { "resolved": "libs/components/badge", "link": true diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 24faefa318..35778c31ba 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -128,6 +128,9 @@ }, { "path": "./libs/components/upload-modal" + }, + { + "path": "./libs/components/action-reminder-modal" } ] }