From a2ca42739a180acc0842713fcc5c5ae40db7db86 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 7 Nov 2024 11:50:13 +0100 Subject: [PATCH] feat(ai.notebooks): header tabs and dashboard page ref:DATATR-1636, DATATR-1638 Signed-off-by: Arthur Bullet --- .../notebooks/Messages_fr_FR.json | 2 +- .../notebooks/create/Messages_fr_FR.json | 2 +- .../notebooks/notebook/Messages_fr_FR.json | 4 + .../notebook/dashboard/Messages_fr_FR.json | 21 +++ .../src/__tests__/helpers/mocks/label.ts | 6 + .../cli-code-block/CliCodeBlock.component.tsx | 12 +- .../src/components/ui/button.tsx | 3 +- .../src/components/ui/slider.tsx | 3 +- .../data/api/ai/notebook/label/label.api.ts | 16 ++ .../data/api/ai/notebook/label/label.spec.tsx | 36 +++++ .../ai/notebook/label/useEditLabel.hook.tsx | 28 ++++ .../ai/notebook/label/useEditLabel.spec.tsx | 40 +++++ .../{statusHelper.ts => notebookHelper.ts} | 9 ++ .../[notebookId]/Notebook.layout.tsx | 6 +- .../_components/NotebookHeader.component.tsx | 77 +++++++++ .../_components/NotebookTabs.component.tsx | 22 +++ .../[notebookId]/dashboard/Dashboard.page.tsx | 141 ++++++++++++++++ .../_components/Configuration.component.tsx | 66 ++++++++ .../_components/Privacy.component.tsx | 95 +++++++++++ .../_components/Resources.component.tsx | 151 ++++++++++++++++++ .../NotebooksListColumns.component.tsx | 2 +- .../_components/CliEquivalent.component.tsx | 1 + .../pci-ai-notebooks/src/routes/routes.tsx | 9 ++ .../src/types/cloud/project/ai/Label.ts | 2 +- 24 files changed, 743 insertions(+), 11 deletions(-) create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/dashboard/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/label.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.api.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.spec.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.hook.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.spec.tsx rename packages/manager/apps/pci-ai-notebooks/src/lib/{statusHelper.ts => notebookHelper.ts} (72%) create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookHeader.component.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookTabs.component.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/Dashboard.page.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Configuration.component.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Privacy.component.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/Messages_fr_FR.json index 0c243601e95a..7bc5f777d904 100644 --- a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/Messages_fr_FR.json @@ -8,7 +8,7 @@ "tableHeaderPrivacy": "Confidentialité ", "networkSecureTitle": "Privé", "networkPublicTitle": "Public", - "tableHeaderDuration": "Durée de fonctionnement", + "tableHeaderDuration": "Durée", "tableHeaderStatus": "Statut", "tableActionManage": "Gérer", "tableActionStart": "Démarrer", diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/create/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/create/Messages_fr_FR.json index 61c1938c535c..991232f9ec85 100644 --- a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/create/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/create/Messages_fr_FR.json @@ -53,6 +53,6 @@ "cliCode": "Equivalent CLI", "errorGetCommandCli": "Une erreur est survenue lors de la génération de code équivalent pour la CLI", "cliEquivalentModalTitle": "Création d’un notebook équivalent", - "cliEquivalentModalDescription": "Créer le même notebook via la CLI", + "cliEquivalentModalDescription": "Commande CLI", "cliEquivalentModalToastMessage": "Le code a été copié" } diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/Messages_fr_FR.json index afeff6751635..36d7b5cbc248 100644 --- a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/Messages_fr_FR.json @@ -1,4 +1,8 @@ { + "dashboardTab": "Dashboard", + "dataTab": "Données attachées", + "backupTab": "Backup", + "logsTab": "Logs", "deleteNotebookTitle": "Supprimer le notebook", "deleteNotebookDescription": "Etes-vous sur de vouloir supprimer le notebook {{name}} ?", "notebookButtonCancel": "Annuler", diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/dashboard/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/dashboard/Messages_fr_FR.json new file mode 100644 index 000000000000..691aa0179608 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/notebooks/notebook/dashboard/Messages_fr_FR.json @@ -0,0 +1,21 @@ +{ + "dashboardTitle": "Dashboard", + "resourcesTitle": "Ressources", + "powerTitleSection": "Power", + "computeTitleSection": "Compute", + "storageTitleSection": "Stockage", + "gpuMemoryField": "{{ gpu }} x {{ memory }} RAM", + "gcuComputeField": "CPU: {{ cpu }} vCores ", + "memoryField": "RAM: {{ memory }}", + "publicNetworkField": "Réseau public: {{ network }}/s", + "temporaryLocalStorageField": "Stockage local éphémère: {{ storage }} SSD", + "temporaryLocalStorageHelper": "Not that much for now", + "workspaceStorage": "Espace de travail {{ storage }} SSD inclus", + "workspaceStorageHelper": "Nothing for now", + "sliderInfo": "{{ usedStorage }} / {{ totalStorage }}", + "notebookIdLabel": "Id du notebook", + "notebookIdCopyToast": "L'identifiant du notebook a été copié", + "billingLink": "Gérer la facturation", + "supportLink": "Contacter le support", + "deleteNotebookButton": "Supprimer" +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/label.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/label.ts new file mode 100644 index 000000000000..4aad2e74b23b --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/mocks/label.ts @@ -0,0 +1,6 @@ +import * as ai from '@/types/cloud/project/ai'; + +export const mockedLabel: ai.Label = { + name: 'labelName', + value: 'labelValue', +}; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/cli-code-block/CliCodeBlock.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/cli-code-block/CliCodeBlock.component.tsx index a38aceed6c35..5ffb8326e28a 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/components/cli-code-block/CliCodeBlock.component.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/components/cli-code-block/CliCodeBlock.component.tsx @@ -8,9 +8,15 @@ interface CliCodeBlockProps { title: string; code: string; toastMessage?: string; + size?: string; } -const CliCodeBlock = ({ title, code, toastMessage }: CliCodeBlockProps) => { +const CliCodeBlock = ({ + title, + code, + toastMessage, + size, +}: CliCodeBlockProps) => { const { t } = useTranslation('common'); const toast = useToast(); const handleCopyPass = (valueToCopy: string) => { @@ -22,7 +28,7 @@ const CliCodeBlock = ({ title, code, toastMessage }: CliCodeBlockProps) => { return (
-
+

{title}

- +
       
     
-    
-  
+ 
 ))
 Slider.displayName = SliderPrimitive.Root.displayName
 
diff --git a/packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.api.ts b/packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.api.ts
new file mode 100644
index 000000000000..cd10779e8a16
--- /dev/null
+++ b/packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.api.ts
@@ -0,0 +1,16 @@
+import { apiClient } from '@ovh-ux/manager-core-api';
+import { NotebookData } from '@/data/api';
+import * as ai from '@/types/cloud/project/ai';
+
+export interface EditLabelProps extends NotebookData {
+  label: ai.Label;
+}
+
+export const editLabel = async ({
+  projectId,
+  notebookId,
+  label,
+}: EditLabelProps) =>
+  apiClient.v6
+    .put(`/cloud/project/${projectId}/ai/notebook/${notebookId}/label`, label)
+    .then((res) => res.data);
diff --git a/packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.spec.tsx b/packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.spec.tsx
new file mode 100644
index 000000000000..92ebaf77445b
--- /dev/null
+++ b/packages/manager/apps/pci-ai-notebooks/src/data/api/ai/notebook/label/label.spec.tsx
@@ -0,0 +1,36 @@
+import { apiClient } from '@ovh-ux/manager-core-api';
+import { describe, expect, vi } from 'vitest';
+import { editLabel } from './label.api';
+import { mockedLabel } from '@/__tests__/helpers/mocks/label';
+
+vi.mock('@ovh-ux/manager-core-api', () => {
+  const put = vi.fn(() => {
+    return Promise.resolve({ data: null });
+  });
+  return {
+    apiClient: {
+      v6: {
+        put,
+      },
+    },
+  };
+});
+
+describe('label functions', () => {
+  afterEach(() => {
+    vi.clearAllMocks();
+  });
+
+  it('should call editLabel with labelInput', async () => {
+    expect(apiClient.v6.put).not.toHaveBeenCalled();
+    await editLabel({
+      projectId: 'projectId',
+      notebookId: 'notebookId',
+      label: mockedLabel,
+    });
+    expect(apiClient.v6.put).toHaveBeenCalledWith(
+      '/cloud/project/projectId/ai/notebook/notebookId/label',
+      mockedLabel,
+    );
+  });
+});
diff --git a/packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.hook.tsx b/packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.hook.tsx
new file mode 100644
index 000000000000..c7ad2f1213f7
--- /dev/null
+++ b/packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.hook.tsx
@@ -0,0 +1,28 @@
+import { useMutation } from '@tanstack/react-query';
+import { AIError } from '@/data/api';
+import {
+  EditLabelProps,
+  editLabel,
+} from '@/data/api/ai/notebook/label/label.api';
+
+export interface MutateLabelProps {
+  onError: (cause: AIError) => void;
+  onSuccess: () => void;
+}
+
+export function useEditLabel({ onError, onSuccess }: MutateLabelProps) {
+  const mutation = useMutation({
+    mutationFn: (labelInfo: EditLabelProps) => {
+      return editLabel(labelInfo);
+    },
+    onError,
+    onSuccess,
+  });
+
+  return {
+    editLabel: (labelInfo: EditLabelProps) => {
+      return mutation.mutate(labelInfo);
+    },
+    ...mutation,
+  };
+}
diff --git a/packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.spec.tsx b/packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.spec.tsx
new file mode 100644
index 000000000000..55a3b5bde15f
--- /dev/null
+++ b/packages/manager/apps/pci-ai-notebooks/src/hooks/api/ai/notebook/label/useEditLabel.spec.tsx
@@ -0,0 +1,40 @@
+import { renderHook, waitFor } from '@testing-library/react';
+import { vi } from 'vitest';
+import { QueryClientWrapper } from '@/__tests__/helpers/wrappers/QueryClientWrapper';
+import * as labelApi from '@/data/api/ai/notebook/label/label.api';
+import { useEditLabel } from './useEditLabel.hook';
+import { mockedLabel } from '@/__tests__/helpers/mocks/label';
+
+vi.mock('@/data/api/ai/notebook/label/label.api', () => ({
+  editLabel: vi.fn(),
+}));
+
+describe('useEditLabel', () => {
+  it('should call useEditLabel on mutation with data', async () => {
+    const projectId = 'projectId';
+    const notebookId = 'notebookId';
+    const onSuccess = vi.fn();
+    const onError = vi.fn();
+
+    vi.mocked(labelApi.editLabel).mockResolvedValue(mockedLabel);
+    const { result } = renderHook(() => useEditLabel({ onError, onSuccess }), {
+      wrapper: QueryClientWrapper,
+    });
+
+    const editLabelProps = {
+      projectId,
+      notebookId,
+      label: mockedLabel,
+    };
+    result.current.editLabel(editLabelProps);
+
+    await waitFor(() => {
+      expect(labelApi.editLabel).toHaveBeenCalledWith(editLabelProps);
+      expect(onSuccess).toHaveBeenCalledWith(
+        mockedLabel,
+        editLabelProps,
+        undefined,
+      );
+    });
+  });
+});
diff --git a/packages/manager/apps/pci-ai-notebooks/src/lib/statusHelper.ts b/packages/manager/apps/pci-ai-notebooks/src/lib/notebookHelper.ts
similarity index 72%
rename from packages/manager/apps/pci-ai-notebooks/src/lib/statusHelper.ts
rename to packages/manager/apps/pci-ai-notebooks/src/lib/notebookHelper.ts
index 8f58386e1e08..fbd97ae518d0 100644
--- a/packages/manager/apps/pci-ai-notebooks/src/lib/statusHelper.ts
+++ b/packages/manager/apps/pci-ai-notebooks/src/lib/notebookHelper.ts
@@ -13,3 +13,12 @@ export function isDeletingNotebook(
 ) {
   return currentState === ai.notebook.NotebookStateEnum.DELETING;
 }
+
+export const OVH_TAGS_CONFIG = {
+  id: 'ovh/id',
+  type: 'ovh/type',
+};
+
+export function isOvhTags(key: string) {
+  return key === OVH_TAGS_CONFIG.id || key === OVH_TAGS_CONFIG.type;
+}
diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/Notebook.layout.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/Notebook.layout.tsx
index 28be211174d1..bd120e297903 100644
--- a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/Notebook.layout.tsx
+++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/Notebook.layout.tsx
@@ -7,6 +7,8 @@ import { useUserActivityContext } from '@/contexts/UserActivityContext';
 import { getNotebook } from '@/data/api/ai/notebook/notebook.api';
 import { useGetNotebook } from '@/hooks/api/ai/notebook/useGetNotebook.hook';
 import { NotebookLayoutContext } from './Notebook.context';
+import { NotebookHeader } from './_components/NotebookHeader.component';
+import NotebookTabs from './_components/NotebookTabs.component';
 
 interface NotebookLayoutProps {
   params: {
@@ -55,6 +57,7 @@ export default function NotebookLayout() {
   if (!notebook) {
     return (
       <>
+        
         
         Loading your notebook data
       
@@ -67,7 +70,8 @@ export default function NotebookLayout() {
 
   return (
     <>
-      
{notebook.spec.name}
+ +
diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookHeader.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookHeader.component.tsx new file mode 100644 index 000000000000..7e2a0005a9ee --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookHeader.component.tsx @@ -0,0 +1,77 @@ +import { useTranslation } from 'react-i18next'; +import { + NotebookText, + Play, + PlayCircle, + PlayIcon, + Square, + Trash2, +} from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { Skeleton } from '@/components/ui/skeleton'; +import * as ai from '@/types/cloud/project/ai'; +import NotebookStatusBadge from '../../_components/NotebookStatusBadge.component'; +import { Button } from '@/components/ui/button'; + +export const NotebookHeader = ({ + notebook, +}: { + notebook: ai.notebook.Notebook; +}) => { + const { t } = useTranslation('regions'); + return ( +
+
+ +
+
+
+

{notebook.spec.name ?? 'Dashboard'}

+
+ + +
+
+
+ + + {notebook.spec.env.frameworkId} + + + {notebook.spec.env.frameworkVersion} + + + {t(`region_${notebook.spec.region}`)} + + + {notebook.spec.env.editorId} + +
+
+
+ ); +}; + +NotebookHeader.Skeleton = function NotebookHeaderSkeleton() { + return ( +
+ +
+

Dashboard

+
+ + + + +
+
+
+ ); +}; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookTabs.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookTabs.component.tsx new file mode 100644 index 000000000000..bf977ff641a6 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/_components/NotebookTabs.component.tsx @@ -0,0 +1,22 @@ +import { useTranslation } from 'react-i18next'; +import TabsMenu from '@/components/tabs-menu/TabsMenu.component'; +import * as ai from '@/types/cloud/project/ai'; + +interface NotebookTabsProps { + notebook: ai.notebook.Notebook; +} + +const NotebookTabs = ({ notebook }: NotebookTabsProps) => { + const { t } = useTranslation('pci-ai-notebooks/notebooks/notebook'); + + const tabs = [ + { href: '', label: t('dashboardTab'), end: true }, + { href: 'attach-data', label: t('dataTab') }, + { href: 'backup', label: t('backupTab') }, + { href: 'logs', label: t('logsTab') }, + ].filter((tab) => tab); + + return ; +}; + +export default NotebookTabs; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/Dashboard.page.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/Dashboard.page.tsx new file mode 100644 index 000000000000..9104193b0d48 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/Dashboard.page.tsx @@ -0,0 +1,141 @@ +import { useTranslation } from 'react-i18next'; +import { + ArrowRight, + Atom, + Globe2, + Settings2, + TerminalSquare, + UserCheck, +} from 'lucide-react'; +import { useEffect, useMemo, useState } from 'react'; +import { Card, CardContent, CardHeader } from '@/components/ui/card'; +import { useNotebookData } from '../Notebook.context'; +import Resources from './_components/Resources.component'; +import Configurations from './_components/Configuration.component'; +import Privacy from './_components/Privacy.component'; +import { useGetCommand } from '@/hooks/api/ai/notebook/useGetCommand.hook'; +import { getAIApiErrorMessage } from '@/lib/apiHelper'; +import { useToast } from '@/components/ui/use-toast'; +import * as ai from '@/types/cloud/project/ai'; +import CliCodeBlock from '@/components/cli-code-block/CliCodeBlock.component'; +import OvhLink from '@/components/links/OvhLink.component'; + +const Dashboard = () => { + const { notebook, projectId } = useNotebookData(); + const { t } = useTranslation('pci-ai-notebooks/notebooks/notebook/dashboard'); + const { toast } = useToast(); + const [command, setCommand] = useState({}); + + const { getCommand, isPending: isPendingCommand } = useGetCommand({ + onError: (err) => { + toast({ + title: t('errorGetCommandCli'), + variant: 'destructive', + description: getAIApiErrorMessage(err), + }); + }, + onSuccess: (cliCommand) => { + setCommand(cliCommand); + }, + }); + + useEffect(() => { + const filteredVolume: ai.volume.Volume[] = notebook.spec.volumes.filter( + (vol) => vol.dataStore.internal === false, + ); + getCommand({ ...notebook.spec, volumes: filteredVolume }); + }, [notebook]); + + return ( + <> +

{t('dashboardTitle')}

+
+ + +

+ + {t('resourcesTitle')} +

+
+ + + +
+ Cycle de vie + + +

+ + Privacy +

+
+ + + +
+
+
+ + +

+ + Support & Facturation +

+
+ +
+ + {t('billingLink')} + + +
+
+ + {t('supportLink')} + + +
+
+
+ + +

+ + Configuration +

+
+ + + +
+
+ + +

+ + CLI +

+
+ + {command && ( + + )} + +
+ + ); +}; + +export default Dashboard; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Configuration.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Configuration.component.tsx new file mode 100644 index 000000000000..63dbb9967299 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Configuration.component.tsx @@ -0,0 +1,66 @@ +import { Files } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { useNotebookData } from '../../Notebook.context'; +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/use-toast'; +import DeleteNotebook from '../../_components/DeleteNotebook.component'; +import { useGetNotebooks } from '@/hooks/api/ai/notebook/useGetNotebooks.hook'; +import { useModale } from '@/hooks/useModale'; + +const Configurations = () => { + const { notebook, notebookQuery, projectId } = useNotebookData(); + const { t } = useTranslation('pci-ai-notebooks/notebooks/notebook/dashboard'); + const navigate = useNavigate(); + const toast = useToast(); + const deleteModale = useModale('delete'); + + const getNotebooksQuery = useGetNotebooks(projectId, { + enabled: false, + }); + + return ( +
+
+
+

{t('notebookIdLabel')}

+

{notebook.id}

+
+ +
+ + { + deleteModale.close(); + notebookQuery.refetch(); + getNotebooksQuery.refetch(); + navigate(`../../`); + }} + /> +
+ ); +}; + +export default Configurations; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Privacy.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Privacy.component.tsx new file mode 100644 index 000000000000..5e96d2ea7641 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Privacy.component.tsx @@ -0,0 +1,95 @@ +import { Plus, ShieldAlert, ShieldCheck, X } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { useNotebookData } from '../../Notebook.context'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { isOvhTags } from '@/lib/notebookHelper'; +import { useEditLabel } from '@/hooks/api/ai/notebook/label/useEditLabel.hook'; +import { useToast } from '@/components/ui/use-toast'; +import { getAIApiErrorMessage } from '@/lib/apiHelper'; + +const Privacy = () => { + const { notebook, notebookQuery, projectId } = useNotebookData(); + const { t } = useTranslation('pci-ai-notebooks/notebooks/notebook/dashboard'); + const toast = useToast(); + + const { editLabel, isPending } = useEditLabel({ + onError: (err) => { + toast.toast({ + title: t('notebookToastErrorTitle'), + variant: 'destructive', + description: getAIApiErrorMessage(err), + }); + }, + onSuccess: () => { + toast.toast({ + title: t('notebookToastSuccessTitle'), + description: t('deleteNotebookSuccess'), + }); + notebookQuery.refetch(); + }, + }); + + const handleDeleteLabel = (key: string) => { + editLabel({ + projectId, + notebookId: notebook.id, + label: { + name: key, + }, + }); + }; + + return ( +
+
+
Tags
+ +
+
+ {notebook.spec.labels && + Object.entries(notebook.spec.labels).map(([key, value]) => ( + +
+ + {key} = {value} + + {!isOvhTags(key) && ( + + )} +
+
+ ))} +
+
+ {notebook.spec.unsecureHttp ? ( +
+
Accès publique
+ +
+ ) : ( +
+
Accès privé
+ +
+ )} +
+
+ ); +}; + +export default Privacy; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx new file mode 100644 index 000000000000..ebb72f05e129 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/[notebookId]/dashboard/_components/Resources.component.tsx @@ -0,0 +1,151 @@ +import { Cpu, HardDrive, HelpCircle, MemoryStick, Zap } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import { useNotebookData } from '../../Notebook.context'; +import { bytesConverter } from '@/lib/bytesHelper'; +import { Slider } from '@/components/ui/slider'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; + +const Resources = () => { + const { notebook } = useNotebookData(); + const { t } = useTranslation('pci-ai-notebooks/notebooks/notebook/dashboard'); + return ( +
+ {notebook.spec.resources.gpu > 0 ? ( +
+
+
Power
+ +
+ + {`${notebook.spec.resources.gpu} x ${notebook.spec.resources.flavor}`} + + + {`${notebook.spec.resources.gpu} x ${notebook.spec.resources.gpuModel}`} + + + {t('gpuMemoryField', { + gpu: notebook.spec.resources.gpu, + memory: bytesConverter( + notebook.spec.resources.gpuMemory, + false, + 0, + ), + })} + +
+ ) : ( +
+
+
{t('powerTitleSection')}
+ +
+ + {`${notebook.spec.resources.cpu} x ${notebook.spec.resources.flavor}`} + + {`${notebook.spec.resources.cpu} x INTEL CPU VCORES`} +
+ )} +
+
+
{t('computeTitleSection')}
+ +
+ {notebook.spec.resources.gpu > 0 && ( + + {t('gcuComputeField', { + cpu: notebook.spec.resources.cpu, + })} + + )} + + {t('memoryField', { + memory: bytesConverter(notebook.spec.resources.memory, false, 0), + })} + + + {t('publicNetworkField', { + network: bytesConverter( + notebook.spec.resources.publicNetwork, + true, + 2, + ), + })} + + +
+
{t('storageTitleSection')}
+ +
+
+ + {t('temporaryLocalStorageField', { + storage: bytesConverter( + notebook.spec.resources.ephemeralStorage, + false, + 0, + ), + })} + + + + + + +

{t('temporaryLocalStorageHelper')}

+
+
+
+
+
+ + {t('workspaceStorage', { + storage: bytesConverter( + notebook.status.workspace.storageFree, + false, + 1, + ), + })} + + + + + + +

{t('workspaceStorageHelper')}

+
+
+
+
+ + + {t('sliderInfo', { + usedStorage: bytesConverter( + notebook.status.workspace.storageUsed, + false, + 1, + ), + totalStorage: bytesConverter( + notebook.status.workspace.storageFree, + false, + 1, + ), + })} + +
+
+ ); +}; + +export default Resources; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/_components/NotebooksListColumns.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/_components/NotebooksListColumns.component.tsx index fbb0b68c22e9..0b7854c5a76b 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/_components/NotebooksListColumns.component.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/_components/NotebooksListColumns.component.tsx @@ -23,7 +23,7 @@ import { SortableHeader } from '@/components/ui/data-table'; import Link from '@/components/links/Link.component'; import { convertSecondsToTimeString } from '@/lib/durationHelper'; import NotebookStatusBadge from './NotebookStatusBadge.component'; -import { isDeletingNotebook, isRunningNotebook } from '@/lib/statusHelper'; +import { isDeletingNotebook, isRunningNotebook } from '@/lib/notebookHelper'; interface NotebooksListColumnsProps { onStartClicked: (notebook: ai.notebook.Notebook) => void; diff --git a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/CliEquivalent.component.tsx b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/CliEquivalent.component.tsx index 03ce10313f1b..b9e8ccd930f4 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/CliEquivalent.component.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/pages/notebooks/create/_components/CliEquivalent.component.tsx @@ -28,6 +28,7 @@ const CliEquivalent = ({ command, controller }: CliEquivalentModalProps) => { title={t('cliEquivalentModalDescription')} code={command.command} toastMessage={t('cliEquivalentModalToastMessage')} + size="max-h-[60vh] px-6" /> diff --git a/packages/manager/apps/pci-ai-notebooks/src/routes/routes.tsx b/packages/manager/apps/pci-ai-notebooks/src/routes/routes.tsx index 8164e37b43f1..fdd624492653 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/routes/routes.tsx +++ b/packages/manager/apps/pci-ai-notebooks/src/routes/routes.tsx @@ -42,6 +42,15 @@ export default [ ...lazyRouteConfig(() => import('@/pages/notebooks/[notebookId]/Notebook.layout'), ), + children: [ + { + path: '', + id: 'notebook.dashboard', + ...lazyRouteConfig(() => + import('@/pages/notebooks/[notebookId]/dashboard/Dashboard.page'), + ), + }, + ], }, ], }, diff --git a/packages/manager/apps/pci-ai-notebooks/src/types/cloud/project/ai/Label.ts b/packages/manager/apps/pci-ai-notebooks/src/types/cloud/project/ai/Label.ts index d05f8a7a9f85..23ec8bb75de8 100644 --- a/packages/manager/apps/pci-ai-notebooks/src/types/cloud/project/ai/Label.ts +++ b/packages/manager/apps/pci-ai-notebooks/src/types/cloud/project/ai/Label.ts @@ -3,5 +3,5 @@ export interface Label { /** Name of the label to update/add */ name: string; /** Value of the label to update/add, is there is no value the label is deleted */ - value: string; + value?: string; }