mirror of
https://github.com/Telecominfraproject/wlan-cloud-owprov-ui.git
synced 2025-11-03 03:58:07 +00:00
Merge pull request #137 from stephb9959/main
[WIFI-10916] Added icons field to passpoint config
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "wlan-cloud-owprov-ui",
|
"name": "wlan-cloud-owprov-ui",
|
||||||
"version": "2.7.0(20)",
|
"version": "2.7.1(1)",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "wlan-cloud-owprov-ui",
|
"name": "wlan-cloud-owprov-ui",
|
||||||
"version": "2.7.0(20)",
|
"version": "2.7.1(1)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/icons": "^1.1.1",
|
"@chakra-ui/icons": "^1.1.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "wlan-cloud-owprov-ui",
|
"name": "wlan-cloud-owprov-ui",
|
||||||
"version": "2.7.0(20)",
|
"version": "2.7.1(1)",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.tsx",
|
"main": "index.tsx",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -510,6 +510,7 @@
|
|||||||
"invalid_file_content": "Ungültiger Dateiinhalt, bitte bestätigen Sie, dass es sich um ein gültiges Format handelt",
|
"invalid_file_content": "Ungültiger Dateiinhalt, bitte bestätigen Sie, dass es sich um ein gültiges Format handelt",
|
||||||
"invalid_fqdn_host": "Ungültiger FQDN-Hostname",
|
"invalid_fqdn_host": "Ungültiger FQDN-Hostname",
|
||||||
"invalid_hostname": "Ungültiger Hostname: Er darf nur aus alphanumerischen Zeichen und Bindestrichen bestehen",
|
"invalid_hostname": "Ungültiger Hostname: Er darf nur aus alphanumerischen Zeichen und Bindestrichen bestehen",
|
||||||
|
"invalid_icon_lang": "Ungültige Sprache, sollte aus 3 Buchstaben bestehen (eng, fre, ger, ita usw.)",
|
||||||
"invalid_ieee": "Für dieses Verschlüsselungsprotokoll muss ieee80211w entweder „optional“ oder „erforderlich“ sein.",
|
"invalid_ieee": "Für dieses Verschlüsselungsprotokoll muss ieee80211w entweder „optional“ oder „erforderlich“ sein.",
|
||||||
"invalid_interfaces": "Ungültige Schnittstellen-JSON-Zeichenfolge. Bitte bestätigen Sie, dass Ihr Wert gültiges JSON ist und Schnittstellen als einzigen Schlüssel hat und dass der Schnittstellenwert ein Array ist. Beispiel: {\"interfaces\": []}",
|
"invalid_interfaces": "Ungültige Schnittstellen-JSON-Zeichenfolge. Bitte bestätigen Sie, dass Ihr Wert gültiges JSON ist und Schnittstellen als einzigen Schlüssel hat und dass der Schnittstellenwert ein Array ist. Beispiel: {\"interfaces\": []}",
|
||||||
"invalid_ipv4": "Ungültige IPv4-Adresse (Bsp.: 192.168.0.1 oder 192.168.0.1/16",
|
"invalid_ipv4": "Ungültige IPv4-Adresse (Bsp.: 192.168.0.1 oder 192.168.0.1/16",
|
||||||
|
|||||||
@@ -510,6 +510,7 @@
|
|||||||
"invalid_file_content": "Invalid file content, please confirm that it is of the valid format",
|
"invalid_file_content": "Invalid file content, please confirm that it is of the valid format",
|
||||||
"invalid_fqdn_host": "Invalid FQDN hostname",
|
"invalid_fqdn_host": "Invalid FQDN hostname",
|
||||||
"invalid_hostname": "Invalid hostname: it needs to be composed of alphanumeric characters and dashes only",
|
"invalid_hostname": "Invalid hostname: it needs to be composed of alphanumeric characters and dashes only",
|
||||||
|
"invalid_icon_lang": "Invalid language, it should be in a 3-letter format (eng, fre, ger, ita, etc.)",
|
||||||
"invalid_ieee": "For this encryption protocol, ieee80211w needs to be either 'optional' or 'required'",
|
"invalid_ieee": "For this encryption protocol, ieee80211w needs to be either 'optional' or 'required'",
|
||||||
"invalid_interfaces": "Invalid Interfaces JSON string. Please confirm that your value is: valid JSON and has interfaces as its only key and that the interfaces value is an array. Example: {\"interfaces\": []}",
|
"invalid_interfaces": "Invalid Interfaces JSON string. Please confirm that your value is: valid JSON and has interfaces as its only key and that the interfaces value is an array. Example: {\"interfaces\": []}",
|
||||||
"invalid_ipv4": "Invalid IPv4 address (ex.: 192.168.0.1 or 192.168.0.1/16",
|
"invalid_ipv4": "Invalid IPv4 address (ex.: 192.168.0.1 or 192.168.0.1/16",
|
||||||
|
|||||||
@@ -510,6 +510,7 @@
|
|||||||
"invalid_file_content": "Contenido de archivo no válido, confirme que tiene un formato válido",
|
"invalid_file_content": "Contenido de archivo no válido, confirme que tiene un formato válido",
|
||||||
"invalid_fqdn_host": "Nombre de host FQDN no válido",
|
"invalid_fqdn_host": "Nombre de host FQDN no válido",
|
||||||
"invalid_hostname": "Nombre de host no válido: debe estar compuesto solo de caracteres alfanuméricos y guiones",
|
"invalid_hostname": "Nombre de host no válido: debe estar compuesto solo de caracteres alfanuméricos y guiones",
|
||||||
|
"invalid_icon_lang": "Idioma no válido, debe estar en un formato de 3 letras (eng, fre, ger, ita, etc.)",
|
||||||
"invalid_ieee": "Para este protocolo de encriptación, ieee80211w debe ser 'opcional' u 'requerido'",
|
"invalid_ieee": "Para este protocolo de encriptación, ieee80211w debe ser 'opcional' u 'requerido'",
|
||||||
"invalid_interfaces": "Cadena JSON de interfaces no válida. Confirme que su valor es: JSON válido y tiene interfaces como su única clave y que el valor de las interfaces es una matriz. Ejemplo: {\"interfaces\": []}",
|
"invalid_interfaces": "Cadena JSON de interfaces no válida. Confirme que su valor es: JSON válido y tiene interfaces como su única clave y que el valor de las interfaces es una matriz. Ejemplo: {\"interfaces\": []}",
|
||||||
"invalid_ipv4": "Dirección IPv4 no válida (ej.: 192.168.0.1 o 192.168.0.1/16",
|
"invalid_ipv4": "Dirección IPv4 no válida (ej.: 192.168.0.1 o 192.168.0.1/16",
|
||||||
|
|||||||
@@ -510,6 +510,7 @@
|
|||||||
"invalid_file_content": "Contenu de fichier non valide, veuillez confirmer qu'il est au format valide",
|
"invalid_file_content": "Contenu de fichier non valide, veuillez confirmer qu'il est au format valide",
|
||||||
"invalid_fqdn_host": "Nom d'hôte FQDN non valide",
|
"invalid_fqdn_host": "Nom d'hôte FQDN non valide",
|
||||||
"invalid_hostname": "Nom d'hôte non valide : il doit être composé uniquement de caractères alphanumériques et de tirets",
|
"invalid_hostname": "Nom d'hôte non valide : il doit être composé uniquement de caractères alphanumériques et de tirets",
|
||||||
|
"invalid_icon_lang": "Langue non valide, elle doit être dans un format à 3 lettres (eng, fre, ger, ita, etc.)",
|
||||||
"invalid_ieee": "Pour ce protocole de cryptage, ieee80211w doit être soit 'facultatif' soit 'obligatoire'",
|
"invalid_ieee": "Pour ce protocole de cryptage, ieee80211w doit être soit 'facultatif' soit 'obligatoire'",
|
||||||
"invalid_interfaces": "Chaîne JSON d'interfaces non valide. Veuillez confirmer que votre valeur est : JSON valide et a des interfaces comme seule clé et que la valeur des interfaces est un tableau. Exemple : {\"interfaces\": []}",
|
"invalid_interfaces": "Chaîne JSON d'interfaces non valide. Veuillez confirmer que votre valeur est : JSON valide et a des interfaces comme seule clé et que la valeur des interfaces est un tableau. Exemple : {\"interfaces\": []}",
|
||||||
"invalid_ipv4": "Adresse IPv4 invalide (ex. : 192.168.0.1 ou 192.168.0.1/16",
|
"invalid_ipv4": "Adresse IPv4 invalide (ex. : 192.168.0.1 ou 192.168.0.1/16",
|
||||||
|
|||||||
@@ -510,6 +510,7 @@
|
|||||||
"invalid_file_content": "Conteúdo de arquivo inválido. Confirme se está no formato válido",
|
"invalid_file_content": "Conteúdo de arquivo inválido. Confirme se está no formato válido",
|
||||||
"invalid_fqdn_host": "Nome de host FQDN inválido",
|
"invalid_fqdn_host": "Nome de host FQDN inválido",
|
||||||
"invalid_hostname": "Nome de host inválido: precisa ser composto apenas de caracteres alfanuméricos e traços",
|
"invalid_hostname": "Nome de host inválido: precisa ser composto apenas de caracteres alfanuméricos e traços",
|
||||||
|
"invalid_icon_lang": "Idioma inválido, deve estar em formato de 3 letras (eng, fre, ger, ita, etc.)",
|
||||||
"invalid_ieee": "Para este protocolo de criptografia, ieee80211w precisa ser 'opcional' ou 'obrigatório'",
|
"invalid_ieee": "Para este protocolo de criptografia, ieee80211w precisa ser 'opcional' ou 'obrigatório'",
|
||||||
"invalid_interfaces": "Sequência JSON de interfaces inválida. Confirme se seu valor é: JSON válido e tem interfaces como sua única chave e que o valor de interfaces é uma matriz. Exemplo: {\"interfaces\": []}",
|
"invalid_interfaces": "Sequência JSON de interfaces inválida. Confirme se seu valor é: JSON válido e tem interfaces como sua única chave e que o valor de interfaces é uma matriz. Exemplo: {\"interfaces\": []}",
|
||||||
"invalid_ipv4": "Endereço IPv4 inválido (ex.: 192.168.0.1 ou 192.168.0.1/16",
|
"invalid_ipv4": "Endereço IPv4 inválido (ex.: 192.168.0.1 ou 192.168.0.1/16",
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const NotesTable = ({ name, isDisabled }) => {
|
|||||||
</InputGroup>
|
</InputGroup>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={notes.sort((a, b) => b.created - a.created)}
|
data={notes?.sort((a, b) => b.created - a.created)}
|
||||||
obj={t('common.notes')}
|
obj={t('common.notes')}
|
||||||
minHeight="200px"
|
minHeight="200px"
|
||||||
/>
|
/>
|
||||||
|
|||||||
80
src/components/FormFields/ImageField/Input.tsx
Normal file
80
src/components/FormFields/ImageField/Input.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/* eslint-disable react/no-unused-prop-types */
|
||||||
|
/* eslint-disable react/destructuring-assignment */
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Box, FormControl, FormLabel, Image, Input } from '@chakra-ui/react';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
name: string;
|
||||||
|
height?: number;
|
||||||
|
width?: number;
|
||||||
|
typeName: string;
|
||||||
|
typeValue: string;
|
||||||
|
label?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
displayImage?: boolean;
|
||||||
|
isRequired?: boolean;
|
||||||
|
emptyIsUndefined?: boolean;
|
||||||
|
isHidden?: boolean;
|
||||||
|
definitionKey?: string;
|
||||||
|
hideLabel?: boolean;
|
||||||
|
value?: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
onTypeChange: (fileType: string) => void;
|
||||||
|
};
|
||||||
|
const ImageFieldInput = (props: Props) => {
|
||||||
|
const [fileKey, setFileKey] = React.useState(uuid());
|
||||||
|
|
||||||
|
let fileReader: FileReader | undefined;
|
||||||
|
|
||||||
|
const handleStringFileRead = () => {
|
||||||
|
if (fileReader) {
|
||||||
|
const content = fileReader.result;
|
||||||
|
if (content && typeof content === 'string') {
|
||||||
|
const split = content.split('base64,');
|
||||||
|
if (split[1]) {
|
||||||
|
props.onChange(split[1] as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files ? e.target.files[0] : undefined;
|
||||||
|
if (file) {
|
||||||
|
props.onTypeChange(file.type);
|
||||||
|
fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = handleStringFileRead;
|
||||||
|
fileReader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (props.value === '') setFileKey(uuid());
|
||||||
|
}, [props.value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box mb={2}>
|
||||||
|
<FormControl hidden={props.isHidden} isDisabled={props.isDisabled} w="50%">
|
||||||
|
<FormLabel>{props.label ?? props.name}</FormLabel>
|
||||||
|
<Input borderRadius="15px" pt={1} fontSize="sm" type="file" onChange={changeFile} key={fileKey} />
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2}>
|
||||||
|
{props.value && (
|
||||||
|
<Image
|
||||||
|
height={props.height !== undefined ? `${props.height}px` : 200}
|
||||||
|
width={props.width !== undefined ? `${props.width}px` : 200}
|
||||||
|
ml="auto"
|
||||||
|
mr="auto"
|
||||||
|
src={`data:${props.typeValue ?? 'image/png'};base64,${props.value}`}
|
||||||
|
alt="New Image"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(ImageFieldInput);
|
||||||
56
src/components/FormFields/ImageField/index.tsx
Normal file
56
src/components/FormFields/ImageField/index.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/* eslint-disable react/destructuring-assignment */
|
||||||
|
import useFastField from 'hooks/useFastField';
|
||||||
|
import * as React from 'react';
|
||||||
|
import ImageFieldInput from './Input';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
name: string;
|
||||||
|
typeName: string;
|
||||||
|
heightName: string;
|
||||||
|
widthName: string;
|
||||||
|
label?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
displayImage?: boolean;
|
||||||
|
isRequired?: boolean;
|
||||||
|
emptyIsUndefined?: boolean;
|
||||||
|
isHidden?: boolean;
|
||||||
|
definitionKey?: string;
|
||||||
|
hideLabel?: boolean;
|
||||||
|
};
|
||||||
|
const ImageField = (props: Props) => {
|
||||||
|
const image = useFastField<string | undefined>({
|
||||||
|
name: props.name,
|
||||||
|
});
|
||||||
|
const imageType = useFastField<string | undefined>({
|
||||||
|
name: props.typeName,
|
||||||
|
});
|
||||||
|
const height = useFastField<number | undefined>({
|
||||||
|
name: props.heightName,
|
||||||
|
});
|
||||||
|
const width = useFastField<string | undefined>({
|
||||||
|
name: props.widthName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onChange = (value: string) => {
|
||||||
|
image.onChange(value);
|
||||||
|
};
|
||||||
|
const onTypeChange = (fileType: string) => {
|
||||||
|
if (props.typeName) {
|
||||||
|
imageType.onChange(fileType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ImageFieldInput
|
||||||
|
{...props}
|
||||||
|
value={image.value}
|
||||||
|
onChange={onChange}
|
||||||
|
onTypeChange={onTypeChange}
|
||||||
|
typeValue={imageType.value}
|
||||||
|
height={height.value}
|
||||||
|
width={width.value}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(ImageField);
|
||||||
@@ -122,7 +122,7 @@ const _NotesField: React.FC<NotesFieldProps> = ({ name = 'notes', isDisabled, ha
|
|||||||
</InputGroup>
|
</InputGroup>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={notes.sort((a: Note, b: Note) => b.created - a.created)}
|
data={notes?.sort((a: Note, b: Note) => b.created - a.created)}
|
||||||
obj={hasDeleteButton ? undefined : t('common.notes')}
|
obj={hasDeleteButton ? undefined : t('common.notes')}
|
||||||
minHeight="200px"
|
minHeight="200px"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Flex, Heading, SimpleGrid } from '@chakra-ui/react';
|
import { Flex, Heading, Image, NumberInputField, SimpleGrid } from '@chakra-ui/react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import DisplayNumberField from 'components/DisplayFields/DisplayNumberField';
|
import DisplayNumberField from 'components/DisplayFields/DisplayNumberField';
|
||||||
|
import DisplayObjectArrayField from 'components/DisplayFields/DisplayObjectArrayField';
|
||||||
import DisplaySelectField from 'components/DisplayFields/DisplaySelectField';
|
import DisplaySelectField from 'components/DisplayFields/DisplaySelectField';
|
||||||
import DisplayStringField from 'components/DisplayFields/DisplayStringField';
|
import DisplayStringField from 'components/DisplayFields/DisplayStringField';
|
||||||
import DisplayToggleField from 'components/DisplayFields/DisplayToggleField';
|
import DisplayToggleField from 'components/DisplayFields/DisplayToggleField';
|
||||||
import FastCreatableSelectInput from 'components/FormFields/CreatableSelectField/FastCreatableSelectInput';
|
import FastCreatableSelectInput from 'components/FormFields/CreatableSelectField/FastCreatableSelectInput';
|
||||||
|
import ImageField from 'components/FormFields/ImageField';
|
||||||
|
import StringField from 'components/FormFields/StringField';
|
||||||
|
import NumberField from 'components/FormFields/NumberField';
|
||||||
|
import { INTERFACE_PASSPOINT_ICONS_SCHEMA } from '../../interfacesConstants';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
data: PropTypes.instanceOf(Object).isRequired,
|
data: PropTypes.instanceOf(Object).isRequired,
|
||||||
@@ -20,6 +25,66 @@ const LockedPasspoint = ({ data }) => {
|
|||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const iconCell = React.useCallback(
|
||||||
|
(src, fileType) => (
|
||||||
|
<Image boxSize={100} mx="auto" my="auto" src={`data:${fileType ?? 'image/png'};base64,${src}`} alt="New Image" />
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const iconFields = React.useMemo(
|
||||||
|
() => (
|
||||||
|
<>
|
||||||
|
<SimpleGrid minChildWidth="180px" gap={4} mb={4}>
|
||||||
|
<NumberInputField name="width" label="width" w="140px" emptyIsUndefined isRequired unit="px" />
|
||||||
|
<NumberField name="height" label="height" w="140px" isRequired unit="px" />
|
||||||
|
<StringField name="language" label="language" w="100px" isRequired />
|
||||||
|
</SimpleGrid>
|
||||||
|
<ImageField name="icon" heightName="height" widthName="width" typeName="type" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const iconCols = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'icon',
|
||||||
|
Header: 'icon',
|
||||||
|
Footer: '',
|
||||||
|
Cell: ({ cell }) => iconCell(cell.row.original.icon, cell.row.original.type),
|
||||||
|
accessor: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'type',
|
||||||
|
Header: 'type',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'type',
|
||||||
|
customWidth: '100px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'width',
|
||||||
|
Header: 'width',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'width',
|
||||||
|
customWidth: '150px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'height',
|
||||||
|
Header: 'height',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'height',
|
||||||
|
customWidth: '100px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'language',
|
||||||
|
Header: 'language',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'language',
|
||||||
|
customWidth: '100px',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -89,6 +154,15 @@ const LockedPasspoint = ({ data }) => {
|
|||||||
emptyIsUndefined
|
emptyIsUndefined
|
||||||
/>
|
/>
|
||||||
<FastCreatableSelectInput {...fieldProps('connection-capability')} />
|
<FastCreatableSelectInput {...fieldProps('connection-capability')} />
|
||||||
|
<DisplayObjectArrayField
|
||||||
|
{...fieldProps('icons')}
|
||||||
|
fields={iconFields}
|
||||||
|
columns={iconCols}
|
||||||
|
schema={INTERFACE_PASSPOINT_ICONS_SCHEMA}
|
||||||
|
isDisabled
|
||||||
|
emptyIsUndefined
|
||||||
|
isRequired
|
||||||
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Heading, SimpleGrid, Switch, Text } from '@chakra-ui/react';
|
import { Heading, Image, SimpleGrid, Switch, Text } from '@chakra-ui/react';
|
||||||
import ToggleField from 'components/FormFields/ToggleField';
|
import ToggleField from 'components/FormFields/ToggleField';
|
||||||
import CreatableSelectField from 'components/FormFields/CreatableSelectField';
|
import CreatableSelectField from 'components/FormFields/CreatableSelectField';
|
||||||
import NumberField from 'components/FormFields/NumberField';
|
import NumberField from 'components/FormFields/NumberField';
|
||||||
import SelectField from 'components/FormFields/SelectField';
|
import SelectField from 'components/FormFields/SelectField';
|
||||||
import StringField from 'components/FormFields/StringField';
|
import StringField from 'components/FormFields/StringField';
|
||||||
|
import ObjectArrayFieldModal from 'components/FormFields/ObjectArrayFieldModal';
|
||||||
|
import ImageField from 'components/FormFields/ImageField';
|
||||||
|
import { INTERFACE_PASSPOINT_ICONS_SCHEMA } from '../../../interfacesConstants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
@@ -23,6 +26,77 @@ const PassPointForm: React.FC<Props> = ({ isDisabled, namePrefix, isEnabled, onT
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const iconCell = React.useCallback(
|
||||||
|
(src: string, fileType: string) => (
|
||||||
|
<Image boxSize={100} mx="auto" my="auto" src={`data:${fileType ?? 'image/png'};base64,${src}`} alt="New Image" />
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const iconFields = React.useMemo(
|
||||||
|
() => (
|
||||||
|
<>
|
||||||
|
<SimpleGrid minChildWidth="180px" gap={4} mb={4}>
|
||||||
|
<NumberField name="width" label="width" w="140px" emptyIsUndefined isRequired unit="px" />
|
||||||
|
<NumberField name="height" label="height" w="140px" isRequired unit="px" />
|
||||||
|
<StringField name="language" label="language" isRequired />
|
||||||
|
</SimpleGrid>
|
||||||
|
<ImageField name="icon" heightName="height" widthName="width" typeName="type" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const iconCols = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'icon',
|
||||||
|
Header: 'icon',
|
||||||
|
Footer: '',
|
||||||
|
Cell: ({
|
||||||
|
cell,
|
||||||
|
}: {
|
||||||
|
cell: {
|
||||||
|
row: {
|
||||||
|
original: {
|
||||||
|
icon: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) => iconCell(cell.row.original.icon, cell.row.original.type),
|
||||||
|
accessor: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'type',
|
||||||
|
Header: 'type',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'type',
|
||||||
|
customWidth: '100px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'width',
|
||||||
|
Header: 'width',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'width',
|
||||||
|
customWidth: '150px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'height',
|
||||||
|
Header: 'height',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'height',
|
||||||
|
customWidth: '100px',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'language',
|
||||||
|
Header: 'language',
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'language',
|
||||||
|
customWidth: '100px',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading size="md" display="flex">
|
<Heading size="md" display="flex">
|
||||||
@@ -106,6 +180,14 @@ const PassPointForm: React.FC<Props> = ({ isDisabled, namePrefix, isEnabled, onT
|
|||||||
emptyIsUndefined
|
emptyIsUndefined
|
||||||
/>
|
/>
|
||||||
<CreatableSelectField {...fieldProps('connection-capability')} emptyIsUndefined placeholder="17:5060:0" />
|
<CreatableSelectField {...fieldProps('connection-capability')} emptyIsUndefined placeholder="17:5060:0" />
|
||||||
|
<ObjectArrayFieldModal
|
||||||
|
{...fieldProps('icons')}
|
||||||
|
fields={iconFields}
|
||||||
|
// @ts-ignore
|
||||||
|
columns={iconCols}
|
||||||
|
schema={INTERFACE_PASSPOINT_ICONS_SCHEMA}
|
||||||
|
emptyIsUndefined
|
||||||
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
testIpv6,
|
testIpv6,
|
||||||
testLeaseTime,
|
testLeaseTime,
|
||||||
testLength,
|
testLength,
|
||||||
|
testRegex,
|
||||||
testSelectPorts,
|
testSelectPorts,
|
||||||
testUcMac,
|
testUcMac,
|
||||||
} from 'constants/formTests';
|
} from 'constants/formTests';
|
||||||
@@ -74,6 +75,26 @@ export const CREATE_INTERFACE_SCHEMA = (t) =>
|
|||||||
role: string().required(t('form.required')).default('upstream'),
|
role: string().required(t('form.required')).default('upstream'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const INTERFACE_PASSPOINT_ICONS_SCHEMA = (t, useDefault = false) => {
|
||||||
|
const shape = object()
|
||||||
|
.shape({
|
||||||
|
width: number().required(t('form.required')).moreThan(-1).lessThan(65535).integer().default(64),
|
||||||
|
height: number().required(t('form.required')).moreThan(-1).lessThan(65535).integer().default(64),
|
||||||
|
icon: string().required(t('form.required')).default(''),
|
||||||
|
language: string()
|
||||||
|
.required(t('form.required'))
|
||||||
|
.test('test-passpoint-icon-lang', t('form.invalid_icon_lang'), (v) => testRegex(v, '^[a-z][a-z][a-z]$'))
|
||||||
|
.default('eng'),
|
||||||
|
})
|
||||||
|
.default({
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
language: 'eng',
|
||||||
|
});
|
||||||
|
|
||||||
|
return useDefault ? shape : shape.nullable().default(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
export const INTERFACE_SSID_PASS_POINT_SCHEMA = (t, useDefault = false) => {
|
export const INTERFACE_SSID_PASS_POINT_SCHEMA = (t, useDefault = false) => {
|
||||||
const shape = object()
|
const shape = object()
|
||||||
.shape({
|
.shape({
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ const EditConfigurationForm = ({ editing, configuration, formRef }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getEntity = () => {
|
const getEntity = () => {
|
||||||
if (configuration.entity !== '') return `ent:${configuration.entity}`;
|
if (configuration?.entity !== '') return `ent:${configuration?.entity}`;
|
||||||
if (configuration.venue !== '') return `ven:${configuration.venue}`;
|
if (configuration?.venue !== '') return `ven:${configuration?.venue}`;
|
||||||
return `ent:0000-0000-0000`;
|
return `ent:0000-0000-0000`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user