diff --git a/src/frontend/apps/desk/package.json b/src/frontend/apps/desk/package.json index bf0a02a..f5c6fb8 100644 --- a/src/frontend/apps/desk/package.json +++ b/src/frontend/apps/desk/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@gouvfr-lasuite/integration": "0.1.3", + "@hookform/resolvers": "3.4.2", "@openfun/cunningham-react": "2.9.0", "@tanstack/react-query": "5.36.0", "i18next": "23.11.4", @@ -25,12 +26,15 @@ "react": "*", "react-aria-components": "1.2.0", "react-dom": "*", + "react-hook-form": "7.51.5", "react-i18next": "14.1.1", "react-select": "5.8.0", "styled-components": "6.1.11", + "zod": "3.23.8", "zustand": "4.5.2" }, "devDependencies": { + "@hookform/devtools": "4.3.1", "@svgr/webpack": "8.1.0", "@tanstack/react-query-devtools": "5.36.0", "@testing-library/jest-dom": "6.4.5", diff --git a/src/frontend/apps/desk/src/features/mail-domains/api/useCreateMailbox.tsx b/src/frontend/apps/desk/src/features/mail-domains/api/useCreateMailbox.tsx new file mode 100644 index 0000000..cf39dfb --- /dev/null +++ b/src/frontend/apps/desk/src/features/mail-domains/api/useCreateMailbox.tsx @@ -0,0 +1,64 @@ +import { UUID } from 'crypto'; + +import { + UseMutationOptions, + useMutation, + useQueryClient, +} from '@tanstack/react-query'; + +import { APIError, errorCauses, fetchAPI } from '@/api'; + +import { KEY_LIST_MAILBOX } from './useMailboxes'; + +export interface CreateMailboxParams { + first_name: string; + last_name: string; + local_part: string; + secondary_email: string; + phone_number: string; + mailDomainId: UUID; +} + +export const createMailbox = async ({ + mailDomainId, + ...data +}: CreateMailboxParams): Promise => { + const response = await fetchAPI(`mail-domains/${mailDomainId}/mailboxes/`, { + method: 'POST', + body: JSON.stringify(data), + }); + + if (!response.ok) { + // TODO: extend errorCauses to return the name of the invalid field names to highlight in the form? + throw new APIError( + 'Failed to create the mailbox', + await errorCauses(response), + ); + } +}; + +type UseCreateMailboxParams = { domainId: UUID } & UseMutationOptions< + void, + APIError, + CreateMailboxParams +>; + +export function useCreateMailbox(options: UseCreateMailboxParams) { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: createMailbox, + onSuccess: (data, variables, context) => { + void queryClient.invalidateQueries({ + queryKey: [KEY_LIST_MAILBOX, { id: variables.mailDomainId }], + }); + if (options?.onSuccess) { + options.onSuccess(data, variables, context); + } + }, + onError: (error, variables, context) => { + if (options?.onError) { + options.onError(error, variables, context); + } + }, + }); +} diff --git a/src/frontend/apps/desk/src/features/mail-domains/api/useMailDomainMailboxes.tsx b/src/frontend/apps/desk/src/features/mail-domains/api/useMailDomainMailboxes.tsx index b55c1f5..a0d1a7e 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/api/useMailDomainMailboxes.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/api/useMailDomainMailboxes.tsx @@ -35,7 +35,7 @@ export const getMailDomainMailboxes = async ({ return response.json() as Promise; }; -const KEY_LIST_MAILBOX = 'mailboxes'; +export const KEY_LIST_MAILBOX = 'mailboxes'; export function useMailDomainMailboxes( param: MailDomainMailboxesParams, diff --git a/src/frontend/apps/desk/src/features/mail-domains/assets/create-mailbox.svg b/src/frontend/apps/desk/src/features/mail-domains/assets/create-mailbox.svg new file mode 100644 index 0000000..08de3aa --- /dev/null +++ b/src/frontend/apps/desk/src/features/mail-domains/assets/create-mailbox.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/src/frontend/apps/desk/src/features/mail-domains/components/MailDomainsContent.tsx b/src/frontend/apps/desk/src/features/mail-domains/components/MailDomainsContent.tsx index d2c0c5f..2fdb807 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/components/MailDomainsContent.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/components/MailDomainsContent.tsx @@ -1,4 +1,5 @@ import { + Button, DataGrid, Loader, SortModel, @@ -8,11 +9,13 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Box, Card, Text, TextErrors } from '@/components'; -import { MailDomain } from '@/features/mail-domains'; -import { useMailDomainMailboxes } from '@/features/mail-domains/api/useMailDomainMailboxes'; -import { PAGE_SIZE } from '@/features/mail-domains/conf'; +import { useMailboxes } from '../api/useMailboxes'; import { default as MailDomainsLogo } from '../assets/mail-domains-logo.svg'; +import { PAGE_SIZE } from '../conf'; +import { MailDomain, MailDomainMailbox } from '../types'; + +import { CreateMailboxForm } from './forms/CreateMailboxForm'; export type ViewMailbox = { email: string; id: string }; @@ -44,6 +47,9 @@ function formatSortModel( export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) { const [sortModel, setSortModel] = useState([]); const { t } = useTranslation(); + const [isCreateMailboxFormVisible, setIsCreateMailboxFormVisible] = + useState(false); + const pagination = usePagination({ defaultPage: 1, pageSize: PAGE_SIZE, @@ -52,7 +58,7 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) { const { page, pageSize, setPagesCount } = pagination; const ordering = sortModel.length ? formatSortModel(sortModel[0]) : undefined; - const { data, isLoading, error } = useMailDomainMailboxes({ + const { data, isLoading, error } = useMailboxes({ id: mailDomain.id, page, ordering, @@ -60,7 +66,7 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) { const viewMailboxes: ViewMailbox[] = mailDomain && data?.results?.length - ? data.results.map((mailbox) => ({ + ? data.results.map((mailbox: MailDomainMailbox) => ({ email: `${mailbox.local_part}@${mailDomain.name}`, id: mailbox.id, })) @@ -75,7 +81,16 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) { ) : ( <> - + {isCreateMailboxFormVisible && mailDomain ? ( + + ) : null} + { +const TopBanner = ({ + name, + setIsFormVisible, +}: { + name: string; + setIsFormVisible: (value: boolean) => void; +}) => { const { t } = useTranslation(); return ( - - - - {name} - - + <> + + + + {name} + + + + + + ); }; diff --git a/src/frontend/apps/desk/src/features/mail-domains/components/forms/CreateMailboxForm.tsx b/src/frontend/apps/desk/src/features/mail-domains/components/forms/CreateMailboxForm.tsx new file mode 100644 index 0000000..bd8e56d --- /dev/null +++ b/src/frontend/apps/desk/src/features/mail-domains/components/forms/CreateMailboxForm.tsx @@ -0,0 +1,276 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { + Button, + Input, + Modal, + ModalSize, + VariantType, + useToastProvider, +} from '@openfun/cunningham-react'; +import React from 'react'; +import { + Controller, + FormProvider, + UseFormReturn, + useForm, +} from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +import { Box, Text, TextErrors } from '@/components'; +import { useCunninghamTheme } from '@/cunningham'; + +import { CreateMailboxParams, useCreateMailbox } from '../../api'; +import IconCreateMailbox from '../../assets/create-mailbox.svg'; +import { MailDomain } from '../../types'; + +const FORM_ID: string = 'form-create-mailbox'; + +const createMailboxValidationSchema = z.object({ + first_name: z.string().min(1), + last_name: z.string().min(1), + local_part: z.string().min(1), + secondary_email: z.string().min(1), + phone_number: z.string().min(1), +}); + +export const CreateMailboxForm = ({ + mailDomain, + setIsFormVisible, +}: { + mailDomain: MailDomain; + setIsFormVisible: (value: boolean) => void; +}) => { + const { t } = useTranslation(); + const { toast } = useToastProvider(); + const { colorsTokens } = useCunninghamTheme(); + + const methods = useForm({ + delayError: 0, + defaultValues: { + first_name: '', + last_name: '', + local_part: '', + secondary_email: '', + phone_number: '', + }, + mode: 'onChange', + reValidateMode: 'onChange', + resolver: zodResolver(createMailboxValidationSchema), + }); + + const { mutate: createMailbox, ...queryState } = useCreateMailbox({ + domainId: mailDomain.id, + onSuccess: () => { + toast(t('Mailbox created!'), VariantType.SUCCESS, { + duration: 4000, + }); + + setIsFormVisible(false); + }, + }); + + const closeModal = () => setIsFormVisible(false); + + const onSubmitCallback = (event: React.FormEvent) => { + event.preventDefault(); + void methods.handleSubmit((data) => + createMailbox({ ...data, mailDomainId: mailDomain.id }), + )(); + }; + + return ( + + + {t('Cancel')} + + } + onClose={closeModal} + closeOnClickOutside + hideCloseButton + rightActions={ + + } + size={ModalSize.MEDIUM} + title={ + + + + {t('Create a mailbox')} + + + } + > + + {queryState.isError && ( + + )} + {methods ? ( +
+ ) : null} + + + + ); +}; + +const Form = ({ + methods, + mailDomain, + onSubmitCallback, +}: { + methods: UseFormReturn; + mailDomain: MailDomain; + onSubmitCallback: (event: React.FormEvent) => void; +}) => { + const { t } = useTranslation(); + + return ( + + + + ( + + )} + /> + + + + ( + + )} + /> + + + + + ( + + )} + /> + + + + @{mailDomain.name} + + + + + ( + + )} + /> + + + + ( + + )} + /> + + + + ); +}; diff --git a/src/frontend/apps/desk/src/features/mail-domains/index.tsx b/src/frontend/apps/desk/src/features/mail-domains/index.tsx index 40f5848..a042235 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/index.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/index.tsx @@ -1,3 +1,4 @@ -export * from './components/'; +export * from './components'; export * from './types'; export * from './api'; +export * from './store'; diff --git a/src/frontend/apps/desk/src/features/mail-domains/store/index.ts b/src/frontend/apps/desk/src/features/mail-domains/store/index.ts new file mode 100644 index 0000000..2e2f4c9 --- /dev/null +++ b/src/frontend/apps/desk/src/features/mail-domains/store/index.ts @@ -0,0 +1 @@ +export * from './useMailDomainsStore'; diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 929fce2..e894b0e 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -1120,7 +1120,7 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== -"@emotion/is-prop-valid@1.2.2": +"@emotion/is-prop-valid@1.2.2", "@emotion/is-prop-valid@^1.2.2": version "1.2.2" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337" integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw== @@ -1132,7 +1132,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== -"@emotion/react@^11.8.1": +"@emotion/react@^11.1.5", "@emotion/react@^11.8.1": version "11.11.4" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.4.tgz#3a829cac25c1f00e126408fab7f891f00ecc3c1d" integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw== @@ -1146,7 +1146,7 @@ "@emotion/weak-memoize" "^0.3.1" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.1.3": +"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.1.3", "@emotion/serialize@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.4.tgz#fc8f6d80c492cfa08801d544a05331d1cc7cd451" integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== @@ -1162,6 +1162,18 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== +"@emotion/styled@^11.3.0": + version "11.11.5" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.5.tgz#0c5c8febef9d86e8a926e663b2e5488705545dfb" + integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/is-prop-valid" "^1.2.2" + "@emotion/serialize" "^1.1.4" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/unitless@0.8.1", "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" @@ -1403,6 +1415,25 @@ resolved "https://registry.yarnpkg.com/@gouvfr-lasuite/integration/-/integration-0.1.3.tgz#cbf44473cd2a5b9497814faff459c3d88bc1ddce" integrity sha512-WvAaMyEcNZkNX88Rbi6xo1rxFIGjsg3w8Gxi5NKVyNY0Ph2dXcGkP63ybCKD8JAsWHfxwAvs82wqXFZk5RCJ1g== +"@hookform/devtools@4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@hookform/devtools/-/devtools-4.3.1.tgz#5df1b77ea12b4f1c220da3d2dba737f81cbb12bc" + integrity sha512-CrWxEoHQZaOXJZVQ8KBgOuAa8p2LI8M0DAN5GTRTmdCieRwFVjVDEmuTAVazWVRRkpEQSgSt3KYp7VmmqXdEnw== + dependencies: + "@emotion/react" "^11.1.5" + "@emotion/styled" "^11.3.0" + "@types/lodash" "^4.14.168" + little-state-machine "^4.1.0" + lodash "^4.17.21" + react-simple-animate "^3.3.12" + use-deep-compare-effect "^1.8.1" + uuid "^8.3.2" + +"@hookform/resolvers@3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.4.2.tgz#b69525248c2a9a1b2546411251ea25029915841a" + integrity sha512-1m9uAVIO8wVf7VCDAGsuGA0t6Z3m6jVGAN50HkV9vYLl0yixKK/Z1lr01vaRvYCkIKGoy1noVRxMzQYb4y/j1Q== + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -3393,6 +3424,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.1.tgz#0fabfcf2f2127ef73b119d98452bd317c4a17eb8" integrity sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q== +"@types/lodash@^4.14.168": + version "4.17.4" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.4.tgz#0303b64958ee070059e3a7184048a55159fe20b7" + integrity sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ== + "@types/luxon@3.4.2": version "3.4.2" resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" @@ -4704,7 +4740,7 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -dequal@^2.0.3: +dequal@^2.0.2, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== @@ -6991,6 +7027,11 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +little-state-machine@^4.1.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/little-state-machine/-/little-state-machine-4.8.0.tgz#4853d01f71dc7e15fec00193692f845020a57686" + integrity sha512-xfi5+iDxTLhu0hbnNubUs+qoQQqxhtEZeObP5ELjUlHnl74bbasY7mOonsGQrAouyrbag3ebNLSse5xX1T7buQ== + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -7885,6 +7926,11 @@ react-dom@*, react-dom@18.2.0, react-dom@18.3.1: loose-envify "^1.1.0" scheduler "^0.23.0" +react-hook-form@7.51.5: + version "7.51.5" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.5.tgz#4afbfb819312db9fea23e8237a3a0d097e128b43" + integrity sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q== + react-i18next@14.1.1: version "14.1.1" resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.1.tgz#3d942a99866555ae3552c40f9fddfa061e29d7f3" @@ -7938,6 +7984,11 @@ react-select@5.8.0: react-transition-group "^4.3.0" use-isomorphic-layout-effect "^1.1.2" +react-simple-animate@^3.3.12: + version "3.5.2" + resolved "https://registry.yarnpkg.com/react-simple-animate/-/react-simple-animate-3.5.2.tgz#ab08865c8bd47872b92bd1e25902326bf7c695b3" + integrity sha512-xLE65euP920QMTOmv5haPlml+hmOPDkbIr5WeF7ADIXWBYt5kW/vwpNfWg8EKMab8aeDxIZ6QjffVh8v2dUyhg== + react-stately@3.30.1: version "3.30.1" resolved "https://registry.yarnpkg.com/react-stately/-/react-stately-3.30.1.tgz#7d87649c69f1bcf42c68a732f121ff23393f5abb" @@ -8463,7 +8514,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -8541,7 +8601,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9106,6 +9173,14 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-deep-compare-effect@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/use-deep-compare-effect/-/use-deep-compare-effect-1.8.1.tgz#ef0ce3b3271edb801da1ec23bf0754ef4189d0c6" + integrity sha512-kbeNVZ9Zkc0RFGpfMN3MNfaKNvcLNyxOAAd9O4CBZ+kCBXXscn9s/4I+8ytUER4RDpEYs5+O6Rs4PqiZ+rHr5Q== + dependencies: + "@babel/runtime" "^7.12.5" + dequal "^2.0.2" + use-isomorphic-layout-effect@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" @@ -9126,6 +9201,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -9369,7 +9449,16 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -9471,6 +9560,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +zod@3.23.8: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== + zustand@4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"