From e95154ca1a59122c8d06a362d3cb722632967384 Mon Sep 17 00:00:00 2001 From: Chris <47886055+Masta2000@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:24:59 +0200 Subject: [PATCH] feat(pci-ai-tools): add qpu app in Manager (#19967) ref: #AIS-1352 Signed-off-by: Chris Fradet Co-authored-by: Arthur Bullet --- .../manager/apps/pci-ai-tools/package.json | 2 +- .../public/assets/quantum-notebooks-qpu.png | Bin 0 -> 33912 bytes .../public/assets/quantum-notebooks.png | Bin 62498 -> 33912 bytes .../translations/ai-tools/Messages_de_DE.json | 3 +- .../translations/ai-tools/Messages_en_GB.json | 3 +- .../translations/ai-tools/Messages_es_ES.json | 3 +- .../translations/ai-tools/Messages_fr_CA.json | 3 +- .../translations/ai-tools/Messages_fr_FR.json | 3 +- .../translations/ai-tools/Messages_it_IT.json | 3 +- .../translations/ai-tools/Messages_pl_PL.json | 3 +- .../translations/ai-tools/Messages_pt_PT.json | 3 +- .../components/qpu/Messages_de_DE.json | 24 + .../components/qpu/Messages_en_GB.json | 24 + .../components/qpu/Messages_es_ES.json | 24 + .../components/qpu/Messages_fr_CA.json | 24 + .../components/qpu/Messages_fr_FR.json | 24 + .../components/qpu/Messages_it_IT.json | 24 + .../components/qpu/Messages_pl_PL.json | 24 + .../components/qpu/Messages_pt_PT.json | 24 + .../components/resources/Messages_de_DE.json | 3 +- .../components/resources/Messages_en_GB.json | 3 +- .../components/resources/Messages_es_ES.json | 3 +- .../components/resources/Messages_fr_CA.json | 1 + .../components/resources/Messages_fr_FR.json | 1 + .../components/resources/Messages_it_IT.json | 3 +- .../components/resources/Messages_pl_PL.json | 3 +- .../components/resources/Messages_pt_PT.json | 3 +- .../notebooks/create/Messages_de_DE.json | 3 +- .../notebooks/create/Messages_en_GB.json | 3 +- .../notebooks/create/Messages_es_ES.json | 3 +- .../notebooks/create/Messages_fr_CA.json | 1 + .../notebooks/create/Messages_fr_FR.json | 1 + .../notebooks/create/Messages_it_IT.json | 3 +- .../notebooks/create/Messages_pl_PL.json | 3 +- .../notebooks/create/Messages_pt_PT.json | 3 +- .../notebooks/notebook/Messages_de_DE.json | 4 +- .../notebooks/notebook/Messages_en_GB.json | 5 +- .../notebooks/notebook/Messages_es_ES.json | 4 +- .../notebooks/notebook/Messages_fr_CA.json | 4 +- .../notebooks/notebook/Messages_fr_FR.json | 4 +- .../notebooks/notebook/Messages_it_IT.json | 4 +- .../notebooks/notebook/Messages_pl_PL.json | 4 +- .../notebooks/notebook/Messages_pt_PT.json | 4 +- .../ai-tools/qpu/create/Messages_de_DE.json | 59 ++ .../ai-tools/qpu/create/Messages_en_GB.json | 59 ++ .../ai-tools/qpu/create/Messages_es_ES.json | 59 ++ .../ai-tools/qpu/create/Messages_fr_CA.json | 59 ++ .../ai-tools/qpu/create/Messages_fr_FR.json | 59 ++ .../ai-tools/qpu/create/Messages_it_IT.json | 59 ++ .../ai-tools/qpu/create/Messages_pl_PL.json | 59 ++ .../ai-tools/qpu/create/Messages_pt_PT.json | 59 ++ .../qpu/onboarding/Messages_de_DE.json | 7 + .../qpu/onboarding/Messages_en_GB.json | 7 + .../qpu/onboarding/Messages_es_ES.json | 7 + .../qpu/onboarding/Messages_fr_CA.json | 7 + .../qpu/onboarding/Messages_fr_FR.json | 7 + .../qpu/onboarding/Messages_it_IT.json | 7 + .../qpu/onboarding/Messages_pl_PL.json | 7 + .../qpu/onboarding/Messages_pt_PT.json | 7 + .../helpers/mocks/capabilities/qpuFlavor.ts | 25 + .../order/qpu/QpuSelect.component.tsx | 129 +++ .../ResourcesSpec.component.tsx | 12 + .../roadmap-changelog.constants.ts | 18 + .../api/ai/capabilities/capabilities.api.ts | 35 +- .../api/ai/capabilities/capabilities.spec.tsx | 44 +- .../api/ai/notebook/quantumSuggestions.api.ts | 8 + .../ai/notebook/quantumSuggestions.spec.tsx | 29 + .../ai/capabilities/useGetFramework.hook.tsx | 4 +- .../ai/capabilities/useGetQpuFlavor.hook.tsx | 25 + .../ai/capabilities/useGetQpuFlavor.spec.tsx | 45 + .../ai/capabilities/useGetQpuFlavors.hook.tsx | 24 + .../ai/capabilities/useGetQpuFlavors.spec.tsx | 38 + .../ai/capabilities/useGetQpuRegions.hook.tsx | 16 + .../ai/capabilities/useGetQpuRegions.spec.tsx | 36 + .../useGetQuantumSuggestions.hook.tsx | 16 + .../useGetQuantumSuggestions.spec.tsx | 33 + .../src/hooks/useQuantum.hook.tsx | 29 +- .../src/lib/orderFunnelHelper.spec.tsx | 4 +- .../pci-ai-tools/src/lib/orderFunnelHelper.ts | 3 + .../src/lib/priceQpuFlavorHelper.ts | 25 + .../src/pages/dashboard/Dashboard.layout.tsx | 2 +- .../src/pages/notebooks/Notebooks.page.tsx | 76 +- .../_components/StopNotebook.component.tsx | 29 +- .../_components/Resources.component.tsx | 20 + .../NotebooksListColumns.component.tsx | 30 +- .../_components/useOrderFunnel.hook.tsx | 14 +- .../notebooks/onboarding/Onboarding.page.tsx | 2 +- .../pci-ai-tools/src/pages/qpus/Qpu.page.tsx | 92 ++ .../src/pages/qpus/QpuRoot.layout.tsx | 21 + .../_components/QpusListTable.component.tsx | 79 ++ .../src/pages/qpus/create/Create.page.tsx | 63 ++ .../src/pages/qpus/create/Create.spec.tsx | 186 ++++ .../_components/CliEquivalent.component.tsx | 52 ++ .../_components/OrderFunnel.component.tsx | 798 +++++++++++++++++ .../_components/OrderSummary.component.tsx | 345 +++++++ .../create/_components/OrderSummary.spec.tsx | 110 +++ .../_components/useOrderFunnel.hook.tsx | 277 ++++++ .../pages/qpus/onboarding/Onboarding.page.tsx | 31 + .../pages/qpus/onboarding/Onboarding.spec.tsx | 32 + .../apps/pci-ai-tools/src/routes/routes.tsx | 322 +++++++ .../apps/pci-ai-tools/src/types/Quantum.ts | 3 + .../src/types/orderFunnel/index.ts | 10 + .../manager/apps/public-cloud/src/links.json | 20 + yarn.lock | 845 +++++++++++++++++- 104 files changed, 4773 insertions(+), 103 deletions(-) create mode 100644 packages/manager/apps/pci-ai-tools/public/assets/quantum-notebooks-qpu.png create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/components/qpu/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/create/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-tools/public/translations/ai-tools/qpu/onboarding/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-tools/src/__tests__/helpers/mocks/capabilities/qpuFlavor.ts create mode 100644 packages/manager/apps/pci-ai-tools/src/components/order/qpu/QpuSelect.component.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.api.ts create mode 100644 packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.hook.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.hook.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.hook.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.hook.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/lib/priceQpuFlavorHelper.ts create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/Qpu.page.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/QpuRoot.layout.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/_components/QpusListTable.component.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.page.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/CliEquivalent.component.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderFunnel.component.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.component.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/useOrderFunnel.hook.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.page.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.spec.tsx create mode 100644 packages/manager/apps/pci-ai-tools/src/types/Quantum.ts diff --git a/packages/manager/apps/pci-ai-tools/package.json b/packages/manager/apps/pci-ai-tools/package.json index 9f3f8bfff3bc..9dd78571956d 100644 --- a/packages/manager/apps/pci-ai-tools/package.json +++ b/packages/manager/apps/pci-ai-tools/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@datatr-ux/ods-tailwind-config": "^1.0.3", - "@datatr-ux/ovhcloud-types": "^1.0.11", + "@datatr-ux/ovhcloud-types": "^1.0.34", "@datatr-ux/uxlib": "^0.0.11-beta-8", "@hookform/resolvers": "^5.0.1", "@ovh-ux/manager-config": "^8.6.7", diff --git a/packages/manager/apps/pci-ai-tools/public/assets/quantum-notebooks-qpu.png b/packages/manager/apps/pci-ai-tools/public/assets/quantum-notebooks-qpu.png new file mode 100644 index 0000000000000000000000000000000000000000..288053e0ccfd9f8707958f0ad2f9f8acc6c3a70d GIT binary patch literal 33912 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 literal 0 HcmV?d00001 diff --git a/packages/manager/apps/pci-ai-tools/public/assets/quantum-notebooks.png b/packages/manager/apps/pci-ai-tools/public/assets/quantum-notebooks.png index 75eb62e2c8fc068e0d419063626e8ad5b82eece8..288053e0ccfd9f8707958f0ad2f9f8acc6c3a70d 100644 GIT binary patch literal 33912 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 literal 62498 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 Y$zR+ void; + className?: string; + isUpdate?: boolean; +} + +const QpusSelect = React.forwardRef( + ({ qpus, value, onChange, isUpdate, className }, ref) => { + const { t } = useTranslation('ai-tools/components/qpu'); + + qpus.sort((a, b) => a.pricing[0].price - b.pricing[0].price); + const clickInput = (qpuName: string) => { + const inputElement = document.getElementById( + `qpu-${qpuName}`, + ) as HTMLInputElement | null; + if (inputElement) { + inputElement.click(); + } + }; + const handleKeyDown = ( + e: React.KeyboardEvent, + qpuName: string, + ) => { + if (e.key === 'Enter') { + clickInput(qpuName); + } + }; + return ( + + + + + {t('tableHeadType')} + + {!isUpdate && ( + + {t('tableHeadDescription')} + + )} + + {t('tableHeadQubits')} + + + {t('tableHeadPrice')} + + + + + {qpus.map((qpu) => ( + clickInput(qpu.id)} + onKeyDown={(e) => handleKeyDown(e, qpu.id)} + key={qpu.id} + className={`border hover:bg-primary-50 cursor-pointer text-[#4d5592] ${ + value === qpu.id + ? 'bg-primary-50 font-semibold !border-2 border-primary-500' + : '' + }`} + > + + +
+ + {qpu.id} +
+
+ {!isUpdate && ( + + {qpu.description} + + )} + + {qpu.qubits} + + + + + + ))} + +
+ onChange(e.target.value)} + className="hidden" + id={`qpu-${qpu.id}`} + value={qpu.id} + checked={value === qpu.id} + /> +
+ ); + }, +); + +QpusSelect.displayName = 'QpusSelect'; + +export default QpusSelect; diff --git a/packages/manager/apps/pci-ai-tools/src/components/resources-spec/ResourcesSpec.component.tsx b/packages/manager/apps/pci-ai-tools/src/components/resources-spec/ResourcesSpec.component.tsx index 5158bf897ffe..20cbff8ae9b7 100644 --- a/packages/manager/apps/pci-ai-tools/src/components/resources-spec/ResourcesSpec.component.tsx +++ b/packages/manager/apps/pci-ai-tools/src/components/resources-spec/ResourcesSpec.component.tsx @@ -20,14 +20,17 @@ import { } from '@datatr-ux/uxlib'; import { bytesConverter, octetConverter } from '@/lib/bytesHelper'; import ai from '@/types/AI'; +import quantum from '@/types/Quantum'; interface ResourcesProps { resources: ai.Resources; + qpuDetail?: quantum.capabilities.QPUFlavor; allowUpdate?: boolean; disabled?: boolean; } const ResourcesSpec = ({ resources, + qpuDetail, allowUpdate = false, disabled, }: ResourcesProps) => { @@ -52,6 +55,7 @@ const ResourcesSpec = ({ memory: bytesConverter(resources.gpuMemory, false, 0), })} + {qpuDetail && 1 x {qpuDetail.name}} ) : (

@@ -63,6 +67,7 @@ const ResourcesSpec = ({ {`${resources.cpu} x ${resources.flavor}`} {`${resources.cpu} x INTEL CPU VCORES`} + {qpuDetail && 1 x {qpuDetail.name}}
)} {allowUpdate && ( @@ -119,6 +124,13 @@ const ResourcesSpec = ({ network: bytesConverter(resources.publicNetwork, true, 2), })} + {qpuDetail && ( + + {t('qpuQubitsFiel', { + qubits: qpuDetail.qubits, + })} + + )}
{t('storageTitleSection')}
diff --git a/packages/manager/apps/pci-ai-tools/src/configuration/roadmap-changelog.constants.ts b/packages/manager/apps/pci-ai-tools/src/configuration/roadmap-changelog.constants.ts index b1a4af5b4b7f..f65bffd75480 100644 --- a/packages/manager/apps/pci-ai-tools/src/configuration/roadmap-changelog.constants.ts +++ b/packages/manager/apps/pci-ai-tools/src/configuration/roadmap-changelog.constants.ts @@ -16,6 +16,24 @@ export const NotebookRoadmapLinks = [ }, ]; +export const QpuRoadmapLinks = [ + { + label: 'Changelog', + url: + 'https://github.com/orgs/ovh/projects/16/views/6?sliceBy%5Bvalue%5D=Quantum+QPU', + }, + { + label: 'Roadmap', + url: + 'https://github.com/orgs/ovh/projects/16/views/1?sliceBy%5Bvalue%5D=Quantum+QPU', + }, + { + label: 'Feature Request', + url: + 'https://github.com/ovh/public-cloud-roadmap/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=', + }, +]; + export const EmulatorRoadmapLinks = [ { label: 'Changelog', diff --git a/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.api.ts b/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.api.ts index aa460de2fb88..d35fc7d7aac0 100644 --- a/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.api.ts +++ b/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.api.ts @@ -1,6 +1,7 @@ import { apiClient } from '@ovh-ux/manager-core-api'; import ai from '@/types/AI'; import { PCIAi } from '../..'; +import quantum from '@/types/Quantum'; export const getRegions = async ({ projectId }: PCIAi) => apiClient.v6 @@ -12,14 +13,26 @@ export const getRegions = async ({ projectId }: PCIAi) => }) .then((res) => res.data as ai.capabilities.Region[]); +export const getQpuRegions = async ({ projectId }: PCIAi) => + apiClient.v6 + .get(`/cloud/project/${projectId}/quantum/capabilities/region`, { + headers: { + 'X-Pagination-Mode': 'CachedObjectList-Pages', + 'X-Pagination-Size': '50000', + }, + }) + .then((res) => res.data as ai.capabilities.Region[]); + export interface AIRegion extends PCIAi { + type?: string; region: string; + qpuFlavorId?: string; } -export const getFramework = async ({ projectId, region }: AIRegion) => +export const getFramework = async ({ projectId, region, type }: AIRegion) => apiClient.v6 .get( - `/cloud/project/${projectId}/ai/capabilities/region/${region}/notebook/framework`, + `/cloud/project/${projectId}/ai/capabilities/region/${region}/notebook/framework?type=${type}`, ) .then((res) => res.data as ai.capabilities.notebook.Framework[]); @@ -35,6 +48,24 @@ export const getFlavor = async ({ projectId, region }: AIRegion) => .get(`/cloud/project/${projectId}/ai/capabilities/region/${region}/flavor`) .then((res) => res.data as ai.capabilities.Flavor[]); +export const getQpuFlavors = async ({ projectId, region }: AIRegion) => + apiClient.v6 + .get( + `/cloud/project/${projectId}/quantum/capabilities/region/${region}/qpu`, + ) + .then((res) => res.data as quantum.capabilities.QPUFlavor[]); + +export const getQpuFlavor = async ({ + projectId, + region, + qpuFlavorId, +}: AIRegion) => + apiClient.v6 + .get( + `/cloud/project/${projectId}/quantum/capabilities/region/${region}/qpu/${qpuFlavorId}`, + ) + .then((res) => res.data as quantum.capabilities.QPUFlavor); + export const getAppImages = async ({ projectId, region }: AIRegion) => apiClient.v6 .get( diff --git a/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.spec.tsx b/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.spec.tsx index 89ce1ecddf4f..93a3072c0b5c 100644 --- a/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.spec.tsx +++ b/packages/manager/apps/pci-ai-tools/src/data/api/ai/capabilities/capabilities.spec.tsx @@ -6,6 +6,9 @@ import { getAppImages, getEditor, getFramework, + getQpuFlavors, + getQpuFlavor, + getQpuRegions, } from './capabilities.api'; describe('Capabilities functions', () => { @@ -29,6 +32,22 @@ describe('Capabilities functions', () => { ); }); + it('should call getQpuRegions', async () => { + expect(apiClient.v6.get).not.toHaveBeenCalled(); + await getQpuRegions({ + projectId: 'projectId', + }); + expect(apiClient.v6.get).toHaveBeenCalledWith( + '/cloud/project/projectId/quantum/capabilities/region', + { + headers: { + 'X-Pagination-Mode': 'CachedObjectList-Pages', + 'X-Pagination-Size': '50000', + }, + }, + ); + }); + it('should call getFlavor', async () => { expect(apiClient.v6.get).not.toHaveBeenCalled(); await getFlavor({ @@ -69,7 +88,30 @@ describe('Capabilities functions', () => { region: 'region', }); expect(apiClient.v6.get).toHaveBeenCalledWith( - '/cloud/project/projectId/ai/capabilities/region/region/notebook/framework', + '/cloud/project/projectId/ai/capabilities/region/region/notebook/framework?type=undefined', + ); + }); + + it('should call getQpuFlavors', async () => { + expect(apiClient.v6.get).not.toHaveBeenCalled(); + await getQpuFlavors({ + projectId: 'projectId', + region: 'regionId', + }); + expect(apiClient.v6.get).toHaveBeenCalledWith( + '/cloud/project/projectId/quantum/capabilities/region/regionId/qpu', + ); + }); + + it('should call getQpuFlavor', async () => { + expect(apiClient.v6.get).not.toHaveBeenCalled(); + await getQpuFlavor({ + projectId: 'projectId', + region: 'regionId', + qpuFlavorId: 'flavorId', + }); + expect(apiClient.v6.get).toHaveBeenCalledWith( + '/cloud/project/projectId/quantum/capabilities/region/regionId/qpu/flavorId', ); }); }); diff --git a/packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.api.ts b/packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.api.ts new file mode 100644 index 000000000000..bec1f4259480 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.api.ts @@ -0,0 +1,8 @@ +import { apiClient } from '@ovh-ux/manager-core-api'; +import { PCIAi } from '../..'; +import { NotebookSuggestions } from '@/types/orderFunnel'; + +export const getQuantumSuggestions = async ({ projectId }: PCIAi) => + apiClient.v6 + .get(`/cloud/project/${projectId}/ai/suggestion/notebook-quantum/qpu`) + .then((res) => res.data as NotebookSuggestions); diff --git a/packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.spec.tsx b/packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.spec.tsx new file mode 100644 index 000000000000..1f0b08723d8e --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/api/ai/notebook/quantumSuggestions.spec.tsx @@ -0,0 +1,29 @@ +import { describe, expect, vi } from 'vitest'; +import { getQuantumSuggestions } from './quantumSuggestions.api'; +import { NotebookSuggestions } from '@/types/orderFunnel'; + +vi.mock('@ovh-ux/manager-core-api', () => ({ + apiClient: { + v6: { + get: vi.fn().mockResolvedValue({ data: {} as NotebookSuggestions }), + }, + }, +})); + +describe('getQuantumSuggestions', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should call getQuantumSuggestions', async () => { + const { apiClient } = await import('@ovh-ux/manager-core-api'); + + expect(apiClient.v6.get).not.toHaveBeenCalled(); + + await getQuantumSuggestions({ projectId: 'projectId' }); + + expect(apiClient.v6.get).toHaveBeenCalledWith( + '/cloud/project/projectId/ai/suggestion/notebook-quantum/qpu', + ); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetFramework.hook.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetFramework.hook.tsx index 6f2a8785ee17..6d280e458260 100644 --- a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetFramework.hook.tsx +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetFramework.hook.tsx @@ -6,6 +6,7 @@ import { getFramework } from '@/data/api/ai/capabilities/capabilities.api'; export function useGetFramework( projectId: string, region: string, + type?: string, options: Omit = {}, ) { const queryKey = [ @@ -14,11 +15,12 @@ export function useGetFramework( 'capabilities', 'region', region, + type, 'framework', ]; return useQueryImmediateRefetch({ queryKey, - queryFn: () => getFramework({ projectId, region }), + queryFn: () => getFramework({ projectId, region, type }), ...options, }) as UseQueryResult; } diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.hook.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.hook.tsx new file mode 100644 index 000000000000..4ffb1a390231 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.hook.tsx @@ -0,0 +1,25 @@ +import { QueryObserverOptions, UseQueryResult } from '@tanstack/react-query'; +import { getQpuFlavor } from '@/data/api/ai/capabilities/capabilities.api'; +import { useQueryImmediateRefetch } from '@/hooks/useImmediateRefetch'; +import quantum from '@/types/Quantum'; + +export function useGetQpuFlavor( + projectId: string, + region: string, + qpuFlavorId: string, + options: Omit = {}, +) { + const queryKey = [ + projectId, + 'qpu', + 'capabilities', + 'region', + region, + qpuFlavorId, + ]; + return useQueryImmediateRefetch({ + queryKey, + queryFn: () => getQpuFlavor({ projectId, region, qpuFlavorId }), + ...options, + }) as UseQueryResult; +} diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.spec.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.spec.tsx new file mode 100644 index 000000000000..e87c5b49416f --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavor.spec.tsx @@ -0,0 +1,45 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { vi } from 'vitest'; +import { QueryClientWrapper } from '@/__tests__/helpers/wrappers/QueryClientWrapper'; +import * as capabilitiesApi from '@/data/api/ai/capabilities/capabilities.api'; +import { mockedCapabilitiesQpuFlavor } from '@/__tests__/helpers/mocks/capabilities/qpuFlavor'; +import { useGetQpuFlavor } from './useGetQpuFlavor.hook'; + +vi.mock('@/data/api/ai/capabilities/capabilities.api', () => ({ + getQpuFlavor: vi.fn(), +})); + +describe('useGetQpuFlavor', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return QpuFlavor capabilities by region', async () => { + const projectId = 'projectId'; + const region = 'region'; + const qpuFlavorId = 'qpuFlavorId'; + + vi.mocked(capabilitiesApi.getQpuFlavor).mockResolvedValue( + mockedCapabilitiesQpuFlavor, + ); + + const { result } = renderHook( + () => useGetQpuFlavor(projectId, region, qpuFlavorId), + { + wrapper: QueryClientWrapper, + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + + expect(result.current.data).toEqual(mockedCapabilitiesQpuFlavor); + + expect(capabilitiesApi.getQpuFlavor).toHaveBeenCalledWith({ + projectId, + region, + qpuFlavorId, + }); + }); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.hook.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.hook.tsx new file mode 100644 index 000000000000..e99836d1aa8f --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.hook.tsx @@ -0,0 +1,24 @@ +import { QueryObserverOptions, UseQueryResult } from '@tanstack/react-query'; +import { getQpuFlavors } from '@/data/api/ai/capabilities/capabilities.api'; +import { useQueryImmediateRefetch } from '@/hooks/useImmediateRefetch'; +import quantum from '@/types/Quantum'; + +export function useGetQpuFlavors( + projectId: string, + region: string, + options: Omit = {}, +) { + const queryKey = [ + projectId, + 'quantum', + 'capabilities', + 'region', + region, + 'qpu', + ]; + return useQueryImmediateRefetch({ + queryKey, + queryFn: () => getQpuFlavors({ projectId, region }), + ...options, + }) as UseQueryResult; +} diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.spec.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.spec.tsx new file mode 100644 index 000000000000..53d3ffc0bcfe --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuFlavors.spec.tsx @@ -0,0 +1,38 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { vi } from 'vitest'; +import { QueryClientWrapper } from '@/__tests__/helpers/wrappers/QueryClientWrapper'; +import * as capabilitiesApi from '@/data/api/ai/capabilities/capabilities.api'; +import { mockedCapabilitiesQpuFlavor } from '@/__tests__/helpers/mocks/capabilities/qpuFlavor'; +import { useGetQpuFlavors } from './useGetQpuFlavors.hook'; + +vi.mock('@/data/api/ai/capabilities/capabilities.api', () => ({ + getQpuFlavors: vi.fn(), +})); + +describe('useGetQpuFlavors', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return QpuFlavor capabilities by region', async () => { + const projectId = 'projectId'; + const region = 'region'; + + vi.mocked(capabilitiesApi.getQpuFlavors).mockResolvedValue([ + mockedCapabilitiesQpuFlavor, + ]); + + const { result } = renderHook(() => useGetQpuFlavors(projectId, region), { + wrapper: QueryClientWrapper, + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + expect(result.current.data).toEqual([mockedCapabilitiesQpuFlavor]); + expect(capabilitiesApi.getQpuFlavors).toHaveBeenCalledWith({ + projectId, + region, + }); + }); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.hook.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.hook.tsx new file mode 100644 index 000000000000..5f8a8eb95f3a --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.hook.tsx @@ -0,0 +1,16 @@ +import { QueryObserverOptions, UseQueryResult } from '@tanstack/react-query'; +import { getQpuRegions } from '@/data/api/ai/capabilities/capabilities.api'; +import { useQueryImmediateRefetch } from '@/hooks/useImmediateRefetch'; +import quantum from '@/types/Quantum'; + +export function useGetQpuRegions( + projectId: string, + options: Omit = {}, +) { + const queryKey = [projectId, 'quantum', 'capabilities', 'region']; + return useQueryImmediateRefetch({ + queryKey, + queryFn: () => getQpuRegions({ projectId }), + ...options, + }) as UseQueryResult; +} diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.spec.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.spec.tsx new file mode 100644 index 000000000000..f8cde08db717 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/capabilities/useGetQpuRegions.spec.tsx @@ -0,0 +1,36 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { vi } from 'vitest'; +import { QueryClientWrapper } from '@/__tests__/helpers/wrappers/QueryClientWrapper'; +import * as capabilitiesApi from '@/data/api/ai/capabilities/capabilities.api'; +import { mockedCapabilitiesRegionGRA } from '@/__tests__/helpers/mocks/capabilities/region'; +import { useGetQpuRegions } from './useGetQpuRegions.hook'; + +vi.mock('@/data/api/ai/capabilities/capabilities.api', () => ({ + getQpuRegions: vi.fn(), +})); + +describe('useGetQpuRegions', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return capabilities region', async () => { + const projectId = 'projectId'; + + vi.mocked(capabilitiesApi.getQpuRegions).mockResolvedValue([ + mockedCapabilitiesRegionGRA, + ]); + + const { result } = renderHook(() => useGetQpuRegions(projectId), { + wrapper: QueryClientWrapper, + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + expect(result.current.data).toEqual([mockedCapabilitiesRegionGRA]); + expect(capabilitiesApi.getQpuRegions).toHaveBeenCalledWith({ + projectId, + }); + }); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.hook.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.hook.tsx new file mode 100644 index 000000000000..78c8e85d2cfa --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.hook.tsx @@ -0,0 +1,16 @@ +import { QueryObserverOptions, UseQueryResult } from '@tanstack/react-query'; +import { useQueryImmediateRefetch } from '@/hooks/useImmediateRefetch'; +import { NotebookSuggestions } from '@/types/orderFunnel'; +import { getQuantumSuggestions } from '@/data/api/ai/notebook/quantumSuggestions.api'; + +export function useGetQuantumSuggestions( + projectId: string, + options: Omit = {}, +) { + const queryKey = [projectId, 'ai', 'suggestion', 'notebook-quantum', 'qpu']; + return useQueryImmediateRefetch({ + queryKey, + queryFn: () => getQuantumSuggestions({ projectId }), + ...options, + }) as UseQueryResult; +} diff --git a/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.spec.tsx b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.spec.tsx new file mode 100644 index 000000000000..330e4f788d1d --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/data/hooks/ai/notebook/useGetQuantumSuggestions.spec.tsx @@ -0,0 +1,33 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { vi } from 'vitest'; +import { QueryClientWrapper } from '@/__tests__/helpers/wrappers/QueryClientWrapper'; +import * as suggestionApi from '@/data/api/ai/notebook/quantumSuggestions.api'; +import { mockedSuggestionsForNotebook } from '@/__tests__/helpers/mocks/suggestion'; +import { useGetQuantumSuggestions } from './useGetQuantumSuggestions.hook'; + +vi.mock('@/data/api/ai/notebook/quantumSuggestions.api', () => ({ + getQuantumSuggestions: vi.fn(), +})); + +describe('useGetQuantumSuggestions', () => { + it('should return Quantum Suggestions', async () => { + const projectId = 'projectId'; + + const mockedGetQuantumSuggestions = (suggestionApi.getQuantumSuggestions as unknown) as ReturnType< + typeof vi.fn + >; + + mockedGetQuantumSuggestions.mockResolvedValue(mockedSuggestionsForNotebook); + + const { result } = renderHook(() => useGetQuantumSuggestions(projectId), { + wrapper: QueryClientWrapper, + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toEqual(mockedSuggestionsForNotebook); + expect(suggestionApi.getQuantumSuggestions).toHaveBeenCalledWith({ + projectId, + }); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/hooks/useQuantum.hook.tsx b/packages/manager/apps/pci-ai-tools/src/hooks/useQuantum.hook.tsx index 355de0cc8488..e1d3bffa3d70 100644 --- a/packages/manager/apps/pci-ai-tools/src/hooks/useQuantum.hook.tsx +++ b/packages/manager/apps/pci-ai-tools/src/hooks/useQuantum.hook.tsx @@ -1,20 +1,35 @@ import { useTranslation } from 'react-i18next'; -import { useParams } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; export function useQuantum(namespace?: string) { - const { quantum } = useParams(); const { t: tOrig } = useTranslation(namespace); - const isQuantum = quantum === 'quantum'; + const location = useLocation(); + + const pathWithHash = `${location.pathname ?? ''}${location.hash ?? + ''}`.toLowerCase(); + + const mode: 'emulators' | 'qpu' | 'ai' = (() => { + if (pathWithHash.includes('/quantum/qpu/')) return 'qpu'; + if (pathWithHash.includes('/quantum/')) return 'emulators'; + return 'ai'; + })(); + const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1); + const t = (key: string) => { - if (isQuantum) { - const quantumKey = `quantum${capitalize(key)}`; - return tOrig([quantumKey, key]); + if (mode === 'emulators') { + return tOrig([`quantum${capitalize(key)}`, key]); + } + if (mode === 'qpu') { + return tOrig([`qpu${capitalize(key)}`, key]); } return tOrig(key); }; + return { - isQuantum, + mode, + isQuantum: mode === 'emulators', + isQpu: mode === 'qpu', t, }; } diff --git a/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.spec.tsx b/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.spec.tsx index 8b819986e07b..742d01fc05ad 100644 --- a/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.spec.tsx +++ b/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.spec.tsx @@ -61,10 +61,10 @@ describe('orderFunnelHelper', () => { volumes: [mockedOrderPublicGit], }; - expect(getNotebookSpec(orderResultCPU)).toStrictEqual( + expect(getNotebookSpec(orderResultCPU)).toMatchObject( mockedNotebookSpecInput, ); - expect(getNotebookSpec(orderResultGPU)).toStrictEqual( + expect(getNotebookSpec(orderResultGPU)).toMatchObject( mockedNotebookSpecInputGPU, ); }); diff --git a/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.ts b/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.ts index 8a8ed8bf7596..79c177268c85 100644 --- a/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.ts +++ b/packages/manager/apps/pci-ai-tools/src/lib/orderFunnelHelper.ts @@ -57,6 +57,9 @@ export function getNotebookSpec(formResult: NotebookOrderResult) { unsecureHttp: formResult.unsecureHttp, sshPublicKeys: formResult.sshKey, labels: formResult.labels, + quantumResources: { + qpuFlavorId: formResult?.qpuFlavor?.id, + }, }; if (formResult.volumes.length > 0) { diff --git a/packages/manager/apps/pci-ai-tools/src/lib/priceQpuFlavorHelper.ts b/packages/manager/apps/pci-ai-tools/src/lib/priceQpuFlavorHelper.ts new file mode 100644 index 000000000000..ddc67d4e6534 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/lib/priceQpuFlavorHelper.ts @@ -0,0 +1,25 @@ +import quantum from '@/types/Quantum'; +import publicCatalog from '@/types/Catalog'; +import { Qpu } from '@/types/orderFunnel'; + +export function createQpuFlavorPricingList( + qpuFlavors: quantum.capabilities.QPUFlavor[], + catalog: publicCatalog.Catalog, + productPrefix: string, +): Qpu[] { + return qpuFlavors.map((qpuFlavor) => { + const qpuFlavorPlanCode = `${productPrefix}.${qpuFlavor.id}.unit.consumption`; + const qpuFlavorPrice = + catalog.addons.find((ad) => ad.planCode === qpuFlavorPlanCode) + ?.pricings || []; + + const qpu: Qpu = { + id: qpuFlavor.id, + name: qpuFlavor.name, + description: qpuFlavor.description, + qubits: qpuFlavor.qubits, + pricing: qpuFlavorPrice, + }; + return qpu; + }); +} diff --git a/packages/manager/apps/pci-ai-tools/src/pages/dashboard/Dashboard.layout.tsx b/packages/manager/apps/pci-ai-tools/src/pages/dashboard/Dashboard.layout.tsx index 47677748d6a4..8ae93e931ec6 100644 --- a/packages/manager/apps/pci-ai-tools/src/pages/dashboard/Dashboard.layout.tsx +++ b/packages/manager/apps/pci-ai-tools/src/pages/dashboard/Dashboard.layout.tsx @@ -32,7 +32,7 @@ export default function DashboardLayout() { }); const regionId = regionQuery?.data?.length > 0 && regionQuery?.data[0]?.id; - const frameworkQuery = useGetFramework(projectId, regionId, { + const frameworkQuery = useGetFramework(projectId, regionId, '', { enabled: !!regionId, }); diff --git a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/Notebooks.page.tsx b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/Notebooks.page.tsx index d03773c637de..bc79a52150cc 100644 --- a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/Notebooks.page.tsx +++ b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/Notebooks.page.tsx @@ -30,34 +30,49 @@ interface NotebooksProps { export const Loader = async ({ params }: NotebooksProps) => { const { projectId, quantum } = params; - const regions = await queryClient.fetchQuery({ - queryKey: [projectId], - queryFn: () => getRegions({ projectId }), - }); - const notebooks = await queryClient.fetchQuery({ - queryKey: [projectId, 'ai', 'notebook'], - queryFn: () => getNotebooks({ projectId }), - }); - const fmks = await queryClient.fetchQuery({ - queryKey: [projectId, 'ai', 'capabilities', regions[0].id, 'framework'], - queryFn: () => getFramework({ projectId, region: regions[0].id }), + + const [regions, notebooks] = await Promise.all([ + queryClient.fetchQuery({ + queryKey: [projectId], + queryFn: () => getRegions({ projectId }), + }), + queryClient.fetchQuery({ + queryKey: [projectId, 'ai', 'notebook'], + queryFn: () => getNotebooks({ projectId }), + }), + ]); + + const frameworks = await queryClient.fetchQuery({ + queryKey: [ + projectId, + 'ai', + 'capabilities', + regions[0].id, + 'framework', + quantum === 'quantum' ? 'Quantum' : 'AI', + ], + queryFn: () => + getFramework({ + projectId, + region: regions[0].id, + type: quantum === 'quantum' ? 'Quantum' : 'AI', + }), }); - const filterFmkIds = fmks - .filter((fmk) => - quantum === 'quantum' ? fmk.type === 'Quantum' : fmk.type === 'AI', - ) - .map((fwk) => fwk.id); - if ( - notebooks.filter((nb) => filterFmkIds.includes(nb.spec.env.frameworkId)) - .length === 0 - ) { + const frameworkIds = frameworks.map((f) => f.id); + + const hasNotebook = notebooks.some((nb) => + frameworkIds.includes(nb.spec.env.frameworkId), + ); + + if (!hasNotebook) { return quantum === 'quantum' ? redirect( `/pci/projects/${projectId}/ai-ml/quantum/notebooks/onboarding`, ) : redirect(`/pci/projects/${projectId}/ai-ml/notebooks/onboarding`); } + return null; }; @@ -65,22 +80,25 @@ const Notebooks = () => { const { projectId } = useParams(); const { isQuantum, t } = useQuantum('ai-tools/notebooks'); const { isUserActive } = useUserActivityContext(); + const regionQuery = useGetRegions(projectId); + const regionId = regionQuery?.data?.[0]?.id; const notebooksQuery = useGetNotebooks(projectId, { refetchInterval: isUserActive && POLLING.NOTEBOOKS, }); - const regionId = regionQuery?.data?.length > 0 && regionQuery?.data[0]?.id; - const fmkQuery = useGetFramework(projectId, regionId, { - enabled: !!regionId, - }); + + const fmkQuery = useGetFramework( + projectId, + regionId, + isQuantum ? 'quantum' : 'ai', + { + enabled: !!regionId, + }, + ); if (notebooksQuery.isLoading || regionQuery.isLoading || fmkQuery.isLoading) return ; - - const filterFmkIds = fmkQuery.data - .filter((fmk) => (isQuantum ? fmk.type === 'Quantum' : fmk.type === 'AI')) - .map((fwk) => fwk.id); - + const filterFmkIds = fmkQuery.data.map((fwk) => fwk.id); return ( <>
{ @@ -62,6 +64,18 @@ const StopNotebook = ({ notebookId: notebook.id, }); }; + + const getNotebookType = () => { + switch (mode) { + case 'emulators': + return t('notebookTypeEmulators', { defaultValue: 'Emulator' }); + case 'qpu': + return t('notebookTypeQPU', { defaultValue: 'QPU' }); + default: + return t('notebookTypeAI', { defaultValue: 'AI' }); + } + }; + return ( @@ -70,18 +84,27 @@ const StopNotebook = ({ {t('stopNotebookTitle')} -

{t('stopNotebookDescription1')}

+ {/* Inject the computed type into the translation */} +

+ {mode === 'qpu' + ? t('stopQpuNotebookDescription1', { type: getNotebookType() }) + : t('stopNotebookDescription1', { type: getNotebookType() })} +

  • {t('stopNotebookDescriptionList1')}
  • {t('stopNotebookDescriptionList2')}
  • {t('stopNotebookDescriptionList3')}
- {t('stopNotebookDescription2')} + + {t('stopNotebookDescription2', { type: getNotebookType() })} + {t('stopNotebookDescription2Bis')} - {t('stopNotebookDescription2Ter')} + + {t('stopNotebookDescription2Ter', { type: getNotebookType() })} + {t('stopNotebookDescription2Quater')} diff --git a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx index c13858b0b9f7..e4738ca2eb68 100644 --- a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx +++ b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx @@ -1,5 +1,7 @@ +import { useEffect, useState } from 'react'; import { HelpCircle } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router-dom'; import { Popover, PopoverContent, @@ -7,17 +9,35 @@ import { Progress, } from '@datatr-ux/uxlib'; import { useNotebookData } from '../../Notebook.context'; +import { getQpuFlavor } from '@/data/api/ai/capabilities/capabilities.api'; import { bytesConverter } from '@/lib/bytesHelper'; import ResourcesSpec from '@/components/resources-spec/ResourcesSpec.component'; import { isStoppedNotebook } from '@/lib/statusHelper'; +import quantum from '@/types/Quantum'; const Resources = () => { const { notebook } = useNotebookData(); + const { projectId } = useParams(); const { t } = useTranslation('ai-tools/notebooks/notebook/dashboard'); + const [qpuDetail, setQpuDetail] = useState< + quantum.capabilities.QPUFlavor | undefined + >(); + + useEffect(() => { + const qpuFlavorId = notebook.spec.quantumResources?.qpuFlavorId; + const { region } = notebook.spec; + if (qpuFlavorId && region && projectId) { + getQpuFlavor({ projectId, region, qpuFlavorId }) + .then(setQpuDetail) + .catch(() => setQpuDetail(undefined)); + } + }, [notebook, projectId]); + return (
diff --git a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/_components/NotebooksListColumns.component.tsx b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/_components/NotebooksListColumns.component.tsx index 8b9446e45045..2cfdc6d012f5 100644 --- a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/_components/NotebooksListColumns.component.tsx +++ b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/_components/NotebooksListColumns.component.tsx @@ -152,17 +152,33 @@ export const getColumns = ({ ), cell: ({ row }) => { const { cpu, gpu, gpuModel } = row.original.spec.resources; + const qpu = row.original.spec.quantumResources?.qpuFlavorId; return (
{gpu > 0 ? ( -
- - {`${gpu} GPU ${gpuModel}`} -
+ <> +
+ + {`${gpu} GPU ${gpuModel}`} +
+ ) : ( -
- - {`${cpu} CPU`} + <> +
+ + {`${cpu} CPU`} +
+ + )} + {qpu && ( +
+ + + {`QPU ${qpu + .replace(/-/g, ' ') + .toLowerCase() + .replace(/^./, (c) => c.toUpperCase())}`} +
)}
diff --git a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/create/_components/useOrderFunnel.hook.tsx b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/create/_components/useOrderFunnel.hook.tsx index fd1a7c02ddae..217793dab6ab 100644 --- a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/create/_components/useOrderFunnel.hook.tsx +++ b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/create/_components/useOrderFunnel.hook.tsx @@ -107,7 +107,11 @@ export function useOrderFunnel( const sshKey = form.watch('sshKey'); const volumes = form.watch('volumes'); - const frameworkQuery = useGetFramework(projectId, region); + const frameworkQuery = useGetFramework( + projectId, + region, + isQuantum ? 'quantum' : 'ai', + ); const editorQuery = useGetEditor(projectId, region); const flavorQuery = useGetFlavor(projectId, region); const datastoreQuery = useGetDatastores(projectId, region); @@ -129,9 +133,7 @@ export function useOrderFunnel( const listFramework: ai.capabilities.notebook.Framework[] = useMemo(() => { if (frameworkQuery.isLoading) return []; - return frameworkQuery.data.filter((fmk) => - isQuantum ? fmk.type === 'Quantum' : fmk.type === 'AI', - ); + return frameworkQuery.data; }, [region, frameworkQuery.isSuccess]); const listEditor: ai.capabilities.notebook.Editor[] = useMemo(() => { @@ -193,8 +195,8 @@ export function useOrderFunnel( // Select default Flavor Id / Flavor number when region change useEffect(() => { const suggestedFlavor = - suggestions.suggestions.find((sug) => sug.region === regionObject.id) - .resources.flavorId ?? listFlavor[0].id; + suggestions.suggestions.find((sug) => sug.region === regionObject?.id) + ?.resources?.flavorId ?? listFlavor[0]?.id; const suggestedQuantity = suggestions.suggestions.find((sug) => sug.region === regionObject.id) .resources.quantity ?? 1; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/onboarding/Onboarding.page.tsx b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/onboarding/Onboarding.page.tsx index 014df0d9cce7..7a9fbba43025 100644 --- a/packages/manager/apps/pci-ai-tools/src/pages/notebooks/onboarding/Onboarding.page.tsx +++ b/packages/manager/apps/pci-ai-tools/src/pages/notebooks/onboarding/Onboarding.page.tsx @@ -4,7 +4,7 @@ import Link from '@/components/links/Link.component'; import onboardingImgSrc from '@/../public/assets/notebooks.png'; import { useTrackAction } from '@/hooks/useTracking'; import { TRACKING } from '@/configuration/tracking.constants'; -import onboardingQuantumImgSrc from '@/../public/assets/quantum-notebooks.png'; +import onboardingQuantumImgSrc from '@/../public/assets/quantum-notebooks-qpu.png'; import { GUIDES, getGuideUrl, diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/Qpu.page.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/Qpu.page.tsx new file mode 100644 index 000000000000..9bc53246b30c --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/Qpu.page.tsx @@ -0,0 +1,92 @@ +import { Outlet, redirect, useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { useUserActivityContext } from '@/contexts/UserActivityContext'; +import RoadmapChangelog from '@/components/roadmap-changelog/RoadmapChangelog.component'; +import QpusList from './_components/QpusListTable.component'; +import { POLLING } from '@/configuration/polling.constants'; +import { useGetFramework } from '@/data/hooks/ai/capabilities/useGetFramework.hook'; +import { useGetNotebooks } from '@/data/hooks/ai/notebook/useGetNotebooks.hook'; +import queryClient from '@/query.client'; +import { + getFramework, + getRegions, +} from '@/data/api/ai/capabilities/capabilities.api'; +import { getNotebooks } from '@/data/api/ai/notebook/notebook.api'; +import { QpuRoadmapLinks } from '@/configuration/roadmap-changelog.constants'; +import { useGetQpuRegions } from '@/data/hooks/ai/capabilities/useGetQpuRegions.hook'; + +export const Loader = async ({ params }: { params: { projectId: string } }) => { + const { projectId } = params; + + const [regions, notebooks] = await Promise.all([ + queryClient.fetchQuery({ + queryKey: [projectId, 'regions'], + queryFn: () => getRegions({ projectId }), + }), + queryClient.fetchQuery({ + queryKey: [projectId, 'ai', 'notebook'], + queryFn: () => getNotebooks({ projectId }), + }), + ]); + + const frameworks = await queryClient.fetchQuery({ + queryKey: [projectId, 'ai', 'capabilities', regions[0].id, 'framework'], + queryFn: () => + getFramework({ projectId, region: regions[0].id, type: 'quantum-qpu' }), + }); + + const qpuFrameworkIds = frameworks.map((f) => f.id); + + const hasQpuNotebook = notebooks.some((nb) => + qpuFrameworkIds.includes(nb.spec.env.frameworkId), + ); + + return hasQpuNotebook + ? null + : redirect(`/pci/projects/${projectId}/ai-ml/quantum/qpu/onboarding`); +}; + +const Qpus = () => { + const { projectId } = useParams(); + const { isUserActive } = useUserActivityContext(); + const { t } = useTranslation('ai-tools/qpu/onboarding'); + const regionQuery = useGetQpuRegions(projectId); + const regionId = regionQuery?.data?.length > 0 && regionQuery?.data[0]?.id; + const notebooksQuery = useGetNotebooks(projectId, { + refetchInterval: isUserActive && POLLING.NOTEBOOKS, + }); + + const fmkQuery = useGetFramework(projectId, regionId, 'quantum-qpu', { + enabled: !!regionId, + }); + + const notebooks = notebooksQuery.data ?? []; + + if (notebooksQuery.isLoading || regionQuery.isLoading || fmkQuery.isLoading) { + return ; + } + + const qpuFrameworkIds = fmkQuery.data?.map((f) => f.id) ?? []; + + const filteredNotebooks = notebooks.filter((nb) => + qpuFrameworkIds.includes(nb.spec.env.frameworkId), + ); + + return ( + <> +
+

{t('Title')}

+
+ +
+
+ + + + ); +}; + +export default Qpus; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/QpuRoot.layout.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/QpuRoot.layout.tsx new file mode 100644 index 000000000000..9eff9139b15d --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/QpuRoot.layout.tsx @@ -0,0 +1,21 @@ +import { Outlet } from 'react-router-dom'; +import BreadcrumbItem from '@/components/breadcrumb/BreadcrumbItem.component'; + +function ManageBreadcrumb() { + const translationKey = 'crumb-qpu'; + return ( + + ); +} + +export function breadcrumb() { + return ; +} + +export default function Root() { + return ( + <> + + + ); +} diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/_components/QpusListTable.component.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/_components/QpusListTable.component.tsx new file mode 100644 index 000000000000..d682a952f186 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/_components/QpusListTable.component.tsx @@ -0,0 +1,79 @@ +import { useTranslation } from 'react-i18next'; +import { Plus } from 'lucide-react'; +import { ColumnDef } from '@tanstack/react-table'; +import { useNavigate } from 'react-router-dom'; +import { Button, Skeleton } from '@datatr-ux/uxlib'; +import { getColumns } from '@/pages/notebooks/_components/NotebooksListColumns.component'; +import { getFilters } from '@/pages/notebooks/_components/NotebookListFilters.component'; +import DataTable from '@/components/data-table'; +import { useTrackAction } from '@/hooks/useTracking'; +import { TRACKING } from '@/configuration/tracking.constants'; +import { NotebookWithQpu } from '@/types/orderFunnel'; + +interface QpusListProps { + qpus: NotebookWithQpu[]; +} + +export default function QpusList({ qpus }: QpusListProps) { + const { t } = useTranslation('ai-tools/notebooks'); + const track = useTrackAction(); + const navigate = useNavigate(); + + const columns: ColumnDef[] = getColumns({ + onStartClicked: (notebook) => navigate(`./start/${notebook.id}`), + onStopClicked: (notebook) => navigate(`./stop/${notebook.id}`), + onDeleteClicked: (notebook) => navigate(`./delete/${notebook.id}`), + }); + + const notebooksFilters = getFilters(); + + return ( + + + + + + + + + + + + + ); +} + +QpusList.Skeleton = function NotebooksListSkeleton() { + return ( + <> +
+ +
+ + +
+
+ + + ); +}; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.page.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.page.tsx new file mode 100644 index 000000000000..3eacd89cc387 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.page.tsx @@ -0,0 +1,63 @@ +import { Outlet, useParams } from 'react-router-dom'; +import { Skeleton } from '@datatr-ux/uxlib'; +import OrderFunnel from './_components/OrderFunnel.component'; +import { useGetCatalog } from '@/data/hooks/catalog/useGetCatalog.hook'; +import { useGetSshkey } from '@/data/hooks/sshkey/useGetSshkey.hook'; +import { useGetQpuRegions } from '@/data/hooks/ai/capabilities/useGetQpuRegions.hook'; +import { useGetQuantumSuggestions } from '@/data/hooks/ai/notebook/useGetQuantumSuggestions.hook'; + +const Notebook = () => { + const { projectId } = useParams(); + const suggestionsQuery = useGetQuantumSuggestions(projectId, { + refetchOnWindowFocus: false, + }); + + const regionsQuery = useGetQpuRegions(projectId, { + refetchOnWindowFocus: false, + }); + const catalogQuery = useGetCatalog({ refetchOnWindowFocus: false }); + + const sshKeyQuery = useGetSshkey(projectId, { + refetchOnWindowFocus: false, + }); + + const loading = + regionsQuery.isPending || + catalogQuery.isPending || + sshKeyQuery.isPending || + suggestionsQuery.isPending; + + return ( + <> + {loading ? ( +
+
+ +
+ + + + + + +
+
+ +
+ ) : ( + + )} + + + ); +}; + +export default Notebook; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.spec.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.spec.tsx new file mode 100644 index 000000000000..51e79ebed07f --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/Create.spec.tsx @@ -0,0 +1,186 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; +import { useToast } from '@datatr-ux/uxlib'; +import { mockedUsedNavigate } from '@/__tests__/helpers/mockRouterDomHelper'; +import { mockManagerReactShellClient } from '@/__tests__/helpers/mockShellHelper'; +import Notebook from '@/pages/notebooks/create/Create.page'; +import { RouterWithQueryClientWrapper } from '@/__tests__/helpers/wrappers/RouterWithQueryClientWrapper'; +import { mockedCatalog } from '@/__tests__/helpers/mocks/catalog/catalog'; +import { mockedPciProject } from '@/__tests__/helpers/mocks/project'; +import { + mockedCapabilitiesRegionBHS, + mockedCapabilitiesRegionGRA, +} from '@/__tests__/helpers/mocks/capabilities/region'; +import { + mockedEditor, + mockedEditorBis, +} from '@/__tests__/helpers/mocks/capabilities/notebookEditor'; +import { + mockedSshKey, + mockedSshKeyBis, +} from '@/__tests__/helpers/mocks/sshkey'; +import { mockedCommand } from '@/__tests__/helpers/mocks/shared/command'; +import { mockedCapabilitiesFlavorCPU } from '@/__tests__/helpers/mocks/capabilities/flavor'; +import { + mockedDatastoreWithContainerGit, + mockedDatastoreWithContainerS3, +} from '@/__tests__/helpers/mocks/volume/datastore'; +import * as notebookApi from '@/data/api/ai/notebook/notebook.api'; +import { apiErrorMock } from '@/__tests__/helpers/mocks/shared/aiError'; +import { mockedSuggestionsForNotebook } from '@/__tests__/helpers/mocks/suggestion'; +import { + mockedFramework, + mockedFrameworkBis, +} from '@/__tests__/helpers/mocks/capabilities/notebookFramework'; + +describe('Order funnel page', () => { + beforeEach(() => { + vi.restoreAllMocks(); + mockedUsedNavigate(); + mockManagerReactShellClient(); + + vi.mock('@/data/api/project/project.api', () => { + return { + getProject: vi.fn(() => mockedPciProject), + }; + }); + + vi.mock('@/data/api/ai/notebook/notebook.api', () => ({ + getCommand: vi.fn(() => mockedCommand), + addNotebook: vi.fn((notebook) => notebook), + })); + + vi.mock('@/data/api/ai/notebook/suggestions.api', () => ({ + getSuggestions: vi.fn(() => mockedSuggestionsForNotebook), + })); + + vi.mock('@/data/api/catalog/catalog.api', () => ({ + catalogApi: { + getCatalog: vi.fn(() => mockedCatalog), + }, + })); + + vi.mock('@/data/api/ai/capabilities/capabilities.api', () => ({ + getFramework: vi.fn(() => [mockedFramework, mockedFrameworkBis]), + getEditor: vi.fn(() => [mockedEditor, mockedEditorBis]), + getRegions: vi.fn(() => [ + mockedCapabilitiesRegionGRA, + mockedCapabilitiesRegionBHS, + ]), + getFlavor: vi.fn(() => [mockedCapabilitiesFlavorCPU]), + })); + + vi.mock('@/data/api/ai/data/datastore.api', () => ({ + getDatastores: vi.fn(() => [ + mockedDatastoreWithContainerGit, + mockedDatastoreWithContainerS3, + ]), + })); + + vi.mock('@/data/api/sshkey/sshkey.api', () => ({ + getSshkey: vi.fn(() => [mockedSshKey, mockedSshKeyBis]), + })); + }); + afterEach(() => { + vi.clearAllMocks(); + }); + + it('renders the skeleton component while loading', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-skeleton')).toBeTruthy(); + }); + }); + + it('renders the order funnel', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeTruthy(); + expect(screen.getByTestId('name-section')).toBeTruthy(); + expect(screen.getByTestId('flavor-section')).toBeTruthy(); + expect(screen.getByTestId('region-section')).toBeTruthy(); + expect(screen.getByTestId('framework-section')).toBeTruthy(); + expect(screen.getByTestId('editor-section')).toBeTruthy(); + expect(screen.getByTestId('advance-config-section')).toBeTruthy(); + expect(screen.getByTestId('order-submit-button')).toBeTruthy(); + }); + }); + + it('trigger toast error on getCommand API Error', async () => { + vi.mocked(notebookApi.getCommand).mockImplementation(() => { + throw apiErrorMock; + }); + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeTruthy(); + }); + act(() => { + fireEvent.click(screen.getByTestId('advanced-config-button')); + }); + act(() => { + fireEvent.click(screen.getByTestId('cli-command-button')); + }); + await waitFor(() => { + expect(notebookApi.getCommand).toHaveBeenCalled(); + expect(useToast().toast).toHaveBeenCalledWith({ + title: 'errorGetCommandCli', + description: apiErrorMock.response.data.message, + variant: 'destructive', + }); + }); + }); + + it('trigger getCommand on Cli Command button click', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeTruthy(); + }); + act(() => { + fireEvent.click(screen.getByTestId('cli-command-button')); + }); + await waitFor(() => { + expect(notebookApi.getCommand).toHaveBeenCalled(); + }); + }); + + it('trigger toast error on addNotebook API Error', async () => { + vi.mocked(notebookApi.addNotebook).mockImplementation(() => { + throw apiErrorMock; + }); + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeTruthy(); + }); + act(() => { + fireEvent.click(screen.getByTestId('order-submit-button')); + }); + await waitFor(() => { + expect(notebookApi.addNotebook).toHaveBeenCalled(); + expect(useToast().toast).toHaveBeenCalledWith({ + title: 'errorCreatingNotebook', + description: apiErrorMock.response.data.message, + variant: 'destructive', + }); + }); + }); + + it('trigger add notebook on click', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('order-funnel-container')).toBeTruthy(); + }); + act(() => { + fireEvent.click(screen.getByTestId('order-submit-button')); + }); + await waitFor(() => { + expect(notebookApi.addNotebook).toHaveBeenCalled(); + }); + expect(mockedUsedNavigate).toHaveBeenCalledWith('../undefined'); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/CliEquivalent.component.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/CliEquivalent.component.tsx new file mode 100644 index 000000000000..717a8fc24fb9 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/CliEquivalent.component.tsx @@ -0,0 +1,52 @@ +import { + Code, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + ScrollArea, + bash, + githubDark, + useToast, +} from '@datatr-ux/uxlib'; +import { useTranslation } from 'react-i18next'; +import ai from '@/types/AI'; +import { ModalController } from '@/hooks/useModale'; + +interface CliEquivalentModalProps { + command: ai.Command; + controller: ModalController; +} +const CliEquivalent = ({ command, controller }: CliEquivalentModalProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + const toast = useToast(); + return ( + + + + + {t('cliEquivalentModalTitle')} + + + +
+ + toast.toast({ + title: t('cliEquivalentModalToastMessage'), + }) + } + lineNumbers={true} + /> +
+
+
+
+ ); +}; + +export default CliEquivalent; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderFunnel.component.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderFunnel.component.tsx new file mode 100644 index 000000000000..21120df59875 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderFunnel.component.tsx @@ -0,0 +1,798 @@ +import { useTranslation } from 'react-i18next'; +import { SshKey } from '@datatr-ux/ovhcloud-types/cloud/sshkey/index'; +import { + AlertCircle, + Check, + ChevronDown, + ChevronUp, + ChevronsUpDown, + Plus, + TerminalSquare, +} from 'lucide-react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useEffect, useRef, useState } from 'react'; +import { + Button, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, + Input, + Popover, + PopoverContent, + PopoverTrigger, + Separator, + useToast, +} from '@datatr-ux/uxlib'; +import { useOrderFunnel } from './useOrderFunnel.hook'; +import ai from '@/types/AI'; +import { useModale } from '@/hooks/useModale'; +import { useAddNotebook } from '@/data/hooks/ai/notebook/useAddNotebook.hook'; +import { useGetCommand } from '@/data/hooks/ai/notebook/useGetCommand.hook'; +import { getAIApiErrorMessage } from '@/lib/apiHelper'; +import { getNotebookSpec } from '@/lib/orderFunnelHelper'; +import ErrorList from '@/components/order/error-list/ErrorList.component'; +import RegionsSelect from '@/components/order/region/RegionSelect.component'; +import FlavorsSelect from '@/components/order/flavor/FlavorSelect.component'; +import QpusSelect from '@/components/order/qpu/QpuSelect.component'; +import FrameworksSelect from '@/components/order/framework/FrameworkSelect.component'; +import EditorsSelect from '@/components/order/editor/EditorSelect.component'; +import PrivacyRadioInput from '@/components/order/privacy-radio/PrivacyRadio.component'; +import { NotebookSuggestions, PrivacyEnum } from '@/types/orderFunnel'; +import VolumeForm from '@/components/order/volumes/VolumesForm.component'; + +import LabelsForm from '@/components/labels/LabelsForm.component'; +import SshKeyForm from '@/components/order/configuration/SshKeyForm.component'; +import OrderSummary from './OrderSummary.component'; +import CliEquivalent from './CliEquivalent.component'; +import publicCatalog from '@/types/Catalog'; +import { cn } from '@/lib/utils'; +import { useTrackAction, useTrackBanner } from '@/hooks/useTracking'; +import { TRACKING } from '@/configuration/tracking.constants'; +import Price from '@/components/price/Price.component'; +import quantum from '@/types/Quantum'; + +interface OrderFunnelProps { + regions: quantum.capabilities.Region[]; + catalog: publicCatalog.Catalog; + sshKeys: SshKey[]; + suggestions: NotebookSuggestions; +} + +const OrderFunnel = ({ + regions, + catalog, + sshKeys, + suggestions, +}: OrderFunnelProps) => { + const model = useOrderFunnel(regions, catalog, suggestions); + const trackBanner = useTrackBanner(); + const track = useTrackAction(); + const { t } = useTranslation('ai-tools/qpu/create'); + const { projectId } = useParams(); + // Manage datastore combobox + const [openVersionCb, setOpenVersionCb] = useState(false); + const [showAdvancedConfiguration, setShowAdvancedConfiguration] = useState( + false, + ); + const accordionContentRef = useRef(null); + const cliEquivalentModale = useModale('cli'); + const navigate = useNavigate(); + + const { toast } = useToast(); + const [command, setCommand] = useState({ command: '' }); + + const { addNotebook, isPending: isPendingAddNotebook } = useAddNotebook({ + onError: (err) => { + trackBanner( + TRACKING.notebooks.banner.errorBannerInfo( + model.result.region.id, + model.result.flavor.type, + model.result.framework.name, + ), + 'banner', + ); + toast({ + title: t('errorCreatingNotebook'), + variant: 'destructive', + description: getAIApiErrorMessage(err), + }); + }, + onSuccess: (notebook) => { + trackBanner( + TRACKING.notebooks.banner.successBannerInfo( + model.result.region.id, + model.result.flavor.type, + model.result.framework.name, + ), + 'banner', + ); + toast({ + title: t('successCreatingNotebookTitle'), + description: t('successCreatingNotebookDescription'), + }); + navigate(`../${notebook.id}`); + }, + }); + + const { getCommand, isPending: isPendingCommand } = useGetCommand({ + onError: (err) => { + toast({ + title: t('errorGetCommandCli'), + variant: 'destructive', + description: getAIApiErrorMessage(err), + }); + }, + onSuccess: (cliCommand) => { + setCommand(cliCommand); + }, + }); + + const getCliCommand = () => { + const notebookInfo: ai.notebook.NotebookSpecInput = getNotebookSpec( + model.result, + ); + getCommand({ projectId, notebookInfo }); + }; + + const onSubmit = model.form.handleSubmit( + () => { + const notebookInfos: ai.notebook.NotebookSpecInput = getNotebookSpec( + model.result, + ); + + track( + TRACKING.notebooks.funnel.createNotebookConfirmClick( + model.result.region.id, + model.result.flavor.type, + model.result.framework.name, + ), + 'funnel', + ); + addNotebook(notebookInfos); + }, + (error) => { + toast({ + title: t('errorFormTitle'), + variant: 'destructive', + description: , + }); + }, + ); + + const scrollToDiv = (target: string) => { + const div = document.getElementById(target); + if (div) { + div.scrollIntoView({ behavior: 'smooth' }); + } + }; + + useEffect(() => { + if (accordionContentRef?.current) { + accordionContentRef.current.style.position = showAdvancedConfiguration + ? 'unset' + : 'relative'; + if (showAdvancedConfiguration) { + scrollToDiv('advancedConfig'); + } + } + }, [showAdvancedConfiguration, accordionContentRef?.current]); + + const classNameLabel = 'scroll-m-20 text-xl font-bold'; + + return ( + <> +
+ +
+ + + {t('fieldConfigurationNameLabel')} + + + ( + + + + + + + )} + /> + + + + + + {t('fieldRegionLabel')} + + + ( + + + { + model.form.setValue('region', newRegion); + model.form.setValue('volumes', []); + }} + /> + + + + )} + /> + + + + + + {t('fieldFlavorLabel')} + + + + {t('fieldFlavorDescription')} + + { + return ( + + + { + model.form.setValue( + 'flavorWithQuantity.flavor', + newFlavor, + ); + model.form.setValue( + 'flavorWithQuantity.quantity', + 1, + ); + }} + /> + + + + ); + }} + /> + + + + {t('fieldFlavorQuantityDescription')} + + ( + + + + +
+ + {model.result.flavor && ( +
+ {t('fieldFlavorQuantityInformation')}{' '} + + {model.result?.flavor?.id} + + {': '} + {model.result?.flavor?.max} +
+ )} +
+
+ )} + /> +
+
+ + + + {t('fieldFrameworkLabel')} + + + ( + + + {model.result.framework && ( + { + model.form.setValue( + 'frameworkWithVersion.framework', + newFramework, + ); + model.form.setValue( + 'frameworkWithVersion.version', + model.lists.frameworks.find( + (fmk) => fmk.id === newFramework, + ).versions[0], + ); + }} + /> + )} + + + + )} + /> + + {model.result.framework?.versions.length > 1 && ( + + + {t('fieldFrameworkVersionDescription')} + + ( + + + +
+ {model.result.framework?.versions.map( + (fmkVersion) => ( + + ), + )} +
+ + + + + + + + + {t('noVersionFound')} + + + {model.result.framework?.versions.map( + (fmkVersion) => ( + { + model.form.setValue( + 'frameworkWithVersion.version', + value, + ); + setOpenVersionCb(false); + }} + > + + + {fmkVersion} + + + ), + )} + + + + +
+
+
+ )} + /> +
+ )} + + + + {t('fieldFQpuDescription')} + + + { + return ( + + + { + model.form.setValue('qpuFlavor', newQPUFlavor); + }} + /> + + + + ); + }} + /> + +
+ + + {t('fieldEditorLabel')} + + + ( + + + + model.form.setValue('editor', newEditor) + } + /> + + + + )} + /> + + + + + {t('fieldConfigurationPrivacyLabel')} + + + ( + + + model.form.setValue('privacy', newPrivacyValue) + } + /> + + )} + /> + + + + {/* Advanced configuration */} +
+ + + + + +
+
+ ( + + + + model.form.setValue('volumes', newVolumes) + } + /> + + + + )} + /> +
+
+ ( + + + {t('fieldConfigurationLabelsLabel')} + + + + model.form.setValue('labels', newLabels) + } + /> + + + + )} + /> +
+
+ ( + +
+ + {t('fieldConfigurationSSHKeysLabel')} + +
+

{t('sshKeyConfigurationHelper')}

+

+ + {t('sshKeyConfigurationHelperBis')} +

+ + + + model.form.setValue('sshKey', newSshKey) + } + /> + + +
+ )} + /> +
+
+
+
+
+
+ + + {t('summaryTitle')} + + + + { + if (['volumes', 'labels', 'sshKey'].includes(target)) { + setShowAdvancedConfiguration(true); + } + scrollToDiv(target); + }} + /> + +

{t('pricingLabel')}

+ +
+ {t('notebookPrice')} + + {model.result.flavor && ( + + )} + +
+ + + + {model.result.qpuFlavor && ( + <> +
+ {t('qpuPrice')} + + + +
+
+ {/* Note d'information */} +
+ +

+ {t('notePriceDesc')} +

+
+
+ + )} +
+ +
+ + + + +
+
+ + + + ); +}; +export default OrderFunnel; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.component.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.component.tsx new file mode 100644 index 000000000000..6e750905f13a --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.component.tsx @@ -0,0 +1,345 @@ +import { Button, Skeleton } from '@datatr-ux/uxlib'; +import { + Cpu, + Globe, + HardDrive, + Hash, + LockKeyhole, + MemoryStick, +} from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { bytesConverter } from '@/lib/bytesHelper'; +import { OrderVolumes } from '@/types/orderFunnel'; +import { humanizeFramework } from '@/lib/orderFunnelHelper'; +import ai from '@/types/AI'; +import quantum from '@/types/Quantum'; + +interface OrderSummaryProps { + order: { + region: quantum.capabilities.Region; + flavor: ai.capabilities.Flavor; + qpuFlavor: quantum.capabilities.QPUFlavor; + resourcesQuantity: number; + framework: ai.capabilities.notebook.Framework; + version: string; + editor: ai.capabilities.notebook.Editor; + notebookName: string; + unsecureHttp: boolean; + labels: { [key: string]: string }; + sshKey: string[]; + volumes: OrderVolumes[]; + }; + onSectionClicked?: (target: string) => void; +} +const buttonClassName = + 'p-0 bg-transparent hover:bg-transparent font-bold text-primary underline-offset-4 hover:underline'; + +const NameDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+
+ + {order.notebookName} +
+
+ ); +}; + +const RegionDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + const { t: tRegions } = useTranslation('regions'); + return ( +
+ + {order.region ? ( + {tRegions(`region_${order.region.id}`)} + ) : ( + + )} +
+ ); +}; + +const FlavorDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+
+ +
+ {order.flavor ? ( + {`${order.resourcesQuantity} ${order.flavor.id}`} + ) : ( + + )} + + {order.flavor && ( +
+
+ + + {t('summaryFieldFlavorCores', { + count: + order.resourcesQuantity * order.flavor.resourcesPerUnit.cpu, + })} + +
+
+ + + {t('summaryFieldFlavorMemory', { + memory: bytesConverter( + order.resourcesQuantity * + order.flavor.resourcesPerUnit.memory, + false, + 0, + ), + })} + +
+
+ + + {t('summaryFieldStorage', { + disk: bytesConverter( + order.resourcesQuantity * + order.flavor.resourcesPerUnit.ephemeralStorage, + false, + 0, + ), + })} + +
+
+ + + {t('summaryFieldNetwork', { + network: bytesConverter( + order.resourcesQuantity * + order.flavor.resourcesPerUnit.publicNetwork, + true, + 2, + ), + })} + +
+ {order.qpuFlavor && ( + <> + 1 {order.qpuFlavor?.name} + +
+ + + {order.qpuFlavor?.qubits} {t('qpuQubits')} + +
+ + )} +
+ )} +
+ ); +}; + +const FrameworkDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+
+ + {order.framework && ( + <> + {humanizeFramework(order.framework)} + + )} +
+
+ + {order.version ? ( + {order.version} + ) : ( + + )} +
+
+ ); +}; + +const EditorDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+
+ + {order.editor && ( + <> + {order.editor.name} + {order.editor.logoUrl && ( + {order.editor.name} + )} + + )} +
+
+ ); +}; + +const PrivacyDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+
+ + {order.unsecureHttp ? ( +
+ {t('summaryFieldPublicLabel')} + +
+ ) : ( +
+ {t('summaryFieldPrivateLabel')} + +
+ )} +
+
+ ); +}; + +const VolumesDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+
+ + + {t(`summaryFieldVolumes`, { + count: order.volumes.length, + context: `${order.volumes.length}`, + })} + +
+
+ ); +}; + +const LabelsDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+ + + {t(`summaryFieldLabels`, { + count: Object.keys(order.labels).length, + context: `${Object.keys(order.labels).length}`, + })} + +
+ ); +}; + +const SshKeysDetails = ({ order, onSectionClicked }: OrderSummaryProps) => { + const { t } = useTranslation('ai-tools/notebooks/create'); + return ( +
+ + + {t(`summaryFieldSshKey`, { + count: order.sshKey.length, + context: `${order.sshKey.length}`, + })} + +
+ ); +}; +const OrderSummary = ({ order, onSectionClicked }: OrderSummaryProps) => { + return ( +
+ + + + + + + {order.volumes.length > 0 && ( + + )} + {Object.keys(order.labels).length > 0 && ( + + )} + {order.sshKey.length > 0 && ( + + )} +
+ ); +}; + +export default OrderSummary; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.spec.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.spec.tsx new file mode 100644 index 000000000000..0e2ca3904bc3 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/OrderSummary.spec.tsx @@ -0,0 +1,110 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; +import OrderSummary from './OrderSummary.component'; +import { mockedCapabilitiesRegionGRA } from '@/__tests__/helpers/mocks/capabilities/region'; +import { mockedCapabilitiesFlavorCPU } from '@/__tests__/helpers/mocks/capabilities/flavor'; +import { mockedEditor } from '@/__tests__/helpers/mocks/capabilities/notebookEditor'; +import { mockedOrderVolumesS3 } from '@/__tests__/helpers/mocks/volume/datastore'; +import { RouterWithQueryClientWrapper } from '@/__tests__/helpers/wrappers/RouterWithQueryClientWrapper'; +import { mockedFramework } from '@/__tests__/helpers/mocks/capabilities/notebookFramework'; +import { mockedCapabilitiesQpuFlavor } from '@/__tests__/helpers/mocks/capabilities/qpuFlavor'; + +const mockedOrder = { + region: mockedCapabilitiesRegionGRA, + flavor: mockedCapabilitiesFlavorCPU, + resourcesQuantity: 2, + framework: mockedFramework, + qpuFlavor: mockedCapabilitiesQpuFlavor, + version: 'version', + editor: mockedEditor, + notebookName: 'myNotebook', + unsecureHttp: false, + labels: { test: 'test' }, + sshKey: ['myNewSSHKEY'], + volumes: [mockedOrderVolumesS3], +}; + +describe('Order summary', () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + afterEach(() => { + vi.clearAllMocks(); + }); + + it('order summary click link display section', async () => { + const mockedOnSectionClicked = vi.fn(); + render( + , + { wrapper: RouterWithQueryClientWrapper }, + ); + await waitFor(() => { + expect(screen.getByText(mockedOrder.notebookName)).toBeTruthy(); + }); + + act(() => { + fireEvent.click(screen.getByTestId('name-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('name'); + }); + act(() => { + fireEvent.click(screen.getByTestId('region-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('region'); + }); + act(() => { + fireEvent.click(screen.getByTestId('flavor-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('flavor'); + }); + act(() => { + fireEvent.click(screen.getByTestId('framework-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('framework'); + }); + + act(() => { + fireEvent.click(screen.getByTestId('editor-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('editor'); + }); + act(() => { + fireEvent.click(screen.getByTestId('access-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('access'); + }); + act(() => { + fireEvent.click(screen.getByTestId('volumes-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('volumes'); + }); + act(() => { + fireEvent.click(screen.getByTestId('labels-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('labels'); + }); + act(() => { + fireEvent.click(screen.getByTestId('sshKeys-section-button')); + }); + await waitFor(() => { + expect(mockedOnSectionClicked).toHaveBeenCalledWith('sshKey'); + }); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/useOrderFunnel.hook.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/useOrderFunnel.hook.tsx new file mode 100644 index 000000000000..2c5f9e43f1b4 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/create/_components/useOrderFunnel.hook.tsx @@ -0,0 +1,277 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { useEffect, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useParams } from 'react-router-dom'; +import { z } from 'zod'; +import { + Flavor, + NotebookSuggestions, + OrderSshKey, + PrivacyEnum, + Qpu, +} from '@/types/orderFunnel'; +import ai from '@/types/AI'; + +import { generateName } from '@/lib/nameGenerator'; +import { useGetDatastores } from '@/data/hooks/ai/data/useGetDatastores.hook'; +import { + DataStoresWithContainers, + useGetDatastoresWithContainers, +} from '@/data/hooks/ai/data/useGetDatastoresWithContainers.hook'; +import { useGetFlavor } from '@/data/hooks/ai/capabilities/useGetFlavor.hook'; +import { createFlavorPricingList } from '@/lib/priceFlavorHelper'; +import publicCatalog from '@/types/Catalog'; +import { useGetFramework } from '@/data/hooks/ai/capabilities/useGetFramework.hook'; +import { useGetEditor } from '@/data/hooks/ai/capabilities/useGetEditor.hook'; +import quantum from '@/types/Quantum'; +import { useGetQpuFlavors } from '@/data/hooks/ai/capabilities/useGetQpuFlavors.hook'; +import { createQpuFlavorPricingList } from '@/lib/priceQpuFlavorHelper'; + +export function useOrderFunnel( + regions: quantum.capabilities.Region[], + catalog: publicCatalog.Catalog, + suggestions: NotebookSuggestions, +) { + const { projectId } = useParams(); + const orderSchema = z.object({ + region: z.string(), + flavorWithQuantity: z.object({ + flavor: z.string(), + quantity: z.coerce.number(), + }), + qpuFlavor: z.string().optional(), + frameworkWithVersion: z.object({ + framework: z.string(), + version: z.string(), + }), + editor: z.string(), + notebookName: z.string().min(1), + privacy: z.nativeEnum(PrivacyEnum), + labels: z + .array( + z.object({ + name: z.string().min(1), + value: z.string().optional(), + }), + ) + .optional(), + sshKey: z.array( + z.object({ + name: z.string(), + sshKey: z.string(), + }), + ), + volumes: z.array( + z.object({ + cache: z.boolean().optional(), + dataStore: z + .object({ + alias: z.string(), + container: z.string(), + }) + .optional(), + publicGit: z + .object({ + url: z.string(), + }) + .optional(), + mountPath: z.string(), + permission: z.nativeEnum(ai.VolumePermissionEnum), + }), + ), + }); + + const form = useForm({ + resolver: zodResolver(orderSchema), + defaultValues: { + region: suggestions?.defaultRegion || regions?.[0]?.id, + flavorWithQuantity: { flavor: '', quantity: 1 }, + frameworkWithVersion: { framework: '', version: '' }, + qpuFlavor: '', + editor: '', + notebookName: generateName(), + privacy: suggestions?.suggestions?.find( + (sug) => sug?.region === suggestions?.defaultRegion, + )?.unsecureHttp + ? PrivacyEnum.private + : PrivacyEnum.public, + labels: [], + sshKey: [], + volumes: [], + }, + }); + + const region = form.watch('region'); + const flavorWithQuantity = form.watch('flavorWithQuantity'); + const frameworkWithversion = form.watch('frameworkWithVersion'); + const editor = form.watch('editor'); + const notebookName = form.watch('notebookName'); + const unsecureHttp = form.watch('privacy'); + const labels = form.watch('labels'); + const sshKey = form.watch('sshKey'); + const volumes = form.watch('volumes'); + const qpuFlavorId = form.watch('qpuFlavor'); + + const frameworkQuery = useGetFramework(projectId, region, 'quantum-qpu'); + const editorQuery = useGetEditor(projectId, region); + const flavorQuery = useGetFlavor(projectId, region); + const qpuFlavorQuery = useGetQpuFlavors(projectId, region); + const datastoreQuery = useGetDatastores(projectId, region); + const containersQuery = useGetDatastoresWithContainers( + projectId, + region, + datastoreQuery.data, + ); + + const regionObject: quantum.capabilities.Region | undefined = useMemo( + () => regions.find((r) => r.id === region), + [regions, region], + ); + + const listFlavor: Flavor[] = useMemo(() => { + if (flavorQuery.isLoading) return []; + return createFlavorPricingList(flavorQuery.data, catalog, 'ai-notebook'); + }, [region, flavorQuery.isSuccess]); + + const listFramework: ai.capabilities.notebook.Framework[] = useMemo(() => { + if (frameworkQuery.isLoading) return []; + return frameworkQuery.data; + }, [region, frameworkQuery.isSuccess]); + + const listEditor: ai.capabilities.notebook.Editor[] = useMemo(() => { + if (editorQuery.isLoading) return []; + return editorQuery.data; + }, [region, editorQuery.isSuccess]); + + const flavorObject: Flavor | undefined = useMemo( + () => listFlavor.find((f) => f.id === flavorWithQuantity.flavor), + [region, listFlavor, flavorWithQuantity.flavor], + ); + + const frameworkObject: + | ai.capabilities.notebook.Framework + | undefined = useMemo( + () => + listFramework.find((fmk) => fmk.id === frameworkWithversion.framework), + [listFramework, frameworkWithversion.framework], + ); + + const listQpuFlavor: Qpu[] = useMemo(() => { + if (qpuFlavorQuery.isLoading) return []; + const allQpus = createQpuFlavorPricingList( + qpuFlavorQuery?.data, + catalog, + 'quantum-processing-unit', + ); + if (!frameworkObject || !frameworkObject.supportedQpus) return []; + return allQpus.filter((qpu) => + frameworkObject.supportedQpus?.includes(qpu.id), + ); + }, [region, qpuFlavorQuery.isSuccess, qpuFlavorQuery?.data, frameworkObject]); + + const qpuflavorObject: Qpu | undefined = useMemo(() => { + if (!listQpuFlavor?.length) return undefined; + return listQpuFlavor.find((qpu) => qpu.id === qpuFlavorId); + }, [listQpuFlavor, qpuFlavorId]); + + const versionObject: string | undefined = useMemo( + () => + frameworkObject?.versions?.find( + (v) => v === frameworkWithversion?.version, + ), + [frameworkObject, frameworkWithversion?.version], + ); + + const editorObject: + | ai.capabilities.notebook.Editor + | undefined = useMemo(() => listEditor.find((ed) => ed.id === editor), [ + listEditor, + editor, + ]); + + const listDatastores: DataStoresWithContainers[] = useMemo(() => { + if (datastoreQuery.isLoading) return []; + return containersQuery.data; + }, [datastoreQuery.isSuccess, containersQuery.data]); + + const unsecureHttpObject: boolean = useMemo( + () => unsecureHttp === PrivacyEnum.public, + [unsecureHttp], + ); + + const publicSshKeyList: string[] = useMemo(() => { + if (sshKey.length === 0) return []; + return sshKey.map((key: OrderSshKey) => key.sshKey); + }, [sshKey]); + + const labelsObject: { [key: string]: string } = useMemo(() => { + if (labels.length === 0) return {}; + return labels.reduce((acc, label) => { + acc[label.name] = label.value; + return acc; + }, {} as { [key: string]: string }); + }, [labels]); + + // Select default Flavor Id / Flavor number when region change + useEffect(() => { + const suggestedFlavor = + suggestions?.suggestions?.find((sug) => sug?.region === regionObject?.id) + ?.resources?.flavorId ?? + listFlavor?.[0]?.id ?? + ''; + const suggestedQuantity = + suggestions?.suggestions?.find((sug) => sug?.region === regionObject?.id) + ?.resources?.quantity ?? 1; + + form.setValue('flavorWithQuantity.flavor', suggestedFlavor); + form.setValue('flavorWithQuantity.quantity', suggestedQuantity); + }, [regionObject, region, flavorQuery.isSuccess]); + + // Change Framework when region change? + useEffect(() => { + if (!frameworkQuery.isSuccess || !qpuFlavorQuery.isSuccess) return; + form.setValue('frameworkWithVersion.framework', listFramework[0]?.id); + const firstQpuId = qpuFlavorQuery?.data?.[0]?.id ?? ''; + form.setValue('qpuFlavor', firstQpuId); + form.setValue( + 'frameworkWithVersion.version', + listFramework[0]?.versions[0], + ); + }, [regionObject, region, listFramework, flavorQuery.isSuccess]); + + // Change editors when region change? + useEffect(() => { + const suggestedEditor = + suggestions?.suggestions?.find((sug) => sug?.region === regionObject?.id) + ?.editor?.id ?? + listEditor?.[0]?.id ?? + ''; + form.setValue('editor', suggestedEditor); + }, [regionObject, region, listEditor]); + + return { + form, + lists: { + regions, + flavors: listFlavor, + frameworks: listFramework, + editors: listEditor, + volumes: listDatastores, + qpuFlavors: listQpuFlavor, + }, + result: { + region: regionObject, + flavor: flavorObject, + resourcesQuantity: flavorWithQuantity.quantity, + framework: frameworkObject, + version: versionObject, + editor: editorObject, + notebookName, + unsecureHttp: unsecureHttpObject, + labels: labelsObject, + sshKey: publicSshKeyList, + volumes, + qpuFlavor: qpuflavorObject, + }, + }; +} diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.page.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.page.tsx new file mode 100644 index 000000000000..51f8652629d8 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.page.tsx @@ -0,0 +1,31 @@ +import { Button } from '@datatr-ux/uxlib'; +import Link from '@/components/links/Link.component'; +import onboardingQuantumImgSrc from '@/../public/assets/quantum-notebooks-qpu.png'; +import { useQuantum } from '@/hooks/useQuantum.hook'; + +const Onboarding = () => { + const { t } = useQuantum('ai-tools/qpu/onboarding'); + + return ( +
+

{t('Title')}

+ +

{t('Description1')}

+

{t('Description2')}

+

{t('Description3')}

+ +
+ ); +}; + +export default Onboarding; diff --git a/packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.spec.tsx b/packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.spec.tsx new file mode 100644 index 000000000000..cdb0c332e1f3 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/pages/qpus/onboarding/Onboarding.spec.tsx @@ -0,0 +1,32 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { render, screen, waitFor } from '@testing-library/react'; +import { mockedUsedNavigate } from '@/__tests__/helpers/mockRouterDomHelper'; +import { mockManagerReactShellClient } from '@/__tests__/helpers/mockShellHelper'; +import { RouterWithQueryClientWrapper } from '@/__tests__/helpers/wrappers/RouterWithQueryClientWrapper'; +import Onboarding from './Onboarding.page'; +import { + mockedGuideOnboarding, + mockedGuides, +} from '@/__tests__/helpers/mocks/shared/guides'; + +describe('Onboarding page', () => { + beforeEach(() => { + vi.restoreAllMocks(); + mockedUsedNavigate(); + mockManagerReactShellClient(); + + vi.mock('@/data/api/ai/guide.api', () => ({ + getGuides: vi.fn(() => [mockedGuides, mockedGuideOnboarding]), + })); + }); + afterEach(() => { + vi.clearAllMocks(); + }); + it('renders the onboarding Page', async () => { + render(, { wrapper: RouterWithQueryClientWrapper }); + await waitFor(() => { + expect(screen.getByTestId('onbaording-container')).toBeTruthy(); + expect(screen.getByTestId('create-notebook-link')).toBeTruthy(); + }); + }); +}); diff --git a/packages/manager/apps/pci-ai-tools/src/routes/routes.tsx b/packages/manager/apps/pci-ai-tools/src/routes/routes.tsx index 08b19fc3df16..e6b86f23cb74 100644 --- a/packages/manager/apps/pci-ai-tools/src/routes/routes.tsx +++ b/packages/manager/apps/pci-ai-tools/src/routes/routes.tsx @@ -1115,6 +1115,328 @@ export default [ }, ], }, + + { + path: 'quantum/qpu', + ...lazyRouteConfig(() => import('@/pages/qpus/QpuRoot.layout')), + children: [ + { + path: '', + id: 'qpu', + ...lazyRouteConfig(() => import('@/pages/qpus/Qpu.page')), + children: [ + { + path: 'start/:notebookId', + id: 'qpusNotebooks.start', + handle: { + tracking: { + id: 'qpus_ai_notebooks.popup.start', + category: 'listing', + }, + }, + ...lazyRouteConfig(() => + import('@/pages/notebooks/start/Start.modal'), + ), + }, + { + path: 'stop/:notebookId', + id: 'qpusNotebooks.stop', + handle: { + tracking: { + id: 'qpus_ai_notebooks.popup.stop', + category: 'listing', + }, + }, + ...lazyRouteConfig(() => + import('@/pages/notebooks/stop/Stop.modal'), + ), + }, + { + path: 'delete/:notebookId', + id: 'qpusNotebooks.delete', + handle: { + tracking: { + id: 'qpus_ai_notebooks.popup.delete', + category: 'listing', + }, + }, + ...lazyRouteConfig(() => + import('@/pages/notebooks/delete/Delete.modal'), + ), + }, + ], + }, + + { + path: 'onboarding', + id: 'onboarding-qpu', + ...lazyRouteConfig(() => + import('@/pages/qpus/onboarding/Onboarding.page'), + ), + }, + + { + path: 'new', + id: 'qpu.create', + ...lazyRouteConfig(() => + import('@/pages/qpus/create/Create.page'), + ), + handle: { + tracking: { + id: 'qpu.funnel.create_qpu', + category: 'funnel', + }, + breadcrumb: () => ( + + ), + }, + children: [ + { + path: 'add-sshkey', + id: 'qpu.create.add-sshkey', + handle: { + tracking: { + id: 'qpu.funnel.popup.add-sshkey', + category: 'funnel', + }, + }, + ...lazyRouteConfig(() => + import('@/pages/_components/AddSSHKey.modal'), + ), + }, + ], + }, + + { + path: ':notebookId', + ...lazyRouteConfig(() => + import('@/pages/notebooks/[notebookId]/Notebook.layout'), + ), + children: [ + { + path: '', + id: 'notebooks.qpus.dashboard', + handle: { + tracking: { + id: 'ai_notebooks.qpus.dashboard', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/dashboard/Dashboard.page' + ), + ), + children: [ + { + path: 'delete', + id: 'notebooks.qpus.dashboard.delete', + handle: { + tracking: { + id: 'ai_notebooks.qpus.dashboard.popup.delete', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/dashboard/delete/Delete.modal' + ), + ), + }, + { + path: 'update-flavor', + id: 'notebooks.qpus.dashboard.update-flavor', + handle: { + tracking: { + id: + 'ai_notebooks.qpus.dashboard.popup.update-flavor', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/dashboard/_components/update-flavor/UpdateFlavor.modal' + ), + ), + }, + ], + }, + { + path: 'containers', + id: 'notebooks.qpus.containers', + handle: { + tracking: { + id: 'ai_notebooks.qpus.containers', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/containers/Containers.page' + ), + ), + children: [ + { + path: 'data-sync', + id: 'notebooks.qpus.containers.data-sync', + handle: { + tracking: { + id: 'ai_notebooks.qpus.containers.data-sync', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/containers/dataSync/DataSync.modal' + ), + ), + }, + { + path: 'add-container', + id: 'notebooks.qpus.container.add', + handle: { + tracking: { + id: 'ai_notebooks.qpus.container.popup.add', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/containers/addVolume/AddVolume.modal' + ), + ), + }, + { + path: 'delete/:volumeId?', + id: 'notebooks.qpus.container.delete', + handle: { + tracking: { + id: 'ai_notebooks.qpus.container.popup.delete', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/containers/deleteVolume/DeleteVolume.modal' + ), + ), + }, + { + path: 'data-sync/:volumeId?', + id: 'notebooks.qpus.containers.data-sync.volume', + handle: { + tracking: { + id: 'ai_notebooks.qpus.containers.data-sync.volume', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/containers/dataSync/DataSync.modal' + ), + ), + }, + ], + }, + { + path: 'public-git', + id: 'notebooks.qpus.public-git', + handle: { + tracking: { + id: 'ai_notebooks.qpus.public-git', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/public-git/PublicGit.page' + ), + ), + children: [ + { + path: 'add-public-git', + id: 'notebooks.qpus.public-git.add', + handle: { + tracking: { + id: 'ai_notebooks.qpus.public-git.popup.add', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/public-git/addVolume/AddVolume.modal' + ), + ), + }, + { + path: 'delete/:volumeId?', + id: 'notebooks.qpus.public-git.delete', + handle: { + tracking: { + id: 'ai_notebooks.qpus.public-git.popup.delete', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/public-git/deleteVolume/DeleteVolume.modal' + ), + ), + }, + ], + }, + { + path: 'logs', + id: 'notebooks.qpus.logs', + handle: { + tracking: { + id: 'ai_notebooks.qpus.logs', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import('@/pages/notebooks/[notebookId]/logs/Logs.page'), + ), + }, + { + path: 'backups', + id: 'notebooks.qpus.backups', + handle: { + tracking: { + id: 'ai_notebooks.qpus.backups', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/backups/Backups.page' + ), + ), + children: [ + { + path: 'fork/:backupId?', + id: 'notebooks.qpus.backups.fork', + handle: { + tracking: { + id: 'ai_notebooks.qpus.backups.popup.fork', + category: 'dashboard', + }, + }, + ...lazyRouteConfig(() => + import( + '@/pages/notebooks/[notebookId]/backups/fork/Fork.modal' + ), + ), + }, + ], + }, + ], + }, + ], + }, ], }, ], diff --git a/packages/manager/apps/pci-ai-tools/src/types/Quantum.ts b/packages/manager/apps/pci-ai-tools/src/types/Quantum.ts new file mode 100644 index 000000000000..8c3e7fa77b45 --- /dev/null +++ b/packages/manager/apps/pci-ai-tools/src/types/Quantum.ts @@ -0,0 +1,3 @@ +import * as quantum from '@datatr-ux/ovhcloud-types/cloud/project/quantum/index'; + +export default quantum; diff --git a/packages/manager/apps/pci-ai-tools/src/types/orderFunnel/index.ts b/packages/manager/apps/pci-ai-tools/src/types/orderFunnel/index.ts index 8d8c3fa92274..ae0dd5b8af35 100644 --- a/packages/manager/apps/pci-ai-tools/src/types/orderFunnel/index.ts +++ b/packages/manager/apps/pci-ai-tools/src/types/orderFunnel/index.ts @@ -1,5 +1,6 @@ import ai from '../AI'; import catalog from '../Catalog'; +import quantum from '../Quantum'; export interface AppPricing { price: number; @@ -27,6 +28,14 @@ export interface FrameworkWithVersion { version?: string; } +export interface Qpu extends quantum.capabilities.QPUFlavor { + pricing: catalog.Pricing[]; +} + +export interface NotebookWithQpu extends ai.notebook.Notebook { + qpuDetail?: quantum.capabilities.QPUFlavor; +} + export interface OrderLabel { name?: string; value?: string; @@ -128,6 +137,7 @@ export interface NotebookOrderResult { }; sshKey: string[]; volumes: OrderVolumes[]; + qpuFlavor?: Qpu; } export interface JobOrderResult { diff --git a/packages/manager/apps/public-cloud/src/links.json b/packages/manager/apps/public-cloud/src/links.json index d37df52a0a15..4f7fc1e1478d 100644 --- a/packages/manager/apps/public-cloud/src/links.json +++ b/packages/manager/apps/public-cloud/src/links.json @@ -440,6 +440,26 @@ "path": "/pci/projects/{project}/ai/endpoints" } }, + { + "public": { + "application": "public-cloud", + "path": "/create-quantum-emulators" + }, + "redirect": { + "application": "public-cloud", + "path": "/pci/projects/{project}/ai-ml/quantum/notebooks" + } + }, + { + "public": { + "application": "public-cloud", + "path": "/create-quantum-qpu" + }, + "redirect": { + "application": "public-cloud", + "path": "/pci/projects/{project}/ai-ml/quantum/qpu" + } + }, { "public": { "application": "public-cloud", diff --git a/yarn.lock b/yarn.lock index b4d9e2c8ac74..3b384d9901d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -358,6 +358,73 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@ark-ui/react@5.25.1": + version "5.25.1" + resolved "https://registry.yarnpkg.com/@ark-ui/react/-/react-5.25.1.tgz#4b34885e46abf2218da1f95d8574df4e02cac3d6" + integrity sha512-5ncHjwMuCmCDz+a3PD2voFJk+3VDmGWZ3Hs6WQIxEztIWbXC9sEj86BkpcSV84s57ELCALIXm9fQk4TUXGoZlw== + dependencies: + "@internationalized/date" "3.9.0" + "@zag-js/accordion" "1.24.2" + "@zag-js/anatomy" "1.24.2" + "@zag-js/angle-slider" "1.24.2" + "@zag-js/async-list" "1.24.2" + "@zag-js/auto-resize" "1.24.2" + "@zag-js/avatar" "1.24.2" + "@zag-js/bottom-sheet" "1.24.2" + "@zag-js/carousel" "1.24.2" + "@zag-js/checkbox" "1.24.2" + "@zag-js/clipboard" "1.24.2" + "@zag-js/collapsible" "1.24.2" + "@zag-js/collection" "1.24.2" + "@zag-js/color-picker" "1.24.2" + "@zag-js/color-utils" "1.24.2" + "@zag-js/combobox" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/date-picker" "1.24.2" + "@zag-js/date-utils" "1.24.2" + "@zag-js/dialog" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/editable" "1.24.2" + "@zag-js/file-upload" "1.24.2" + "@zag-js/file-utils" "1.24.2" + "@zag-js/floating-panel" "1.24.2" + "@zag-js/focus-trap" "1.24.2" + "@zag-js/highlight-word" "1.24.2" + "@zag-js/hover-card" "1.24.2" + "@zag-js/i18n-utils" "1.24.2" + "@zag-js/json-tree-utils" "1.24.2" + "@zag-js/listbox" "1.24.2" + "@zag-js/menu" "1.24.2" + "@zag-js/number-input" "1.24.2" + "@zag-js/pagination" "1.24.2" + "@zag-js/password-input" "1.24.2" + "@zag-js/pin-input" "1.24.2" + "@zag-js/popover" "1.24.2" + "@zag-js/presence" "1.24.2" + "@zag-js/progress" "1.24.2" + "@zag-js/qr-code" "1.24.2" + "@zag-js/radio-group" "1.24.2" + "@zag-js/rating-group" "1.24.2" + "@zag-js/react" "1.24.2" + "@zag-js/scroll-area" "1.24.2" + "@zag-js/select" "1.24.2" + "@zag-js/signature-pad" "1.24.2" + "@zag-js/slider" "1.24.2" + "@zag-js/splitter" "1.24.2" + "@zag-js/steps" "1.24.2" + "@zag-js/switch" "1.24.2" + "@zag-js/tabs" "1.24.2" + "@zag-js/tags-input" "1.24.2" + "@zag-js/timer" "1.24.2" + "@zag-js/toast" "1.24.2" + "@zag-js/toggle" "1.24.2" + "@zag-js/toggle-group" "1.24.2" + "@zag-js/tooltip" "1.24.2" + "@zag-js/tour" "1.24.2" + "@zag-js/tree-view" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@asamuzakjp/css-color@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.1.1.tgz#41a612834dafd9353b89855b37baa8a03fb67bf2" @@ -3609,7 +3676,7 @@ dependencies: "@ovhcloud/ods-themes" "^18.4.1" -"@datatr-ux/ovhcloud-types@1.0.11", "@datatr-ux/ovhcloud-types@^1.0.11": +"@datatr-ux/ovhcloud-types@1.0.11": version "1.0.11" resolved "https://registry.yarnpkg.com/@datatr-ux/ovhcloud-types/-/ovhcloud-types-1.0.11.tgz#bac1d9b460cc969a191967cd2b1d0858de713d78" integrity sha512-NSqgUoIXKHxVRY5fnp5hV2kNQJxbX9/Om0naInG1tnztBvdkRuuhfzga9+ReYVqkktaSg/kb3CGZzyWi2tpvCg== @@ -3619,6 +3686,11 @@ resolved "https://registry.yarnpkg.com/@datatr-ux/ovhcloud-types/-/ovhcloud-types-1.0.8.tgz#0e6a8359cc8a52389f65c1aebfae57261ec8215a" integrity sha512-Fze56Qv4qkucB3/w5/bl1lNVCNKlOdSBKYDVLaHNCJhHs+JbHCtMR4Ki29dA+Lk8mti0r3g1fdMZ7Hb2YBZ8gA== +"@datatr-ux/ovhcloud-types@^1.0.34": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@datatr-ux/ovhcloud-types/-/ovhcloud-types-1.0.38.tgz#db207335ed3e66f29023cedc8d6611fd928802d0" + integrity sha512-3DJcNoCsosdLAmd0UX+pTBMUxc4Zl69JjrNtVBBbUjHsau2l16nQnBQSU1GcExsln2gn91OyPg87B1eHRoLvHQ== + "@datatr-ux/uxlib@0.0.10": version "0.0.10" resolved "https://registry.yarnpkg.com/@datatr-ux/uxlib/-/uxlib-0.0.10.tgz#0a66c5d3ad49b0e1effa1cd02c36167e3ac6eef1" @@ -5011,6 +5083,13 @@ dependencies: "@floating-ui/utils" "^0.2.10" +"@floating-ui/core@^1.7.3": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.3.tgz#462d722f001e23e46d86fd2bd0d21b7693ccb8b7" + integrity sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w== + dependencies: + "@floating-ui/utils" "^0.2.10" + "@floating-ui/dom@1.6.11": version "1.6.11" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.11.tgz#8631857838d34ee5712339eb7cbdfb8ad34da723" @@ -5035,6 +5114,14 @@ "@floating-ui/core" "^1.7.3" "@floating-ui/utils" "^0.2.10" +"@floating-ui/dom@1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.4.tgz#ee667549998745c9c3e3e84683b909c31d6c9a77" + integrity sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA== + dependencies: + "@floating-ui/core" "^1.7.3" + "@floating-ui/utils" "^0.2.10" + "@floating-ui/dom@^1.0.1", "@floating-ui/dom@^1.6.1": version "1.6.3" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef" @@ -5581,6 +5668,13 @@ dependencies: "@swc/helpers" "^0.5.0" +"@internationalized/date@3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.9.0.tgz#cf241989b5dd07a2a9f1c91aabd2ad93968a0cc3" + integrity sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg== + dependencies: + "@swc/helpers" "^0.5.0" + "@internationalized/number@3.6.2": version "3.6.2" resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.6.2.tgz#504bf772238420c06b63ec58957c1cfcf6d92755" @@ -5595,6 +5689,13 @@ dependencies: "@swc/helpers" "^0.5.0" +"@internationalized/number@3.6.5": + version "3.6.5" + resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.6.5.tgz#1103f2832ca8d9dd3e4eecf95733d497791dbbbe" + integrity sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g== + dependencies: + "@swc/helpers" "^0.5.0" + "@isaacs/balanced-match@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" @@ -7747,6 +7848,15 @@ classnames "2.5.1" libphonenumber-js "1.12.8" +"@ovhcloud/ods-react@^19.0.2": + version "19.2.1" + resolved "https://registry.yarnpkg.com/@ovhcloud/ods-react/-/ods-react-19.2.1.tgz#babc8f579f4109b091f7fb208b49858ca49563b8" + integrity sha512-xk7u7B/EsxyMrnW1n13PcNNj93SBxKcOzQH8/RNJ3gQYJO+TYcG7w858xlry+KUi+hO2q8aYDcBjS5iD/4x4Eg== + dependencies: + "@ark-ui/react" "5.25.1" + classnames "2.5.1" + libphonenumber-js "1.12.8" + "@ovhcloud/ods-react@^19.0.2", "@ovhcloud/ods-react@^19.2.0", "@ovhcloud/ods-react@^19.2.1": version "19.2.1" resolved "https://registry.yarnpkg.com/@ovhcloud/ods-react/-/ods-react-19.2.1.tgz#babc8f579f4109b091f7fb208b49858ca49563b8" @@ -7799,6 +7909,11 @@ resolved "https://registry.yarnpkg.com/@ovhcloud/ods-themes/-/ods-themes-19.0.1.tgz#2d31d9eb97fc7a4f3c220354ef4d4d17bacb2b1a" integrity sha512-OeVEwshdex36zZqFhVFix/kbb/4+ggRQXeMbAJwe17RrOVWC+YvuuHlgutSCbGZqi5gujdWv4c7lJ9qWUvqRvA== +"@ovhcloud/ods-themes@^19.0.2": + version "19.2.1" + resolved "https://registry.yarnpkg.com/@ovhcloud/ods-themes/-/ods-themes-19.2.1.tgz#c30976d911207f5309f2a3daaab04aed50d7a7ff" + integrity sha512-zjtSoX2gaVMtpBVIGmg3HBNfjWeHRAHzTyTOxjeR47L+qo4iAj2pghL4riZpNvn9/YlVaIJT8GpO+Lw/IVAAnQ== + "@ovhcloud/ods-themes@^19.0.2", "@ovhcloud/ods-themes@^19.2.0", "@ovhcloud/ods-themes@^19.2.1": version "19.2.1" resolved "https://registry.yarnpkg.com/@ovhcloud/ods-themes/-/ods-themes-19.2.1.tgz#c30976d911207f5309f2a3daaab04aed50d7a7ff" @@ -13247,6 +13362,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/accordion@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/accordion/-/accordion-1.24.2.tgz#28731e3e165e2762193666cc259305d52e54f545" + integrity sha512-sGNhbWR85oAiMyQLk+dliRhNQGP59T56M1gAkQ7bwJJZ7l++hFEQpYcr/FbAHJshXWpvUKm0wV18wHR/56Y30w== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/anatomy@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/anatomy/-/anatomy-1.15.0.tgz#b6d03411884d33fcaec19c77ba53fb826d34033a" @@ -13257,6 +13383,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/anatomy/-/anatomy-1.24.2.tgz#dd33741a0a54c31a93982979dcbbb82272280b0f" integrity sha512-qWmxopxVHMjP9UGoUdxqKtrot8MaU0UvoJWh0O03b9eOPgLwMydkcwuGAc8s3x6GDREu3D3GvcRgrQ5JteITjg== +"@zag-js/anatomy@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/anatomy/-/anatomy-1.24.2.tgz#dd33741a0a54c31a93982979dcbbb82272280b0f" + integrity sha512-qWmxopxVHMjP9UGoUdxqKtrot8MaU0UvoJWh0O03b9eOPgLwMydkcwuGAc8s3x6GDREu3D3GvcRgrQ5JteITjg== + "@zag-js/angle-slider@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/angle-slider/-/angle-slider-1.15.0.tgz#a61f7b72704f915d9e039703c0ff50206a447802" @@ -13281,6 +13412,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/angle-slider@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/angle-slider/-/angle-slider-1.24.2.tgz#a6330e088fd3fcb9b751755f65b7541b9dd65b42" + integrity sha512-QPBWxji84sEyB519uU+n07IkowvDaLSpon1oDQvNc3wFKz35F5IAXQo85pDQPPDAzEHG4oJ2W3cXPRWmkVuTrg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/rect-utils" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/aria-hidden@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/aria-hidden/-/aria-hidden-1.15.0.tgz#173ed717e066cd7817c15eb8d7bedfdd337a8721" @@ -13299,6 +13442,19 @@ "@zag-js/core" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/aria-hidden@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/aria-hidden/-/aria-hidden-1.24.2.tgz#163ef9b11eee4934e60a7fbb46a85cc74e3668ec" + integrity sha512-btbVDdfHq2wcvV2KmpBlkKN+36XIMkXTIy7zvQniBQ/V6X5WGKBaXGvllqrLLaJVU2HmnhM/IYUBQW4H0BLWBA== + +"@zag-js/async-list@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/async-list/-/async-list-1.24.2.tgz#a70844b7b79ff661ffc6a7d978ccb27214b65570" + integrity sha512-W2720aU4ANdhcrFtOQX+AVYFSxxuH+TT8kAPtIZtnd6ffOTuiR8j8PV7U45gC5KUvixIBQ+oot11m/n3YVilUw== + dependencies: + "@zag-js/core" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/auto-resize@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/auto-resize/-/auto-resize-1.15.0.tgz#e82ffe10643941ec2410d6f164cc909683498350" @@ -13313,6 +13469,13 @@ dependencies: "@zag-js/dom-query" "1.24.2" +"@zag-js/auto-resize@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/auto-resize/-/auto-resize-1.24.2.tgz#bd11e9d0f9b1fb7bad63d036fbdba962699a0b2b" + integrity sha512-oOzFY+4mif6PXpVMR+db8+b33C8uplAkiuDwZhNEaQDj97S4kxySlQkFovbmY55DYAlBRguaQKXjn0pboqwIqA== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/avatar@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/avatar/-/avatar-1.15.0.tgz#543b5e15a5604d19a25f6a5eb00b12b71b758b05" @@ -13350,6 +13513,32 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/avatar@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/avatar/-/avatar-1.24.2.tgz#6a294be5c6a52a54dd5a37882b5f959d9ce855bd" + integrity sha512-qSqJQLjscmWCMPosWKoTwSdFL6/hHyLeBAL4iyLcVby5Y7tz4o3u5zjkcLGPoZTH1DxicwEDBaTkONHvMthaLw== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + +"@zag-js/bottom-sheet@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/bottom-sheet/-/bottom-sheet-1.24.2.tgz#1012a7eda62b6fb87aabe5cd5aa68a0059fa864d" + integrity sha512-T1nWvpNUvzE29ODZhkjsZCE4yHzkWQ7dNohrDMvMOc7jNlxUOy0SRd4LuiT4ioAg2FUvMZrfcTBBVagc9kv9SQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/aria-hidden" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-trap" "1.24.2" + "@zag-js/remove-scroll" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/carousel@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/carousel/-/carousel-1.15.0.tgz#5b3d56a831ac808c253916d6f94a0d3d3d65b5c3" @@ -13374,6 +13563,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/carousel@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/carousel/-/carousel-1.24.2.tgz#8eef1d715be2973d0e718fa285d4fecd3821a9d2" + integrity sha512-gQYZ4+UyCk1vXYYkIBGHWxAZNQBlV1/e1XdQTT1CApKmUAjNwDWf91fcQ733mB9n7MEDTS/vtgGtWQFQNVVh1g== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/scroll-snap" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/checkbox@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/checkbox/-/checkbox-1.15.0.tgz#a48b1f17432d1f0dacad623154cdfc6309c4ee9c" @@ -13398,6 +13599,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/checkbox@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/checkbox/-/checkbox-1.24.2.tgz#ee83e63b5040313468d650b7207826cda0c371d8" + integrity sha512-+ibuzfVW9Nx84r04cd1SxdI3P19/bnexmMzw7zZu/17pSvO4u5v6HSKi24ARVv15sw1ujjcfHd1qlDmtWZFyJg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-visible" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/clipboard@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/clipboard/-/clipboard-1.15.0.tgz#6aa972ac0376ea0b19ea9626e6fe65cf4f8a772b" @@ -13420,6 +13633,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/clipboard@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/clipboard/-/clipboard-1.24.2.tgz#cef666f1c682dd04e8fc815093686c8cc657af47" + integrity sha512-3E8c3IJubkJGxGJRAB7nmoFGLxM6LiaANz0JH4WxOt7lIu+5jxIxzLVRGAQI3/Vtn1Qxm34+Ffqqw2PW/J+6qA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/collapsible@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/collapsible/-/collapsible-1.15.0.tgz#ffecb1e522642ccff338a21fadc4a290ca4e1ac2" @@ -13442,6 +13666,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/collapsible@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/collapsible/-/collapsible-1.24.2.tgz#5f3b72156367faf2427f0ebabd0706d60f493dae" + integrity sha512-nmMRljiM2AfcmGW04dgPOQjjuteGj4wbaURJiN8uyFnDKavUZH7BIT8knu8iA59Nj8m6UctDIcasRreUd6smxA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/collection@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/collection/-/collection-1.15.0.tgz#54dce39eaf4a354668bebdea767e962d604d1af9" @@ -13456,6 +13691,13 @@ dependencies: "@zag-js/utils" "1.24.2" +"@zag-js/collection@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/collection/-/collection-1.24.2.tgz#8942619d2df1c4a546619cab5dc0482c909759b2" + integrity sha512-vqNnn9nAmz5lz8pHhvjNdCrPHj76aZpoaRFe7DQdcnwlrbNjASUKPiN4lG5ZgspOMnQqZ0teR4fyjaCa+cH+xQ== + dependencies: + "@zag-js/utils" "1.24.2" + "@zag-js/color-picker@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/color-picker/-/color-picker-1.15.0.tgz#73d779b8e5e060915245d8fc4fb5a3d37406e4ea" @@ -13484,6 +13726,20 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/color-picker@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/color-picker/-/color-picker-1.24.2.tgz#c71d380a7a25a64a656490db904729b5af947d5e" + integrity sha512-FKK4tNATGKjAfwpxqgAyM8uecZa1ebTP8HSLc2c02hL5C7VUEcMrGP7SYBT/eOYXME1iHIA01R6ArmIzVhVdvQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/color-utils" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/color-utils@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/color-utils/-/color-utils-1.15.0.tgz#33e558a9120b2080b782eb09f2c7c31fe242c08a" @@ -13498,11 +13754,18 @@ dependencies: "@zag-js/utils" "1.24.2" -"@zag-js/combobox@1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@zag-js/combobox/-/combobox-1.15.0.tgz#97408be1943bb80288f904a673367276c37bab3e" - integrity sha512-HBck3wcEeIOa7IQMsUkUKbm9cAU7bjoklIyq2zFGn90k7DcDa++oXK9Z2pmcd4TPoBYiyVuuXucaCcjmLX8V/Q== - dependencies: +"@zag-js/color-utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/color-utils/-/color-utils-1.24.2.tgz#dd481c90e472ba08617a152156932bc7fe67e8fe" + integrity sha512-p/mkEA7lAH9YFyAHIEAbCXJW0cUkJNDXcq8OAkx/pq+mL+dDPLgjN4GKfb+Hhur+n/e2Jd6UTYEDmCEFUgC3fg== + dependencies: + "@zag-js/utils" "1.24.2" + +"@zag-js/combobox@1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@zag-js/combobox/-/combobox-1.15.0.tgz#97408be1943bb80288f904a673367276c37bab3e" + integrity sha512-HBck3wcEeIOa7IQMsUkUKbm9cAU7bjoklIyq2zFGn90k7DcDa++oXK9Z2pmcd4TPoBYiyVuuXucaCcjmLX8V/Q== + dependencies: "@zag-js/anatomy" "1.15.0" "@zag-js/aria-hidden" "1.15.0" "@zag-js/collection" "1.15.0" @@ -13528,6 +13791,21 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/combobox@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/combobox/-/combobox-1.24.2.tgz#0495238595fcac33c9e0381cb33b41e7ce6f125b" + integrity sha512-o/jkby5ry4IUAm8GT04RbMdd3r8xk7RTAdnNRdvv4R8ZJtaDDL+wJVFq5ITeFhtnvmAl3GD+Isn2kJ3fut0fHQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/aria-hidden" "1.24.2" + "@zag-js/collection" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/core@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/core/-/core-1.15.0.tgz#c6a4092a70292d15279cc08a73043a76b430bf42" @@ -13544,6 +13822,14 @@ "@zag-js/dom-query" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/core@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/core/-/core-1.24.2.tgz#df0aef1d951618c9200cf2fab7d84d083cc2154d" + integrity sha512-LBJBNpaEixfIKLjyfcuudTdtnVJmj60iLK9flI3D7BeziU/nGu3CsNxh0miLCR2Sdl7jFEseyGrK7HLhTaRMLw== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/date-picker@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/date-picker/-/date-picker-1.15.0.tgz#48aaa96d4407045da217d06aa6e50837f14e0a71" @@ -13574,6 +13860,21 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/date-picker@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/date-picker/-/date-picker-1.24.2.tgz#995dd43ae8b4075a9bb507d319b8a7846089106f" + integrity sha512-8vpztt7RwrreqXdV4vwotQ0susaqQfOmqdMh++j31UMsvmRinNiFo75hL7/5AvPwpBnt9lmtWZYBw2WY5DFnQQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/date-utils" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/live-region" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/date-utils@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/date-utils/-/date-utils-1.15.0.tgz#c61c119a679778f5253d40e58a856e16e5cb7036" @@ -13584,6 +13885,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/date-utils/-/date-utils-1.24.2.tgz#f50d398a68b3fc31dd8d9610db5fc17d7f5e8988" integrity sha512-wtrbE4LTs8h+9U9mqJPFg+RqdRofTISwMCY1nD1gApCwMdp9Gov9me0CcV+JshXgOEBSsFoZXt8fvSINN0gs+A== +"@zag-js/date-utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/date-utils/-/date-utils-1.24.2.tgz#f50d398a68b3fc31dd8d9610db5fc17d7f5e8988" + integrity sha512-wtrbE4LTs8h+9U9mqJPFg+RqdRofTISwMCY1nD1gApCwMdp9Gov9me0CcV+JshXgOEBSsFoZXt8fvSINN0gs+A== + "@zag-js/dialog@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/dialog/-/dialog-1.15.0.tgz#dae169745a70d55f1d41f11753abca83248c1f57" @@ -13614,6 +13920,21 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/dialog@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/dialog/-/dialog-1.24.2.tgz#fae4fd98e6a8a85056946407caeec6f7d98b8c24" + integrity sha512-70dvyikN/f3qqhI9mGB23oMGeTmjjZmhy5ZXDCwNL1ZmZ0SnGB3QdsHEa/DDyoXC2qSO8XhOUgEp3hNukXujXA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/aria-hidden" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-trap" "1.24.2" + "@zag-js/remove-scroll" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/dismissable@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/dismissable/-/dismissable-1.15.0.tgz#ad5bf067908a189a918c6b018d734a51215829bb" @@ -13632,6 +13953,15 @@ "@zag-js/interact-outside" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/dismissable@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/dismissable/-/dismissable-1.24.2.tgz#9715deb0c9286be797e83171056b9d8b98a8dea7" + integrity sha512-POmhCyjm8cRIThuK3icXjt9ic3OrYjN3N0jQ7uT5xAitX5eyGR+Tb7Mdf5J1R6iuX19t0t6ova+h3XIx6YDWHQ== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/interact-outside" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/dom-query@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/dom-query/-/dom-query-1.15.0.tgz#762a807f6d8a76d1c1f69253732148717747e355" @@ -13646,6 +13976,13 @@ dependencies: "@zag-js/types" "1.24.2" +"@zag-js/dom-query@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/dom-query/-/dom-query-1.24.2.tgz#bf18e96d873e74e02436b97fb19d878ff4ef173e" + integrity sha512-CrjxXni9S9sxuz64uveHDGsvXcZPuN8ydg5+UFZh0MTXCCpS2nFdSWJ1ZN4uyak+X0CdyIEvvbzdxmEhBi33dQ== + dependencies: + "@zag-js/types" "1.24.2" + "@zag-js/editable@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/editable/-/editable-1.15.0.tgz#05daade9134408a02d332c88f0b76fbc8e416597" @@ -13670,6 +14007,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/editable@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/editable/-/editable-1.24.2.tgz#06a2d2feab12ece7537a9aab0c8d1fb6535812de" + integrity sha512-ZSvBcVi3OMuChGj6IGCnX7QoF9UVAhdHelKp3q2/qfWq6ykwFUtO8erYBkEFmkdGqRqdlnbtidBRBoh0e9dLdg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/interact-outside" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/file-upload@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/file-upload/-/file-upload-1.15.0.tgz#32942dac653e960bc6ac0706c5dc30c8142aa842" @@ -13696,6 +14045,19 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/file-upload@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/file-upload/-/file-upload-1.24.2.tgz#c16a25c3ed426d993cc87e1ea9a130e95fc112cb" + integrity sha512-hQHTwbAXDYOjLatRDcoLY8nrxDrctiIMWnzQmbz/53PYbfH5i93KGM7SvUwWX1Zvfwp5IVwpkLA7ARWGNqDOUQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/file-utils" "1.24.2" + "@zag-js/i18n-utils" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/file-utils@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/file-utils/-/file-utils-1.15.0.tgz#e4cf3c080169cfdf92ade634aecaa7f15bfde7dc" @@ -13710,6 +14072,13 @@ dependencies: "@zag-js/i18n-utils" "1.24.2" +"@zag-js/file-utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/file-utils/-/file-utils-1.24.2.tgz#8d6ef44367d7fd5b9c051ddcd995a555814b94b0" + integrity sha512-feQ9nMOZwuGae2VXyFGbqzb5DEej2Eo498o2KZVHmyA216vVx1ZOQpTsVrrvJV8KRxS6ULoKZNGe5QuHfwBWYg== + dependencies: + "@zag-js/i18n-utils" "1.24.2" + "@zag-js/floating-panel@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/floating-panel/-/floating-panel-1.15.0.tgz#8e763e4c9bb065c6e497ed7705321fcd66864ea3" @@ -13739,6 +14108,20 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/floating-panel@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/floating-panel/-/floating-panel-1.24.2.tgz#632db750d4f60264b7693cabecd69500d6263451" + integrity sha512-pI+YQGsJwGn4lTHx67qVLgeLotP87F5PQToUv6t1CsuVJkmL6fxP3vQNflMSL7XNAX06jVCaug7RW7V+SehHIg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/rect-utils" "1.24.2" + "@zag-js/store" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/focus-trap@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/focus-trap/-/focus-trap-1.15.0.tgz#a00a11121422faca3fa3d4a9a3151a5b63ce2999" @@ -13753,6 +14136,13 @@ dependencies: "@zag-js/dom-query" "1.24.2" +"@zag-js/focus-trap@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/focus-trap/-/focus-trap-1.24.2.tgz#da03d4bf25fd33204d70c9d18b9966204e5613d8" + integrity sha512-ztqxOaB7Z8zOZH4HvHnMpKREhrTAcJICRmHgwx1Dfq5SqymlMBnFD0zwS/F0mlbZqzz9eV9yYLAd1Xy54OdeGw== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-visible@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-1.15.0.tgz#4631d795ac50ffb2e76e50703f13bada44ecde85" @@ -13767,6 +14157,13 @@ dependencies: "@zag-js/dom-query" "1.24.2" +"@zag-js/focus-visible@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-1.24.2.tgz#f9b0316a0081c18860171ec31f457ee174a99393" + integrity sha512-/A8CEy+2w0xCTIbDuCB4nfdBxgVQYP6oaFb1zmYXmf8HWFlSXDxuUXb1oXrA3vaoNG/X8sJJilpacYdV1dVhxg== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/highlight-word@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/highlight-word/-/highlight-word-1.15.0.tgz#0557c020d5718c5f7ccd6f36c98866c1714eb2e1" @@ -13777,6 +14174,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/highlight-word/-/highlight-word-1.24.2.tgz#0e020e359ac0d0867db76de7055a82d158e9ed8b" integrity sha512-5aBL0vp8zQ/v2bNOL9SBMxpwM95Sn/TascpceOG3HU66NfqaJmStg38+UvZNIl51QuKZ4Uo6gauNJkbnYcc+hg== +"@zag-js/highlight-word@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/highlight-word/-/highlight-word-1.24.2.tgz#0e020e359ac0d0867db76de7055a82d158e9ed8b" + integrity sha512-5aBL0vp8zQ/v2bNOL9SBMxpwM95Sn/TascpceOG3HU66NfqaJmStg38+UvZNIl51QuKZ4Uo6gauNJkbnYcc+hg== + "@zag-js/hover-card@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/hover-card/-/hover-card-1.15.0.tgz#510221fd17021a059de01e9f6d625ddb37f3e572" @@ -13803,6 +14205,19 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/hover-card@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/hover-card/-/hover-card-1.24.2.tgz#55a34353a8baa5bd47ce14bbdd8ec06deed21c9b" + integrity sha512-qfdLbprSKr3aiBW3guG7lmCGZ2PuJP99if4dl7TZ3BA/zisr3s2YgZOX2bfmHVtpCjeuhrEE0RPqLZ0iqXFrLQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/i18n-utils@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/i18n-utils/-/i18n-utils-1.15.0.tgz#f094867b101852662258b7c077f5a4869eed0346" @@ -13817,6 +14232,13 @@ dependencies: "@zag-js/dom-query" "1.24.2" +"@zag-js/i18n-utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/i18n-utils/-/i18n-utils-1.24.2.tgz#c01b781fe426a032166c79e1343664be9751c747" + integrity sha512-4Y9w7WDJpfy17SI3Ey9h3FZ448KsfqmFu7BshsWWCPJTGAPvkomZurpiU6CHc1sk+v2YKvUKqnJ2jj9aUu4PGw== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/interact-outside@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/interact-outside/-/interact-outside-1.15.0.tgz#a46e9b461dcf6ba780fcd6560e01fedb8dfffb74" @@ -13838,6 +14260,19 @@ resolved "https://registry.yarnpkg.com/@zag-js/json-tree-utils/-/json-tree-utils-1.24.2.tgz#188b5442b5626e662d479cf2d6446e6fd50908c1" integrity sha512-SFHBmOujTIlG2uVUOaywme2G1N8ych9RJGrUh2CBwRaIIGjc8La9GSMJcx4qIm0UtvMx7I18O2Cx29Zvrc3Bmg== +"@zag-js/interact-outside@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/interact-outside/-/interact-outside-1.24.2.tgz#7125c47e6307ae66e6546791088eaf2ea611e005" + integrity sha512-/DH1b58szQgTqz3fL+cbg51X94DohahPkuCgiGs6wdPK3JwMFlPJHkmU3SDUXQJTpwLOsDIqMVq9sO4jo7fiGg== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/utils" "1.24.2" + +"@zag-js/json-tree-utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/json-tree-utils/-/json-tree-utils-1.24.2.tgz#188b5442b5626e662d479cf2d6446e6fd50908c1" + integrity sha512-SFHBmOujTIlG2uVUOaywme2G1N8ych9RJGrUh2CBwRaIIGjc8La9GSMJcx4qIm0UtvMx7I18O2Cx29Zvrc3Bmg== + "@zag-js/listbox@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/listbox/-/listbox-1.15.0.tgz#e18a8566c4b13de3877e48aeeb7d26b13c1e83fb" @@ -13864,6 +14299,19 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/listbox@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/listbox/-/listbox-1.24.2.tgz#f6cf96c6ebca4d1b6c2c28514fc19c21e53ca009" + integrity sha512-RjXxLfBJ52Kod7vqmsMFOimWD4wV+90dzs6itehnAPxtpIVS5hlXM9USCnDda97DQuyL4Ke2j7+NRJ/ikkgSpg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/collection" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-visible" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/live-region@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/live-region/-/live-region-1.15.0.tgz#8215a57c259da272e8a7630bd75da7028660b0a6" @@ -13874,6 +14322,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/live-region/-/live-region-1.24.2.tgz#4fe4b73b196bc63462708f864f50d9442f868caf" integrity sha512-X5gp7m5/o7VeQ8hI2ffi9nEVkdCDcCw5wtSx9gFww6WeD6HJMkY/4HbTdi7ALvGVg9gNIMr5F9zrrzpvgc9DXg== +"@zag-js/live-region@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/live-region/-/live-region-1.24.2.tgz#4fe4b73b196bc63462708f864f50d9442f868caf" + integrity sha512-X5gp7m5/o7VeQ8hI2ffi9nEVkdCDcCw5wtSx9gFww6WeD6HJMkY/4HbTdi7ALvGVg9gNIMr5F9zrrzpvgc9DXg== + "@zag-js/menu@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/menu/-/menu-1.15.0.tgz#0277c45c912bc45fb3817f74d4c1b8eb90a85471" @@ -13902,6 +14355,20 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/menu@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/menu/-/menu-1.24.2.tgz#096001b42abd514c1ba7e58cae080530abc54651" + integrity sha512-vBsFMcEoXfSyN+v1LlLtP2x4aRabLo41/e7ATWjCL41vYSSEyX0dxkpKh5pvJUZebO2SVoQPJ91WGhBCCW9GPQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/rect-utils" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/number-input@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/number-input/-/number-input-1.15.0.tgz#d50590b0945126c8de95032b5956a04130aa8890" @@ -13926,6 +14393,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/number-input@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/number-input/-/number-input-1.24.2.tgz#b3b3a36c8fe42b67b6569dafc16f663850f2f0c1" + integrity sha512-FkgT0cvkDdT2UmDoKtgEkGeJTMV7PhYSwVfnm4mIf4/f6rzSngbQWDw1pUgeQgtQkwyHnONWX9KcUT4bMSe1Zw== + dependencies: + "@internationalized/number" "3.6.5" + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/pagination@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/pagination/-/pagination-1.15.0.tgz#98c977c11ca36e2b6d1cde5c52480b0dd4be8561" @@ -13948,6 +14427,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/pagination@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/pagination/-/pagination-1.24.2.tgz#93829517e7897c307d1a2dd3f7ad5e17616b63f8" + integrity sha512-ULWw+RyOiJ/2OTCo6STxTwlSXoGbQp2cvNDF0gs6G+hukqlITlNWM0cPpgsRYfuFrtb6B6Lw0C1Z3JYbd8uj5Q== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/password-input@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/password-input/-/password-input-1.15.0.tgz#f644cc89b44b1d1be23f42b31133cd5d8f308f6a" @@ -13970,6 +14460,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/password-input@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/password-input/-/password-input-1.24.2.tgz#ac177147e3f98aa39fedd2e0b4bce11c8af6eedc" + integrity sha512-O/mu3oIp3H5TPG7CKfGhvlYRfoHXQA7d65feKd/iyPAnq6U6/nFYMmrrsLnEZ8FUSXYNGRTZnL5eFUKfph7WRA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/pin-input@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/pin-input/-/pin-input-1.15.0.tgz#6e7ff73c83e1b4b8efd8aeb27f87ee827077ddc1" @@ -13992,6 +14493,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/pin-input@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/pin-input/-/pin-input-1.24.2.tgz#0f42ed19b7015301b794e580249a98a3d0b8caeb" + integrity sha512-SQ8StSG/XrWbZetYuSmUWpmEphSKfmYAUZ5SItE4fd61Q1UbrahUDANU6pKhz01sLTIpga0Mjld6avod07qXCw== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/popover@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/popover/-/popover-1.15.0.tgz#7ab8198c69fceeca19f5d72ec4be47680026af39" @@ -14024,6 +14536,22 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/popover@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/popover/-/popover-1.24.2.tgz#26fb2f71e766d15553a4318c5359b34909c6a975" + integrity sha512-BBMBxjMTfeDHCg0wg8ohStk6MPTrmCa0PuOSNDeK0Mr/0i0Vv0EjsSiXuu5Wt1LS237XV1BIaLxt322dW+RfVg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/aria-hidden" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-trap" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/remove-scroll" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/popper@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/popper/-/popper-1.15.0.tgz#b479d6c1bdd7cf7f57d2cb72a13aa0f10b341f4d" @@ -14042,6 +14570,15 @@ "@zag-js/dom-query" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/popper@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/popper/-/popper-1.24.2.tgz#2462771935e0e3d920fb1e408f756f3da462f75b" + integrity sha512-rimqYBOcM5Aj0AntZFIS2hXv96/QnVASIhFx4GoaiHd3DxadMdJVZ3EsKC1JSNveFEjS/0z7IuuAATTF5x61kQ== + dependencies: + "@floating-ui/dom" "1.7.4" + "@zag-js/dom-query" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/presence@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/presence/-/presence-1.15.0.tgz#3cf4275ffc8766e89a74b73bbac69de9e156a19c" @@ -14060,6 +14597,15 @@ "@zag-js/dom-query" "1.24.2" "@zag-js/types" "1.24.2" +"@zag-js/presence@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/presence/-/presence-1.24.2.tgz#e366bf2a2b9c292ac10a3c14fe8b56ab966edd6c" + integrity sha512-aA1P3pe07cLHnGmMpVyAoBY/e38IQYvqFv3cjLT8B8KVUacjXKnv8AhJf4D/60+XzyZCv79HcTwcFU6EGUrVng== + dependencies: + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/progress@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/progress/-/progress-1.15.0.tgz#ea0618e8a124c4b24c6a143e1f9eacd4eab23e1c" @@ -14082,6 +14628,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/progress@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/progress/-/progress-1.24.2.tgz#f30f8b2221fce3227dcf061581263e15bb00209f" + integrity sha512-8A8Cy7b+EOYxoR0tVXa0RiNBVNVcVtWP875QrA5D1/Hj4KnAkj3W3Ee5NOicpRKkORCELHRy5fRcDTUzbsjeRQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/qr-code@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/qr-code/-/qr-code-1.15.0.tgz#eff4e89c9660b683e1854ed6b7bda61770212e81" @@ -14108,6 +14665,19 @@ proxy-memoize "3.0.1" uqr "0.1.2" +"@zag-js/qr-code@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/qr-code/-/qr-code-1.24.2.tgz#5162be572dd473ed4708fdf201d2da99bc0c1df9" + integrity sha512-CCdZ2Wch1inyR5dLb7Kh8QqnlzY28R2Elv19W+O2Yvkdbkp16widXGXPoHMt9tIWqaMhXwIXkNEp1JmoiGnfhg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + proxy-memoize "3.0.1" + uqr "0.1.2" + "@zag-js/radio-group@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/radio-group/-/radio-group-1.15.0.tgz#559759d34b0ceb346d9bf1a5549c3885bd0caa8a" @@ -14132,6 +14702,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/radio-group@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/radio-group/-/radio-group-1.24.2.tgz#88171e8736e4a1fa2356533adaaf9fb88bc930c0" + integrity sha512-ffPeO+P4RvNoGoM1ZsBoiIwJ4zKXbhm8QuHMdKRl34A4TzipzHC2ppAq1cIZ7q+ZubefH0QtZbsvmz1pAo0Z3w== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-visible" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/rating-group@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/rating-group/-/rating-group-1.15.0.tgz#600d3a7b3f9f201e1793c70db1967b447d939b55" @@ -14154,6 +14736,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/rating-group@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/rating-group/-/rating-group-1.24.2.tgz#e7fcd6914eef6b4c0b7da3b9201e7cc71ed50908" + integrity sha512-ZpKayCuPX7ysiZWv+JlNIWnPtflSJlJ5C9lHozTtFqCvyELp5ZAtoO5EQN5cM+aVJe2RNgNsMQf37Fmdv4XaiA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/react@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/react/-/react-1.15.0.tgz#bd3af56f28dc7c8ac8b652ba7b003fb7d27569f2" @@ -14174,6 +14767,16 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/react@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/react/-/react-1.24.2.tgz#98e1e0af17ae9ff7b0a5c4dee088fac0788fea8e" + integrity sha512-s6wV2gzd7AIndJ7rrkka+M3OAuKqUqak3xRj/Q6fZdtq2fBY5n6DupcQLCkFoj6eJhp8LUfFO70D7Z71Jvk57w== + dependencies: + "@zag-js/core" "1.24.2" + "@zag-js/store" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/rect-utils@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/rect-utils/-/rect-utils-1.15.0.tgz#b6234aa041538311386d0d7c6fc5e6169d00b728" @@ -14184,6 +14787,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/rect-utils/-/rect-utils-1.24.2.tgz#d2f118d49add9ca244ba77be2905a1c1fa4bc826" integrity sha512-Iuk/diClriGtYL2PGhProuZhb7SWJ+8IdyaOUP3fgeLORKEQsLcM0bYp1pUO5K5rf/ZnlzGBnkb3/ewkkO/kFA== +"@zag-js/rect-utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/rect-utils/-/rect-utils-1.24.2.tgz#d2f118d49add9ca244ba77be2905a1c1fa4bc826" + integrity sha512-Iuk/diClriGtYL2PGhProuZhb7SWJ+8IdyaOUP3fgeLORKEQsLcM0bYp1pUO5K5rf/ZnlzGBnkb3/ewkkO/kFA== + "@zag-js/remove-scroll@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/remove-scroll/-/remove-scroll-1.15.0.tgz#05fc4b4202f4c53f0b60d361a4fe061a01725599" @@ -14209,6 +14817,24 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/remove-scroll@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/remove-scroll/-/remove-scroll-1.24.2.tgz#ab1da29745a5a0a8b5da93789ee2e5c50ecb6fab" + integrity sha512-PLXXBw3NxVAfm7MbNnM0TVlv70Kn+xYFEbKxUBWhD0d/qoqsKJV/xS7N4yO5QPZg9UgXMvtRDtINGWoJvyuAlw== + dependencies: + "@zag-js/dom-query" "1.24.2" + +"@zag-js/scroll-area@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/scroll-area/-/scroll-area-1.24.2.tgz#c0d1503d50ca5c5e59b408ad8bcec137b95b96b3" + integrity sha512-upT0IorrklcUwsQSzR9UPMYnqP4lbRTUMT0vJtKpKXboPOUL7Fj8mBffQnL6vNBfD9CAMUBV/hPS6SzUFe71og== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/scroll-snap@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/scroll-snap/-/scroll-snap-1.15.0.tgz#56ea809de7d8a79d62b4df3a2e6aa9cb35cb0141" @@ -14223,6 +14849,13 @@ dependencies: "@zag-js/dom-query" "1.24.2" +"@zag-js/scroll-snap@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/scroll-snap/-/scroll-snap-1.24.2.tgz#72fcff418b3085f41f829828a75a194e04ceef49" + integrity sha512-NPJIOEALQYetbaulHvGjnwlbewmmrEvfP/CcB6I5/YrMJDN73nJi+f1vSymdw4uhxBnKDLnxmPkb+oOpyI0Ceg== + dependencies: + "@zag-js/dom-query" "1.24.2" + "@zag-js/select@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/select/-/select-1.15.0.tgz#28a0fc498779ed4b3a3b391fccf4e9bba2924758" @@ -14251,6 +14884,20 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/select@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/select/-/select-1.24.2.tgz#320dd962bbb545771b342a58ed3eaee11fe059e6" + integrity sha512-1HSr+2XlymRUgtchTTD7c/+Shwiloei7BIzcEXQvYaUzVMTc6yjs2fSpemF76SX0KwiqlNSY5++HV3R1Szkvpg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/collection" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/signature-pad@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/signature-pad/-/signature-pad-1.15.0.tgz#ff479c59eeb5fc62e9dc9d6023c77245ad7a23fc" @@ -14275,6 +14922,18 @@ "@zag-js/utils" "1.24.2" perfect-freehand "^1.2.2" +"@zag-js/signature-pad@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/signature-pad/-/signature-pad-1.24.2.tgz#82111401167edef2f02cffde98f85bc37fc7a915" + integrity sha512-NMb+g6wj06JvW1dES0Rk4Q1PNQQM1Y11MGW+rRX1KMsP8ieH6DdS5BvofOvNhxQ6+/Y8p+XWD0tGxudOTU2j/Q== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + perfect-freehand "^1.2.2" + "@zag-js/slider@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/slider/-/slider-1.15.0.tgz#7e4a5d9506cc5044b4a6e515f8e7dcccc13c0b36" @@ -14297,6 +14956,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/slider@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/slider/-/slider-1.24.2.tgz#8f27de28841412ed3dd6b1ef29dffded92bbc1ad" + integrity sha512-QxdSIRW96lPT/zJS/1pr2aj1rGujXBZ2ypUyb6JYZizFG55cZtmJqjJaZ81jU251peLuTdhug34C52vAvXyFZA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/splitter@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/splitter/-/splitter-1.15.0.tgz#fc619ee9c044b4cd6240abaffaac5a916870f675" @@ -14319,6 +14989,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/splitter@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/splitter/-/splitter-1.24.2.tgz#b743a64233db25489fea105c4efa0695a1c66275" + integrity sha512-EQchsOU4cFOFlBZ3Zql0rmAbUumu/mw0CAg7sjNNUPIDJ7NAOpZSwKtsnfT8pDWeOVIAxTLrlALWW6uY2g4UyQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/steps@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/steps/-/steps-1.15.0.tgz#ead902a5c14ad11b235f33233b41a121c15c567e" @@ -14341,6 +15022,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/steps@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/steps/-/steps-1.24.2.tgz#3ebf31ed4044b231299b4724dd6a966f5995188c" + integrity sha512-nunbmsl4WTkZw6955xeWo9D6XORt1ay2tDsUYHyTJ3rUCeh11Itzmwi7IrJTTSeMtbokYgnF5uoYFHj/pSc7fQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/store@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/store/-/store-1.15.0.tgz#125d64bef0883c00f35b8bef52d35d8a6d2d9206" @@ -14355,6 +15047,13 @@ dependencies: proxy-compare "3.0.1" +"@zag-js/store@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/store/-/store-1.24.2.tgz#364c14219008fa516768957f74fb73404d6022e6" + integrity sha512-PZViq7LD4y+6iEB/0tvbqywoEccNXGy6jcOGIg5lwdY0CiPulPMeoLFi0+Etx1/Wom398NqlzecPqT4ZbICYMQ== + dependencies: + proxy-compare "3.0.1" + "@zag-js/switch@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/switch/-/switch-1.15.0.tgz#6c4f3347ea1cad206fafea87503decab0a031e76" @@ -14379,6 +15078,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/switch@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/switch/-/switch-1.24.2.tgz#3da21ce71f6eea79acc137a79a948987433ca6e8" + integrity sha512-NRyWy43Npzjwc15flHRB6eVqKPeu7uw4pjdfmTjhj87185ZM/VYDEQHC6G+teXvl+4LW19QV9aDcTAzg1g5bbg== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-visible" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/tabs@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/tabs/-/tabs-1.15.0.tgz#da12c410e71bbe99f901e045e1afaf930065efd3" @@ -14401,6 +15112,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/tabs@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/tabs/-/tabs-1.24.2.tgz#d7c1bc901a9f09a32c2cdc0125921f52364ea7e1" + integrity sha512-UCVfUSDlIk+EAL0kgDkWZAj55PSRUXVAhuqqlDnB8t3GO9UsvmxG93US7wEQglpYBydyPqUpDpmpNUs16egd/Q== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/tags-input@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/tags-input/-/tags-input-1.15.0.tgz#ce7707c5374af4d41ee5834c5401718fd584c16d" @@ -14429,6 +15151,20 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/tags-input@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/tags-input/-/tags-input-1.24.2.tgz#edd3972d7adf235417247770efdeb2745f1ad558" + integrity sha512-zl9DffAvHdTY11Qdm4rowG5TMNLCreJ/dOqj4nTxoLV2xYAzVAgejgxKzv0jbB11/GiuT6omcslVcoCh5w/hRw== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/auto-resize" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/interact-outside" "1.24.2" + "@zag-js/live-region" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/time-picker@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/time-picker/-/time-picker-1.15.0.tgz#76817d0c633b53adb0cdc9fa70abfa53d6c0b0ee" @@ -14464,6 +15200,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/timer@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/timer/-/timer-1.24.2.tgz#ec5448b98f46b9d2a3dd3245e4c9006d31908881" + integrity sha512-Hp7cMGZ7UE04VtBWprINZFSX578pyoyiHLLIgSPm9pbUkywVmjL14oKmO64fnOqrdwXSkTnumxo8O0X2P4reoA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/toast@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/toast/-/toast-1.15.0.tgz#6025d650c200e382aa974de6a9133357717148ec" @@ -14488,6 +15235,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/toast@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/toast/-/toast-1.24.2.tgz#1416335cb2041ce03e9a22434eab63ba9438ed70" + integrity sha512-mH9iVrAr8asJZNNSWsrCSmtCfzn/aC64fVU610B/pHOiDY6HN0ANYmC+TgSZ29a3Jlts1OTZiKsygWyBuJQ3Og== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/toggle-group@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/toggle-group/-/toggle-group-1.15.0.tgz#0d796b6f7ed72a67dadddb91ab1d3a7514c227d7" @@ -14510,6 +15269,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/toggle-group@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/toggle-group/-/toggle-group-1.24.2.tgz#16109f7fef0c5e6e29dce988d5df5891b886f4f1" + integrity sha512-71idkWYohYONDD/8FSr/VR3mjFrsTGtsJXBEIGEUfQpgorr+11ryaHbKqq2EMMRLnHs+oyzwegW+laOyvoj0bA== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/toggle@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/toggle/-/toggle-1.15.0.tgz#d2ea526c12611ba165dcc02585a8799bc9d98620" @@ -14532,6 +15302,17 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/toggle@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/toggle/-/toggle-1.24.2.tgz#86012d4b74df5555e68eaca5168e6bfb1e9bd9d4" + integrity sha512-sD8/di9RP4cISkp5tml5qJKTmZSCBOHIP/4gwnm2eVeUaT4LOqqH1dRFZWv/3FIQ719xcIo6LX8we2vKYxYCCw== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/tooltip@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/tooltip/-/tooltip-1.15.0.tgz#8dd4754859df6748c11e165805af2b8e4348114f" @@ -14559,6 +15340,19 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/tooltip@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/tooltip/-/tooltip-1.24.2.tgz#54f1dbd21eb6bcae89de6010e50e7f2d46e84ff3" + integrity sha512-GYjoZkCR9UMPutaJa24LnUrZSWnFfXbuiLoUPlp8Xg+HkVSM25zOe7IgiNgvXqf9shp7dE0AaVnXiuDjjdDm1w== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-visible" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/tour@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/tour/-/tour-1.15.0.tgz#e90990d23df2872eadef592db8d3eff83c03fa82" @@ -14589,6 +15383,21 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/tour@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/tour/-/tour-1.24.2.tgz#c69e9156df5b9ffeebaa182e7a46307a99a37de4" + integrity sha512-lsgTr+A+/AK1ct6CPRAFNBQy/PypkVtBmQSsPXkTcwdWWvd8lYp6uuyHapXABLLrfqSWQI7rHokhr7QknCN90A== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dismissable" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/focus-trap" "1.24.2" + "@zag-js/interact-outside" "1.24.2" + "@zag-js/popper" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/tree-view@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/tree-view/-/tree-view-1.15.0.tgz#774c624cf6847d8be4788b0bd6b6c923a8deecb0" @@ -14613,6 +15422,18 @@ "@zag-js/types" "1.24.2" "@zag-js/utils" "1.24.2" +"@zag-js/tree-view@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/tree-view/-/tree-view-1.24.2.tgz#d561af3f34dae19bf4056404aa22169a9dd94879" + integrity sha512-96P8uOTLKTwO3RzGd5X9ZEVy9JBrIoM5RptiMrk2yqN97HdzXint59A06whCBe7wlQVkJyJK58R0fOKDxjTQaQ== + dependencies: + "@zag-js/anatomy" "1.24.2" + "@zag-js/collection" "1.24.2" + "@zag-js/core" "1.24.2" + "@zag-js/dom-query" "1.24.2" + "@zag-js/types" "1.24.2" + "@zag-js/utils" "1.24.2" + "@zag-js/types@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/types/-/types-1.15.0.tgz#1a9b57a67c14e05fb38fcbabfaf501963066c681" @@ -14627,6 +15448,13 @@ dependencies: csstype "3.1.3" +"@zag-js/types@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/types/-/types-1.24.2.tgz#08f8df3fd4ea54fdfe356658a79d720206399796" + integrity sha512-sXL8JHx8yrj8qGwCl/EhydaHoCCEfYwbg1rPWcCwqrpkvhic0KEZAJZMUhcU4dLdx+Oajbv2QeFz6Fk5U2Nn5A== + dependencies: + csstype "3.1.3" + "@zag-js/utils@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@zag-js/utils/-/utils-1.15.0.tgz#e4d8ffaad1139b8f110b6828f5f43cdd15f4deda" @@ -14637,6 +15465,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/utils/-/utils-1.24.2.tgz#1333186ceedc7a34f234a092ae2a4c7f8c57cea8" integrity sha512-3U8aYXjktpDmQV4M7nAOj7E4x1XSifG7PrbHqJbTYRm7/EPbwCQEEDPckkkWBmj4UrvltbkXPi6nzcP4Qpw5bA== +"@zag-js/utils@1.24.2": + version "1.24.2" + resolved "https://registry.yarnpkg.com/@zag-js/utils/-/utils-1.24.2.tgz#1333186ceedc7a34f234a092ae2a4c7f8c57cea8" + integrity sha512-3U8aYXjktpDmQV4M7nAOj7E4x1XSifG7PrbHqJbTYRm7/EPbwCQEEDPckkkWBmj4UrvltbkXPi6nzcP4Qpw5bA== + "@zip.js/zip.js@^2.7.53": version "2.7.61" resolved "https://registry.yarnpkg.com/@zip.js/zip.js/-/zip.js-2.7.61.tgz#417ba7c6afe2f704d7b5e4f93d14488bc20ab3a6"