@truedat/core 4.58.7 → 4.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +2 -2
- package/src/api.js +3 -0
- package/src/components/AdminMenu.js +2 -0
- package/src/components/__tests__/__snapshots__/AdminMenu.spec.js.snap +13 -0
- package/src/hooks/index.js +1 -0
- package/src/hooks/useLocales.js +9 -0
- package/src/hooks/useMessages.js +15 -4
- package/src/i18n/components/EditableCell.js +53 -0
- package/src/i18n/components/I18nRoutes.js +24 -0
- package/src/i18n/components/MessageForm.js +141 -0
- package/src/i18n/components/Messages.js +124 -0
- package/src/i18n/components/MessagesTable.js +86 -0
- package/src/i18n/components/NewMessage.js +47 -0
- package/src/i18n/components/__tests__/EditableCell.spec.js +54 -0
- package/src/i18n/components/__tests__/I18nRoutes.spec.js +14 -0
- package/src/i18n/components/__tests__/MessageForm.spec.js +28 -0
- package/src/i18n/components/__tests__/Messages.spec.js +33 -0
- package/src/i18n/components/__tests__/NewMessage.spec.js +21 -0
- package/src/i18n/components/__tests__/__snapshots__/EditableCell.spec.js.snap +30 -0
- package/src/i18n/components/__tests__/__snapshots__/I18nRoutes.spec.js.snap +3 -0
- package/src/i18n/components/__tests__/__snapshots__/MessageForm.spec.js.snap +91 -0
- package/src/i18n/components/__tests__/__snapshots__/Messages.spec.js.snap +193 -0
- package/src/i18n/components/__tests__/__snapshots__/NewMessage.spec.js.snap +127 -0
- package/src/i18n/components/index.js +3 -0
- package/src/messages/en.js +18 -0
- package/src/messages/es.js +18 -0
- package/src/routes.js +6 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.59.0",
|
|
4
4
|
"description": "Truedat Web Core",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -117,5 +117,5 @@
|
|
|
117
117
|
"react-dom": ">= 16.8.6 < 17",
|
|
118
118
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
119
119
|
},
|
|
120
|
-
"gitHead": "
|
|
120
|
+
"gitHead": "7d2cd52d47210cdd67cf3521697dab38c963a570"
|
|
121
121
|
}
|
package/src/api.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export const API_COMMENTS = "/api/business_concepts/comments";
|
|
2
2
|
export const API_LOCALE_MESSAGES = "/api/locales/:lang/messages";
|
|
3
|
+
export const API_LOCALES = "/api/locales";
|
|
4
|
+
export const API_MESSAGE = "/api/messages/:id";
|
|
5
|
+
export const API_MESSAGES = "/api/messages";
|
|
@@ -3,6 +3,7 @@ import { useAuthorized } from "../hooks";
|
|
|
3
3
|
import {
|
|
4
4
|
CONFIGURATIONS,
|
|
5
5
|
JOBS,
|
|
6
|
+
I18N_MESSAGES,
|
|
6
7
|
RELATION_TAGS,
|
|
7
8
|
SOURCES,
|
|
8
9
|
SUBSCRIPTIONS,
|
|
@@ -17,6 +18,7 @@ const items = [
|
|
|
17
18
|
{ name: "sources", routes: [SOURCES] },
|
|
18
19
|
{ name: "jobs", routes: [JOBS] },
|
|
19
20
|
{ name: "configurations", routes: [CONFIGURATIONS] },
|
|
21
|
+
{ name: "i18nMessages", routes: [I18N_MESSAGES] },
|
|
20
22
|
];
|
|
21
23
|
|
|
22
24
|
export const AdminMenu = () => {
|
|
@@ -108,6 +108,19 @@ exports[`<AdminMenu /> matches the latest snapshot 1`] = `
|
|
|
108
108
|
Configuration
|
|
109
109
|
</span>
|
|
110
110
|
</a>
|
|
111
|
+
<a
|
|
112
|
+
aria-checked="false"
|
|
113
|
+
class="item"
|
|
114
|
+
href="/i18n/messages"
|
|
115
|
+
name="i18nMessages"
|
|
116
|
+
role="option"
|
|
117
|
+
>
|
|
118
|
+
<span
|
|
119
|
+
class="text"
|
|
120
|
+
>
|
|
121
|
+
Translations
|
|
122
|
+
</span>
|
|
123
|
+
</a>
|
|
111
124
|
</div>
|
|
112
125
|
</div>
|
|
113
126
|
</div>
|
package/src/hooks/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import useSWR from "swr";
|
|
2
|
+
import { API_LOCALES } from "../api";
|
|
3
|
+
import { apiJson } from "../services/api";
|
|
4
|
+
|
|
5
|
+
export const useLocales = () => {
|
|
6
|
+
const { data, error, mutate } = useSWR(API_LOCALES, apiJson);
|
|
7
|
+
const locales = data?.data?.data;
|
|
8
|
+
return { locales, error, loading: !error && !data, mutate };
|
|
9
|
+
};
|
package/src/hooks/useMessages.js
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { compile } from "path-to-regexp";
|
|
2
2
|
import useSWRImmutable from "swr/immutable";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import useSWRMutations from "swr/mutation";
|
|
4
|
+
import { API_LOCALE_MESSAGES, API_MESSAGE, API_MESSAGES } from "../api";
|
|
5
|
+
import { apiJson, apiJsonPatch, apiJsonPost } from "../services/api";
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
+
const toApiLocaleMessagesPath = compile(API_LOCALE_MESSAGES);
|
|
8
|
+
const toApiMessagePath = compile(API_MESSAGE);
|
|
7
9
|
|
|
8
10
|
export const useMessages = (lang) => {
|
|
9
|
-
const url =
|
|
11
|
+
const url = toApiLocaleMessagesPath({ lang });
|
|
10
12
|
const { data, error } = useSWRImmutable(url, apiJson);
|
|
11
13
|
const messages = data?.data;
|
|
12
14
|
return { messages, error, loading: !error && !data };
|
|
13
15
|
};
|
|
16
|
+
|
|
17
|
+
export const useMessagePatch = (id) => {
|
|
18
|
+
const url = toApiMessagePath({ id });
|
|
19
|
+
return useSWRMutations(url, (url, { arg }) => apiJsonPatch(url, arg));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const useMessagePost = () => {
|
|
23
|
+
return useSWRMutations(API_MESSAGES, (url, { arg }) => apiJsonPost(url, arg));
|
|
24
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import { Table, Input } from "semantic-ui-react";
|
|
5
|
+
|
|
6
|
+
const MAX_LENGTH = 255;
|
|
7
|
+
|
|
8
|
+
export default function EditableCell({
|
|
9
|
+
value: propValue,
|
|
10
|
+
placeholder,
|
|
11
|
+
onChange,
|
|
12
|
+
}) {
|
|
13
|
+
const [editionMode, setEditMode] = useState(false);
|
|
14
|
+
const [value, setValue] = useState(propValue || "");
|
|
15
|
+
|
|
16
|
+
const onBlur = () => {
|
|
17
|
+
if (_.isEmpty(value)) setValue(propValue);
|
|
18
|
+
else if (value != propValue) onChange(value);
|
|
19
|
+
setEditMode(false);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return editionMode ? (
|
|
23
|
+
<Table.Cell
|
|
24
|
+
className="no-padding"
|
|
25
|
+
width={5}
|
|
26
|
+
content={
|
|
27
|
+
<Input
|
|
28
|
+
fluid
|
|
29
|
+
maxLength={MAX_LENGTH}
|
|
30
|
+
onBlur={onBlur}
|
|
31
|
+
placeholder={placeholder}
|
|
32
|
+
onChange={(e) => setValue(e.target.value)}
|
|
33
|
+
value={value}
|
|
34
|
+
autoFocus
|
|
35
|
+
/>
|
|
36
|
+
}
|
|
37
|
+
/>
|
|
38
|
+
) : (
|
|
39
|
+
<Table.Cell
|
|
40
|
+
className="cursor-pointer"
|
|
41
|
+
width={5}
|
|
42
|
+
content={value}
|
|
43
|
+
onClick={() => setEditMode(true)}
|
|
44
|
+
onFocus={() => setEditMode(true)}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
EditableCell.propTypes = {
|
|
50
|
+
value: PropTypes.string,
|
|
51
|
+
placeholder: PropTypes.string,
|
|
52
|
+
onChange: PropTypes.func,
|
|
53
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Route, Switch } from "react-router-dom";
|
|
3
|
+
import { Unauthorized } from "@truedat/core/components";
|
|
4
|
+
import { useAuthorized } from "@truedat/core/hooks";
|
|
5
|
+
import { I18N, I18N_MESSAGES, I18N_MESSAGES_NEW } from "@truedat/core/routes";
|
|
6
|
+
import Messages from "./Messages";
|
|
7
|
+
import NewMessage from "./NewMessage";
|
|
8
|
+
|
|
9
|
+
export const AuthorizedI18nRoutes = () => (
|
|
10
|
+
<Switch>
|
|
11
|
+
<Route exact path={I18N_MESSAGES} render={() => <Messages />} />
|
|
12
|
+
<Route exact path={I18N_MESSAGES_NEW} render={() => <NewMessage />} />
|
|
13
|
+
</Switch>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export default function I18nRoutes() {
|
|
17
|
+
const authorized = useAuthorized();
|
|
18
|
+
return (
|
|
19
|
+
<Route
|
|
20
|
+
path={I18N}
|
|
21
|
+
render={() => (authorized ? <AuthorizedI18nRoutes /> : <Unauthorized />)}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { useForm, Controller } from "react-hook-form";
|
|
5
|
+
import { useIntl } from "react-intl";
|
|
6
|
+
import { Button, Form, Segment, Header, Loader } from "semantic-ui-react";
|
|
7
|
+
import { HistoryBackButton } from "@truedat/core/components";
|
|
8
|
+
import { useLocales } from "@truedat/core/hooks";
|
|
9
|
+
|
|
10
|
+
export const LangForm = ({
|
|
11
|
+
control,
|
|
12
|
+
lang: { lang, id },
|
|
13
|
+
errors,
|
|
14
|
+
formatMessage,
|
|
15
|
+
}) => {
|
|
16
|
+
return (
|
|
17
|
+
<Segment>
|
|
18
|
+
<Header
|
|
19
|
+
as="h4"
|
|
20
|
+
content={formatMessage({ id: `i18n.messages.locale.${lang}` })}
|
|
21
|
+
/>
|
|
22
|
+
<Controller
|
|
23
|
+
control={control}
|
|
24
|
+
name={`langs.${id}.definition`}
|
|
25
|
+
rules={{
|
|
26
|
+
required: formatMessage(
|
|
27
|
+
{ id: "form.validation.required" },
|
|
28
|
+
{ prop: formatMessage({ id: "i18n.message.props.definition" }) }
|
|
29
|
+
),
|
|
30
|
+
}}
|
|
31
|
+
render={({ field: { onBlur, onChange, value } }) => (
|
|
32
|
+
<Form.Input
|
|
33
|
+
autoComplete="off"
|
|
34
|
+
error={_.path(["langs", id, "definition", "message"])(errors)}
|
|
35
|
+
label={formatMessage({ id: "i18n.message.props.definition" })}
|
|
36
|
+
onBlur={onBlur}
|
|
37
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
38
|
+
placeholder={formatMessage({
|
|
39
|
+
id: "i18n.message.form.definition.placeholder",
|
|
40
|
+
})}
|
|
41
|
+
value={value || ""}
|
|
42
|
+
required
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
/>
|
|
46
|
+
<Controller
|
|
47
|
+
control={control}
|
|
48
|
+
name={`langs.${id}.description`}
|
|
49
|
+
render={({ field: { onBlur, onChange, value } }) => (
|
|
50
|
+
<Form.Input
|
|
51
|
+
autoComplete="off"
|
|
52
|
+
label={formatMessage({ id: "i18n.message.props.description" })}
|
|
53
|
+
onBlur={onBlur}
|
|
54
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
55
|
+
placeholder={formatMessage({
|
|
56
|
+
id: "i18n.message.form.description.placeholder",
|
|
57
|
+
})}
|
|
58
|
+
value={value || ""}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
/>
|
|
62
|
+
</Segment>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const MessageForm = ({ onSubmit, isSubmitting }) => {
|
|
67
|
+
const { locales, loading } = useLocales();
|
|
68
|
+
const { formatMessage } = useIntl();
|
|
69
|
+
const { handleSubmit, control, formState } = useForm({
|
|
70
|
+
mode: "all",
|
|
71
|
+
defaultValues: {
|
|
72
|
+
message_id: "",
|
|
73
|
+
langs: {},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const { errors, isDirty, isValid } = formState;
|
|
77
|
+
|
|
78
|
+
return loading ? (
|
|
79
|
+
<Loader />
|
|
80
|
+
) : (
|
|
81
|
+
<Form onSubmit={handleSubmit(onSubmit)}>
|
|
82
|
+
<Controller
|
|
83
|
+
control={control}
|
|
84
|
+
name="message_id"
|
|
85
|
+
rules={{
|
|
86
|
+
required: formatMessage(
|
|
87
|
+
{ id: "form.validation.required" },
|
|
88
|
+
{ prop: formatMessage({ id: "i18n.message.props.messageId" }) }
|
|
89
|
+
),
|
|
90
|
+
}}
|
|
91
|
+
render={({ field: { onBlur, onChange, value } }) => (
|
|
92
|
+
<Form.Input
|
|
93
|
+
autoComplete="off"
|
|
94
|
+
error={_.path(["message_id", "message"])(errors)}
|
|
95
|
+
label={formatMessage({ id: "i18n.message.props.messageId" })}
|
|
96
|
+
onBlur={onBlur}
|
|
97
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
98
|
+
placeholder={formatMessage({
|
|
99
|
+
id: "i18n.message.form.messageId.placeholder",
|
|
100
|
+
})}
|
|
101
|
+
value={value || ""}
|
|
102
|
+
required
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
{locales.map((lang, i) => (
|
|
108
|
+
<LangForm
|
|
109
|
+
key={i}
|
|
110
|
+
control={control}
|
|
111
|
+
lang={lang}
|
|
112
|
+
errors={errors}
|
|
113
|
+
formatMessage={formatMessage}
|
|
114
|
+
/>
|
|
115
|
+
))}
|
|
116
|
+
|
|
117
|
+
<div className="actions">
|
|
118
|
+
<Button
|
|
119
|
+
floated="right"
|
|
120
|
+
type="submit"
|
|
121
|
+
primary
|
|
122
|
+
loading={isSubmitting}
|
|
123
|
+
disabled={isSubmitting || !isDirty || !isValid}
|
|
124
|
+
content={formatMessage({ id: "actions.save" })}
|
|
125
|
+
/>
|
|
126
|
+
<HistoryBackButton
|
|
127
|
+
content={formatMessage({ id: "actions.cancel" })}
|
|
128
|
+
disabled={isSubmitting}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</Form>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
MessageForm.propTypes = {
|
|
136
|
+
message: PropTypes.object,
|
|
137
|
+
onSubmit: PropTypes.func,
|
|
138
|
+
isSubmitting: PropTypes.bool,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default MessageForm;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import { useIntl, FormattedMessage } from "react-intl";
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Header,
|
|
7
|
+
Divider,
|
|
8
|
+
Icon,
|
|
9
|
+
Segment,
|
|
10
|
+
Dimmer,
|
|
11
|
+
Loader,
|
|
12
|
+
Menu,
|
|
13
|
+
} from "semantic-ui-react";
|
|
14
|
+
import { Link } from "react-router-dom";
|
|
15
|
+
import { SearchInput } from "@truedat/core/components";
|
|
16
|
+
import { lowerDeburrTrim } from "@truedat/core/services/sort";
|
|
17
|
+
import { useLocales } from "@truedat/core/hooks";
|
|
18
|
+
import { I18N_MESSAGES_NEW } from "@truedat/core/routes";
|
|
19
|
+
import { Pagination } from "@truedat/core/components";
|
|
20
|
+
import MessagesTable from "./MessagesTable";
|
|
21
|
+
|
|
22
|
+
const ITEMS_PER_PAGE = 30;
|
|
23
|
+
|
|
24
|
+
export function MessagesContent({ locales, loading }) {
|
|
25
|
+
const langs = _.map("lang")(locales);
|
|
26
|
+
const [selectedLang, setSelectedLang] = useState(_.head(langs));
|
|
27
|
+
const [page, setPage] = useState(1);
|
|
28
|
+
const [filter, setFilter] = useState("");
|
|
29
|
+
const { formatMessage } = useIntl();
|
|
30
|
+
|
|
31
|
+
const handleSearch = (_e, data) => {
|
|
32
|
+
_.flow(_.propOr("", "value"), setFilter)(data);
|
|
33
|
+
setPage(1);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const messages = _.flow(
|
|
37
|
+
_.find({ lang: selectedLang }),
|
|
38
|
+
_.prop("messages"),
|
|
39
|
+
_.filter(({ message_id, definition, description }) => {
|
|
40
|
+
const deburrFilter = lowerDeburrTrim(filter);
|
|
41
|
+
return (
|
|
42
|
+
deburrFilter == "" ||
|
|
43
|
+
lowerDeburrTrim(message_id).includes(deburrFilter) ||
|
|
44
|
+
lowerDeburrTrim(definition).includes(deburrFilter) ||
|
|
45
|
+
lowerDeburrTrim(description).includes(deburrFilter)
|
|
46
|
+
);
|
|
47
|
+
})
|
|
48
|
+
)(locales);
|
|
49
|
+
|
|
50
|
+
const totalPages = Math.ceil(messages.length / ITEMS_PER_PAGE);
|
|
51
|
+
const paginatedMessages = _.flow(
|
|
52
|
+
_.drop((page - 1) * ITEMS_PER_PAGE),
|
|
53
|
+
_.take(ITEMS_PER_PAGE)
|
|
54
|
+
)(messages);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
<Menu attached="top" secondary pointing tabular>
|
|
59
|
+
{langs.map((lang, i) => (
|
|
60
|
+
<Menu.Item
|
|
61
|
+
key={i}
|
|
62
|
+
active={lang === selectedLang}
|
|
63
|
+
onClick={() => setSelectedLang(lang)}
|
|
64
|
+
>
|
|
65
|
+
<FormattedMessage id={`i18n.messages.locale.${lang}`} />
|
|
66
|
+
</Menu.Item>
|
|
67
|
+
))}
|
|
68
|
+
</Menu>
|
|
69
|
+
<Divider hidden />
|
|
70
|
+
<SearchInput
|
|
71
|
+
onChange={handleSearch}
|
|
72
|
+
placeholder={formatMessage({ id: "i18n.messages.search.placeholder" })}
|
|
73
|
+
value={filter}
|
|
74
|
+
/>
|
|
75
|
+
<MessagesTable
|
|
76
|
+
messages={paginatedMessages}
|
|
77
|
+
locales={locales}
|
|
78
|
+
loading={loading}
|
|
79
|
+
/>
|
|
80
|
+
<Pagination
|
|
81
|
+
totalPages={totalPages}
|
|
82
|
+
activePage={page}
|
|
83
|
+
selectPage={({ activePage }) => setPage(activePage)}
|
|
84
|
+
/>
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default function Messages() {
|
|
90
|
+
const { formatMessage } = useIntl();
|
|
91
|
+
const { locales, loading } = useLocales();
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Segment>
|
|
95
|
+
<Header as="h2">
|
|
96
|
+
<Icon circular name="language" />
|
|
97
|
+
<Header.Content>
|
|
98
|
+
<FormattedMessage id={"i18n.messages.header"} />
|
|
99
|
+
<Header.Subheader>
|
|
100
|
+
<FormattedMessage id={"i18n.messages.subheader"} />
|
|
101
|
+
</Header.Subheader>
|
|
102
|
+
</Header.Content>
|
|
103
|
+
</Header>
|
|
104
|
+
<Segment attached="bottom">
|
|
105
|
+
<Dimmer.Dimmable dimmed={loading}>
|
|
106
|
+
<Dimmer active={loading} inverted>
|
|
107
|
+
<Loader />
|
|
108
|
+
</Dimmer>
|
|
109
|
+
</Dimmer.Dimmable>
|
|
110
|
+
<Button
|
|
111
|
+
floated="right"
|
|
112
|
+
primary
|
|
113
|
+
content={formatMessage({ id: "i18n.actions.createMessage" })}
|
|
114
|
+
icon="add circle"
|
|
115
|
+
as={Link}
|
|
116
|
+
to={I18N_MESSAGES_NEW}
|
|
117
|
+
/>
|
|
118
|
+
{!loading ? (
|
|
119
|
+
<MessagesContent locales={locales} loading={loading} />
|
|
120
|
+
) : null}
|
|
121
|
+
</Segment>
|
|
122
|
+
</Segment>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { Table, Header, Icon } from "semantic-ui-react";
|
|
6
|
+
import { useMessagePatch } from "@truedat/core/hooks";
|
|
7
|
+
import EditableCell from "./EditableCell";
|
|
8
|
+
|
|
9
|
+
export const MessageRow = ({ message, formatMessage }) => {
|
|
10
|
+
const { trigger } = useMessagePatch(message.id);
|
|
11
|
+
|
|
12
|
+
const doPatch = (field) => (value) =>
|
|
13
|
+
trigger({ message: { [field]: value } });
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Table.Row>
|
|
17
|
+
<Table.Cell width={6} content={message?.message_id} />
|
|
18
|
+
<EditableCell
|
|
19
|
+
value={message?.definition}
|
|
20
|
+
onChange={doPatch("definition")}
|
|
21
|
+
placeholder={formatMessage({
|
|
22
|
+
id: "i18n.message.form.definition.placeholder",
|
|
23
|
+
})}
|
|
24
|
+
/>
|
|
25
|
+
<EditableCell
|
|
26
|
+
value={message?.description}
|
|
27
|
+
onChange={doPatch("description")}
|
|
28
|
+
placeholder={formatMessage({
|
|
29
|
+
id: "i18n.message.form.description.placeholder",
|
|
30
|
+
})}
|
|
31
|
+
/>
|
|
32
|
+
</Table.Row>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const MessagesTable = ({ messages, loading }) => {
|
|
37
|
+
const { formatMessage } = useIntl();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{!_.isEmpty(messages) && (
|
|
42
|
+
<Table>
|
|
43
|
+
<Table.Header>
|
|
44
|
+
<Table.Row>
|
|
45
|
+
<Table.HeaderCell
|
|
46
|
+
content={formatMessage({ id: "i18n.message.props.messageId" })}
|
|
47
|
+
/>
|
|
48
|
+
<Table.HeaderCell
|
|
49
|
+
content={formatMessage({ id: "i18n.message.props.definition" })}
|
|
50
|
+
/>
|
|
51
|
+
<Table.HeaderCell
|
|
52
|
+
content={formatMessage({
|
|
53
|
+
id: "i18n.message.props.description",
|
|
54
|
+
})}
|
|
55
|
+
/>
|
|
56
|
+
</Table.Row>
|
|
57
|
+
</Table.Header>
|
|
58
|
+
<Table.Body>
|
|
59
|
+
{messages.map((message) => (
|
|
60
|
+
<MessageRow
|
|
61
|
+
key={message.id}
|
|
62
|
+
message={message}
|
|
63
|
+
formatMessage={formatMessage}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</Table.Body>
|
|
67
|
+
</Table>
|
|
68
|
+
)}
|
|
69
|
+
{_.isEmpty(messages) && !loading && (
|
|
70
|
+
<Header as="h4">
|
|
71
|
+
<Icon name="search" />
|
|
72
|
+
<Header.Content>
|
|
73
|
+
{formatMessage({ id: "i18n.messages.empty" })}
|
|
74
|
+
</Header.Content>
|
|
75
|
+
</Header>
|
|
76
|
+
)}
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
MessagesTable.propTypes = {
|
|
82
|
+
messages: PropTypes.array,
|
|
83
|
+
loading: PropTypes.bool,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default MessagesTable;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { Breadcrumb, Header, Container, Segment } from "semantic-ui-react";
|
|
4
|
+
import { useIntl, FormattedMessage } from "react-intl";
|
|
5
|
+
import { useHistory, Link } from "react-router-dom";
|
|
6
|
+
import { useMessagePost } from "@truedat/core/hooks";
|
|
7
|
+
import { I18N_MESSAGES } from "@truedat/core/routes";
|
|
8
|
+
import MessageForm from "./MessageForm";
|
|
9
|
+
|
|
10
|
+
const NewMessage = () => {
|
|
11
|
+
const { formatMessage } = useIntl();
|
|
12
|
+
const { trigger } = useMessagePost();
|
|
13
|
+
const history = useHistory();
|
|
14
|
+
|
|
15
|
+
const onSubmit = (message) => {
|
|
16
|
+
trigger({ message });
|
|
17
|
+
history.push(I18N_MESSAGES);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Breadcrumb>
|
|
23
|
+
<Breadcrumb.Section as={Link} to={I18N_MESSAGES} active={false}>
|
|
24
|
+
<FormattedMessage id="i18n.messages.header" />
|
|
25
|
+
</Breadcrumb.Section>
|
|
26
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
27
|
+
<Breadcrumb.Section active>
|
|
28
|
+
<FormattedMessage id="i18n.actions.createMessage" />
|
|
29
|
+
</Breadcrumb.Section>
|
|
30
|
+
</Breadcrumb>
|
|
31
|
+
<Container text as={Segment}>
|
|
32
|
+
<Header
|
|
33
|
+
as="h2"
|
|
34
|
+
icon="language"
|
|
35
|
+
content={formatMessage({ id: "i18n.actions.createMessage" })}
|
|
36
|
+
/>
|
|
37
|
+
<MessageForm onSubmit={onSubmit} />
|
|
38
|
+
</Container>
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
NewMessage.propTypes = {
|
|
44
|
+
createUser: PropTypes.func,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default NewMessage;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import messages from "@truedat/core/messages";
|
|
5
|
+
import EditableCell from "../EditableCell";
|
|
6
|
+
|
|
7
|
+
const renderOpts = { messages };
|
|
8
|
+
|
|
9
|
+
describe("<EditableCell />", () => {
|
|
10
|
+
const onChange = jest.fn();
|
|
11
|
+
const value = "value";
|
|
12
|
+
const placeholder = "placeholder";
|
|
13
|
+
|
|
14
|
+
it("matches the latest snapshot", () => {
|
|
15
|
+
const props = {
|
|
16
|
+
onChange,
|
|
17
|
+
value,
|
|
18
|
+
placeholder,
|
|
19
|
+
};
|
|
20
|
+
const { container } = render(<EditableCell {...props} />, renderOpts);
|
|
21
|
+
expect(container).toMatchSnapshot();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("clicking the cell will enter edition mode and match snapshot", async () => {
|
|
25
|
+
const props = {
|
|
26
|
+
onChange,
|
|
27
|
+
value,
|
|
28
|
+
placeholder,
|
|
29
|
+
};
|
|
30
|
+
const { container, findByText } = render(
|
|
31
|
+
<EditableCell {...props} />,
|
|
32
|
+
renderOpts
|
|
33
|
+
);
|
|
34
|
+
userEvent.click(await findByText(value));
|
|
35
|
+
expect(container).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("on edition mode edition mode will call onChange on blur", async () => {
|
|
39
|
+
const props = {
|
|
40
|
+
onChange,
|
|
41
|
+
value,
|
|
42
|
+
placeholder,
|
|
43
|
+
};
|
|
44
|
+
const { getByRole, findByText } = render(
|
|
45
|
+
<EditableCell {...props} />,
|
|
46
|
+
renderOpts
|
|
47
|
+
);
|
|
48
|
+
userEvent.click(await findByText(value));
|
|
49
|
+
|
|
50
|
+
userEvent.type(getByRole("textbox", { type: /text/i }), "foo");
|
|
51
|
+
userEvent.tab(getByRole("textbox", { type: /text/i }));
|
|
52
|
+
expect(onChange).toHaveBeenCalledWith("valuefoo");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import I18nRoutes from "../I18nRoutes";
|
|
4
|
+
|
|
5
|
+
jest.mock("@truedat/core/hooks", () => ({
|
|
6
|
+
useAuthorized: jest.fn(() => true),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
describe("<I18nRoutes />", () => {
|
|
10
|
+
it("matches the latest snapshot", () => {
|
|
11
|
+
const { container } = render(<I18nRoutes />);
|
|
12
|
+
expect(container).toMatchSnapshot();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import messages from "@truedat/core/messages";
|
|
5
|
+
import MessageForm from "../MessageForm";
|
|
6
|
+
|
|
7
|
+
const renderOpts = { messages };
|
|
8
|
+
|
|
9
|
+
jest.mock("@truedat/core/hooks", () => ({
|
|
10
|
+
useLocales: jest.fn(() => ({
|
|
11
|
+
loading: false,
|
|
12
|
+
locales: [{ lang: "es", id: 1 }],
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe("<MessageForm />", () => {
|
|
17
|
+
const onSubmit = jest.fn();
|
|
18
|
+
const isSubmitting = false;
|
|
19
|
+
|
|
20
|
+
it("matches the latest snapshot", () => {
|
|
21
|
+
const props = {
|
|
22
|
+
onSubmit,
|
|
23
|
+
isSubmitting,
|
|
24
|
+
};
|
|
25
|
+
const { container } = render(<MessageForm {...props} />, renderOpts);
|
|
26
|
+
expect(container).toMatchSnapshot();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import messages from "@truedat/core/messages";
|
|
4
|
+
import Messages from "../Messages";
|
|
5
|
+
|
|
6
|
+
const renderOpts = { messages };
|
|
7
|
+
|
|
8
|
+
jest.mock("@truedat/core/hooks", () => ({
|
|
9
|
+
useLocales: jest.fn(() => ({
|
|
10
|
+
loading: false,
|
|
11
|
+
locales: [
|
|
12
|
+
{
|
|
13
|
+
lang: "es",
|
|
14
|
+
id: 1,
|
|
15
|
+
messages: [
|
|
16
|
+
{
|
|
17
|
+
message_id: "message_id",
|
|
18
|
+
definition: "definition",
|
|
19
|
+
description: "description",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
})),
|
|
25
|
+
useMessagePatch: jest.fn(() => ({ trigger: jest.fn() })),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe("<Messages />", () => {
|
|
29
|
+
it("matches the latest snapshot", () => {
|
|
30
|
+
const { container } = render(<Messages />, renderOpts);
|
|
31
|
+
expect(container).toMatchSnapshot();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import messages from "@truedat/core/messages";
|
|
4
|
+
import NewMessage from "../NewMessage";
|
|
5
|
+
|
|
6
|
+
const renderOpts = { messages };
|
|
7
|
+
|
|
8
|
+
jest.mock("@truedat/core/hooks", () => ({
|
|
9
|
+
useLocales: jest.fn(() => ({
|
|
10
|
+
loading: false,
|
|
11
|
+
locales: [{ lang: "es", id: 1 }],
|
|
12
|
+
})),
|
|
13
|
+
useMessagePost: jest.fn(() => ({ trigger: jest.fn() })),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe("<NewMessage />", () => {
|
|
17
|
+
it("matches the latest snapshot", () => {
|
|
18
|
+
const { container } = render(<NewMessage />, renderOpts);
|
|
19
|
+
expect(container).toMatchSnapshot();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<EditableCell /> clicking the cell will enter edition mode and match snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<td
|
|
6
|
+
class="five wide no-padding"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="ui fluid input"
|
|
10
|
+
>
|
|
11
|
+
<input
|
|
12
|
+
maxlength="255"
|
|
13
|
+
placeholder="placeholder"
|
|
14
|
+
type="text"
|
|
15
|
+
value="value"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
</td>
|
|
19
|
+
</div>
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
exports[`<EditableCell /> matches the latest snapshot 1`] = `
|
|
23
|
+
<div>
|
|
24
|
+
<td
|
|
25
|
+
class="five wide cursor-pointer"
|
|
26
|
+
>
|
|
27
|
+
value
|
|
28
|
+
</td>
|
|
29
|
+
</div>
|
|
30
|
+
`;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<MessageForm /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<form
|
|
6
|
+
class="ui form"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="required field"
|
|
10
|
+
>
|
|
11
|
+
<label>
|
|
12
|
+
Message ID
|
|
13
|
+
</label>
|
|
14
|
+
<div
|
|
15
|
+
class="ui input"
|
|
16
|
+
>
|
|
17
|
+
<input
|
|
18
|
+
autocomplete="off"
|
|
19
|
+
placeholder="Message ID"
|
|
20
|
+
required=""
|
|
21
|
+
type="text"
|
|
22
|
+
value=""
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<div
|
|
27
|
+
class="ui segment"
|
|
28
|
+
>
|
|
29
|
+
<h4
|
|
30
|
+
class="ui header"
|
|
31
|
+
>
|
|
32
|
+
Spanish
|
|
33
|
+
</h4>
|
|
34
|
+
<div
|
|
35
|
+
class="required field"
|
|
36
|
+
>
|
|
37
|
+
<label>
|
|
38
|
+
Definition
|
|
39
|
+
</label>
|
|
40
|
+
<div
|
|
41
|
+
class="ui input"
|
|
42
|
+
>
|
|
43
|
+
<input
|
|
44
|
+
autocomplete="off"
|
|
45
|
+
placeholder="Definition"
|
|
46
|
+
required=""
|
|
47
|
+
type="text"
|
|
48
|
+
value=""
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div
|
|
53
|
+
class="field"
|
|
54
|
+
>
|
|
55
|
+
<label>
|
|
56
|
+
Description
|
|
57
|
+
</label>
|
|
58
|
+
<div
|
|
59
|
+
class="ui input"
|
|
60
|
+
>
|
|
61
|
+
<input
|
|
62
|
+
autocomplete="off"
|
|
63
|
+
placeholder="Description"
|
|
64
|
+
type="text"
|
|
65
|
+
value=""
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div
|
|
71
|
+
class="actions"
|
|
72
|
+
>
|
|
73
|
+
<button
|
|
74
|
+
class="ui primary disabled right floated button"
|
|
75
|
+
disabled=""
|
|
76
|
+
tabindex="-1"
|
|
77
|
+
type="submit"
|
|
78
|
+
>
|
|
79
|
+
Save
|
|
80
|
+
</button>
|
|
81
|
+
<a
|
|
82
|
+
class="ui secondary button"
|
|
83
|
+
href="/"
|
|
84
|
+
role="button"
|
|
85
|
+
>
|
|
86
|
+
Cancel
|
|
87
|
+
</a>
|
|
88
|
+
</div>
|
|
89
|
+
</form>
|
|
90
|
+
</div>
|
|
91
|
+
`;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<Messages /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui segment"
|
|
7
|
+
>
|
|
8
|
+
<h2
|
|
9
|
+
class="ui header"
|
|
10
|
+
>
|
|
11
|
+
<i
|
|
12
|
+
aria-hidden="true"
|
|
13
|
+
class="language circular icon"
|
|
14
|
+
/>
|
|
15
|
+
<div
|
|
16
|
+
class="content"
|
|
17
|
+
>
|
|
18
|
+
Translations
|
|
19
|
+
<div
|
|
20
|
+
class="sub header"
|
|
21
|
+
>
|
|
22
|
+
Messages used in Truedat
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</h2>
|
|
26
|
+
<div
|
|
27
|
+
class="ui bottom attached segment"
|
|
28
|
+
>
|
|
29
|
+
<div
|
|
30
|
+
class="dimmable"
|
|
31
|
+
>
|
|
32
|
+
<div
|
|
33
|
+
class="ui inverted dimmer"
|
|
34
|
+
>
|
|
35
|
+
<div
|
|
36
|
+
class="content"
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
class="ui loader"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<a
|
|
45
|
+
class="ui primary right floated button"
|
|
46
|
+
href="/i18n/messages/new"
|
|
47
|
+
role="button"
|
|
48
|
+
>
|
|
49
|
+
<i
|
|
50
|
+
aria-hidden="true"
|
|
51
|
+
class="add circle icon"
|
|
52
|
+
/>
|
|
53
|
+
Create message
|
|
54
|
+
</a>
|
|
55
|
+
<div
|
|
56
|
+
class="ui pointing secondary top attached tabular menu"
|
|
57
|
+
>
|
|
58
|
+
<a
|
|
59
|
+
class="active item"
|
|
60
|
+
>
|
|
61
|
+
Spanish
|
|
62
|
+
</a>
|
|
63
|
+
</div>
|
|
64
|
+
<div
|
|
65
|
+
class="ui hidden divider"
|
|
66
|
+
/>
|
|
67
|
+
<div
|
|
68
|
+
class="ui icon input"
|
|
69
|
+
>
|
|
70
|
+
<input
|
|
71
|
+
placeholder="Search messages"
|
|
72
|
+
type="text"
|
|
73
|
+
value=""
|
|
74
|
+
/>
|
|
75
|
+
<i
|
|
76
|
+
aria-hidden="true"
|
|
77
|
+
class="search link icon"
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
<table
|
|
81
|
+
class="ui table"
|
|
82
|
+
>
|
|
83
|
+
<thead
|
|
84
|
+
class=""
|
|
85
|
+
>
|
|
86
|
+
<tr
|
|
87
|
+
class=""
|
|
88
|
+
>
|
|
89
|
+
<th
|
|
90
|
+
class=""
|
|
91
|
+
>
|
|
92
|
+
Message ID
|
|
93
|
+
</th>
|
|
94
|
+
<th
|
|
95
|
+
class=""
|
|
96
|
+
>
|
|
97
|
+
Definition
|
|
98
|
+
</th>
|
|
99
|
+
<th
|
|
100
|
+
class=""
|
|
101
|
+
>
|
|
102
|
+
Description
|
|
103
|
+
</th>
|
|
104
|
+
</tr>
|
|
105
|
+
</thead>
|
|
106
|
+
<tbody
|
|
107
|
+
class=""
|
|
108
|
+
>
|
|
109
|
+
<tr
|
|
110
|
+
class=""
|
|
111
|
+
>
|
|
112
|
+
<td
|
|
113
|
+
class="six wide"
|
|
114
|
+
>
|
|
115
|
+
message_id
|
|
116
|
+
</td>
|
|
117
|
+
<td
|
|
118
|
+
class="five wide cursor-pointer"
|
|
119
|
+
>
|
|
120
|
+
definition
|
|
121
|
+
</td>
|
|
122
|
+
<td
|
|
123
|
+
class="five wide cursor-pointer"
|
|
124
|
+
>
|
|
125
|
+
description
|
|
126
|
+
</td>
|
|
127
|
+
</tr>
|
|
128
|
+
</tbody>
|
|
129
|
+
</table>
|
|
130
|
+
<div
|
|
131
|
+
aria-label="Pagination Navigation"
|
|
132
|
+
class="ui pagination menu"
|
|
133
|
+
role="navigation"
|
|
134
|
+
>
|
|
135
|
+
<a
|
|
136
|
+
aria-current="false"
|
|
137
|
+
aria-disabled="true"
|
|
138
|
+
aria-label="First item"
|
|
139
|
+
class="disabled item"
|
|
140
|
+
tabindex="-1"
|
|
141
|
+
type="firstItem"
|
|
142
|
+
value="1"
|
|
143
|
+
>
|
|
144
|
+
«
|
|
145
|
+
</a>
|
|
146
|
+
<a
|
|
147
|
+
aria-current="false"
|
|
148
|
+
aria-disabled="true"
|
|
149
|
+
aria-label="Previous item"
|
|
150
|
+
class="disabled item"
|
|
151
|
+
tabindex="-1"
|
|
152
|
+
type="prevItem"
|
|
153
|
+
value="1"
|
|
154
|
+
>
|
|
155
|
+
⟨
|
|
156
|
+
</a>
|
|
157
|
+
<a
|
|
158
|
+
aria-current="true"
|
|
159
|
+
aria-disabled="true"
|
|
160
|
+
class="active disabled item"
|
|
161
|
+
tabindex="-1"
|
|
162
|
+
type="pageItem"
|
|
163
|
+
value="1"
|
|
164
|
+
>
|
|
165
|
+
1
|
|
166
|
+
</a>
|
|
167
|
+
<a
|
|
168
|
+
aria-current="false"
|
|
169
|
+
aria-disabled="true"
|
|
170
|
+
aria-label="Next item"
|
|
171
|
+
class="disabled item"
|
|
172
|
+
tabindex="-1"
|
|
173
|
+
type="nextItem"
|
|
174
|
+
value="1"
|
|
175
|
+
>
|
|
176
|
+
⟩
|
|
177
|
+
</a>
|
|
178
|
+
<a
|
|
179
|
+
aria-current="false"
|
|
180
|
+
aria-disabled="true"
|
|
181
|
+
aria-label="Last item"
|
|
182
|
+
class="disabled item"
|
|
183
|
+
tabindex="-1"
|
|
184
|
+
type="lastItem"
|
|
185
|
+
value="1"
|
|
186
|
+
>
|
|
187
|
+
»
|
|
188
|
+
</a>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
`;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<NewMessage /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui breadcrumb"
|
|
7
|
+
>
|
|
8
|
+
<a
|
|
9
|
+
class="section"
|
|
10
|
+
href="/i18n/messages"
|
|
11
|
+
>
|
|
12
|
+
Translations
|
|
13
|
+
</a>
|
|
14
|
+
<i
|
|
15
|
+
aria-hidden="true"
|
|
16
|
+
class="right angle icon divider"
|
|
17
|
+
/>
|
|
18
|
+
<div
|
|
19
|
+
class="active section"
|
|
20
|
+
>
|
|
21
|
+
Create message
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div
|
|
25
|
+
class="ui segment ui text container"
|
|
26
|
+
>
|
|
27
|
+
<h2
|
|
28
|
+
class="ui header"
|
|
29
|
+
>
|
|
30
|
+
<i
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
class="language icon"
|
|
33
|
+
/>
|
|
34
|
+
<div
|
|
35
|
+
class="content"
|
|
36
|
+
>
|
|
37
|
+
Create message
|
|
38
|
+
</div>
|
|
39
|
+
</h2>
|
|
40
|
+
<form
|
|
41
|
+
class="ui form"
|
|
42
|
+
>
|
|
43
|
+
<div
|
|
44
|
+
class="required field"
|
|
45
|
+
>
|
|
46
|
+
<label>
|
|
47
|
+
Message ID
|
|
48
|
+
</label>
|
|
49
|
+
<div
|
|
50
|
+
class="ui input"
|
|
51
|
+
>
|
|
52
|
+
<input
|
|
53
|
+
autocomplete="off"
|
|
54
|
+
placeholder="Message ID"
|
|
55
|
+
required=""
|
|
56
|
+
type="text"
|
|
57
|
+
value=""
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div
|
|
62
|
+
class="ui segment"
|
|
63
|
+
>
|
|
64
|
+
<h4
|
|
65
|
+
class="ui header"
|
|
66
|
+
>
|
|
67
|
+
Spanish
|
|
68
|
+
</h4>
|
|
69
|
+
<div
|
|
70
|
+
class="required field"
|
|
71
|
+
>
|
|
72
|
+
<label>
|
|
73
|
+
Definition
|
|
74
|
+
</label>
|
|
75
|
+
<div
|
|
76
|
+
class="ui input"
|
|
77
|
+
>
|
|
78
|
+
<input
|
|
79
|
+
autocomplete="off"
|
|
80
|
+
placeholder="Definition"
|
|
81
|
+
required=""
|
|
82
|
+
type="text"
|
|
83
|
+
value=""
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div
|
|
88
|
+
class="field"
|
|
89
|
+
>
|
|
90
|
+
<label>
|
|
91
|
+
Description
|
|
92
|
+
</label>
|
|
93
|
+
<div
|
|
94
|
+
class="ui input"
|
|
95
|
+
>
|
|
96
|
+
<input
|
|
97
|
+
autocomplete="off"
|
|
98
|
+
placeholder="Description"
|
|
99
|
+
type="text"
|
|
100
|
+
value=""
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div
|
|
106
|
+
class="actions"
|
|
107
|
+
>
|
|
108
|
+
<button
|
|
109
|
+
class="ui primary disabled right floated button"
|
|
110
|
+
disabled=""
|
|
111
|
+
tabindex="-1"
|
|
112
|
+
type="submit"
|
|
113
|
+
>
|
|
114
|
+
Save
|
|
115
|
+
</button>
|
|
116
|
+
<a
|
|
117
|
+
class="ui secondary button"
|
|
118
|
+
href="/"
|
|
119
|
+
role="button"
|
|
120
|
+
>
|
|
121
|
+
Cancel
|
|
122
|
+
</a>
|
|
123
|
+
</div>
|
|
124
|
+
</form>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
package/src/messages/en.js
CHANGED
|
@@ -56,6 +56,23 @@ export default {
|
|
|
56
56
|
"form.validation.required": "{prop} is required",
|
|
57
57
|
"form.validation.minLength": "{prop} must have at least {value} characters",
|
|
58
58
|
"form.validation.email.invalid": "Invalid email address",
|
|
59
|
+
|
|
60
|
+
"i18n.actions.createMessage": "Create message",
|
|
61
|
+
|
|
62
|
+
"i18n.message.form.messageId.placeholder": "Message ID",
|
|
63
|
+
"i18n.message.form.definition.placeholder": "Definition",
|
|
64
|
+
"i18n.message.form.description.placeholder": "Description",
|
|
65
|
+
"i18n.message.props.messageId": "Message ID",
|
|
66
|
+
"i18n.message.props.definition": "Definition",
|
|
67
|
+
"i18n.message.props.description": "Description",
|
|
68
|
+
|
|
69
|
+
"i18n.messages.header": "Translations",
|
|
70
|
+
"i18n.messages.subheader": "Messages used in Truedat",
|
|
71
|
+
"i18n.messages.empty": "No messages",
|
|
72
|
+
"i18n.messages.locale.es": "Spanish",
|
|
73
|
+
"i18n.messages.locale.en": "English",
|
|
74
|
+
"i18n.messages.search.placeholder": "Search messages",
|
|
75
|
+
|
|
59
76
|
"navigation.dashboard": "Dashboard",
|
|
60
77
|
"navigation.menu": "Menu",
|
|
61
78
|
"search.applied_filters": "Filters:",
|
|
@@ -91,6 +108,7 @@ export default {
|
|
|
91
108
|
"sidemenu.grant_requests": "Grant Requests",
|
|
92
109
|
"sidemenu.grants": "Grants",
|
|
93
110
|
"sidemenu.hide": "Collapse sidebar",
|
|
111
|
+
"sidemenu.i18nMessages": "Translations",
|
|
94
112
|
"sidemenu.implementations": "Implementations",
|
|
95
113
|
"sidemenu.implementations_management": "Drafts",
|
|
96
114
|
"sidemenu.implementations_deprecated": "Deprecated",
|
package/src/messages/es.js
CHANGED
|
@@ -56,6 +56,23 @@ export default {
|
|
|
56
56
|
"form.validation.required": "{prop} es un campo requerido",
|
|
57
57
|
"form.validation.minLength": "{prop} debe tener al menos {value} elementos",
|
|
58
58
|
"form.validation.email.invalid": "Dirección de email inválida",
|
|
59
|
+
|
|
60
|
+
"i18n.actions.createMessage": "Crear mensaje",
|
|
61
|
+
|
|
62
|
+
"i18n.message.form.messageId.placeholder": "Id del mensaje",
|
|
63
|
+
"i18n.message.form.definition.placeholder": "Definición",
|
|
64
|
+
"i18n.message.form.description.placeholder": "Descripción",
|
|
65
|
+
"i18n.message.props.messageId": "Id del mensaje",
|
|
66
|
+
"i18n.message.props.definition": "Definición",
|
|
67
|
+
"i18n.message.props.description": "Descripción",
|
|
68
|
+
|
|
69
|
+
"i18n.messages.header": "Traducciones",
|
|
70
|
+
"i18n.messages.subheader": "Mensajes utilizados en Truedat",
|
|
71
|
+
"i18n.messages.empty": "No hay mensajes",
|
|
72
|
+
"i18n.messages.locale.es": "Español",
|
|
73
|
+
"i18n.messages.locale.en": "Inglés",
|
|
74
|
+
"i18n.messages.search.placeholder": "Buscar mensajes",
|
|
75
|
+
|
|
59
76
|
loading: "cargando...",
|
|
60
77
|
"navigation.dashboard": "Dashboard",
|
|
61
78
|
"navigation.menu": "Menú",
|
|
@@ -94,6 +111,7 @@ export default {
|
|
|
94
111
|
"sidemenu.grant_requests": "Peticiones de Accesos",
|
|
95
112
|
"sidemenu.grants": "Accesos",
|
|
96
113
|
"sidemenu.hide": "Ocultar",
|
|
114
|
+
"sidemenu.i18nMessages": "Traducciones",
|
|
97
115
|
"sidemenu.implementations": "Implementaciones",
|
|
98
116
|
"sidemenu.implementations_management": "Borradores",
|
|
99
117
|
"sidemenu.implementations_deprecated": "Archivadas",
|
package/src/routes.js
CHANGED
|
@@ -62,6 +62,9 @@ export const GROUP = "/groups/:id";
|
|
|
62
62
|
export const GROUPS = "/groups";
|
|
63
63
|
export const GROUP_CREATE = "/groups/new";
|
|
64
64
|
export const GROUP_EDIT = "/groups/:id/edit";
|
|
65
|
+
export const I18N = "/i18n";
|
|
66
|
+
export const I18N_MESSAGES = "/i18n/messages";
|
|
67
|
+
export const I18N_MESSAGES_NEW = "/i18n/messages/new";
|
|
65
68
|
export const IMPLEMENTATION = "/implementations/:implementation_id(\\d+)";
|
|
66
69
|
export const IMPLEMENTATIONS = "/implementations";
|
|
67
70
|
export const IMPLEMENTATIONS_DEPRECATED = "/deprecatedImplementations";
|
|
@@ -258,6 +261,9 @@ const routes = {
|
|
|
258
261
|
GROUPS,
|
|
259
262
|
GROUP_CREATE,
|
|
260
263
|
GROUP_EDIT,
|
|
264
|
+
I18N,
|
|
265
|
+
I18N_MESSAGES,
|
|
266
|
+
I18N_MESSAGES_NEW,
|
|
261
267
|
IMPLEMENTATION,
|
|
262
268
|
IMPLEMENTATIONS,
|
|
263
269
|
IMPLEMENTATIONS_DEPRECATED,
|