diff --git a/src/client/app/components/HeaderButtonsComponent.tsx b/src/client/app/components/HeaderButtonsComponent.tsx index 5e289288e..dfbf0ef51 100644 --- a/src/client/app/components/HeaderButtonsComponent.tsx +++ b/src/client/app/components/HeaderButtonsComponent.tsx @@ -209,7 +209,7 @@ export default function HeaderButtonsComponent() { disabled={state.shouldAdminButtonDisabled} tag={Link} to="/admin"> - + ('graph'); + - const sectionTitleStyle: React.CSSProperties = { - fontWeight: 'bold', - margin: 0, - paddingBottom: '5px' - }; const titleStyle: React.CSSProperties = { - textAlign: 'center' + textAlign: 'start', + paddingLeft: '10px', + margin: 0 }; const tooltipStyle = { display: 'inline', fontSize: '50%' }; return ( -
- -
-

- +
+
+ + +

+

-
-
-
-

:

-
- -
+
+ +
+
+
-
diff --git a/src/client/app/components/admin/AdminSideBar.tsx b/src/client/app/components/admin/AdminSideBar.tsx new file mode 100644 index 000000000..da50823b7 --- /dev/null +++ b/src/client/app/components/admin/AdminSideBar.tsx @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react'; +import translate from '../../utils/translate'; + +interface SidebarProps { + onSelectPreference: (preference: string) => void, + selectedPreference: string; +} + +/** + * Admin navigation side bar + * @param props Props for side bar + * @returns Admin navigation side bar + */ +export default function AdminSideBar(props: SidebarProps): React.JSX.Element { + return ( +
+
+ + + + +
+ +
+ ); +} diff --git a/src/client/app/components/admin/CreateUserModalComponent.tsx b/src/client/app/components/admin/CreateUserModalComponent.tsx new file mode 100644 index 000000000..7d1d62ded --- /dev/null +++ b/src/client/app/components/admin/CreateUserModalComponent.tsx @@ -0,0 +1,167 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react'; +import { useState } from 'react'; +import { Alert, Button, Col, Container, FormFeedback, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader, Row } from 'reactstrap'; +import { FormattedMessage } from 'react-intl'; +import { UserRole } from '../../types/items'; +import { userApi } from '../../redux/api/userApi'; +import { NewUser } from '../../types/items'; +import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; +import translate from '../../utils/translate'; + +/** + * Defines the create user modal form + * @returns CreateUserModal component + */ +export default function CreateUserModal() { + const [showModal, setShowModal] = useState(false); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [passwordMatch, setPasswordMatch] = useState(true); + const [role, setRole] = useState(''); + const [createUser] = userApi.useCreateUserMutation(); + + const handleShowModal = () => setShowModal(true); + const handleCloseModal = () => { + setShowModal(false); + resetForm(); + }; + + const resetForm = () => { + setEmail(''); + setPassword(''); + setConfirmPassword(''); + setRole(''); + setPasswordMatch(true); + }; + + const handleSubmit = async () => { + if (password === confirmPassword) { + setPasswordMatch(true); + const userRole: UserRole = UserRole[role as keyof typeof UserRole]; + const newUser: NewUser = { email, role: userRole, password }; + createUser(newUser) + .unwrap() + .then(() => { + showSuccessNotification(translate('users.successfully.create.user')); + handleCloseModal(); + }) + .catch(() => { + showErrorNotification(translate('users.failed.to.create.user')); + }); + } else { + setPasswordMatch(false); + } + }; + + const isFormValid = email && password && confirmPassword === password && role; + + return ( + <> + + + + + + + + + + + + setEmail(e.target.value)} + required + /> + + + + {!passwordMatch && ( + + + {translate('user.password.mismatch')} + + + )} + + + + + setPassword(e.target.value)} + required + /> + + + + + + setConfirmPassword(e.target.value)} + invalid={confirmPassword !== password && confirmPassword !== ''} + required + /> + + + + + + + + + + + setRole(e.target.value)} + invalid={!role} + required + > + + {Object.entries(UserRole).map(([role, val]) => ( + + ))} + + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/client/app/components/admin/PreferencesComponent.tsx b/src/client/app/components/admin/PreferencesComponent.tsx index fee69356a..7509fbcee 100644 --- a/src/client/app/components/admin/PreferencesComponent.tsx +++ b/src/client/app/components/admin/PreferencesComponent.tsx @@ -17,12 +17,16 @@ import translate from '../../utils/translate'; import TimeZoneSelect from '../TimeZoneSelect'; import { defaultAdminState } from '../../redux/slices/adminSlice'; +interface PreferencesProps { + selectedPreference: string; +} // TODO: Add warning for invalid data /** + * @param props variables passed in to define * @returns Preferences Component for Administrative use */ -export default function PreferencesComponent() { +export default function PreferencesComponent(props: PreferencesProps) { const { data: adminPreferences = defaultAdminState } = preferencesApi.useGetPreferencesQuery(); const [localAdminPref, setLocalAdminPref] = React.useState(cloneDeep(adminPreferences)); const [submitPreferences] = preferencesApi.useSubmitPreferencesMutation(); @@ -47,284 +51,298 @@ export default function PreferencesComponent() { successMessage='updated.preferences' failureMessage='failed.to.submit.changes' /> -
-

