[WIFI-12412] Fixing SSID.services configuration options

Signed-off-by: Charles <charles.bourque96@gmail.com>
This commit is contained in:
Charles
2023-03-17 09:21:40 +01:00
parent 8a262eb36b
commit e196d4b9ef
52 changed files with 5942 additions and 6027 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "wlan-cloud-owprov-ui", "name": "wlan-cloud-owprov-ui",
"version": "2.9.0(16)", "version": "2.9.0(18)",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wlan-cloud-owprov-ui", "name": "wlan-cloud-owprov-ui",
"version": "2.9.0(16)", "version": "2.9.0(18)",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@chakra-ui/icons": "^2.0.11", "@chakra-ui/icons": "^2.0.11",

View File

@@ -1,6 +1,6 @@
{ {
"name": "wlan-cloud-owprov-ui", "name": "wlan-cloud-owprov-ui",
"version": "2.9.0(16)", "version": "2.9.0(18)",
"description": "", "description": "",
"main": "index.tsx", "main": "index.tsx",
"scripts": { "scripts": {

View File

@@ -714,7 +714,7 @@
"invalid_ipv6": "Ungültige IPv6-Adresse (Bsp.: 2001:db8:3333:4444:5555:6666:7777:8888)", "invalid_ipv6": "Ungültige IPv6-Adresse (Bsp.: 2001:db8:3333:4444:5555:6666:7777:8888)",
"invalid_json": "Ungültige JSON-Zeichenfolge", "invalid_json": "Ungültige JSON-Zeichenfolge",
"invalid_lease_time": "Ungültiger Lease-Time-Wert! Sie müssen im digitUnit-Format vorliegen. Zum Beispiel: 6d2h5m, was 6 Tage, 2 Stunden und 5 Minuten bedeutet. Hier sind die akzeptierten Einheiten: m, h, d. Wenn Sie eine Einheit nicht verwenden möchten, lassen Sie sie vollständig weg. Anstatt also 0d2h0m zu sagen, verwenden Sie 2h", "invalid_lease_time": "Ungültiger Lease-Time-Wert! Sie müssen im digitUnit-Format vorliegen. Zum Beispiel: 6d2h5m, was 6 Tage, 2 Stunden und 5 Minuten bedeutet. Hier sind die akzeptierten Einheiten: m, h, d. Wenn Sie eine Einheit nicht verwenden möchten, lassen Sie sie vollständig weg. Anstatt also 0d2h0m zu sagen, verwenden Sie 2h",
"invalid_mac_uc": "Ungültiger UC-MAC-Wert, zum Beispiel: 00:00:5e:00:53:af", "invalid_mac_uc": "Ungültiger MAC-Wert, zum Beispiel: 00:00:5e:00:53:af",
"invalid_password": "Ungültiges Passwort, bitte sehen Sie sich die Passwortrichtlinie an", "invalid_password": "Ungültiges Passwort, bitte sehen Sie sich die Passwortrichtlinie an",
"invalid_phone_number": "Ungültige Telefonnummer", "invalid_phone_number": "Ungültige Telefonnummer",
"invalid_phone_numbers": "Mindestens eine der Telefonnummern ist ungültig. Bitte geben Sie sie ohne Symbole und Leerzeichen oder in diesem Format an: +1(123)123-1234", "invalid_phone_numbers": "Mindestens eine der Telefonnummern ist ungültig. Bitte geben Sie sie ohne Symbole und Leerzeichen oder in diesem Format an: +1(123)123-1234",

View File

@@ -714,7 +714,7 @@
"invalid_ipv6": "Invalid IPv6 address (ex.: 2001:db8:3333:4444:5555:6666:7777:8888)", "invalid_ipv6": "Invalid IPv6 address (ex.: 2001:db8:3333:4444:5555:6666:7777:8888)",
"invalid_json": "Invalid JSON string", "invalid_json": "Invalid JSON string",
"invalid_lease_time": "Invalid lease time value! They need to be in the digitUnit format. For example: 6d2h5m, which means 6 days, 2 hours and 5 minutes. Here are the accepted units: m, h, d. If you do not want to use a unit, omit it completely. So instead of saying 0d2h0m, use 2h", "invalid_lease_time": "Invalid lease time value! They need to be in the digitUnit format. For example: 6d2h5m, which means 6 days, 2 hours and 5 minutes. Here are the accepted units: m, h, d. If you do not want to use a unit, omit it completely. So instead of saying 0d2h0m, use 2h",
"invalid_mac_uc": "Invalid UC-MAC value, for example: 00:00:5e:00:53:af", "invalid_mac_uc": "Invalid MAC value, for example: 00:00:5e:00:53:af",
"invalid_password": "Invalid password, please look at the password policy", "invalid_password": "Invalid password, please look at the password policy",
"invalid_phone_number": "Invalid Phone Number", "invalid_phone_number": "Invalid Phone Number",
"invalid_phone_numbers": "One or more of the phone numbers are invalid. Please provide them without symbols and spaces, or in this format: +1(123)123-1234", "invalid_phone_numbers": "One or more of the phone numbers are invalid. Please provide them without symbols and spaces, or in this format: +1(123)123-1234",
@@ -755,8 +755,8 @@
"success_remove_claim": "Successfully removed claim on: {{serial}}", "success_remove_claim": "Successfully removed claim on: {{serial}}",
"successful_reboots": "Started Rebooting: {{count}}", "successful_reboots": "Started Rebooting: {{count}}",
"successful_upgrades": "Successful upgrades: {{count}}", "successful_upgrades": "Successful upgrades: {{count}}",
"tag_one": "Tag", "tag_one": "Device",
"tags": "Inventory Tags", "tags": "Inventory Devices",
"title": "Inventory", "title": "Inventory",
"warning_reboots": "Not connected: {{count}}", "warning_reboots": "Not connected: {{count}}",
"warning_upgrades": "Devices not connected: {{count}}" "warning_upgrades": "Devices not connected: {{count}}"

View File

@@ -714,7 +714,7 @@
"invalid_ipv6": "Dirección IPv6 no válida (ej.: 2001:db8:3333:4444:5555:6666:7777:8888)", "invalid_ipv6": "Dirección IPv6 no válida (ej.: 2001:db8:3333:4444:5555:6666:7777:8888)",
"invalid_json": "Cadena JSON no válida", "invalid_json": "Cadena JSON no válida",
"invalid_lease_time": "¡Valor de tiempo de arrendamiento no válido! Deben estar en el formato digitUnit. Por ejemplo: 6d2h5m, lo que significa 6 días, 2 horas y 5 minutos. Estas son las unidades aceptadas: m, h, d. Si no desea utilizar una unidad, omítala por completo. Entonces, en lugar de decir 0d2h0m, usa 2h", "invalid_lease_time": "¡Valor de tiempo de arrendamiento no válido! Deben estar en el formato digitUnit. Por ejemplo: 6d2h5m, lo que significa 6 días, 2 horas y 5 minutos. Estas son las unidades aceptadas: m, h, d. Si no desea utilizar una unidad, omítala por completo. Entonces, en lugar de decir 0d2h0m, usa 2h",
"invalid_mac_uc": "Valor de UC-MAC no válido, por ejemplo: 00:00:5e:00:53:af", "invalid_mac_uc": "Valor de MAC no válido, por ejemplo: 00:00:5e:00:53:af",
"invalid_password": "Contraseña no válida, consulte la política de contraseñas", "invalid_password": "Contraseña no válida, consulte la política de contraseñas",
"invalid_phone_number": "Numero de telefono invalido", "invalid_phone_number": "Numero de telefono invalido",
"invalid_phone_numbers": "Uno o más de los números de teléfono no son válidos. Proporciónelos sin símbolos ni espacios, o en este formato: +1(123)123-1234", "invalid_phone_numbers": "Uno o más de los números de teléfono no son válidos. Proporciónelos sin símbolos ni espacios, o en este formato: +1(123)123-1234",

View File

@@ -714,7 +714,7 @@
"invalid_ipv6": "Adresse IPv6 invalide (ex. : 2001:db8:3333:4444:5555:6666:7777:8888)", "invalid_ipv6": "Adresse IPv6 invalide (ex. : 2001:db8:3333:4444:5555:6666:7777:8888)",
"invalid_json": "Chaîne JSON non valide", "invalid_json": "Chaîne JSON non valide",
"invalid_lease_time": "Valeur de durée de bail non valide ! Ils doivent être au format digitUnit. Par exemple : 6d2h5m, ce qui signifie 6 jours, 2 heures et 5 minutes. Voici les unités acceptées : m, h, d. Si vous ne voulez pas utiliser une unité, omettez-la complètement. Donc au lieu de dire 0d2h0m, utilisez 2h", "invalid_lease_time": "Valeur de durée de bail non valide ! Ils doivent être au format digitUnit. Par exemple : 6d2h5m, ce qui signifie 6 jours, 2 heures et 5 minutes. Voici les unités acceptées : m, h, d. Si vous ne voulez pas utiliser une unité, omettez-la complètement. Donc au lieu de dire 0d2h0m, utilisez 2h",
"invalid_mac_uc": "Valeur UC-MAC non valide, par exemple : 00:00:5e:00:53:af", "invalid_mac_uc": "Valeur MAC non valide, par exemple : 00:00:5e:00:53:af",
"invalid_password": "Mot de passe invalide, veuillez consulter la politique de mot de passe", "invalid_password": "Mot de passe invalide, veuillez consulter la politique de mot de passe",
"invalid_phone_number": "Numéro de téléphone invalide", "invalid_phone_number": "Numéro de téléphone invalide",
"invalid_phone_numbers": "Un ou plusieurs des numéros de téléphone sont invalides. Veuillez les fournir sans symboles ni espaces, ou dans ce format : +1(123)123-1234", "invalid_phone_numbers": "Un ou plusieurs des numéros de téléphone sont invalides. Veuillez les fournir sans symboles ni espaces, ou dans ce format : +1(123)123-1234",

View File

@@ -714,7 +714,7 @@
"invalid_ipv6": "Endereço IPv6 inválido (ex.: 2001:db8:3333:4444:5555:6666:7777:8888)", "invalid_ipv6": "Endereço IPv6 inválido (ex.: 2001:db8:3333:4444:5555:6666:7777:8888)",
"invalid_json": "Sequência JSON inválida", "invalid_json": "Sequência JSON inválida",
"invalid_lease_time": "Valor de tempo de locação inválido! Eles precisam estar no formato digitUnit. Por exemplo: 6d2h5m, que significa 6 dias, 2 horas e 5 minutos. Aqui estão as unidades aceitas: m, h, d. Se você não quiser usar uma unidade, omita-a completamente. Então, em vez de dizer 0d2h0m, use 2h", "invalid_lease_time": "Valor de tempo de locação inválido! Eles precisam estar no formato digitUnit. Por exemplo: 6d2h5m, que significa 6 dias, 2 horas e 5 minutos. Aqui estão as unidades aceitas: m, h, d. Se você não quiser usar uma unidade, omita-a completamente. Então, em vez de dizer 0d2h0m, use 2h",
"invalid_mac_uc": "Valor UC-MAC inválido, por exemplo: 00:00:5e:00:53:af", "invalid_mac_uc": "Valor MAC inválido, por exemplo: 00:00:5e:00:53:af",
"invalid_password": "Senha inválida, consulte a política de senha", "invalid_password": "Senha inválida, consulte a política de senha",
"invalid_phone_number": "Número de telefone inválido", "invalid_phone_number": "Número de telefone inválido",
"invalid_phone_numbers": "Um ou mais números de telefone são inválidos. Forneça-os sem símbolos e espaços ou neste formato: +1(123)123-1234", "invalid_phone_numbers": "Um ou mais números de telefone são inválidos. Forneça-os sem símbolos e espaços ou neste formato: +1(123)123-1234",

View File

@@ -15,7 +15,7 @@ interface Props extends ThemeProps {
const defaultProps = { const defaultProps = {
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
label: undefined, label: undefined,
}; };

View File

@@ -15,7 +15,7 @@ const defaultProps = {
onClick: () => {}, onClick: () => {},
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
label: undefined, label: undefined,
}; };

View File

@@ -15,7 +15,7 @@ interface Props {
const defaultProps = { const defaultProps = {
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
label: undefined, label: undefined,
ml: undefined, ml: undefined,
}; };

View File

@@ -15,7 +15,7 @@ const defaultProps = {
label: 'Edit', label: 'Edit',
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
ml: undefined, ml: undefined,
}; };

View File

@@ -14,7 +14,7 @@ interface Props {
const defaultProps = { const defaultProps = {
isDisabled: false, isDisabled: false,
isFetching: false, isFetching: false,
isCompact: false, isCompact: true,
ml: undefined, ml: undefined,
}; };

View File

@@ -15,7 +15,7 @@ const ResponsiveButton = ({
onClick, onClick,
isDisabled, isDisabled,
isLoading, isLoading,
isCompact, isCompact = true,
color, color,
label, label,
icon, icon,

View File

@@ -18,7 +18,7 @@ const defaultProps = {
onSave: undefined, onSave: undefined,
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
ml: undefined, ml: undefined,
}; };

View File

@@ -17,7 +17,7 @@ interface Props {
const defaultProps = { const defaultProps = {
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
isDirty: false, isDirty: false,
ml: undefined, ml: undefined,
}; };

View File

@@ -15,7 +15,7 @@ interface Props extends ThemeProps {
const defaultProps = { const defaultProps = {
isDisabled: false, isDisabled: false,
isLoading: false, isLoading: false,
isCompact: false, isCompact: true,
label: undefined, label: undefined,
}; };

View File

@@ -33,7 +33,7 @@ const ColumnPicker = ({
setHiddenColumns, setHiddenColumns,
defaultHiddenColumns = [], defaultHiddenColumns = [],
size, size,
isCompact, isCompact = true,
}: ColumnPickerProps) => { }: ColumnPickerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { getPref, setPref } = useAuth(); const { getPref, setPref } = useAuth();

View File

@@ -5,27 +5,29 @@ import useFastField from 'hooks/useFastField';
import { FieldProps } from 'models/Form'; import { FieldProps } from 'models/Form';
interface Props extends FieldProps, LayoutProps { interface Props extends FieldProps, LayoutProps {
formatValue?: (value: string) => string;
hideButton?: boolean; hideButton?: boolean;
} }
const StringField = ({ const StringField: React.FC<Props> = ({
name, name,
isDisabled = false, isDisabled = false,
label, label,
hideButton = false, hideButton = false,
isRequired = false, isRequired = false,
element, element,
formatValue,
isArea = false, isArea = false,
emptyIsUndefined = false, emptyIsUndefined = false,
definitionKey, definitionKey,
...props ...props
}: Props) => { }) => {
const { value, error, isError, onChange, onBlur } = useFastField<string | undefined>({ name }); const { value, error, isError, onChange, onBlur } = useFastField<string | undefined>({ name });
const onFieldChange = useCallback( const onFieldChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
if (emptyIsUndefined && e.target.value.length === 0) onChange(undefined); if (emptyIsUndefined && e.target.value.length === 0) onChange(undefined);
else onChange(e.target.value); else onChange(formatValue ? formatValue(e.target.value) : e.target.value);
}, },
[onChange], [onChange],
); );

View File

@@ -1,5 +1,14 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Button, Modal, ModalOverlay, ModalContent, ModalBody, useDisclosure, Flex } from '@chakra-ui/react'; import {
Modal,
ModalOverlay,
ModalContent,
ModalBody,
useDisclosure,
Flex,
Tooltip,
IconButton,
} from '@chakra-ui/react';
import { MagnifyingGlass } from 'phosphor-react'; import { MagnifyingGlass } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import SubscriberSearchDisplayTable from './Table'; import SubscriberSearchDisplayTable from './Table';
@@ -19,9 +28,9 @@ const SubscriberSearchModal = ({ operatorId }: Props) => {
return ( return (
<> <>
<Button alignItems="center" colorScheme="blue" rightIcon={<MagnifyingGlass />} onClick={onOpen} ml={2}> <Tooltip label={t('common.search')} hasArrow>
{t('common.search')} <IconButton aria-label={t('common.search')} icon={<MagnifyingGlass />} onClick={onOpen} colorScheme="teal" />
</Button> </Tooltip>
<Modal onClose={onClose} isOpen={isOpen} size="xl"> <Modal onClose={onClose} isOpen={isOpen} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}> <ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}>

View File

@@ -1,12 +1,12 @@
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { AddIcon } from '@chakra-ui/icons'; import { Modal, ModalOverlay, ModalContent, ModalBody, Center, Spinner } from '@chakra-ui/react';
import { Button, Modal, ModalOverlay, ModalContent, ModalBody, Center, Spinner } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CreateSubscriberDeviceStep0 from './MultiStepForm/Step0'; import CreateSubscriberDeviceStep0 from './MultiStepForm/Step0';
import CreateSubscriberDeviceStep1 from './MultiStepForm/Step1'; import CreateSubscriberDeviceStep1 from './MultiStepForm/Step1';
import CreateSubscriberDeviceStep2 from './MultiStepForm/Step2'; import CreateSubscriberDeviceStep2 from './MultiStepForm/Step2';
import CreateSubscriberDeviceStep3 from './MultiStepForm/Step3'; import CreateSubscriberDeviceStep3 from './MultiStepForm/Step3';
import CloseButton from 'components/Buttons/CloseButton'; import CloseButton from 'components/Buttons/CloseButton';
import CreateButton from 'components/Buttons/CreateButton';
import StepButton from 'components/Buttons/StepButton'; import StepButton from 'components/Buttons/StepButton';
import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert'; import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert';
import ModalHeader from 'components/Modals/ModalHeader'; import ModalHeader from 'components/Modals/ModalHeader';
@@ -141,16 +141,7 @@ const CreateSubscriberDeviceModal = ({ refresh, operatorId, subscriberId, device
return ( return (
<> <>
<Button {user?.userRole === 'CSR' ? null : <CreateButton onClick={onOpen} ml={2} />}
hidden={user?.userRole === 'CSR'}
alignItems="center"
colorScheme="blue"
rightIcon={<AddIcon />}
onClick={onOpen}
ml={2}
>
{t('crud.create')}
</Button>
<Modal onClose={closeModal} isOpen={isOpen} size="xl"> <Modal onClose={closeModal} isOpen={isOpen} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}> <ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}>

View File

@@ -66,7 +66,7 @@ const DeviceActionDropdown = ({
return ( return (
<Menu> <Menu>
<Tooltip label={t('commands.other')}> <Tooltip label={t('common.actions')}>
<MenuButton <MenuButton
as={IconButton} as={IconButton}
aria-label="Commands" aria-label="Commands"

View File

@@ -1,11 +1,11 @@
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { AddIcon } from '@chakra-ui/icons'; import { useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { Button, useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CreateConfigurationForm from './Form'; import CreateConfigurationForm from './Form';
import CloseButton from 'components/Buttons/CloseButton'; import CloseButton from 'components/Buttons/CloseButton';
import CreateButton from 'components/Buttons/CreateButton';
import SaveButton from 'components/Buttons/SaveButton'; import SaveButton from 'components/Buttons/SaveButton';
import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert'; import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert';
import ModalHeader from 'components/Modals/ModalHeader'; import ModalHeader from 'components/Modals/ModalHeader';
@@ -51,9 +51,7 @@ const CreateConfigurationModal = ({ refresh, entityId }) => {
return ( return (
<> <>
<Button alignItems="center" colorScheme="blue" rightIcon={<AddIcon />} onClick={openModal} ml={2}> <CreateButton onClick={openModal} ml={2} />
{t('crud.create')}
</Button>
<Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside"> <Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}> <ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}>

View File

@@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import { AddIcon } from '@chakra-ui/icons'; import { useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { Button, useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CreateContactForm from './Form'; import CreateContactForm from './Form';
import CloseButton from 'components/Buttons/CloseButton'; import CloseButton from 'components/Buttons/CloseButton';
import CreateButton from 'components/Buttons/CreateButton';
import SaveButton from 'components/Buttons/SaveButton'; import SaveButton from 'components/Buttons/SaveButton';
import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert'; import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert';
import ModalHeader from 'components/Modals/ModalHeader'; import ModalHeader from 'components/Modals/ModalHeader';
@@ -47,16 +47,7 @@ const CreateContactModal = ({ refresh, entityId, isVenue, onCreate }) => {
return ( return (
<> <>
<Button {user?.userRole === 'CSR' ? null : <CreateButton onClick={onOpen} ml={2} />}
hidden={user?.userRole === 'CSR'}
alignItems="center"
colorScheme="blue"
rightIcon={<AddIcon />}
onClick={onOpen}
ml={2}
>
{t('crud.create')}
</Button>
<Modal onClose={closeModal} isOpen={isOpen} size="xl" initialFocusRef={initialRef}> <Modal onClose={closeModal} isOpen={isOpen} size="xl" initialFocusRef={initialRef}>
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -66,7 +66,7 @@ const DeviceActionDropdown = ({
return ( return (
<Menu> <Menu>
<Tooltip label={t('commands.other')}> <Tooltip label={t('common.actions')}>
<MenuButton <MenuButton
as={IconButton} as={IconButton}
aria-label="Commands" aria-label="Commands"

View File

@@ -149,12 +149,6 @@ const EditTagModal = ({
title={t('crud.edit_obj', { obj: tag?.name ?? tag?.serialNumber })} title={t('crud.edit_obj', { obj: tag?.name ?? tag?.serialNumber })}
right={ right={
<> <>
<SaveButton
onClick={form.submitForm}
isLoading={form.isSubmitting}
isDisabled={!editing || !form.isValid || (configuration !== null && !configuration.__form.isValid)}
mr={2}
/>
<Popover isOpen={isDeleteOpen} onOpen={onDeleteOpen} onClose={onDeleteClose}> <Popover isOpen={isDeleteOpen} onOpen={onDeleteOpen} onClose={onDeleteClose}>
<Tooltip hasArrow label={t('crud.delete')} placement="top" isDisabled={isDeleteOpen}> <Tooltip hasArrow label={t('crud.delete')} placement="top" isDisabled={isDeleteOpen}>
<Box> <Box>
@@ -208,6 +202,13 @@ const EditTagModal = ({
onClick={handlePushConfig} onClick={handlePushConfig}
/> />
</Tooltip> </Tooltip>
<SaveButton
onClick={form.submitForm}
isLoading={form.isSubmitting}
isDisabled={!editing || !form.isValid || (configuration !== null && !configuration.__form.isValid)}
hidden={!editing}
ml={2}
/>
<EditButton ml={2} isDisabled={editing} onClick={setEditing.toggle} isCompact /> <EditButton ml={2} isDisabled={editing} onClick={setEditing.toggle} isCompact />
<CloseButton ml={2} onClick={closeModal} /> <CloseButton ml={2} onClick={closeModal} />
</> </>

View File

@@ -1,15 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import { useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody, Tooltip, IconButton } from '@chakra-ui/react';
useDisclosure,
Modal,
ModalOverlay,
ModalContent,
ModalBody,
useBreakpoint,
Button,
Tooltip,
IconButton,
} from '@chakra-ui/react';
import { UploadSimple } from 'phosphor-react'; import { UploadSimple } from 'phosphor-react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -36,7 +26,6 @@ const defaultProps = {
const ImportDeviceCsvModal = ({ refresh, deviceClass, parent }) => { const ImportDeviceCsvModal = ({ refresh, deviceClass, parent }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const breakpoint = useBreakpoint();
const [refreshId, setRefreshId] = useState(uuid()); const [refreshId, setRefreshId] = useState(uuid());
// 0: explanation, file import and file analysis // 0: explanation, file import and file analysis
// 1: testing the serial number list with the API // 1: testing the serial number list with the API
@@ -91,22 +80,6 @@ const ImportDeviceCsvModal = ({ refresh, deviceClass, parent }) => {
onOpen(); onOpen();
}; };
const getButton = () => {
if (breakpoint !== 'base' && breakpoint !== 'sm') {
return (
<Button ml={2} colorScheme="blue" onClick={openModal} rightIcon={<UploadSimple size={20} />}>
{t('devices.import_batch_tags')}
</Button>
);
}
return (
<Tooltip label={t('devices.import_batch_tags')}>
<IconButton ml={2} colorScheme="blue" onClick={openModal} icon={<UploadSimple size={20} />} />
</Tooltip>
);
};
const closeModal = () => (isCloseable ? onClose() : openConfirm()); const closeModal = () => (isCloseable ? onClose() : openConfirm());
const closeCancelAndForm = () => { const closeCancelAndForm = () => {
@@ -116,7 +89,9 @@ const ImportDeviceCsvModal = ({ refresh, deviceClass, parent }) => {
return ( return (
<> <>
{getButton()} <Tooltip label={t('devices.import_batch_tags')}>
<IconButton ml={2} colorScheme="teal" onClick={openModal} icon={<UploadSimple size={20} />} />
</Tooltip>
<Modal onClose={closeModal} isOpen={isOpen} size="xl"> <Modal onClose={closeModal} isOpen={isOpen} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -1,14 +1,13 @@
import React from 'react'; import React from 'react';
import { AddIcon } from '@chakra-ui/icons'; import { useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { Button, useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CreateLocationForm from './Form'; import CreateLocationForm from './Form';
import CloseButton from 'components/Buttons/CloseButton'; import CloseButton from 'components/Buttons/CloseButton';
import CreateButton from 'components/Buttons/CreateButton';
import SaveButton from 'components/Buttons/SaveButton'; import SaveButton from 'components/Buttons/SaveButton';
import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert'; import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert';
import ModalHeader from 'components/Modals/ModalHeader'; import ModalHeader from 'components/Modals/ModalHeader';
import { useAuth } from 'contexts/AuthProvider';
import useFormRef from 'hooks/useFormRef'; import useFormRef from 'hooks/useFormRef';
const propTypes = { const propTypes = {
@@ -22,7 +21,6 @@ const defaultProps = {
const CreateLocationModal = ({ refresh, entityId }) => { const CreateLocationModal = ({ refresh, entityId }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { isOpen: showConfirm, onOpen: openConfirm, onClose: closeConfirm } = useDisclosure(); const { isOpen: showConfirm, onOpen: openConfirm, onClose: closeConfirm } = useDisclosure();
const { form, formRef } = useFormRef(); const { form, formRef } = useFormRef();
@@ -36,16 +34,7 @@ const CreateLocationModal = ({ refresh, entityId }) => {
return ( return (
<> <>
<Button <CreateButton onClick={onOpen} ml={2} />
hidden={user?.userRole === 'CSR'}
alignItems="center"
colorScheme="blue"
rightIcon={<AddIcon />}
onClick={onOpen}
ml={2}
>
{t('crud.create')}
</Button>
<Modal onClose={closeModal} isOpen={isOpen} size="xl"> <Modal onClose={closeModal} isOpen={isOpen} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -1,14 +1,13 @@
import React from 'react'; import React from 'react';
import { AddIcon } from '@chakra-ui/icons'; import { Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { Button, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CreateSubscriberForm from './Form'; import CreateSubscriberForm from './Form';
import CloseButton from 'components/Buttons/CloseButton'; import CloseButton from 'components/Buttons/CloseButton';
import CreateButton from 'components/Buttons/CreateButton';
import SaveButton from 'components/Buttons/SaveButton'; import SaveButton from 'components/Buttons/SaveButton';
import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert'; import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert';
import ModalHeader from 'components/Modals/ModalHeader'; import ModalHeader from 'components/Modals/ModalHeader';
import { useAuth } from 'contexts/AuthProvider';
import useFormModal from 'hooks/useFormModal'; import useFormModal from 'hooks/useFormModal';
import useFormRef from 'hooks/useFormRef'; import useFormRef from 'hooks/useFormRef';
@@ -18,7 +17,6 @@ const propTypes = {
}; };
const CreateSubscriberModal = ({ refresh, operatorId }) => { const CreateSubscriberModal = ({ refresh, operatorId }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth();
const { form, formRef } = useFormRef(); const { form, formRef } = useFormRef();
const { isOpen, isConfirmOpen, onOpen, closeConfirm, closeModal, closeCancelAndForm } = useFormModal({ const { isOpen, isConfirmOpen, onOpen, closeConfirm, closeModal, closeCancelAndForm } = useFormModal({
isDirty: form?.dirty, isDirty: form?.dirty,
@@ -26,16 +24,7 @@ const CreateSubscriberModal = ({ refresh, operatorId }) => {
return ( return (
<> <>
<Button <CreateButton onClick={onOpen} ml={2} />
hidden={user?.userRole === 'CSR'}
alignItems="center"
colorScheme="blue"
rightIcon={<AddIcon />}
onClick={onOpen}
ml={2}
>
{t('crud.create')}
</Button>
<Modal onClose={closeModal} isOpen={isOpen} size="xl"> <Modal onClose={closeModal} isOpen={isOpen} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -42,6 +42,18 @@ const DhcpIpV4 = ({ namePrefix, isDisabled }: Props) => {
definitionKey="interface.ipv4.dhcp-lease.macaddr" definitionKey="interface.ipv4.dhcp-lease.macaddr"
isRequired isRequired
w="220px" w="220px"
formatValue={(v) => {
const r = /([a-f0-9]{2})([a-f0-9]{2})/i;
let str = v.replace(/[^a-f0-9]/gi, '');
while (r.test(str)) {
str = str.replace(r, `$1:$2`);
}
str = str.slice(0, 17);
return str;
}}
mr={4} mr={4}
/> />
<StaticLeaseOffsetField subnet={ipv4?.subnet} /> <StaticLeaseOffsetField subnet={ipv4?.subnet} />
@@ -122,7 +134,7 @@ const DhcpIpV4 = ({ namePrefix, isDisabled }: Props) => {
isRequired isRequired
options={{ options={{
modalTitle: 'Reserved Addresses', modalTitle: 'Reserved Addresses',
buttonLabel: 'Reserved Addresses', buttonLabel: 'Manage Reserved Addresses',
onFormSubmit: (v: { onFormSubmit: (v: {
__temp_ip?: string; __temp_ip?: string;
secondMacAddress: string; secondMacAddress: string;

View File

@@ -74,7 +74,6 @@ const AdvancedSettings: React.FC<{ editing: boolean; namePrefix: string }> = ({
definitionKey="interface.ssid.services" definitionKey="interface.ssid.services"
isDisabled={!editing} isDisabled={!editing}
options={[ options={[
{ value: 'captive', label: 'captive' },
{ value: 'radius-gw-proxy', label: 'radius-gw-proxy' }, { value: 'radius-gw-proxy', label: 'radius-gw-proxy' },
{ value: 'wifi-steering', label: 'wifi-steering' }, { value: 'wifi-steering', label: 'wifi-steering' },
]} ]}

View File

@@ -1,15 +1,5 @@
import React from 'react'; import React from 'react';
import { import { IconButton, Tooltip, useDisclosure, Modal, ModalBody, ModalContent, ModalOverlay } from '@chakra-ui/react';
Button,
IconButton,
Tooltip,
useBreakpoint,
useDisclosure,
Modal,
ModalBody,
ModalContent,
ModalOverlay,
} from '@chakra-ui/react';
import { CheckCircle, WarningOctagon } from 'phosphor-react'; import { CheckCircle, WarningOctagon } from 'phosphor-react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
@@ -30,7 +20,6 @@ const defaultProps = {
const ViewConfigErrorsModal = ({ errors, activeConfigurations, isDisabled }) => { const ViewConfigErrorsModal = ({ errors, activeConfigurations, isDisabled }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const breakpoint = useBreakpoint();
const errorAmount = const errorAmount =
errors.globals.length + errors.globals.length +
errors.unit.length + errors.unit.length +
@@ -42,28 +31,20 @@ const ViewConfigErrorsModal = ({ errors, activeConfigurations, isDisabled }) =>
return ( return (
<> <>
{breakpoint !== 'base' && breakpoint !== 'sm' ? ( <Tooltip
<Button label={`${errorAmount} ${errorAmount === 1 ? t('common.error') : t('common.errors')}`}
colorScheme={errorAmount === 0 ? 'green' : 'red'} hasArrow
type="button" shouldWrapChildren
onClick={onOpen}
ml={2}
rightIcon={errorAmount === 0 ? <CheckCircle size={20} /> : <WarningOctagon size={20} />}
isDisabled={isDisabled || errorAmount === 0}
> >
{errorAmount} {errorAmount === 1 ? t('common.error') : t('common.errors')}
</Button>
) : (
<Tooltip label={`${errorAmount} ${errorAmount === 1 ? t('common.error') : t('common.errors')}`}>
<IconButton <IconButton
colorScheme={errorAmount === 0 ? 'green' : 'red'} colorScheme={errorAmount === 0 ? 'green' : 'red'}
type="button" type="button"
onClick={onOpen} onClick={onOpen}
ml={2}
icon={errorAmount === 0 ? <CheckCircle size={20} /> : <WarningOctagon size={20} />} icon={errorAmount === 0 ? <CheckCircle size={20} /> : <WarningOctagon size={20} />}
isDisabled={isDisabled || errorAmount === 0} isDisabled={isDisabled || errorAmount === 0}
/> />
</Tooltip> </Tooltip>
)}
<Modal onClose={onClose} isOpen={isOpen} size="xl" scrollBehavior="inside"> <Modal onClose={onClose} isOpen={isOpen} size="xl" scrollBehavior="inside">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -1,15 +1,5 @@
import React from 'react'; import React from 'react';
import { import { IconButton, Tooltip, useDisclosure, Modal, ModalBody, ModalContent, ModalOverlay } from '@chakra-ui/react';
Button,
IconButton,
Tooltip,
useBreakpoint,
useDisclosure,
Modal,
ModalBody,
ModalContent,
ModalOverlay,
} from '@chakra-ui/react';
import { CheckCircle, WarningOctagon } from 'phosphor-react'; import { CheckCircle, WarningOctagon } from 'phosphor-react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
@@ -30,7 +20,6 @@ const defaultProps = {
const ViewConfigWarningsModal = ({ warnings, activeConfigurations, isDisabled }) => { const ViewConfigWarningsModal = ({ warnings, activeConfigurations, isDisabled }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const breakpoint = useBreakpoint();
const warningsAmount = const warningsAmount =
warnings.globals.length + warnings.globals.length +
warnings.unit.length + warnings.unit.length +
@@ -42,18 +31,11 @@ const ViewConfigWarningsModal = ({ warnings, activeConfigurations, isDisabled })
return ( return (
<> <>
{breakpoint !== 'base' && breakpoint !== 'sm' ? ( <Tooltip
<Button label={`${warningsAmount} ${warningsAmount === 1 ? t('common.warning') : t('common.warnings')}`}
colorScheme={warningsAmount === 0 ? 'green' : 'yellow'} hasArrow
type="button" shouldWrapChildren
onClick={onOpen}
rightIcon={warningsAmount === 0 ? <CheckCircle size={20} /> : <WarningOctagon size={20} />}
isDisabled={isDisabled || warningsAmount === 0}
> >
{warningsAmount} {warningsAmount === 1 ? t('common.warning') : t('common.warnings')}
</Button>
) : (
<Tooltip label={`${warningsAmount} ${warningsAmount === 1 ? t('common.warning') : t('common.warnings')}`}>
<IconButton <IconButton
colorScheme={warningsAmount === 0 ? 'green' : 'yellow'} colorScheme={warningsAmount === 0 ? 'green' : 'yellow'}
type="button" type="button"
@@ -62,7 +44,6 @@ const ViewConfigWarningsModal = ({ warnings, activeConfigurations, isDisabled })
isDisabled={isDisabled || warningsAmount === 0} isDisabled={isDisabled || warningsAmount === 0}
/> />
</Tooltip> </Tooltip>
)}
<Modal onClose={onClose} isOpen={isOpen} size="xl" scrollBehavior="inside"> <Modal onClose={onClose} isOpen={isOpen} size="xl" scrollBehavior="inside">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -149,7 +149,6 @@ const ConfigurationCard = ({ id }) => {
<SaveButton <SaveButton
onClick={handleSubmitClick} onClick={handleSubmitClick}
isLoading={updateEntity.isLoading} isLoading={updateEntity.isLoading}
isCompact={false}
isDisabled={ isDisabled={
!editing || !form.isValid || sections.invalidValues.length > 0 || (!form.dirty && !sections.isDirty) !editing || !form.isValid || sections.invalidValues.length > 0 || (!form.dirty && !sections.isDirty)
} }

View File

@@ -33,7 +33,6 @@ const EntityCard = ({ id }) => {
<SaveButton <SaveButton
onClick={form.submitForm} onClick={form.submitForm}
isLoading={form.isSubmitting} isLoading={form.isSubmitting}
isCompact={false}
isDisabled={!editing || !form.isValid || !form.dirty} isDisabled={!editing || !form.isValid || !form.dirty}
ml={2} ml={2}
/> />

View File

@@ -1,5 +1,19 @@
import * as React from 'react'; import * as React from 'react';
import { Badge, Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react'; import {
Badge,
Box,
Flex,
HStack,
IconButton,
Select,
Spacer,
Table,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import { Download } from 'phosphor-react'; import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv'; import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -131,9 +145,9 @@ const FmsLogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`} filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]} data={downloadableLogs as object[]}
> >
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}> <Tooltip label={t('logs.export')} hasArrow>
{t('logs.export')} <IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Button> </Tooltip>
</CSVLink> </CSVLink>
</HStack> </HStack>
</CardHeader> </CardHeader>

View File

@@ -1,5 +1,19 @@
import * as React from 'react'; import * as React from 'react';
import { Badge, Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react'; import {
Badge,
Box,
Flex,
HStack,
IconButton,
Select,
Spacer,
Table,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import { Download } from 'phosphor-react'; import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv'; import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -134,9 +148,9 @@ const GeneralLogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`} filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]} data={downloadableLogs as object[]}
> >
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}> <Tooltip label={t('logs.export')} hasArrow>
{t('logs.export')} <IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Button> </Tooltip>
</CSVLink> </CSVLink>
</HStack> </HStack>
</CardHeader> </CardHeader>

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Box, Button, Flex, HStack, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react'; import { Box, Flex, HStack, IconButton, Spacer, Table, Text, Th, Thead, Tooltip, Tr } from '@chakra-ui/react';
import { Download } from 'phosphor-react'; import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv'; import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -124,9 +124,9 @@ const NotificationsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`} filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]} data={downloadableLogs as object[]}
> >
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}> <Tooltip label={t('logs.export')} hasArrow>
{t('logs.export')} <IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Button> </Tooltip>
</CSVLink> </CSVLink>
</HStack> </HStack>
</CardHeader> </CardHeader>

View File

@@ -1,5 +1,19 @@
import * as React from 'react'; import * as React from 'react';
import { Badge, Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react'; import {
Badge,
Box,
Flex,
HStack,
IconButton,
Select,
Spacer,
Table,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import { Download } from 'phosphor-react'; import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv'; import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -131,9 +145,9 @@ const SecLogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`} filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]} data={downloadableLogs as object[]}
> >
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}> <Tooltip label={t('logs.export')} hasArrow>
{t('logs.export')} <IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Button> </Tooltip>
</CSVLink> </CSVLink>
</HStack> </HStack>
</CardHeader> </CardHeader>

View File

@@ -29,11 +29,12 @@ const OperatorDetailsCard = ({ id }) => {
<Heading size="md">{operator?.name}</Heading> <Heading size="md">{operator?.name}</Heading>
</Box> </Box>
<Spacer /> <Spacer />
<DeleteOperatorButton isDisabled={editing || isFetching} operator={operator} />
<SaveButton <SaveButton
onClick={form.submitForm} onClick={form.submitForm}
isLoading={form.isSubmitting} isLoading={form.isSubmitting}
isCompact={false}
isDisabled={!editing || !form.isValid || !form.dirty} isDisabled={!editing || !form.isValid || !form.dirty}
hidden={!editing}
ml={2} ml={2}
/> />
<ToggleEditButton <ToggleEditButton
@@ -43,7 +44,6 @@ const OperatorDetailsCard = ({ id }) => {
isDirty={formRef.dirty} isDirty={formRef.dirty}
ml={2} ml={2}
/> />
<DeleteOperatorButton isDisabled={editing || isFetching} operator={operator} />
<RefreshButton onClick={refetch} isFetching={isFetching} isDisabled={editing} ml={2} /> <RefreshButton onClick={refetch} isFetching={isFetching} isDisabled={editing} ml={2} />
</CardHeader> </CardHeader>
<CardBody> <CardBody>

View File

@@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Box } from '@chakra-ui/react';
import ApiKeyTable from './Table'; import ApiKeyTable from './Table';
import Card from 'components/Card'; import Card from 'components/Card';
import CardBody from 'components/Card/CardBody'; import CardBody from 'components/Card/CardBody';
@@ -10,7 +11,9 @@ const ApiKeysCard = () => {
return ( return (
<Card p={4}> <Card p={4}>
<CardBody> <CardBody>
<Box w="100%">
<ApiKeyTable userId={user?.id ?? ''} /> <ApiKeyTable userId={user?.id ?? ''} />
</Box>
</CardBody> </CardBody>
</Card> </Card>
); );

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { ChevronDownIcon } from '@chakra-ui/icons'; import { IconButton, Menu, MenuButton, MenuItem, MenuList, Tooltip } from '@chakra-ui/react';
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react'; import { Wrench } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSendEmailResetSubscriber, useSuspendSubscriber } from 'hooks/Network/Subscribers'; import { useSendEmailResetSubscriber, useSuspendSubscriber } from 'hooks/Network/Subscribers';
import useMutationResult from 'hooks/useMutationResult'; import useMutationResult from 'hooks/useMutationResult';
@@ -12,13 +12,7 @@ interface Props {
isDisabled?: boolean; isDisabled?: boolean;
} }
const SubscriberActions = ( const SubscriberActions: React.FC<Props> = ({ subscriber, refresh, isDisabled }) => {
{
subscriber,
refresh,
isDisabled
}: Props
) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { mutateAsync: suspend } = useSuspendSubscriber({ id: subscriber?.id ?? '' }); const { mutateAsync: suspend } = useSuspendSubscriber({ id: subscriber?.id ?? '' });
const { mutateAsync: resetPassword } = useSendEmailResetSubscriber({ id: subscriber?.id ?? '' }); const { mutateAsync: resetPassword } = useSendEmailResetSubscriber({ id: subscriber?.id ?? '' });
@@ -41,9 +35,9 @@ const SubscriberActions = (
return ( return (
<Menu> <Menu>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />} ml={2} isDisabled={isDisabled}> <Tooltip label={t('common.actions')} aria-label={t('common.actions')} hasArrow>
{t('common.actions')} <MenuButton as={IconButton} icon={<Wrench size={20} />} ml={2} isDisabled={isDisabled} />
</MenuButton> </Tooltip>
<MenuList> <MenuList>
<MenuItem onClick={handleSuspendClick}> <MenuItem onClick={handleSuspendClick}>
{subscriber?.suspended ? t('users.stop_suspension') : t('users.suspend')} {subscriber?.suspended ? t('users.stop_suspension') : t('users.suspend')}

View File

@@ -19,7 +19,7 @@ interface Props {
id: string; id: string;
} }
const SubscriberCard = ({ id }: Props) => { const SubscriberCard: React.FC<Props> = ({ id }) => {
const [editing, setEditing] = useBoolean(); const [editing, setEditing] = useBoolean();
const { data: subscriber, refetch, isFetching } = useGetSubscriber({ id }); const { data: subscriber, refetch, isFetching } = useGetSubscriber({ id });
const { form, formRef } = useFormRef(); const { form, formRef } = useFormRef();
@@ -41,11 +41,12 @@ const SubscriberCard = ({ id }: Props) => {
</Flex> </Flex>
<Spacer /> <Spacer />
<Box> <Box>
<DeleteVenuePopover isDisabled={editing || isFetching} subscriber={subscriber} />
<SaveButton <SaveButton
onClick={form.submitForm} onClick={form.submitForm}
isLoading={form.isSubmitting} isLoading={form.isSubmitting}
isCompact={false}
isDisabled={!editing || !form.isValid || !form.dirty} isDisabled={!editing || !form.isValid || !form.dirty}
hidden={!editing}
ml={2} ml={2}
/> />
<ToggleEditButton <ToggleEditButton
@@ -55,9 +56,8 @@ const SubscriberCard = ({ id }: Props) => {
isDirty={form.dirty} isDirty={form.dirty}
ml={2} ml={2}
/> />
<DeleteVenuePopover isDisabled={editing || isFetching} subscriber={subscriber} />
<RefreshButton onClick={refetch} isFetching={isFetching} isDisabled={editing} ml={2} />
<Actions subscriber={subscriber} refresh={refetch} isDisabled={editing} /> <Actions subscriber={subscriber} refresh={refetch} isDisabled={editing} />
<RefreshButton onClick={refetch} isFetching={isFetching} isDisabled={editing} ml={2} />
</Box> </Box>
</CardHeader> </CardHeader>
<CardBody> <CardBody>

View File

@@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Button, useDisclosure } from '@chakra-ui/react'; import { IconButton, Tooltip, useDisclosure } from '@chakra-ui/react';
import { Article } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import SystemLoggingModal from './Modal'; import SystemLoggingModal from './Modal';
import { EndpointApiResponse } from 'hooks/Network/Endpoints'; import { EndpointApiResponse } from 'hooks/Network/Endpoints';
@@ -15,9 +16,17 @@ const SystemLoggingButton = ({ endpoint, token }: Props) => {
return ( return (
<> <>
<Button colorScheme="teal" onClick={modalProps.onOpen} mr={2} my="auto"> <Tooltip label={t('system.logging')} hasArrow>
{t('system.logging')} <IconButton
</Button> aria-label={t('system.logging')}
colorScheme="teal"
type="button"
my="auto"
onClick={modalProps.onOpen}
icon={<Article size={20} />}
mr={2}
/>
</Tooltip>
<SystemLoggingModal modalProps={modalProps} endpoint={endpoint.uri} token={token} /> <SystemLoggingModal modalProps={modalProps} endpoint={endpoint.uri} token={token} />
</> </>
); );

View File

@@ -21,6 +21,7 @@ import { ArrowsClockwise } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import SystemLoggingButton from './LoggingButton'; import SystemLoggingButton from './LoggingButton';
import SystemCertificatesTable from './SystemCertificatesTable'; import SystemCertificatesTable from './SystemCertificatesTable';
import RefreshButton from 'components/Buttons/RefreshButton';
import Card from 'components/Card'; import Card from 'components/Card';
import CardBody from 'components/Card/CardBody'; import CardBody from 'components/Card/CardBody';
import FormattedDate from 'components/FormattedDate'; import FormattedDate from 'components/FormattedDate';
@@ -70,16 +71,7 @@ const SystemTile = ({ endpoint, token }: Props) => {
<Heading pt={0}>{endpoint.type}</Heading> <Heading pt={0}>{endpoint.type}</Heading>
<Spacer /> <Spacer />
<SystemLoggingButton endpoint={endpoint} token={token} /> <SystemLoggingButton endpoint={endpoint} token={token} />
<Button <RefreshButton onClick={refresh} isFetching={isFetchingSystem || isFetchingSubsystems} />
mt={1}
minWidth="112px"
colorScheme="blue"
rightIcon={<ArrowsClockwise />}
onClick={refresh}
isLoading={isFetchingSystem || isFetchingSubsystems}
>
{t('common.refresh')}
</Button>
</Box> </Box>
<CardBody> <CardBody>
<VStack w="100%"> <VStack w="100%">

View File

@@ -77,7 +77,7 @@ const UserActions = ({ id, isSuspended, isWaitingForCheck, refresh, size = 'sm',
return ( return (
<Menu> <Menu>
<Tooltip label={t('commands.other')}> <Tooltip label={t('common.actions')}>
<MenuButton <MenuButton
as={IconButton} as={IconButton}
aria-label="Commands" aria-label="Commands"

View File

@@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { AddIcon } from '@chakra-ui/icons'; import { useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { Button, useDisclosure, Modal, ModalOverlay, ModalContent, ModalBody } from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CreateUserForm from './Form'; import CreateUserForm from './Form';
import CloseButton from 'components/Buttons/CloseButton'; import CloseButton from 'components/Buttons/CloseButton';
import CreateButton from 'components/Buttons/CreateButton';
import SaveButton from 'components/Buttons/SaveButton'; import SaveButton from 'components/Buttons/SaveButton';
import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert'; import ConfirmCloseAlert from 'components/Modals/Actions/ConfirmCloseAlert';
import ModalHeader from 'components/Modals/ModalHeader'; import ModalHeader from 'components/Modals/ModalHeader';
@@ -47,16 +47,7 @@ const CreateUserModal = ({ requirements, refreshUsers }) => {
return ( return (
<> <>
<Button {user?.userRole === 'CSR' ? null : <CreateButton onClick={onOpen} ml={2} />}
hidden={user?.userRole === 'CSR'}
alignItems="center"
colorScheme="blue"
rightIcon={<AddIcon />}
onClick={onOpen}
ml={2}
>
{t('crud.create')}
</Button>
<Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside"> <Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}> <ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>

View File

@@ -7,6 +7,7 @@ import { v4 as uuid } from 'uuid';
import CreateUserModal from './CreateUserModal'; import CreateUserModal from './CreateUserModal';
import EditUserModal from './EditUserModal'; import EditUserModal from './EditUserModal';
import UserActions from './UserActions'; import UserActions from './UserActions';
import RefreshButton from 'components/Buttons/RefreshButton';
import Card from 'components/Card'; import Card from 'components/Card';
import CardBody from 'components/Card/CardBody'; import CardBody from 'components/Card/CardBody';
import CardHeader from 'components/Card/CardHeader'; import CardHeader from 'components/Card/CardHeader';
@@ -145,15 +146,7 @@ const UserTable = ({ title }) => {
preference="provisioning.userTable.hiddenColumns" preference="provisioning.userTable.hiddenColumns"
/> />
<CreateUserModal requirements={requirements} refreshUsers={refreshUsers} /> <CreateUserModal requirements={requirements} refreshUsers={refreshUsers} />
<Button <RefreshButton onClick={refreshUsers} isFetching={isFetching} ml={2} />
colorScheme="gray"
onClick={refreshUsers}
rightIcon={<ArrowsClockwise />}
ml={2}
isLoading={isFetching}
>
{t('common.refresh')}
</Button>
</Box> </Box>
</Flex> </Flex>
</CardHeader> </CardHeader>

View File

@@ -79,7 +79,7 @@ const DeleteVenuePopover = ({ venue, isDisabled }) => {
<Popover isOpen={isOpen} onOpen={onOpen} onClose={onClose}> <Popover isOpen={isOpen} onOpen={onOpen} onClose={onClose}>
<PopoverAnchor> <PopoverAnchor>
<span> <span>
<DeleteButton onClick={onOpen} isDisabled={isDisabled} ml={2} /> <DeleteButton onClick={onOpen} isDisabled={isDisabled} />
</span> </span>
</PopoverAnchor> </PopoverAnchor>
<PopoverContent> <PopoverContent>

View File

@@ -35,7 +35,6 @@ const VenueCard = ({ id }: { id: string }) => {
<SaveButton <SaveButton
onClick={form.submitForm} onClick={form.submitForm}
isLoading={form.isSubmitting} isLoading={form.isSubmitting}
isCompact={false}
isDisabled={!editing || !form.isValid || !form.dirty} isDisabled={!editing || !form.isValid || !form.dirty}
ml={2} ml={2}
/> />

View File

@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { Button, Modal, ModalOverlay, ModalContent, ModalBody, useDisclosure } from '@chakra-ui/react'; import { Modal, ModalOverlay, ModalContent, ModalBody, useDisclosure, Tooltip, IconButton } from '@chakra-ui/react';
import { Plus } from 'phosphor-react'; import { Copy } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import Actions from './Actions'; import Actions from './Actions';
@@ -38,9 +38,14 @@ const UseExistingContactModal = ({ onAssignContact, venue }: Props) => {
return ( return (
<> <>
<Button type="button" colorScheme="blue" rightIcon={<Plus size={20} />} onClick={onOpen} ml={2}> <Tooltip label={t('venues.use_existing')}>
{t('venues.use_existing')} <IconButton
</Button> aria-label={t('venues.use_existing')}
icon={<Copy size={20} />}
onClick={onOpen}
colorScheme="teal"
/>
</Tooltip>
<Modal initialFocusRef={undefined} onClose={onClose} isOpen={isOpen} size="xl"> <Modal initialFocusRef={undefined} onClose={onClose} isOpen={isOpen} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}> <ModalContent maxWidth={{ sm: '90%', md: '900px', lg: '1000px', xl: '80%' }}>

View File

@@ -1,4 +1,4 @@
import { extendTheme } from '@chakra-ui/react'; import { Tooltip, extendTheme } from '@chakra-ui/react';
import CardComponent from './additions/card/Card'; import CardComponent from './additions/card/Card';
import CardBodyComponent from './additions/card/CardBody'; import CardBodyComponent from './additions/card/CardBody';
import CardHeaderComponent from './additions/card/CardHeader'; import CardHeaderComponent from './additions/card/CardHeader';
@@ -13,19 +13,30 @@ import breakpoints from './foundations/breakpoints';
import font from './foundations/fonts'; import font from './foundations/fonts';
import globalStyles from './styles'; import globalStyles from './styles';
// import { mode } from "@chakra-ui/theme-tools"; const config: ThemeConfig = {
export default extendTheme( initialColorMode: 'light',
{ breakpoints }, // Breakpoints useSystemColorMode: false,
globalStyles, };
font, // Global styles
buttonStyles, // Button styles const theme = extendTheme({
badgeStyles, // Badge styles config,
drawerStyles, // Sidebar variant for Chakra's drawer font,
alertStyles, breakpoints,
CardComponent, // Card component colors: globalStyles.colors,
CardBodyComponent, // Card Body component styles: globalStyles.styles,
CardHeaderComponent, // Card Header component components: {
MainPanelComponent, // Main Panel component Alert: alertStyles.components.Alert,
PanelContentComponent, // Panel Content component Badge: badgeStyles.components.Badge,
PanelContainerComponent, // Panel Container component Button: buttonStyles.components.Button,
); Drawer: drawerStyles.components.Drawer,
Card: CardComponent.components.Card,
CardBody: CardBodyComponent.components.CardBody,
CardHeader: CardHeaderComponent.components.CardHeader,
MainPanel: MainPanelComponent.components.MainPanel,
PanelContent: PanelContentComponent.components.PanelContent,
PanelContainer: PanelContainerComponent.components.PanelContainer,
},
});
Tooltip.defaultProps = { ...Tooltip.defaultProps, hasArrow: true };
export default theme;

View File

@@ -1,49 +1,9 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths'; import tsconfigPaths from 'vite-tsconfig-paths';
import { VitePWA } from 'vite-plugin-pwa';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [tsconfigPaths(), react()],
tsconfigPaths(),
react(),
VitePWA({
registerType: 'autoUpdate',
devOptions: {
enabled: true,
/* other options */
},
manifest: {
name: 'OpenWiFi Provisioning App',
short_name: 'OpenWiFiProvisioning',
description: 'OpenWiFi Provisioning App',
theme_color: '#000000',
icons: [
{
src: 'android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: 'android-chrome-384x384.png',
sizes: '384x384',
type: 'image/png',
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
},
}),
],
build: { build: {
outDir: './build', outDir: './build',
chunkSizeWarningLimit: 1000, chunkSizeWarningLimit: 1000,