@oneuptime/common 10.0.80 → 10.0.83
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/Models/AnalyticsModels/Metric.ts +296 -2
- package/Server/Services/MetricService.ts +228 -3
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +43 -3
- package/Server/Utils/Express.ts +5 -0
- package/Types/AnalyticsDatabase/TableColumnType.ts +1 -0
- package/Types/BaseDatabase/AggregationType.ts +35 -0
- package/Types/Monitor/IncomingMonitor/IncomingMonitorRequest.ts +1 -0
- package/UI/Components/Banner/Banner.tsx +7 -2
- package/UI/Components/Breadcrumbs/Breadcrumbs.tsx +6 -2
- package/UI/Components/Button/Button.tsx +10 -4
- package/UI/Components/Card/Card.tsx +11 -4
- package/UI/Components/EmptyState/EmptyState.tsx +6 -2
- package/UI/Components/EventItem/EventItem.tsx +9 -5
- package/UI/Components/Modal/ConfirmModal.tsx +5 -1
- package/UI/Components/Modal/Modal.tsx +21 -5
- package/UI/Components/ModelTable/BaseModelTable.tsx +7 -1
- package/UI/Components/Navbar/NavBar.tsx +6 -3
- package/UI/Components/Page/Page.tsx +6 -2
- package/UI/Components/SideMenu/SideMenu.tsx +18 -6
- package/UI/Components/SideMenu/SideMenuItem.tsx +9 -2
- package/UI/Components/SideMenu/SideMenuSection.tsx +4 -1
- package/UI/Utils/Translation.tsx +53 -0
- package/UI/esbuild-config.js +8 -0
- package/build/dist/Models/AnalyticsModels/Metric.js +250 -2
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +183 -2
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +36 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Express.js +3 -0
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/TableColumnType.js +1 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumnType.js.map +1 -1
- package/build/dist/Types/BaseDatabase/AggregationType.js +30 -0
- package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
- package/build/dist/UI/Components/Banner/Banner.js +6 -2
- package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
- package/build/dist/UI/Components/Breadcrumbs/Breadcrumbs.js +5 -2
- package/build/dist/UI/Components/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -4
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Card/Card.js +6 -2
- package/build/dist/UI/Components/Card/Card.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +4 -2
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/EventItem/EventItem.js +9 -7
- package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +4 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +13 -3
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +7 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +6 -3
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +5 -2
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +12 -6
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js +8 -2
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuSection.js +4 -1
- package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
- package/build/dist/UI/Utils/Translation.js +36 -0
- package/build/dist/UI/Utils/Translation.js.map +1 -0
- package/package.json +3 -1
|
@@ -4,6 +4,41 @@ enum AggregationType {
|
|
|
4
4
|
Sum = "Sum",
|
|
5
5
|
Avg = "Avg",
|
|
6
6
|
Count = "Count",
|
|
7
|
+
/*
|
|
8
|
+
* Percentile aggregations. For Metric (the only model that carries
|
|
9
|
+
* histogram bucket data), MetricService overrides the aggregate path to
|
|
10
|
+
* fan out histogram buckets into weighted samples and use
|
|
11
|
+
* quantileExactWeighted, so a P95 of an `http.server.request.duration`
|
|
12
|
+
* histogram returns the bucket-derived 95th percentile rather than the
|
|
13
|
+
* 95th percentile of the per-row `sum`. For other models (Span, Log,
|
|
14
|
+
* etc.) the StatementGenerator falls back to ClickHouse's `quantile(p)`
|
|
15
|
+
* over the raw column, which is the right thing for scalar columns.
|
|
16
|
+
*/
|
|
17
|
+
P50 = "P50",
|
|
18
|
+
P90 = "P90",
|
|
19
|
+
P95 = "P95",
|
|
20
|
+
P99 = "P99",
|
|
7
21
|
}
|
|
8
22
|
|
|
9
23
|
export default AggregationType;
|
|
24
|
+
|
|
25
|
+
export const PercentileAggregationLevels: Record<string, number> = {
|
|
26
|
+
[AggregationType.P50]: 0.5,
|
|
27
|
+
[AggregationType.P90]: 0.9,
|
|
28
|
+
[AggregationType.P95]: 0.95,
|
|
29
|
+
[AggregationType.P99]: 0.99,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function isPercentileAggregation(type: AggregationType): boolean {
|
|
33
|
+
return Object.prototype.hasOwnProperty.call(
|
|
34
|
+
PercentileAggregationLevels,
|
|
35
|
+
type,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getPercentileLevel(type: AggregationType): number | null {
|
|
40
|
+
if (!isPercentileAggregation(type)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return PercentileAggregationLevels[type] ?? null;
|
|
44
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Link from "../Link/Link";
|
|
2
2
|
import Route from "../../../Types/API/Route";
|
|
3
3
|
import URL from "../../../Types/API/URL";
|
|
4
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
4
5
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
5
6
|
import { GetReactElementFunction } from "../../Types/FunctionTypes";
|
|
6
7
|
|
|
@@ -15,10 +16,14 @@ export interface ComponentProps {
|
|
|
15
16
|
const Banner: FunctionComponent<ComponentProps> = (
|
|
16
17
|
props: ComponentProps,
|
|
17
18
|
): ReactElement => {
|
|
19
|
+
const { translateString } = useTranslateValue();
|
|
20
|
+
const translatedTitle: string = translateString(props.title) || props.title;
|
|
21
|
+
const translatedDescription: string =
|
|
22
|
+
translateString(props.description) || props.description;
|
|
18
23
|
const getContent: GetReactElementFunction = (): ReactElement => {
|
|
19
24
|
return (
|
|
20
25
|
<>
|
|
21
|
-
<strong className="font-semibold">{
|
|
26
|
+
<strong className="font-semibold">{translatedTitle}</strong>
|
|
22
27
|
<svg
|
|
23
28
|
viewBox="0 0 2 2"
|
|
24
29
|
className="mx-2 inline h-0.5 w-0.5 fill-current"
|
|
@@ -26,7 +31,7 @@ const Banner: FunctionComponent<ComponentProps> = (
|
|
|
26
31
|
>
|
|
27
32
|
<circle cx="1" cy="1" r="1" />
|
|
28
33
|
</svg>
|
|
29
|
-
{
|
|
34
|
+
{translatedDescription}
|
|
30
35
|
<span aria-hidden="true">→</span>
|
|
31
36
|
</>
|
|
32
37
|
);
|
|
@@ -4,6 +4,7 @@ import Route from "../../../Types/API/Route";
|
|
|
4
4
|
import URL from "../../../Types/API/URL";
|
|
5
5
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
6
6
|
import Link from "../../../Types/Link";
|
|
7
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
7
8
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
8
9
|
|
|
9
10
|
interface ComponentProps {
|
|
@@ -13,12 +14,15 @@ interface ComponentProps {
|
|
|
13
14
|
const Breadcrumbs: FunctionComponent<ComponentProps> = ({
|
|
14
15
|
links,
|
|
15
16
|
}: ComponentProps): ReactElement => {
|
|
17
|
+
const { translateString } = useTranslateValue();
|
|
16
18
|
return (
|
|
17
19
|
<nav className="flex hidden md:block" aria-label="Breadcrumb">
|
|
18
20
|
<ol role="list" className="flex items-center space-x-1">
|
|
19
21
|
{links &&
|
|
20
22
|
links.length > 0 &&
|
|
21
23
|
links.map((link: Link, i: number) => {
|
|
24
|
+
const translatedTitle: string =
|
|
25
|
+
translateString(link.title) || link.title;
|
|
22
26
|
return (
|
|
23
27
|
<li className="breadcrumb-item" key={i}>
|
|
24
28
|
{i === 0 && (
|
|
@@ -28,7 +32,7 @@ const Breadcrumbs: FunctionComponent<ComponentProps> = ({
|
|
|
28
32
|
className="text-gray-400 hover:text-gray-500 -mt-1"
|
|
29
33
|
>
|
|
30
34
|
<span className="text-sm font-medium text-gray-500 hover:text-gray-700 -mt-1">
|
|
31
|
-
{
|
|
35
|
+
{translatedTitle}
|
|
32
36
|
</span>
|
|
33
37
|
</UILink>
|
|
34
38
|
</div>
|
|
@@ -48,7 +52,7 @@ const Breadcrumbs: FunctionComponent<ComponentProps> = ({
|
|
|
48
52
|
}
|
|
49
53
|
className="ml-1 text-sm font-medium text-gray-500 hover:text-gray-700 -mt-1"
|
|
50
54
|
>
|
|
51
|
-
{
|
|
55
|
+
{translatedTitle}
|
|
52
56
|
</UILink>
|
|
53
57
|
</div>
|
|
54
58
|
)}
|
|
@@ -3,6 +3,7 @@ import Icon, { SizeProp } from "../Icon/Icon";
|
|
|
3
3
|
import ShortcutKey from "../ShortcutKey/ShortcutKey";
|
|
4
4
|
import ButtonType from "./ButtonTypes";
|
|
5
5
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
6
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
6
7
|
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
|
7
8
|
import Tooltip from "../Tooltip/Tooltip";
|
|
8
9
|
import { GetReactElementFunction } from "../../Types/FunctionTypes";
|
|
@@ -86,6 +87,9 @@ const Button: FunctionComponent<ComponentProps> = ({
|
|
|
86
87
|
ariaHaspopup,
|
|
87
88
|
ariaControls,
|
|
88
89
|
}: ComponentProps): ReactElement => {
|
|
90
|
+
const { translateString } = useTranslateValue();
|
|
91
|
+
const translatedTitle: string | undefined = translateString(title);
|
|
92
|
+
const translatedTooltip: string | undefined = translateString(tooltip);
|
|
89
93
|
useEffect(() => {
|
|
90
94
|
// componentDidMount
|
|
91
95
|
if (shortcutKey) {
|
|
@@ -254,7 +258,7 @@ const Button: FunctionComponent<ComponentProps> = ({
|
|
|
254
258
|
ariaLabel ||
|
|
255
259
|
(buttonStyle === ButtonStyleType.ICON ||
|
|
256
260
|
buttonStyle === ButtonStyleType.ICON_LIGHT
|
|
257
|
-
?
|
|
261
|
+
? translatedTitle || translatedTooltip
|
|
258
262
|
: undefined);
|
|
259
263
|
|
|
260
264
|
const getButton: GetReactElementFunction = (): ReactElement => {
|
|
@@ -289,7 +293,9 @@ const Button: FunctionComponent<ComponentProps> = ({
|
|
|
289
293
|
/>
|
|
290
294
|
)}
|
|
291
295
|
|
|
292
|
-
{
|
|
296
|
+
{translatedTitle && buttonStyle !== ButtonStyleType.ICON
|
|
297
|
+
? translatedTitle
|
|
298
|
+
: ``}
|
|
293
299
|
|
|
294
300
|
{shortcutKey && (
|
|
295
301
|
<div className="ml-2">
|
|
@@ -302,8 +308,8 @@ const Button: FunctionComponent<ComponentProps> = ({
|
|
|
302
308
|
);
|
|
303
309
|
};
|
|
304
310
|
|
|
305
|
-
if (
|
|
306
|
-
return <Tooltip text={
|
|
311
|
+
if (translatedTooltip) {
|
|
312
|
+
return <Tooltip text={translatedTooltip}>{getButton()}</Tooltip>;
|
|
307
313
|
}
|
|
308
314
|
return getButton();
|
|
309
315
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
|
|
2
2
|
import ShortcutKey from "../ShortcutKey/ShortcutKey";
|
|
3
3
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
4
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
4
5
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
5
6
|
|
|
6
7
|
export interface CardButtonSchema {
|
|
@@ -28,8 +29,14 @@ export interface ComponentProps {
|
|
|
28
29
|
const Card: FunctionComponent<ComponentProps> = (
|
|
29
30
|
props: ComponentProps,
|
|
30
31
|
): ReactElement => {
|
|
32
|
+
const { translateValue } = useTranslateValue();
|
|
31
33
|
const noRightElementsOrButtons: boolean =
|
|
32
34
|
!props.rightElement && (!props.buttons || props.buttons.length === 0);
|
|
35
|
+
const translatedTitle: string | ReactElement | undefined = translateValue(
|
|
36
|
+
props.title,
|
|
37
|
+
);
|
|
38
|
+
const translatedDescription: string | ReactElement | undefined =
|
|
39
|
+
translateValue(props.description);
|
|
33
40
|
|
|
34
41
|
return (
|
|
35
42
|
<React.Fragment>
|
|
@@ -40,21 +47,21 @@ const Card: FunctionComponent<ComponentProps> = (
|
|
|
40
47
|
<div
|
|
41
48
|
className={`${noRightElementsOrButtons ? "w-full" : "flex-1 min-w-0"}`}
|
|
42
49
|
>
|
|
43
|
-
{
|
|
50
|
+
{translatedTitle && (
|
|
44
51
|
<h2
|
|
45
52
|
data-testid="card-details-heading"
|
|
46
53
|
id="card-details-heading"
|
|
47
54
|
className="text-lg font-semibold leading-6 text-gray-900"
|
|
48
55
|
>
|
|
49
|
-
{
|
|
56
|
+
{translatedTitle}
|
|
50
57
|
</h2>
|
|
51
58
|
)}
|
|
52
|
-
{
|
|
59
|
+
{translatedDescription && (
|
|
53
60
|
<p
|
|
54
61
|
data-testid="card-description"
|
|
55
62
|
className="mt-1.5 text-sm text-gray-500 w-full hidden md:block leading-relaxed"
|
|
56
63
|
>
|
|
57
|
-
{
|
|
64
|
+
{translatedDescription}
|
|
58
65
|
</p>
|
|
59
66
|
)}
|
|
60
67
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Icon from "../Icon/Icon";
|
|
2
2
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
3
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
3
4
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
4
5
|
|
|
5
6
|
export interface ComponentProps {
|
|
@@ -15,6 +16,7 @@ export interface ComponentProps {
|
|
|
15
16
|
const EmptyState: FunctionComponent<ComponentProps> = (
|
|
16
17
|
props: ComponentProps,
|
|
17
18
|
): ReactElement => {
|
|
19
|
+
const { translateValue } = useTranslateValue();
|
|
18
20
|
return (
|
|
19
21
|
<React.Fragment>
|
|
20
22
|
<div
|
|
@@ -34,9 +36,11 @@ const EmptyState: FunctionComponent<ComponentProps> = (
|
|
|
34
36
|
)}
|
|
35
37
|
|
|
36
38
|
<h3 className="mt-2 text-sm font-medium text-gray-900">
|
|
37
|
-
{props.title}
|
|
39
|
+
{translateValue(props.title)}
|
|
38
40
|
</h3>
|
|
39
|
-
<p className="mt-1 text-sm text-gray-500">
|
|
41
|
+
<p className="mt-1 text-sm text-gray-500">
|
|
42
|
+
{translateValue(props.description)}
|
|
43
|
+
</p>
|
|
40
44
|
{props.footer && <div className="mt-6">{props.footer}</div>}
|
|
41
45
|
</div>
|
|
42
46
|
</div>
|
|
@@ -11,6 +11,7 @@ import Color from "../../../Types/Color";
|
|
|
11
11
|
import OneUptimeDate from "../../../Types/Date";
|
|
12
12
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
13
13
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
14
|
+
import { useTranslation } from "react-i18next";
|
|
14
15
|
|
|
15
16
|
export enum TimelineItemType {
|
|
16
17
|
StateChange = "StateChange",
|
|
@@ -61,6 +62,7 @@ export interface ComponentProps {
|
|
|
61
62
|
const EventItem: FunctionComponent<ComponentProps> = (
|
|
62
63
|
props: ComponentProps,
|
|
63
64
|
): ReactElement => {
|
|
65
|
+
const { t } = useTranslation();
|
|
64
66
|
return (
|
|
65
67
|
<div className="mt-5 mb-5 bg-white shadow rounded-xl border-gray-100 p-5">
|
|
66
68
|
<div>
|
|
@@ -156,7 +158,7 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
156
158
|
<div key={0}>
|
|
157
159
|
<div className="flex flex-wrap gap-y-4 space-x-1 active-event-box-body-reesources">
|
|
158
160
|
<div className="text-sm text-gray-400 mr-3 mt-1">
|
|
159
|
-
|
|
161
|
+
{t("eventItem.affectedResources")}
|
|
160
162
|
</div>
|
|
161
163
|
{props.eventResourcesAffected?.map((item: string, i: number) => {
|
|
162
164
|
return (
|
|
@@ -222,7 +224,7 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
222
224
|
<span className="font-medium text-gray-900 mr-1">
|
|
223
225
|
{props.eventType}
|
|
224
226
|
</span>
|
|
225
|
-
|
|
227
|
+
{t("eventItem.stateChangedTo")}
|
|
226
228
|
</span>
|
|
227
229
|
<span className="mr-1">
|
|
228
230
|
<Pill
|
|
@@ -294,11 +296,13 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
294
296
|
>
|
|
295
297
|
{item.title
|
|
296
298
|
? item.title
|
|
297
|
-
:
|
|
299
|
+
: t("eventItem.updateTo", {
|
|
300
|
+
eventType: props.eventType,
|
|
301
|
+
})}
|
|
298
302
|
</span>
|
|
299
303
|
</div>
|
|
300
304
|
<p className="mt-0.5 text-sm text-gray-500">
|
|
301
|
-
|
|
305
|
+
{t("eventItem.postedOn")}{" "}
|
|
302
306
|
{OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
|
|
303
307
|
item.date,
|
|
304
308
|
)}
|
|
@@ -345,7 +349,7 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
345
349
|
className="cursor-pointer text-gray-400 hover:text-gray-500 text-sm"
|
|
346
350
|
to={props.eventViewRoute}
|
|
347
351
|
>
|
|
348
|
-
<>
|
|
352
|
+
<>{t("eventItem.view", { eventType: props.eventType })}</>
|
|
349
353
|
</Link>
|
|
350
354
|
</span>
|
|
351
355
|
) : (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ButtonStyleType } from "../Button/Button";
|
|
2
2
|
import Modal from "./Modal";
|
|
3
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
3
4
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
4
5
|
|
|
5
6
|
export interface ComponentProps {
|
|
@@ -19,6 +20,9 @@ export interface ComponentProps {
|
|
|
19
20
|
const ConfirmModal: FunctionComponent<ComponentProps> = (
|
|
20
21
|
props: ComponentProps,
|
|
21
22
|
): ReactElement => {
|
|
23
|
+
const { translateValue } = useTranslateValue();
|
|
24
|
+
const translatedDescription: string | ReactElement | undefined =
|
|
25
|
+
translateValue(props.description);
|
|
22
26
|
return (
|
|
23
27
|
<Modal
|
|
24
28
|
title={props.title}
|
|
@@ -46,7 +50,7 @@ const ConfirmModal: FunctionComponent<ComponentProps> = (
|
|
|
46
50
|
data-testid="confirm-modal-description"
|
|
47
51
|
className="text-gray-500 mt-5 text-sm whitespace-pre-wrap break-words max-h-96 overflow-y-auto pr-1"
|
|
48
52
|
>
|
|
49
|
-
{
|
|
53
|
+
{translatedDescription}
|
|
50
54
|
</div>
|
|
51
55
|
</Modal>
|
|
52
56
|
);
|
|
@@ -6,6 +6,7 @@ import ModalBody from "./ModalBody";
|
|
|
6
6
|
import ModalFooter from "./ModalFooter";
|
|
7
7
|
import { VeryLightGray } from "../../../Types/BrandColors";
|
|
8
8
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
9
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
9
10
|
import React, {
|
|
10
11
|
FunctionComponent,
|
|
11
12
|
ReactElement,
|
|
@@ -43,6 +44,17 @@ export interface ComponentProps {
|
|
|
43
44
|
const Modal: FunctionComponent<ComponentProps> = (
|
|
44
45
|
props: ComponentProps,
|
|
45
46
|
): ReactElement => {
|
|
47
|
+
const { translateString } = useTranslateValue();
|
|
48
|
+
const translatedTitle: string = translateString(props.title) || props.title;
|
|
49
|
+
const translatedDescription: string | undefined = translateString(
|
|
50
|
+
props.description,
|
|
51
|
+
);
|
|
52
|
+
const translatedSubmitButtonText: string | undefined = translateString(
|
|
53
|
+
props.submitButtonText,
|
|
54
|
+
);
|
|
55
|
+
const translatedCloseButtonText: string | undefined = translateString(
|
|
56
|
+
props.closeButtonText,
|
|
57
|
+
);
|
|
46
58
|
const modalRef: React.RefObject<HTMLDivElement> =
|
|
47
59
|
useRef<HTMLDivElement>(null);
|
|
48
60
|
|
|
@@ -162,15 +174,15 @@ const Modal: FunctionComponent<ComponentProps> = (
|
|
|
162
174
|
}`}
|
|
163
175
|
id="modal-title"
|
|
164
176
|
>
|
|
165
|
-
{
|
|
177
|
+
{translatedTitle}
|
|
166
178
|
</h3>
|
|
167
|
-
{
|
|
179
|
+
{translatedDescription && (
|
|
168
180
|
<p
|
|
169
181
|
id="modal-description"
|
|
170
182
|
data-testid="modal-description"
|
|
171
183
|
className="text-sm leading-6 text-gray-500 mt-2"
|
|
172
184
|
>
|
|
173
|
-
{
|
|
185
|
+
{translatedDescription}
|
|
174
186
|
</p>
|
|
175
187
|
)}
|
|
176
188
|
</div>
|
|
@@ -214,10 +226,14 @@ const Modal: FunctionComponent<ComponentProps> = (
|
|
|
214
226
|
: ButtonStyleType.NORMAL
|
|
215
227
|
}
|
|
216
228
|
submitButtonText={
|
|
217
|
-
|
|
229
|
+
translatedSubmitButtonText
|
|
230
|
+
? translatedSubmitButtonText
|
|
231
|
+
: translateString("Save") || "Save"
|
|
218
232
|
}
|
|
219
233
|
closeButtonText={
|
|
220
|
-
|
|
234
|
+
translatedCloseButtonText
|
|
235
|
+
? translatedCloseButtonText
|
|
236
|
+
: translateString("Cancel") || "Cancel"
|
|
221
237
|
}
|
|
222
238
|
onSubmit={props.onSubmit}
|
|
223
239
|
onClose={props.onClose ? props.onClose : undefined}
|
|
@@ -4,6 +4,7 @@ import { API_DOCS_URL, BILLING_ENABLED, getAllEnvVars } from "../../Config";
|
|
|
4
4
|
import { GetReactElementFunction } from "../../Types/FunctionTypes";
|
|
5
5
|
import SelectEntityField from "../../Types/SelectEntityField";
|
|
6
6
|
import API from "../../Utils/API/API";
|
|
7
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
7
8
|
|
|
8
9
|
import Query from "../../../Types/BaseDatabase/Query";
|
|
9
10
|
import GroupBy from "../../../Types/BaseDatabase/GroupBy";
|
|
@@ -266,6 +267,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
266
267
|
) => ReactElement = <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
267
268
|
props: ComponentProps<TBaseModel>,
|
|
268
269
|
): ReactElement => {
|
|
270
|
+
const { translateValue } = useTranslateValue();
|
|
269
271
|
const [tableView, setTableView] = useState<TableView | null>(null);
|
|
270
272
|
|
|
271
273
|
const matchBulkSelectedItemByField: keyof TBaseModel =
|
|
@@ -1917,6 +1919,10 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1917
1919
|
const getCardTitle: GetCardTitleFunction = (
|
|
1918
1920
|
title: ReactElement | string,
|
|
1919
1921
|
): ReactElement => {
|
|
1922
|
+
const renderedTitle: ReactElement | string =
|
|
1923
|
+
typeof title === "string"
|
|
1924
|
+
? (translateValue(title) as ReactElement | string | undefined) ?? title
|
|
1925
|
+
: title;
|
|
1920
1926
|
const plan: PlanType | null = ProjectUtil.getCurrentPlan();
|
|
1921
1927
|
|
|
1922
1928
|
let showPlan: boolean = Boolean(
|
|
@@ -1950,7 +1956,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1950
1956
|
|
|
1951
1957
|
return (
|
|
1952
1958
|
<span>
|
|
1953
|
-
{
|
|
1959
|
+
{renderedTitle}
|
|
1954
1960
|
{showPlan && (
|
|
1955
1961
|
<span
|
|
1956
1962
|
style={{
|
|
@@ -301,12 +301,15 @@ const Navbar: FunctionComponent<ComponentProps> = (
|
|
|
301
301
|
});
|
|
302
302
|
});
|
|
303
303
|
|
|
304
|
-
|
|
304
|
+
/*
|
|
305
|
+
* Find Home item from navItems. Match by id so this keeps working when the
|
|
306
|
+
* title is translated to a non-English language.
|
|
307
|
+
*/
|
|
305
308
|
const homeItem: NavItem | undefined = props.items.find((item: NavItem) => {
|
|
306
|
-
return item.
|
|
309
|
+
return item.id === "home-nav-bar-item";
|
|
307
310
|
});
|
|
308
311
|
const otherNavItems: NavItem[] = props.items.filter((item: NavItem) => {
|
|
309
|
-
return item.
|
|
312
|
+
return item.id !== "home-nav-bar-item";
|
|
310
313
|
});
|
|
311
314
|
|
|
312
315
|
return (
|
|
@@ -5,6 +5,7 @@ import PageLoader from "../Loader/PageLoader";
|
|
|
5
5
|
import LabelElement from "../Label/Label";
|
|
6
6
|
import Link from "../../../Types/Link";
|
|
7
7
|
import LabelModel from "../../../Models/DatabaseModels/Label";
|
|
8
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
8
9
|
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
|
9
10
|
|
|
10
11
|
export interface ComponentProps {
|
|
@@ -22,6 +23,9 @@ export interface ComponentProps {
|
|
|
22
23
|
const Page: FunctionComponent<ComponentProps> = (
|
|
23
24
|
props: ComponentProps,
|
|
24
25
|
): ReactElement => {
|
|
26
|
+
const { translateString } = useTranslateValue();
|
|
27
|
+
const translatedTitle: string | undefined = translateString(props.title);
|
|
28
|
+
|
|
25
29
|
useEffect(() => {
|
|
26
30
|
if (props.breadcrumbLinks && props.breadcrumbLinks.length > 0) {
|
|
27
31
|
Analytics.capture(
|
|
@@ -60,7 +64,7 @@ const Page: FunctionComponent<ComponentProps> = (
|
|
|
60
64
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between sm:flex-wrap sm:gap-4">
|
|
61
65
|
<div className="flex flex-col gap-1 min-w-0">
|
|
62
66
|
<h1 className="text-xl font-semibold leading-7 text-gray-900 sm:text-xl sm:tracking-tight sm:truncate">
|
|
63
|
-
{
|
|
67
|
+
{translatedTitle}
|
|
64
68
|
</h1>
|
|
65
69
|
</div>
|
|
66
70
|
{props.headerRight && (
|
|
@@ -71,7 +75,7 @@ const Page: FunctionComponent<ComponentProps> = (
|
|
|
71
75
|
{props.labels && props.labels.length > 0 && (
|
|
72
76
|
<div className="hidden sm:flex sm:flex-wrap sm:items-center sm:justify-end sm:gap-3">
|
|
73
77
|
<span className="text-xs font-semibold uppercase tracking-wide text-gray-500 whitespace-nowrap">
|
|
74
|
-
Labels
|
|
78
|
+
{translateString("Labels") || "Labels"}
|
|
75
79
|
</span>
|
|
76
80
|
<div className="flex flex-wrap items-center gap-2 justify-end">
|
|
77
81
|
{props.labels
|
|
@@ -8,6 +8,7 @@ import Icon from "../Icon/Icon";
|
|
|
8
8
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
9
9
|
import useComponentOutsideClick from "../../Types/UseComponentOutsideClick";
|
|
10
10
|
import Navigation from "../../Utils/Navigation";
|
|
11
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
11
12
|
import SideMenuItem from "./SideMenuItem";
|
|
12
13
|
import SideMenuSection from "./SideMenuSection";
|
|
13
14
|
import CountModelSideMenuItem from "./CountModelSideMenuItem";
|
|
@@ -53,6 +54,7 @@ export interface ComponentProps {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
const SideMenu: FunctionComponent<ComponentProps> = (props: ComponentProps) => {
|
|
57
|
+
const { translateString } = useTranslateValue();
|
|
56
58
|
const [isMobile, setIsMobile] = useState<boolean>(false);
|
|
57
59
|
const [isMobileMenuVisible, setIsMobileMenuVisible] =
|
|
58
60
|
useState<boolean>(false);
|
|
@@ -148,10 +150,16 @@ const SideMenu: FunctionComponent<ComponentProps> = (props: ComponentProps) => {
|
|
|
148
150
|
itemTitle?: string;
|
|
149
151
|
icon?: IconProp;
|
|
150
152
|
} = findActiveMenuItem();
|
|
153
|
+
const translatedSectionTitle: string | undefined = translateString(
|
|
154
|
+
activeItem.sectionTitle,
|
|
155
|
+
);
|
|
156
|
+
const translatedItemTitle: string | undefined = translateString(
|
|
157
|
+
activeItem.itemTitle,
|
|
158
|
+
);
|
|
151
159
|
const displayText: string =
|
|
152
|
-
|
|
153
|
-
? `${
|
|
154
|
-
:
|
|
160
|
+
translatedSectionTitle && translatedItemTitle
|
|
161
|
+
? `${translatedSectionTitle} / ${translatedItemTitle}`
|
|
162
|
+
: translatedItemTitle || translateString("Navigation") || "Navigation";
|
|
155
163
|
|
|
156
164
|
// Re-run active item detection when location changes
|
|
157
165
|
useEffect(() => {
|
|
@@ -309,8 +317,10 @@ const SideMenu: FunctionComponent<ComponentProps> = (props: ComponentProps) => {
|
|
|
309
317
|
aria-expanded={isMobileMenuVisible}
|
|
310
318
|
aria-label={
|
|
311
319
|
isMobileMenuVisible
|
|
312
|
-
? "Close navigation menu"
|
|
313
|
-
|
|
320
|
+
? translateString("Close navigation menu") ||
|
|
321
|
+
"Close navigation menu"
|
|
322
|
+
: translateString("Open navigation menu") ||
|
|
323
|
+
"Open navigation menu"
|
|
314
324
|
}
|
|
315
325
|
data-testid="mobile-sidemenu-toggle"
|
|
316
326
|
>
|
|
@@ -321,7 +331,9 @@ const SideMenu: FunctionComponent<ComponentProps> = (props: ComponentProps) => {
|
|
|
321
331
|
</div>
|
|
322
332
|
)}
|
|
323
333
|
<div className="min-w-0">
|
|
324
|
-
<p className="text-xs text-gray-400 font-medium">
|
|
334
|
+
<p className="text-xs text-gray-400 font-medium">
|
|
335
|
+
{translateString("Navigate to")}
|
|
336
|
+
</p>
|
|
325
337
|
<h3 className="text-sm font-semibold text-gray-900 truncate">
|
|
326
338
|
{displayText}
|
|
327
339
|
</h3>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Navigation from "../../Utils/Navigation";
|
|
2
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
2
3
|
import Badge, { BadgeType } from "../Badge/Badge";
|
|
3
4
|
import Icon from "../Icon/Icon";
|
|
4
5
|
import UILink from "../Link/Link";
|
|
@@ -21,10 +22,16 @@ export interface ComponentProps {
|
|
|
21
22
|
const SideMenuItem: FunctionComponent<ComponentProps> = (
|
|
22
23
|
props: ComponentProps,
|
|
23
24
|
) => {
|
|
25
|
+
const { translateString } = useTranslateValue();
|
|
24
26
|
const isActive: boolean = Navigation.isOnThisPage(props.link.to);
|
|
25
27
|
const isSubItemActive: boolean = props.subItemLink
|
|
26
28
|
? Navigation.isOnThisPage(props.subItemLink.to)
|
|
27
29
|
: false;
|
|
30
|
+
const translatedTitle: string =
|
|
31
|
+
translateString(props.link.title) || props.link.title;
|
|
32
|
+
const translatedSubItemTitle: string | undefined = props.subItemLink
|
|
33
|
+
? translateString(props.subItemLink.title) || props.subItemLink.title
|
|
34
|
+
: undefined;
|
|
28
35
|
|
|
29
36
|
return (
|
|
30
37
|
<>
|
|
@@ -84,7 +91,7 @@ const SideMenuItem: FunctionComponent<ComponentProps> = (
|
|
|
84
91
|
${isActive ? "font-semibold" : ""}
|
|
85
92
|
`}
|
|
86
93
|
>
|
|
87
|
-
{
|
|
94
|
+
{translatedTitle}
|
|
88
95
|
</span>
|
|
89
96
|
</div>
|
|
90
97
|
|
|
@@ -160,7 +167,7 @@ const SideMenuItem: FunctionComponent<ComponentProps> = (
|
|
|
160
167
|
}
|
|
161
168
|
`}
|
|
162
169
|
/>
|
|
163
|
-
<span className="truncate">{
|
|
170
|
+
<span className="truncate">{translatedSubItemTitle}</span>
|
|
164
171
|
</div>
|
|
165
172
|
</UILink>
|
|
166
173
|
)}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Icon from "../Icon/Icon";
|
|
2
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
2
3
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
3
4
|
import React, { FunctionComponent, ReactElement, useState } from "react";
|
|
4
5
|
|
|
@@ -13,9 +14,11 @@ export interface ComponentProps {
|
|
|
13
14
|
const SideMenuSection: FunctionComponent<ComponentProps> = (
|
|
14
15
|
props: ComponentProps,
|
|
15
16
|
) => {
|
|
17
|
+
const { translateString } = useTranslateValue();
|
|
16
18
|
const [isCollapsed, setIsCollapsed] = useState<boolean>(
|
|
17
19
|
props.defaultCollapsed || false,
|
|
18
20
|
);
|
|
21
|
+
const translatedTitle: string = translateString(props.title) || props.title;
|
|
19
22
|
|
|
20
23
|
const isCollapsible: boolean = props.collapsible ?? true;
|
|
21
24
|
|
|
@@ -41,7 +44,7 @@ const SideMenuSection: FunctionComponent<ComponentProps> = (
|
|
|
41
44
|
<Icon icon={props.icon} className="h-4 w-4 text-gray-400" />
|
|
42
45
|
)}
|
|
43
46
|
<h6 className="text-xs font-semibold uppercase tracking-wider text-gray-500">
|
|
44
|
-
{
|
|
47
|
+
{translatedTitle}
|
|
45
48
|
</h6>
|
|
46
49
|
</div>
|
|
47
50
|
{isCollapsible && (
|