- {`${translate('default.site.title')}:`} -

- makeLocalChanges('displayTitle', e.target.value)} - maxLength={50} - /> -
-
-

- : -

- { - Object.values(ChartTypes).map(chartType => ( -
- -
-
-

- {translate('default.area.unit')} + {/* File Settings */} +

+

+ {`${translate('default.warning.file.size')}:`} +

+ makeLocalChanges('defaultWarningFileSize', e.target.value)} + maxLength={50} /> +
+
+

+ {`${translate('default.file.size.limit')}:`} +

+ makeLocalChanges('defaultFileSizeLimit', e.target.value)} + maxLength={50} /> +
+ + } -

-
-
-
-

- {translate('default.language')} -

-
-
+
+

+ {`${translate('default.meter.maximum.value')}:`} +

+ makeLocalChanges('defaultMeterMaximumValue', e.target.value)} + maxLength={50} /> - English - -
-
-
+
+

+ {`${translate('default.meter.minimum.date')}:`} +

+ makeLocalChanges('defaultMeterMinimumDate', e.target.value)} + placeholder='YYYY-MM-DD HH:MM:SS' /> - Français - -
-
-
+
+

+ {`${translate('default.meter.maximum.date')}:`} +

+ makeLocalChanges('defaultMeterMaximumDate', e.target.value)} + placeholder='YYYY-MM-DD HH:MM:SS' /> - Español - -
-
-
-

- {`${translate('default.time.zone')}:`} -

- makeLocalChanges('defaultTimezone', e)} /> -
-
-

- {`${translate('default.warning.file.size')}:`} -

- makeLocalChanges('defaultWarningFileSize', e.target.value)} - maxLength={50} - /> -
-
-

- {`${translate('default.file.size.limit')}:`} -

- makeLocalChanges('defaultFileSizeLimit', e.target.value)} - maxLength={50} - /> -
-
-

- {`${translate('default.meter.reading.frequency')}:`} -

- makeLocalChanges('defaultMeterReadingFrequency', e.target.value)} - /> -
-
-

- {`${translate('default.meter.minimum.value')}:`} -

- makeLocalChanges('defaultMeterMinimumValue', e.target.value)} - maxLength={50} - /> -
-
-

- {`${translate('default.meter.maximum.value')}:`} -

- makeLocalChanges('defaultMeterMaximumValue', e.target.value)} - maxLength={50} - /> -
-
-

- {`${translate('default.meter.minimum.date')}:`} -

