1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2025-06-16 13:46:16 +02:00

Compare commits

..

20 Commits

Author SHA1 Message Date
71beeb46f1 Bump v4.5.1.
* Add initial data to ForisForm children.
 * Update .pot file.
2020-05-07 16:34:33 +02:00
060a0489e1 Merge branch 'translations' into 'dev'
Update translations (.pot).

See merge request turris/reforis/foris-js!108
2020-05-07 16:31:44 +02:00
ae49b246cd Update translations (.pot). 2020-05-07 16:13:03 +02:00
27c37eb74b Merge branch 'add-inital-form-data-to-children-of-foris-form' into 'dev'
Add initial form data to children of the ForisForm.

See merge request turris/reforis/foris-js!107
2020-05-07 16:03:51 +02:00
cd708fa294 Add initial form data to children of the ForisForm. 2020-05-07 16:00:02 +02:00
8ec0392852 Merge branch 'dev' into 'master'
Dev

See merge request turris/reforis/foris-js!106
2020-03-26 16:29:27 +01:00
27a5e62d9a Merge branch 'fix-pdfmake' into 'dev'
Fix pdfmake.

See merge request turris/reforis/foris-js!105
2020-03-25 21:34:38 +01:00
aeaad4aa72 Bump v4.5.0. 2020-03-25 21:26:59 +01:00
256a000d61 NPM update. 2020-03-25 21:17:12 +01:00
c78ed9a5d0 NPM audit fix. 2020-03-25 21:17:12 +01:00
bded10211a Use pdfmake from globals. 2020-03-25 21:17:12 +01:00
25ac6cf1e9 Remove pdfmake. 2020-03-25 21:17:12 +01:00
9a2547a6c2 Merge branch 'dev' into 'master'
Release v4.4.0.

See merge request turris/reforis/foris-js!104
2020-03-13 22:16:42 +01:00
7968c7af4a Merge branch 'fix-hostname-validation-regex' into 'dev'
Fix hostname validation regex

See merge request turris/reforis/foris-js!103
2020-03-13 21:41:49 +01:00
4b94c470c3 Bump v4.4.0. 2020-03-13 21:37:21 +01:00
e1b5a25ddd Update domain vadlidation. 2020-03-13 21:37:21 +01:00
95af86c776 NPM audit fix. 2020-03-13 21:37:21 +01:00
02b5583712 Move vadliadtions and forisUrls to utils. 2020-03-13 21:37:21 +01:00
2f4d757a1a Merge branch 'dev' into 'master'
Release v4.3.1.

See merge request turris/reforis/foris-js!102
2020-03-06 14:10:21 +01:00
3c7a67783f Merge branch 'add-logout-url' into 'dev'
Add logout URL.

See merge request turris/reforis/foris-js!101
2020-03-06 14:06:34 +01:00
14 changed files with 6769 additions and 3091 deletions

