Skip to content

Commit

Permalink
feat(pci-instances): improve code quality
Browse files Browse the repository at this point in the history
ref:TAPC-1807
Signed-off-by: Frédéric Vilcot <[email protected]>
  • Loading branch information
fredericvilcot committed Oct 1, 2024
1 parent c48aa2e commit 93eef95
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 141 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { http, HttpResponse, JsonBodyType, RequestHandler, delay } from 'msw';
import mockedCatalog from './fullCatalogGenerated.json';
import { http, HttpResponse, JsonBodyType, RequestHandler } from 'msw';

export const catalogHandlers = <T extends JsonBodyType>(
mockedResponsePayload?: T,
Expand All @@ -10,10 +9,3 @@ export const catalogHandlers = <T extends JsonBodyType>(
: HttpResponse.json(mockedResponsePayload);
}),
];

export const browserHandlers: RequestHandler[] = [
http.get('*/cloud/project/:projectId/catalog/instance', async () => {
await delay(2000);
return HttpResponse.json(mockedCatalog);
}),
];
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { vi } from 'vitest';
* These lines allow to:
* - run tests that use 'createStore' curried function
* - clear stores after each test
* Warning: to work with vitest, this file must be under __mocks__ folder in the root project (.)
* Warning: to work with vitest, this file must be under __mocks__ folder in the vitest config's root project (./src)
* */
const {
create: actualCreate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ const sortModels = (models: DeepReadonly<TModel[]>) =>
models
.slice()
.sort((a, b) => {
const aGroup = Number((a.name.match(/[0-9]+/) || [])[0]);
const bGroup = Number((b.name.match(/[0-9]+/) || [])[0]);
const aRank = Number((a.name.match(/-([^-]+)$/) || [])[1]);
const bRank = Number((b.name.match(/-([^-]+)$/) || [])[1]);
const aGroup = Number((/\d+/.exec(a.name) || [])[0]);
const bGroup = Number((/\d+/.exec(b.name) || [])[0]);
const aRank = Number((/-([^-]+)$/.exec(a.name) || [])[1]);
const bRank = Number((/-([^-]+)$/.exec(b.name) || [])[1]);
return aGroup === bGroup ? aRank - bRank : bGroup - aGroup;
})
.sort(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StepComponent, TabsComponent } from '@ovh-ux/manager-react-components';
import clsx from 'clsx';
import { TFunction } from 'i18next';
import { FC, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
Expand All @@ -19,6 +20,7 @@ import { modelSelector, useCatalog } from '@/data/hooks/catalog/useCatalog';
import { Spinner } from '@/components/spinner/Spinner.component';
import {
TModelCategory,
TModelEntity,
TModelPricing,
TPriceInterval,
TStorage,
Expand All @@ -29,30 +31,37 @@ import { useAppStore } from '@/store/hooks/useAppStore';
import { TStep, TStepId } from '@/store/slices/stepper.slice';

const modelStepId: TStepId = 'model';
const validatedStepState: TStep = {
isOpen: true,
const validatedModelStepState: Partial<TStep> = {
isChecked: true,
isLocked: true,
};
const editedStepState: TStep = {
isOpen: true,
const editedModelStepState: Partial<TStep> = {
isChecked: false,
isLocked: false,
};

export const ModelsStep: FC = () => {
const { projectId } = useParams() as { projectId: string };
const { t } = useTranslation(['create', 'stepper']);
const { data, isLoading } = useCatalog(projectId, modelSelector);
const { stepState, modelName, setModelName, updateStep } = useAppStore(
useShallow((state) => ({
stepState: state.stepState(),
modelName: state.modelName(),
setModelName: state.setModelName,
updateStep: state.updateStep,
})),
);
type TTabProps = {
t: TFunction;
category: DeepReadonly<TModelCategory>;
};

type TTabContentProps = TTabProps & {
modelName: string | null;
setModelName: (newName: string) => void;
data?: TModelEntity;
};

type TTabTitleProps = TTabProps & {
isSelected?: boolean;
};

const TabContent: FC<TTabContentProps> = ({
t,
category,
data,
modelName,
setModelName,
}) => {
const getModelPrice = useCallback(
(
pricings: DeepReadonly<TModelPricing[]>,
Expand All @@ -66,30 +75,158 @@ export const ModelsStep: FC = () => {
(name: string) => () => setModelName(name),
[setModelName],
);
return (
<div className="p-6 pt-8">
<OsdsText
color={ODS_THEME_COLOR_INTENT.text}
level={ODS_TEXT_LEVEL.body}
size={ODS_TEXT_SIZE._400}
>
{t(`model_category_${category.name}_description`)}
</OsdsText>
<div className="grid gap-6 pt-10 grid-cols-1 md:grid-cols-2 xl:grid-cols-4">
{data?.models.data
.filter((model) => model.category === category.name)
.map(
({
name,
specifications,
compatibleLocalzone,
compatibleRegion,
isNew,
pricings,
}) => {
const monthlyPrice = getModelPrice(pricings, 'month');
const hourlyPrice = getModelPrice(pricings, 'hour') ?? 0;
return (
<div key={name}>
<FlavorTile
flavorName={name.toUpperCase()}
flavorSpecs={{
ram: specifications.memory.size,
vcores: specifications.cpu.cores,
frequency: specifications.cpu.frequency,
bandwidth: specifications.bandwidth,
gpuModel: specifications.gpu.model,
gpuNumber: specifications.gpu.number,
disk: specifications.storage as TStorage[],
nvme: [],
}}
flavorCompatibility={{
localzone: compatibleLocalzone,
globalzone: compatibleRegion,
}}
isNewFlavor={isNew}
isSelected={modelName === name}
onClick={handleModelTileClick(name)}
flavorPrice={{
hourly: hourlyPrice,
...(monthlyPrice && {
monthly: monthlyPrice,
}),
}}
hasEnoughQuota
/>
</div>
);
},
)}
</div>
</div>
);
};

const TabTitle: FC<TTabTitleProps> = ({ t, category, isSelected }) => (
<OsdsText
breakSpaces={false}
size={ODS_THEME_TYPOGRAPHY_SIZE._600}
color={
isSelected ? ODS_THEME_COLOR_INTENT.text : ODS_THEME_COLOR_INTENT.primary
}
>
<div className="flex items-center gap-4">
<span
className={clsx(isSelected && 'font-bold', 'whitespace-nowrap text-lg')}
>
{t(`model_category_${category.name}_title`)}
</span>
{category.isNew && (
<OsdsChip
color={ODS_THEME_COLOR_INTENT.error}
size={ODS_CHIP_SIZE.sm}
inline
>
<OsdsText
color={ODS_THEME_COLOR_INTENT.promotion}
level={ODS_TEXT_LEVEL.body}
size={ODS_TEXT_SIZE._100}
>
{t('new')}
</OsdsText>
</OsdsChip>
)}
</div>
</OsdsText>
);

export const ModelsStep: FC = () => {
const { projectId } = useParams() as { projectId: string };
const { t } = useTranslation(['create', 'stepper']);
const { data, isLoading } = useCatalog(projectId, modelSelector);
const { stepStateById, modelName, setModelName, updateStep } = useAppStore(
useShallow((state) => ({
stepStateById: state.stepStateById(),
modelName: state.modelName(),
setModelName: state.setModelName,
updateStep: state.updateStep,
})),
);

const handleNextStep = useCallback(
(id: string) => {
updateStep(id as TStepId, validatedStepState);
updateStep(id as TStepId, validatedModelStepState);
},
[updateStep],
);

const handleEditStep = useCallback(
(id: string) => {
updateStep(id as TStepId, editedStepState);
updateStep(id as TStepId, editedModelStepState);
},
[updateStep],
);

const currentStepState = useMemo(() => stepState(modelStepId), [stepState]);
const modelStepState = useMemo(() => stepStateById(modelStepId), [
stepStateById,
]);

const tabTitle = useCallback(
(category: DeepReadonly<TModelCategory>, isSelected?: boolean) => (
<TabTitle isSelected={isSelected} category={category} t={t} />
),
[t],
);

const tabElement = useCallback(
(category: DeepReadonly<TModelCategory>) => (
<TabContent
category={category}
t={t}
data={data}
modelName={modelName}
setModelName={setModelName}
/>
),
[data, modelName, setModelName, t],
);

return (
<div>
<StepComponent
id={modelStepId}
isOpen={!!currentStepState?.isOpen}
isChecked={!!currentStepState?.isChecked}
isLocked={!!currentStepState?.isLocked}
isOpen={!!modelStepState?.isOpen}
isChecked={!!modelStepState?.isChecked}
isLocked={!!modelStepState?.isLocked}
order={1}
title={t('select_template')}
{...(modelName && {
Expand All @@ -110,105 +247,9 @@ export const ModelsStep: FC = () => {
{data && (
<TabsComponent<DeepReadonly<TModelCategory>>
items={data.models.categories as TModelCategory[]}
className="[&:last-child>li]:py-0"
itemKey={({ name }) => name}
titleElement={(category, isSelected) => (
<OsdsText
breakSpaces={false}
size={ODS_THEME_TYPOGRAPHY_SIZE._600}
color={
isSelected
? ODS_THEME_COLOR_INTENT.text
: ODS_THEME_COLOR_INTENT.primary
}
>
<div className="flex items-center gap-4">
<span
className={clsx(
isSelected && 'font-bold',
'whitespace-nowrap text-lg',
)}
>
{t(`model_category_${category.name}_title`)}
</span>
{category.isNew && (
<OsdsChip
color={ODS_THEME_COLOR_INTENT.error}
size={ODS_CHIP_SIZE.sm}
inline
>
<OsdsText
color={ODS_THEME_COLOR_INTENT.promotion}
level={ODS_TEXT_LEVEL.body}
size={ODS_TEXT_SIZE._100}
>
{t('new')}
</OsdsText>
</OsdsChip>
)}
</div>
</OsdsText>
)}
contentElement={(category) => (
<div className="p-6 pt-8">
<OsdsText
color={ODS_THEME_COLOR_INTENT.text}
level={ODS_TEXT_LEVEL.body}
size={ODS_TEXT_SIZE._400}
>
{t(`model_category_${category.name}_description`)}
</OsdsText>
<div className="grid gap-6 pt-10 grid-cols-1 md:grid-cols-2 xl:grid-cols-4">
{data.models.data
.filter((model) => model.category === category.name)
.map(
({
name,
specifications,
compatibleLocalzone,
compatibleRegion,
isNew,
pricings,
}) => {
const monthlyPrice = getModelPrice(pricings, 'month');
const hourlyPrice =
getModelPrice(pricings, 'hour') ?? 0;
return (
<div key={name}>
<FlavorTile
flavorName={name.toUpperCase()}
flavorSpecs={{
ram: specifications.memory.size,
vcores: specifications.cpu.cores,
frequency: specifications.cpu.frequency,
bandwidth: specifications.bandwidth,
gpuModel: specifications.gpu.model,
gpuNumber: specifications.gpu.number,
disk: specifications.storage as TStorage[],
nvme: [],
}}
flavorCompatibility={{
localzone: compatibleLocalzone,
globalzone: compatibleRegion,
}}
isNewFlavor={isNew}
isSelected={modelName === name}
onClick={handleModelTileClick(name)}
flavorPrice={{
hourly: hourlyPrice,
...(monthlyPrice && {
monthly: monthlyPrice,
}),
}}
hasEnoughQuota
/>
</div>
);
},
)}
</div>
</div>
)}
titleElement={tabTitle}
contentElement={tabElement}
/>
)}
</>
Expand Down
Loading

0 comments on commit 93eef95

Please sign in to comment.