Skip to content

Commit 3d1a95e

Browse files
feat(pci-instances): add instance quantity input with form
ref: #TAPC-4680 Signed-off-by: Manon Carbonnel <[email protected]>
1 parent 91c33fe commit 3d1a95e

File tree

5 files changed

+121
-31
lines changed

5 files changed

+121
-31
lines changed
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"pci_instance_creation_name_rule": "Doit uniquement contenir des nombres, lettres, underscores, tirets ou points."
2+
"pci_instance_creation_name_rule": "Doit uniquement contenir des nombres, lettres, underscores, tirets ou points.",
3+
"pci_instances_creation_advanced_parameters": "Paramètres avancés",
4+
"pci_instances_creation_quantity_title": "Choisir une quantité",
5+
"pci_instances_creation_quantity_label": "Nombre d'instances à créer",
6+
"pci_instance_creation_quantity_rule": "Votre quota actuel vous permet de créer simultanément jusqu'à {{ quota }} instance(s) de type {{ type }} pour la région de {{ region }}."
37
}

packages/manager/apps/pci-instances/src/components/cart/Cart.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,26 @@ import { useTranslation } from 'react-i18next';
33
import { useMemo } from 'react';
44
import { GenericCart, TCartProduct } from './genericCart/GenericCart';
55
import { TInstanceCreationForm } from '@/pages/instances/create/CreateInstance.page';
6+
import { TInstance } from '@/types/instance/entity.type';
67

78
export const Cart = () => {
89
const { control } = useFormContext<TInstanceCreationForm>();
9-
const instanceName = useWatch({ control, name: 'name' });
10+
const instance: Partial<TInstance> = {
11+
name: useWatch({ control, name: 'name' }),
12+
quantity: useWatch({ control, name: 'quantity', defaultValue: 1 }),
13+
};
1014
const { t } = useTranslation();
1115

1216
const products: TCartProduct[] = useMemo(
1317
() => [
1418
{
1519
title: t('pci_instances_common_instance_title'),
16-
designation: instanceName,
20+
designation: instance.name,
21+
quantity: instance.quantity,
1722
expanded: true,
1823
},
1924
],
20-
[instanceName, t],
25+
[instance.name, instance.quantity, t],
2126
);
2227

2328
return <GenericCart products={products} />;

packages/manager/apps/pci-instances/src/components/cart/genericCart/GenericCart.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useCallback, useState } from 'react';
1212
export type TCartProduct = {
1313
title: string;
1414
designation?: string;
15+
quantity?: number;
1516
expanded?: boolean;
1617
};
1718

@@ -57,7 +58,11 @@ export const GenericCart = ({ products }: TCartProps) => {
5758
)}
5859
</AccordionTrigger>
5960
<AccordionContent className="bg-[--ods-color-neutral-050] py-5 px-8">
60-
content
61+
{product.quantity && (
62+
<Text className="text-[--ods-color-heading]">
63+
{product.quantity} x
64+
</Text>
65+
)}
6166
</AccordionContent>
6267
</AccordionItem>
6368
))}

packages/manager/apps/pci-instances/src/pages/instances/create/CreateInstance.page.tsx

Lines changed: 101 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,35 @@ import { FC } from 'react';
22
import { useRouteLoaderData } from 'react-router-dom';
33
import { useTranslation } from 'react-i18next';
44
import { TProject } from '@ovh-ux/manager-pci-common';
5-
import { FormField, FormFieldLabel, Input, Text } from '@ovhcloud/ods-react';
6-
import { FormProvider, useForm } from 'react-hook-form';
5+
import {
6+
Input,
7+
Quantity,
8+
QuantityControl,
9+
QuantityInput,
10+
FormField,
11+
FormFieldLabel,
12+
Text,
13+
} from '@ovhcloud/ods-react';
14+
import { FormProvider, useForm, Controller } from 'react-hook-form';
715
import { z } from 'zod';
816
import { zodResolver } from '@hookform/resolvers/zod';
917
import clsx from 'clsx';
1018
import { Breadcrumb } from '@/components/breadcrumb/Breadcrumb.component';
1119
import { Cart } from '@/components/cart/Cart';
1220
import { instanceNameRegex } from '@/constants';
1321

22+
const quantityRules = {
23+
min: 1,
24+
max: 5,
25+
};
26+
1427
export type TInstanceCreationForm = z.infer<typeof instanceCreationSchema>;
1528
export const instanceCreationSchema = z.object({
1629
name: z.string().regex(instanceNameRegex),
30+
quantity: z
31+
.number()
32+
.min(quantityRules.min)
33+
.max(quantityRules.max),
1734
});
1835

1936
const CreateInstance: FC = () => {
@@ -24,12 +41,13 @@ const CreateInstance: FC = () => {
2441
resolver: zodResolver(instanceCreationSchema),
2542
defaultValues: {
2643
name: 'default name to add',
44+
quantity: 1,
2745
},
2846
mode: 'onChange',
2947
});
3048