9637
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "foris", "name": "foris",
"version": "4.3.1", "version": "4.5.1",
"description": "Set of components and utils for Foris and its plugins.", "description": "Set of components and utils for Foris and its plugins.",
"author": "CZ.NIC, z.s.p.o.", "author": "CZ.NIC, z.s.p.o.",
"repository": { "repository": {
@ -14,10 +14,9 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"main": "./src/index.js", "main": "./src/index.js",
"dependencies": { "dependencies": {
"axios": "^0.19.1", "axios": "^0.19.2",
"immutability-helper": "3.0.1", "immutability-helper": "3.0.1",
"moment": "^2.24.0", "moment": "^2.24.0",
"pdfmake": "^0.1.63",
"qrcode.react": "^0.9.3", "qrcode.react": "^0.9.3",
"react-datetime": "^2.16.3", "react-datetime": "^2.16.3",
"react-uid": "^2.2.0" "react-uid": "^2.2.0"
@ -30,26 +29,26 @@
"react-router-dom": "^5.1.2" "react-router-dom": "^5.1.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.7.7", "@babel/cli": "^7.8.4",
"@babel/core": "^7.7.7", "@babel/core": "^7.9.0",
"@babel/plugin-transform-runtime": "^7.7.6", "@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.7.7", "@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.7.4", "@babel/preset-react": "^7.9.4",
"@fortawesome/fontawesome-free": "^5.12.0", "@fortawesome/fontawesome-free": "^5.13.0",
"@testing-library/react": "^8.0.9", "@testing-library/react": "^8.0.9",
"babel-loader": "^8.0.6", "babel-loader": "^8.1.0",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-reforis": "^1.0.0", "eslint-config-reforis": "^1.0.0",
"jest": "^24.9.0", "jest": "^25.2.0",
"jest-mock-axios": "^3.2.0", "jest-mock-axios": "^3.2.0",
"moment-timezone": "^0.5.27", "moment-timezone": "^0.5.28",
"prop-types": "15.7.2", "prop-types": "15.7.2",
"react": "16.9.0", "react": "16.9.0",
"react-dom": "16.9.0", "react-dom": "16.9.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-styleguidist": "^10.4.2", "react-styleguidist": "^10.6.2",
"snapshot-diff": "^0.5.1" "snapshot-diff": "^0.7.0"
}, },
"scripts": { "scripts": {
"lint": "eslint src", "lint": "eslint src",

View File

@ -9,7 +9,7 @@ import {
useCallback, useEffect, useReducer, useState, useCallback, useEffect, useReducer, useState,
} from "react"; } from "react";
import { ForisURLs } from "../forisUrls"; import { ForisURLs } from "../utils/forisUrls";
import { import {
API_ACTIONS, API_METHODS, API_STATE, getErrorPayload, HEADERS, TIMEOUT, API_ACTIONS, API_METHODS, API_STATE, getErrorPayload, HEADERS, TIMEOUT,
} from "./utils"; } from "./utils";

View File

@ -10,7 +10,7 @@ import PropTypes from "prop-types";
import { useAPIPost } from "../api/hooks"; import { useAPIPost } from "../api/hooks";
import { API_STATE } from "../api/utils"; import { API_STATE } from "../api/utils";
import { ForisURLs } from "../forisUrls"; import { ForisURLs } from "../utils/forisUrls";
import { Button } from "../bootstrap/Button"; import { Button } from "../bootstrap/Button";
import { import {

View File

@ -36,7 +36,7 @@ WiFiForm.defaultProps = {
}; };
export default function WiFiForm({ export default function WiFiForm({
formData, formErrors, setFormValue, hasGuestNetwork, ...props formData, formErrors, setFormValue, hasGuestNetwork, disabled,
}) { }) {
return formData.devices.map((device) => ( return formData.devices.map((device) => (
<DeviceForm <DeviceForm
@ -45,8 +45,7 @@ export default function WiFiForm({
formErrors={(formErrors || [])[device.id]} formErrors={(formErrors || [])[device.id]}
setFormValue={setFormValue} setFormValue={setFormValue}
hasGuestNetwork={hasGuestNetwork} hasGuestNetwork={hasGuestNetwork}
disabled={disabled}
{...props}
/> />
)); ));
} }

View File

@ -9,7 +9,7 @@ import React, { useState } from "react";
import QRCode from "qrcode.react"; import QRCode from "qrcode.react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { ForisURLs } from "../../forisUrls"; import { ForisURLs } from "../../utils/forisUrls";
import { Button } from "../../bootstrap/Button"; import { Button } from "../../bootstrap/Button";
import { import {
Modal, ModalBody, ModalFooter, ModalHeader, Modal, ModalBody, ModalFooter, ModalHeader,

View File

@ -5,22 +5,36 @@
* See /LICENSE for more information. * See /LICENSE for more information.
*/ */
import pdfMake from "pdfmake";
export function createAndDownloadPdf(SSID, password) { export function createAndDownloadPdf(SSID, password) {
const docDefinition = { const docDefinition = {
content: [ content: [
{ {
text: "Wi-Fi", style: "header", fontSize: 55, alignment: "center", text: "Wi-Fi",
style: "header",
fontSize: 55,
alignment: "center",
}, },
{ {
qr: toQRCodeContent(SSID, password), fit: "350", margin: [0, 80], alignment: "center", qr: toQRCodeContent(SSID, password),
fit: "350",
margin: [0, 80],
alignment: "center",
},
{
text: `SSID: ${SSID}`,
fontSize: 25,
alignment: "center",
},
{
text: `Password: ${password}`,
fontSize: 25,
alignment: "center",
}, },
{ text: `SSID: ${SSID}`, fontSize: 25, alignment: "center" },
{ text: `Password: ${password}`, fontSize: 25, alignment: "center" },
], ],
}; };
pdfMake.createPdf(docDefinition).download("wifi.pdf");
// pdfmake is exposed by reForis main application. Thus we can use it from globals.
window.pdfMake.createPdf(docDefinition).download("wifi.pdf");
} }
export function toQRCodeContent(SSID, password) { export function toQRCodeContent(SSID, password) {

View File

@ -12,7 +12,7 @@ import {
validateIPv6Address, validateIPv6Address,
validateIPv6Prefix, validateIPv6Prefix,
validateMAC, validateMAC,
} from "validations"; } from "utils/validations";
describe("Validation functions", () => { describe("Validation functions", () => {
it("validateIPv4Address valid", () => { it("validateIPv4Address valid", () => {

View File

@ -149,6 +149,7 @@ export function ForisForm({
const childrenWithFormProps = React.Children.map( const childrenWithFormProps = React.Children.map(
children, children,
(child) => React.cloneElement(child, { (child) => React.cloneElement(child, {
initialData: formState.initialData,
formData: formState.data, formData: formState.data,
formErrors: formState.errors, formErrors: formState.errors,
setFormValue: onFormChangeHandler, setFormValue: onFormChangeHandler,

View File

@ -67,7 +67,7 @@ export { useClickOutside } from "./utils/hooks";
export { toLocaleDateString } from "./utils/datetime"; export { toLocaleDateString } from "./utils/datetime";
// Foris URL // Foris URL
export { ForisURLs, REFORIS_URL_PREFIX } from "./forisUrls"; export { ForisURLs, REFORIS_URL_PREFIX } from "./utils/forisUrls";
// Validation // Validation
export { export {
@ -78,7 +78,7 @@ export {
validateDUID, validateDUID,
validateMAC, validateMAC,
validateMultipleEmails, validateMultipleEmails,
} from "./validations"; } from "./utils/validations";
// Alert context // Alert context
export { AlertContextProvider, useAlert } from "./alertContext/AlertContext"; export { AlertContextProvider, useAlert } from "./alertContext/AlertContext";

View File

@ -23,7 +23,7 @@ const REs = {
IPv4: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, IPv4: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
IPv6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/, IPv6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
IPv6Prefix: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))$/, IPv6Prefix: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))$/,
domain: /^([a-zA-Z0-9-]{1,63}\.?)*$/, domain: /^[A-Za-z0-9][A-Za-z0-9.-]{1,255}$/,
DUID: /^([0-9a-fA-F]{2}){4}([0-9a-fA-F]{2})*$/, DUID: /^([0-9a-fA-F]{2}){4}([0-9a-fA-F]{2})*$/,
MAC: /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/, MAC: /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/,
MultipleEmails: /^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( *, *[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$/, MultipleEmails: /^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( *, *[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$/,

View File

@ -7,7 +7,7 @@
/* eslint no-console: "off" */ /* eslint no-console: "off" */
import { ForisURLs } from "../forisUrls"; import { ForisURLs } from "../utils/forisUrls";
const PROTOCOL = window.location.protocol === "http:" ? "ws" : "wss"; const PROTOCOL = window.location.protocol === "http:" ? "ws" : "wss";

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-02-20 17:28+0100\n" "POT-Creation-Date: 2020-05-07 16:12+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,34 +17,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.8.0\n" "Generated-By: Babel 2.8.0\n"
#: src/validations.js:13
msgid "This is not a valid IPv4 address."
msgstr ""
#: src/validations.js:14
msgid "This is not a valid IPv6 address."
msgstr ""
#: src/validations.js:15
msgid "This is not a valid IPv6 prefix."
msgstr ""
#: src/validations.js:16
msgid "This is not a valid domain name."
msgstr ""
#: src/validations.js:17
msgid "This is not a valid DUID."
msgstr ""
#: src/validations.js:18
msgid "This is not a valid MAC address."
msgstr ""
#: src/validations.js:19
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""
#: src/api/utils.js:58 #: src/api/utils.js:58
msgid "The session is expired. Please log in again." msgid "The session is expired. Please log in again."
msgstr "" msgstr ""
@ -107,15 +79,15 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: src/common/WiFiSettings/WiFiForm.js:82 #: src/common/WiFiSettings/WiFiForm.js:81
msgid "Wi-Fi ${deviceID + 1}" msgid "Wi-Fi ${deviceID + 1}"
msgstr "" msgstr ""
#: src/common/WiFiSettings/WiFiForm.js:84 #: src/common/WiFiSettings/WiFiForm.js:83
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
#: src/common/WiFiSettings/WiFiForm.js:215 #: src/common/WiFiSettings/WiFiForm.js:214
msgid "auto" msgid "auto"
msgstr "" msgstr ""
@ -222,7 +194,7 @@ msgstr ""
msgid "Settings saved successfully" msgid "Settings saved successfully"
msgstr "" msgstr ""
#: src/form/components/ForisForm.js:165 #: src/form/components/ForisForm.js:166
msgid "Changes you made may not be saved. Are you sure you want to leave?" msgid "Changes you made may not be saved. Are you sure you want to leave?"
msgstr "" msgstr ""
@ -242,3 +214,31 @@ msgstr ""
msgid "An error occurred while fetching data." msgid "An error occurred while fetching data."
msgstr "" msgstr ""
#: src/utils/validations.js:13
msgid "This is not a valid IPv4 address."
msgstr ""
#: src/utils/validations.js:14
msgid "This is not a valid IPv6 address."
msgstr ""
#: src/utils/validations.js:15
msgid "This is not a valid IPv6 prefix."
msgstr ""
#: src/utils/validations.js:16
msgid "This is not a valid domain name."
msgstr ""
#: src/utils/validations.js:17
msgid "This is not a valid DUID."
msgstr ""
#: src/utils/validations.js:18
msgid "This is not a valid MAC address."
msgstr ""
#: src/utils/validations.js:19
msgid "Doesn't contain a list of emails separated by commas."
msgstr ""