- makeLocalChanges('defaultMeterMinimumDate', e.target.value)} - placeholder='YYYY-MM-DD HH:MM:SS' - /> -
-
-

- {`${translate('default.meter.maximum.date')}:`} -

- makeLocalChanges('defaultMeterMaximumDate', e.target.value)} - placeholder='YYYY-MM-DD HH:MM:SS' - /> -
-
-

- {`${translate('default.meter.reading.gap')}:`} -

- makeLocalChanges('defaultMeterReadingGap', e.target.value)} - maxLength={50} - /> -
-
-

- {`${translate('default.meter.maximum.errors')}:`} -

- makeLocalChanges('defaultMeterMaximumErrors', e.target.value)} - maxLength={50} - /> -
-
-

- {`${translate('default.meter.disable.checks')}:`} -

- makeLocalChanges('defaultMeterDisableChecks', e.target.value)}> - {Object.keys(TrueFalseType).map(key => { - return (); - })} - -
-
-

- : -

- makeLocalChanges('defaultHelpUrl', e.target.value)} - /> -
+
+
+

+ {`${translate('default.meter.reading.gap')}:`} +

+ makeLocalChanges('defaultMeterReadingGap', e.target.value)} + maxLength={50} + /> +
+
+

+ {`${translate('default.meter.maximum.errors')}:`} +

+ makeLocalChanges('defaultMeterMaximumErrors', e.target.value)} + maxLength={50} + /> +
+
+

+ {`${translate('default.meter.disable.checks')}:`} +