3149
const {
32-
formState: { errors },
50+
formState: { errors, defaultValues },
3351
register,
3452
} = formMethods;
3553

@@ -52,7 +70,7 @@ const CreateInstance: FC = () => {
5270
<FormProvider {...formMethods}>
5371
<div className="flex gap-6">
5472
<section className="w-2/3">
55-
<section className="mb-9">
73+
<article className="mb-9">
5674
<Text preset="heading-1">
5775
{t('common:pci_instances_common_create_instance')}
5876
</Text>
@@ -61,29 +79,86 @@ const CreateInstance: FC = () => {
6179
eiusmod tempor incididunt ut labore et dolore magna aliqua. (Not
6280
mandatory) Si besoin d’un texte d’introduction... .
6381
</Text>
64-
</section>
65-
<section className="w-1/2">
66-
{/* TODO: Pre-fill name with combination of {flavor_name region_id month day hour minute} */}
67-
<div className="pt-3 pb-4">
68-
<FormField>
69-
<FormFieldLabel>
70-
{t('common:pci_instances_common_instance_name')}
71-
</FormFieldLabel>
72-
<Input
73-
{...register('name')}
74-
invalid={!!errors.name}
75-
className="w-full"
76-
/>
77-
</FormField>
78-
</div>
79-
<Text
80-
className={clsx('text-sm', {
81-
'text-[--ods-color-critical-500]': !!errors.name,
82-
})}
83-
preset="span"
84-
>
85-
{t('creation:pci_instance_creation_name_rule')}
82+
</article>
83+
<section>
84+
<hr className={'h-px my-8 bg-gray-200 border-0 w-full'} />
85+
<Text preset="heading-2">
86+
{t('creation:pci_instances_creation_advanced_parameters')}
87+
</Text>
88+
<Text preset="paragraph">
89+
Lorem ipsum dolor sit amet consectetur. Tempor malesuada sit a
90+
bibendum sit tempus pretium imperdiet
8691
</Text>
92+
<hr className={'h-px my-8 bg-gray-200 border-0 w-full'} />
93+
<article className="flex flex-col w-full mb-8">
94+
{/* TODO: Pre-fill name with combination of {flavor_name region_id month day hour minute} */}
95+
<div className="pt-3 pb-4">
96+
<FormField>
97+
<FormFieldLabel>
98+
{t('common:pci_instances_common_instance_name')}
99+
</FormFieldLabel>
100+
<Input
101+
{...register('name')}
102+
invalid={!!errors.name}
103+
className="w-full"
104+
/>
105+
</FormField>
106+
</div>
107+
<Text
108+
className={clsx('text-sm', {
109+
'text-[--ods-color-critical-500]': !!errors.name,
110+
})}
111+
preset="span"
112+
>
113+
{t('creation:pci_instance_creation_name_rule')}
114+
</Text>
115+
</article>
116+
<hr className={'h-px my-8 bg-gray-200 border-0 w-full'} />
117+
<article className="flex flex-col w-full mb-8">
118+
<Text preset="heading-2">
119+
{t('creation:pci_instances_creation_quantity_title')}
120+
</Text>
121+
<div className="pt-3 pb-4">
122+
<FormField>
123+
<FormFieldLabel>
124+
{t('creation:pci_instances_creation_quantity_label')}
125+
</FormFieldLabel>
126+
<Controller
127+
name="quantity"
128+
render={({ field }) => (
129+
<Quantity
130+
min={quantityRules.min}
131+
max={quantityRules.max}
132+
invalid={!!errors.quantity}
133+
onValueChange={({ value }) =>
134+
field.onChange(Number(value))
135+
}
136+
defaultValue={
137+
defaultValues ? String(defaultValues.quantity) : '1'
138+
}
139+
required
140+
>
141+
<QuantityControl>
142+
<QuantityInput value={field.value} />
143+
</QuantityControl>
144+
</Quantity>
145+
)}
146+
/>
147+
</FormField>
148+
</div>
149+
<Text
150+
className={clsx('text-sm', {
151+
'text-[--ods-color-critical-500]': !!errors.quantity,
152+
})}
153+
preset="span"
154+
>
155+
{t('creation:pci_instance_creation_quantity_rule', {
156+
quota: 5,
157+
type: 'type',
158+
region: 'region',
159+
})}
160+
</Text>
161+
</article>
87162
</section>
88163
</section>
89164
<aside className="min-w-[320px] w-full max-w-[640px]">

packages/manager/apps/pci-instances/src/types/instance/entity.type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ type TInstanceBackup = TBackup;
123123
export type TInstance = {
124124
id: string;
125125
name: string;
126+
quantity: number;
126127
region: TInstanceRegion;
127128
status: TInstanceStatus;
128129
task: TInstanceTaskStatus;

0 commit comments

Comments
 (0)