+ makeLocalChanges('defaultMeterDisableChecks', e.target.value)}> + {Object.keys(TrueFalseType).map(key => { + return (); + })} + +
+ + } diff --git a/src/client/app/translations/data.ts b/src/client/app/translations/data.ts index 0b21ce823..140ff6b1d 100644 --- a/src/client/app/translations/data.ts +++ b/src/client/app/translations/data.ts @@ -14,7 +14,8 @@ const LocaleTranslationData = { "action": "Action", "add.new.meters": "Add new meters", "admin.only": "Admin Only", - "admin.panel": "Admin Panel", + "admin.settings": "Admin Settings", + "all.settings": "Show All Settings", "alphabetically": "Alphabetically", "area": "Area:", "area.but.no.unit": "You have entered a nonzero area but no area unit.", @@ -129,7 +130,6 @@ const LocaleTranslationData = { "defaultGraphicUnit": "Default Graphic Unit:", "default.language": "Default Language", "default.meter.reading.frequency": "Default meter reading frequency", - "default.site.title": "Default Site Title", "default.warning.file.size": "Default Warning File Size", "default.file.size.limit": "Default File Size Limit", "default.help.url": "Help URL", @@ -223,7 +223,7 @@ const LocaleTranslationData = { "help.admin.groupcreate": "This page allows admins to create groups. Please visit {link} for further details and information.", "help.admin.groupedit": "This page allows admins to edit groups. Please visit {link} for further details and information.", "help.admin.groupview": "This page allows admins to view groups. Please visit {link} for further details and information.", - "help.admin.header": "This page is to allow site administrators to set values and manage users. Please visit {link} for further details and information.", + "help.admin.header": "This page is to allow site administrators to set values. Please visit {link} for further details and information.", "help.admin.mapview": "This page allows admins to view and edit maps. Please visit {link} for further details and information", "help.admin.metercreate": "This page allows admins to create meters. Please visit {link} for further details and information.", "help.admin.meteredit": "This page allows admins to edit meters. Please visit {link} for further details and information.", @@ -366,6 +366,7 @@ const LocaleTranslationData = { "meter.type": "Type:", "minute": "Minute", "min": "min", + "misc": "Miscellaneous", "more.energy": "more energy", "more.options": "More Options", "name": "Name:", @@ -414,6 +415,7 @@ const LocaleTranslationData = { "show": "Show", "show.grid": "Show grid", "show.options": "Show options", + "site.title": "Site Title", "sort": "Sort", "submit": "Submit", "submitting": "submitting", @@ -510,7 +512,8 @@ const LocaleTranslationData = { "action": "Action\u{26A1}", "add.new.meters": "Ajouter de Nouveaux Mètres", "admin.only": "Uniquement pour Les Administrateurs", - "admin.panel": "Panneau d'administration", + "admin.settings": "Admin Settings\u{26A1}", + "all.settings": "Show All Settings\u{26A1}", "alphabetically": "Alphabétiquement", "area": "Région:", "area.but.no.unit": "You have entered a nonzero area but no area unit.\u{26A1}", @@ -625,7 +628,6 @@ const LocaleTranslationData = { "defaultGraphicUnit": "Default Graphic Unit:\u{26A1}", "default.language": "Langue par Défaut", "default.meter.reading.frequency": "Default meter reading frequency\u{26A1}", - "default.site.title": "Titre du Site par Défaut", "default.warning.file.size": "Taille du fichier d'avertissement par défaut", "default.file.size.limit": "Limite de taille de fichier par défaut", "default.help.url": "Help URL\u{26A1}", @@ -862,6 +864,7 @@ const LocaleTranslationData = { "meter.type": "Type de Mèters", "minute": "Minute\u{26A1}", "min": "min\u{26A1}", + "misc": "Miscellaneous\u{26A1}", "more.energy": "plus d'énergie", "more.options": "More Options\u{26A1}", "name": "Nom:", @@ -910,6 +913,7 @@ const LocaleTranslationData = { "show": "Montrer", "show.grid": "Show grid\u{26A1}", "show.options": "Options de désancrage", + "site.title": "Site Title\u{26A1}", "sort": "Trier", "submit": "Soumettre", "submitting": "submitting\u{26A1}", @@ -1006,7 +1010,8 @@ const LocaleTranslationData = { "action": "Acción", "add.new.meters": "Agregar nuevos medidores", "admin.only": "Solo administrador", - "admin.panel": "Panel de administración", + "admin.settings": "Admin Settings\u{26A1}", + "all.settings": "Show All Settings\u{26A1}", "alphabetically": "Alfabéticamente", "area": "Área:", "area.but.no.unit": "Ha ingresado un área distinta a cero sin unidad de área.", @@ -1120,10 +1125,9 @@ const LocaleTranslationData = { "default.graph.settings": "Configuraciones predeterminadas del gráfico", "defaultGraphicUnit": "Unidad del gráfico predeterminada:", "default.language": "Idioma predeterminado", - "default.meter.reading.frequency": "Frecuencia de lectura del medidor predeterminada", - "default.site.title": "Título predeterminado de la página ", - "default.warning.file.size": "Advertencia predeterminada de tamaño del archivo", - "default.file.size.limit": "Límite predeterminado de tamaño del archivo", + "default.meter.reading.frequency": "Frecuencia de la lectura del medidor predeterminada", + "default.warning.file.size": "Advertencia Predeterminada del Tamaño de Archivo", + "default.file.size.limit": "El límite del tamaño del archivo predeterminado", "default.help.url": "URL Ayuda", "default.time.zone": "Zona de horario predeterminada", "default.meter.minimum.value": "Revisión del valor de lectura mínima del medidor predeterminado", @@ -1358,6 +1362,7 @@ const LocaleTranslationData = { "meter.type": "Tipo:", "minute": "Minuto", "min": "mínimo", + "misc": "Miscellaneous\u{26A1}", "more.energy": "más energía", "more.options": "More Options\u{26A1}", "name": "Nombre:", @@ -1406,6 +1411,7 @@ const LocaleTranslationData = { "show": "Mostrar", "show.grid": "Mostrar rejilla", "show.options": "Mostrar opciones", + "site.title": "Site Title\u{26A1}", "sort": "Ordenar", "submit": "Enviar", "submitting": "Enviando",