@cccsaurora/howler-ui 2.13.0-dev.171 → 2.13.0-dev.176
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/commons/components/notification/elements/item/NotificationItemDate.js +2 -2
- package/components/app/drawers/ApiKeyDrawer.js +4 -4
- package/components/elements/hit/HitSummary.js +1 -1
- package/components/elements/hit/aggregate/HitGraph.d.ts +1 -1
- package/components/elements/hit/aggregate/HitGraph.js +7 -7
- package/components/elements/hit/elements/HitTimestamp.js +8 -8
- package/components/hooks/useMyChart.d.ts +1 -1
- package/components/hooks/useMyChart.js +1 -1
- package/components/routes/advanced/QueryBuilder.js +3 -3
- package/components/routes/analytics/AnalyticOverview.d.ts +1 -1
- package/components/routes/analytics/AnalyticOverview.js +1 -1
- package/components/routes/analytics/AnalyticOverviews.d.ts +1 -1
- package/components/routes/analytics/AnalyticOverviews.js +1 -1
- package/components/routes/analytics/AnalyticTemplates.d.ts +1 -1
- package/components/routes/analytics/AnalyticTemplates.js +1 -1
- package/components/routes/analytics/RuleView.d.ts +1 -1
- package/components/routes/analytics/RuleView.js +1 -1
- package/components/routes/analytics/TriageSettings.d.ts +1 -1
- package/components/routes/analytics/TriageSettings.js +1 -1
- package/components/routes/analytics/widgets/Assessment.d.ts +1 -1
- package/components/routes/analytics/widgets/Assessment.js +1 -1
- package/components/routes/analytics/widgets/Created.d.ts +1 -1
- package/components/routes/analytics/widgets/Created.js +1 -1
- package/components/routes/analytics/widgets/Detection.d.ts +1 -1
- package/components/routes/analytics/widgets/Detection.js +1 -1
- package/components/routes/analytics/widgets/Escalation.d.ts +1 -1
- package/components/routes/analytics/widgets/Escalation.js +1 -1
- package/components/routes/analytics/widgets/Stacked.d.ts +1 -1
- package/components/routes/analytics/widgets/Stacked.js +1 -1
- package/components/routes/analytics/widgets/Status.d.ts +1 -1
- package/components/routes/analytics/widgets/Status.js +1 -1
- package/components/routes/help/TemplateDocumentation.js +5 -5
- package/components/routes/hits/search/HitBrowser.js +2 -2
- package/components/routes/hits/search/shared/CustomSpan.js +6 -6
- package/components/routes/home/index.js +4 -4
- package/components/routes/settings/SecuritySection.js +2 -2
- package/index.js +7 -0
- package/package.json +3 -3
- package/utils/constants.js +2 -2
- package/utils/utils.js +3 -3
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Typography } from '@mui/material';
|
|
3
3
|
import {} from '@cccsaurora/howler-ui/commons/components/notification';
|
|
4
|
-
import
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
5
|
import { memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
export const NotificationItemDate = memo(({ date_published = null }) => {
|
|
8
8
|
const { i18n } = useTranslation(['notification']);
|
|
9
|
-
return !date_published ? null : (_jsx(Typography, { lineHeight: "revert", display: "block", variant: "caption", color: "textSecondary", children: _jsx(_Fragment, { children:
|
|
9
|
+
return !date_published ? null : (_jsx(Typography, { lineHeight: "revert", display: "block", variant: "caption", color: "textSecondary", children: _jsx(_Fragment, { children: dayjs(date_published).locale(i18n.language).fromNow() }) }));
|
|
10
10
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Alert, AlertTitle, Button, Checkbox, Divider, FormControl, FormControlLabel, FormGroup, FormLabel, Stack, TextField, Typography } from '@mui/material';
|
|
3
3
|
import { LocalizationProvider, StaticDateTimePicker } from '@mui/x-date-pickers';
|
|
4
|
-
import {
|
|
4
|
+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
5
5
|
import api from '@cccsaurora/howler-ui/api';
|
|
6
6
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
7
7
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
8
|
+
import dayjs from 'dayjs';
|
|
8
9
|
import {} from '@cccsaurora/howler-ui/models/entities/generated/ApiType';
|
|
9
|
-
import moment from 'moment';
|
|
10
10
|
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
11
11
|
import { Trans, useTranslation } from 'react-i18next';
|
|
12
12
|
import { ApiConfigContext } from '../providers/ApiConfigProvider';
|
|
@@ -30,7 +30,7 @@ const ApiKeyDrawer = ({ onCreated }) => {
|
|
|
30
30
|
if (!amount || !unit) {
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return dayjs().add(amount, unit);
|
|
34
34
|
}, [amount, unit]);
|
|
35
35
|
const updatePrivs = useCallback((priv) => (ev) => {
|
|
36
36
|
if (ev.target.checked) {
|
|
@@ -62,6 +62,6 @@ const ApiKeyDrawer = ({ onCreated }) => {
|
|
|
62
62
|
useEffect(() => {
|
|
63
63
|
setExpiryDate(maxDate);
|
|
64
64
|
}, [maxDate]);
|
|
65
|
-
return (_jsx(LocalizationProvider, { dateAdapter:
|
|
65
|
+
return (_jsx(LocalizationProvider, { dateAdapter: AdapterDayjs, children: _jsxs(Stack, { direction: "column", spacing: 2, sx: { mt: 2 }, children: [_jsx(Typography, { sx: { maxWidth: '500px' }, children: _jsx(Trans, { i18nKey: "app.drawer.user.apikey.description" }) }), amount && unit && dayjs.duration(amount, unit).asSeconds() < dayjs.duration(7, 'days').asSeconds() && (_jsxs(Alert, { severity: "warning", variant: "outlined", sx: { maxWidth: '500px' }, children: [_jsx(AlertTitle, { children: t('app.drawer.user.apikey.limit.title') }), t('app.drawer.user.apikey.limit.description'), " (", maxDate?.format('MMM D HH:mm:ss'), ")"] })), _jsx(TextField, { label: t('app.drawer.user.apikey.field.name'), required: true, fullWidth: true, value: keyName, onChange: onChange }), _jsxs(FormControl, { required: true, children: [_jsx(FormLabel, { component: "legend", children: t('app.drawer.user.apikey.permissions') }), _jsxs(FormGroup, { sx: { display: 'grid', gridTemplateColumns: '1fr 1fr' }, children: [_jsx(FormControlLabel, { control: _jsx(Checkbox, { onChange: updatePrivs('R') }), label: t('@cccsaurora/howler-ui/apikey.read') }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { onChange: updatePrivs('W') }), label: t('@cccsaurora/howler-ui/apikey.write') }), _jsx(FormControlLabel, { disabled: privs.includes('E'), control: _jsx(Checkbox, { onChange: updatePrivs('I') }), label: t('@cccsaurora/howler-ui/apikey.impersonate') }), config.configuration.auth.allow_extended_apikeys && (_jsx(FormControlLabel, { disabled: privs.includes('I'), control: _jsx(Checkbox, { onChange: updatePrivs('E') }), label: t('@cccsaurora/howler-ui/apikey.extended') }))] })] }), _jsxs(FormControl, { required: !!maxDate, children: [_jsx(FormLabel, { children: t('app.drawer.user.apikey.expiry.date') }), _jsx(StaticDateTimePicker, { orientation: "landscape", ampm: false, value: expiryDate, onChange: newValue => setExpiryDate(newValue), disablePast: true, maxDate: maxDate, maxTime: maxDate, sx: { backgroundColor: 'transparent', '& > div:first-of-type': { maxWidth: '300px' } } })] }), _jsx(Button, { onClick: onSubmit, disabled: !keyName || (!privs.includes('R') && !privs.includes('W')) || (maxDate && !expiryDate), variant: "outlined", children: _jsx(Trans, { i18nKey: "button.create" }) }), createdKey && (_jsxs(_Fragment, { children: [_jsx(Divider, { orientation: "horizontal" }), _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "stretch", children: [_jsx(TextField, { size: "small", value: createdKey, inputProps: { readOnly: true }, fullWidth: true }), _jsx(Button, { variant: "outlined", onClick: onCopy, disabled: !createdKey, children: _jsx(Trans, { i18nKey: "button.copy" }) })] })] }))] }) }));
|
|
66
66
|
};
|
|
67
67
|
export default ApiKeyDrawer;
|
|
@@ -143,7 +143,7 @@ const HitSummary = ({ query, response, onStart, onComplete }) => {
|
|
|
143
143
|
return (_jsxs(Stack, { sx: { mx: 2, height: '100%' }, spacing: 1, children: [_jsx(Typography, { variant: "h6", children: t('hit.summary.aggregate.title') }), _jsx(Divider, { flexItem: true }), _jsx(HitGraph, { query: query }), _jsx(Divider, { flexItem: true }), _jsxs(Stack, { sx: { overflow: 'auto', marginTop: '0 !important' }, pt: 1, spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 2, mb: 2, alignItems: "stretch", children: [_jsx(Autocomplete, { fullWidth: true, multiple: true, sx: { minWidth: '175px' }, size: "small", value: customKeys, options: hitFields.map(_field => _field.key), renderInput: _params => _jsx(TextField, { ..._params, label: t('hit.summary.adhoc') }), onChange: (_, value) => setCustomKeys(value) }), _jsx(Button, { variant: "outlined", startIcon: loading ? _jsx(CircularProgress, { size: 20, sx: { ml: 1 } }) : _jsx(Analytics, { sx: { ml: 1 } }), disabled: loading, onClick: () => performAggregation(), children: t('button.aggregate') })] }), isEmpty(aggregateResults) && (_jsxs(Alert, { severity: "info", variant: "outlined", children: [_jsx(AlertTitle, { children: t('hit.summary.aggregate.nokeys.title') }), t('hit.summary.aggregate.nokeys.description')] })), loading && _jsx(LinearProgress, { sx: { minHeight: '4px' } }), Object.keys(aggregateResults)
|
|
144
144
|
.filter(key => !isEmpty(aggregateResults[key]))
|
|
145
145
|
.flatMap(key => [
|
|
146
|
-
_jsx(Fade, { in: true, children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Typography, { variant: "body1", children: key }, key + '-title'), keyCounts[key]?.count < 0 ? (_jsxs(Typography, { variant: "caption", color: "text.secondary", children: ["(", t('hit.summary.adhoc.custom'), ")"] })) : (_jsxs(Typography, { variant: "caption", color: "text.secondary", children: ["(", keyCounts[key]?.count, " ", t('references'), ")"] })), _jsx(Tooltip, { title: _jsxs(Stack, { children: [_jsx(Typography, { variant: "caption", children: t('hit.summary.aggregate.sources') }), keyCounts[key]
|
|
146
|
+
_jsx(Fade, { in: true, children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Typography, { variant: "body1", children: key }, key + '-title'), keyCounts[key]?.count < 0 ? (_jsxs(Typography, { variant: "caption", color: "text.secondary", children: ["(", t('hit.summary.adhoc.custom'), ")"] })) : (_jsxs(Typography, { variant: "caption", color: "text.secondary", children: ["(", keyCounts[key]?.count ?? '?', " ", t('references'), ")"] })), _jsx(Tooltip, { title: _jsxs(Stack, { children: [_jsx(Typography, { variant: "caption", children: t('hit.summary.aggregate.sources') }), keyCounts[key]?.sources.map(source => (_jsx(Typography, { variant: "caption", children: source }, source))) ?? '?'] }), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }) }, key + '-refs'),
|
|
147
147
|
_jsx(Fade, { in: true, children: hitFields.find(f => f.key === key)?.type !== 'date' ? (_jsx(Box, { sx: theme => ({ ml: `${theme.spacing(1)} !important`, alignSelf: 'start' }), children: _jsx(Grid, { container: true, sx: theme => ({ mr: 1, mt: theme.spacing(-1) }), spacing: 1, children: Object.keys(aggregateResults[key]).map(_key => (_jsx(Grid, { item: true, xs: "auto", children: _jsx(PluginChip, { context: "summary", size: "small", variant: "filled", value: _key, label: `${_key} (${aggregateResults[key][_key]})`, onClick: () => setSearch(key, `"${_key}"`) }) }, _key))) }, key + '-list') })) : (_jsx(Chip, { size: "small", sx: theme => ({ ml: `${theme.spacing(1)} !important`, alignSelf: 'start' }), label: getTimeRange(Object.keys(aggregateResults[key]))
|
|
148
148
|
.map(d => new Date(d).toLocaleString())
|
|
149
149
|
.join(' - '), onClick: () => setSearch(key, `[${getTimeRange(Object.keys(aggregateResults[key])).join(' TO ')}]`) })) }, key + '-results')
|
|
@@ -2,15 +2,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { CenterFocusWeak, Refresh } from '@mui/icons-material';
|
|
3
3
|
import { Alert, AlertTitle, Autocomplete, Box, Button, CircularProgress, IconButton, Stack, TextField, Tooltip, alpha, useTheme } from '@mui/material';
|
|
4
4
|
import api from '@cccsaurora/howler-ui/api';
|
|
5
|
-
import 'chartjs-adapter-
|
|
5
|
+
import 'chartjs-adapter-dayjs';
|
|
6
6
|
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
7
7
|
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
8
8
|
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
9
9
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
10
10
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
11
11
|
import useMyChart from '@cccsaurora/howler-ui/components/hooks/useMyChart';
|
|
12
|
+
import dayjs from 'dayjs';
|
|
12
13
|
import { capitalize } from 'lodash-es';
|
|
13
|
-
import moment from 'moment';
|
|
14
14
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
15
15
|
import { Scatter } from 'react-chartjs-2';
|
|
16
16
|
import { useTranslation } from 'react-i18next';
|
|
@@ -96,10 +96,10 @@ const HitGraph = ({ query }) => {
|
|
|
96
96
|
return {
|
|
97
97
|
label: `${label} (${category.total})`,
|
|
98
98
|
data: category.items.map(hit => {
|
|
99
|
-
const
|
|
99
|
+
const createdDate = dayjs(hit.event?.created ?? hit.timestamp);
|
|
100
100
|
return {
|
|
101
|
-
x:
|
|
102
|
-
y:
|
|
101
|
+
x: createdDate.clone().hour(0).minute(0).second(0).toISOString(),
|
|
102
|
+
y: createdDate.hour() + createdDate.minute() / 60 + createdDate.second() / 3600,
|
|
103
103
|
hit,
|
|
104
104
|
label
|
|
105
105
|
};
|
|
@@ -157,7 +157,7 @@ const HitGraph = ({ query }) => {
|
|
|
157
157
|
tooltip: {
|
|
158
158
|
callbacks: {
|
|
159
159
|
title: entries => `${entries.length} ${t('hits')}`,
|
|
160
|
-
label: entry => `${entry.raw.hit.howler.analytic}: ${entry.raw.hit.howler.detection} (${
|
|
160
|
+
label: entry => `${entry.raw.hit.howler.analytic}: ${entry.raw.hit.howler.detection} (${dayjs(entry.raw.hit.event.created).format('MMM D HH:mm:ss')})`,
|
|
161
161
|
afterLabel: entry => `${entry.raw.hit.howler.outline.threat} ${entry.raw.hit.howler.outline.target}`
|
|
162
162
|
}
|
|
163
163
|
},
|
|
@@ -183,7 +183,7 @@ const HitGraph = ({ query }) => {
|
|
|
183
183
|
ticks: {
|
|
184
184
|
callback: (value) => {
|
|
185
185
|
const [hour, minute] = [Math.floor(value), Math.floor((value - Math.floor(value)) * 60)];
|
|
186
|
-
return
|
|
186
|
+
return dayjs().hour(hour).minute(minute).format('HH:mm');
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Chip, Tooltip } from '@mui/material';
|
|
3
3
|
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
4
|
-
import
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
5
|
import { useContext, useMemo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { formatDate } from '@cccsaurora/howler-ui/utils/utils';
|
|
@@ -14,31 +14,31 @@ const TIMESTAMP_MESSAGES = {
|
|
|
14
14
|
const HitTimestamp = ({ hit, layout }) => {
|
|
15
15
|
const { t } = useTranslation();
|
|
16
16
|
const { config } = useContext(ApiConfigContext);
|
|
17
|
-
const threshold = useMemo(() =>
|
|
17
|
+
const threshold = useMemo(() => dayjs().subtract(config.configuration.system.retention?.limit_amount ?? 350, config.configuration.system.retention?.limit_unit ?? 'days'), [config.configuration.system.retention?.limit_amount, config.configuration.system.retention?.limit_unit]);
|
|
18
18
|
const timestamp = useMemo(() => {
|
|
19
19
|
const validFieldValues = [hit.howler?.expiry, hit.event?.created, hit.timestamp];
|
|
20
20
|
const earliestDate = validFieldValues
|
|
21
21
|
.filter(entry => !!entry)
|
|
22
22
|
.reduce((earliest, current) => {
|
|
23
|
-
return
|
|
23
|
+
return dayjs(earliest).isBefore(current) ? earliest : current;
|
|
24
24
|
});
|
|
25
25
|
return earliestDate;
|
|
26
26
|
}, [hit]);
|
|
27
27
|
const color = useMemo(() => {
|
|
28
|
-
if (
|
|
28
|
+
if (dayjs(timestamp).isBefore(threshold.clone().add(2, 'weeks'))) {
|
|
29
29
|
return 'error';
|
|
30
30
|
}
|
|
31
|
-
if (
|
|
31
|
+
if (dayjs(timestamp).isBefore(threshold.clone().add(1, 'months'))) {
|
|
32
32
|
return 'warning';
|
|
33
33
|
}
|
|
34
34
|
return 'default';
|
|
35
35
|
}, [threshold, timestamp]);
|
|
36
36
|
const duration = useMemo(() => {
|
|
37
|
-
if (
|
|
37
|
+
if (dayjs(timestamp).isBefore(threshold)) {
|
|
38
38
|
return t('retention.imminent');
|
|
39
39
|
}
|
|
40
|
-
const diff =
|
|
41
|
-
const _duration =
|
|
40
|
+
const diff = dayjs(timestamp).diff(threshold, 'seconds');
|
|
41
|
+
const _duration = dayjs.duration(diff, 'seconds');
|
|
42
42
|
return _duration.humanize();
|
|
43
43
|
}, [t, threshold, timestamp]);
|
|
44
44
|
return (_jsx(Tooltip, { title: t(TIMESTAMP_MESSAGES[color], {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTheme } from '@mui/material';
|
|
2
2
|
import { ArcElement, BarElement, CategoryScale, Chart as ChartJS, Filler, Legend, LinearScale, LineElement, PointElement, SubTitle, TimeScale, Title, Tooltip } from 'chart.js';
|
|
3
|
-
import 'chartjs-adapter-
|
|
3
|
+
import 'chartjs-adapter-dayjs';
|
|
4
4
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
@@ -13,7 +13,7 @@ import CustomButton from '@cccsaurora/howler-ui/components/elements/addons/butto
|
|
|
13
13
|
import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
|
|
14
14
|
import JSONViewer from '@cccsaurora/howler-ui/components/elements/display/json/JSONViewer';
|
|
15
15
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
16
|
-
import
|
|
16
|
+
import dayjs from 'dayjs';
|
|
17
17
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
18
18
|
import { useTranslation } from 'react-i18next';
|
|
19
19
|
import { Link } from 'react-router-dom';
|
|
@@ -41,8 +41,8 @@ description: A basic example of using sigma rule notation to query howler
|
|
|
41
41
|
references:
|
|
42
42
|
- https://github.com/SigmaHQ/sigma
|
|
43
43
|
author: You
|
|
44
|
-
date: ${
|
|
45
|
-
modified: ${
|
|
44
|
+
date: ${dayjs().format('YYYY/MM/DD')}
|
|
45
|
+
modified: ${dayjs().format('YYYY/MM/DD')}
|
|
46
46
|
tags:
|
|
47
47
|
- attack.command_and_control
|
|
48
48
|
logsource:
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Check, Edit } from '@mui/icons-material';
|
|
3
3
|
import { Alert, AlertTitle, Box, Card, CardContent, CircularProgress, Divider, IconButton, Skeleton, Stack, TextField, Typography, useMediaQuery, useTheme } from '@mui/material';
|
|
4
4
|
import api from '@cccsaurora/howler-ui/api';
|
|
5
|
-
import 'chartjs-adapter-
|
|
5
|
+
import 'chartjs-adapter-dayjs';
|
|
6
6
|
import Markdown from '@cccsaurora/howler-ui/components/elements/display/Markdown';
|
|
7
7
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
8
8
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Article } from '@mui/icons-material';
|
|
3
3
|
import { Box, Fab, Skeleton, Stack, Typography, useMediaQuery } from '@mui/material';
|
|
4
|
-
import 'chartjs-adapter-
|
|
4
|
+
import 'chartjs-adapter-dayjs';
|
|
5
5
|
import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
|
|
6
6
|
import { OverviewContext } from '@cccsaurora/howler-ui/components/app/providers/OverviewProvider';
|
|
7
7
|
import { useContext, useEffect, useMemo, useState } from 'react';
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Article } from '@mui/icons-material';
|
|
3
3
|
import { Box, Fab, Skeleton, Stack, Typography, useMediaQuery } from '@mui/material';
|
|
4
4
|
import api from '@cccsaurora/howler-ui/api';
|
|
5
|
-
import 'chartjs-adapter-
|
|
5
|
+
import 'chartjs-adapter-dayjs';
|
|
6
6
|
import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
|
|
7
7
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
8
8
|
import { useEffect, useState } from 'react';
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Check, Edit } from '@mui/icons-material';
|
|
3
3
|
import { Box, CircularProgress, IconButton, Stack, Typography, useTheme } from '@mui/material';
|
|
4
4
|
import api from '@cccsaurora/howler-ui/api';
|
|
5
|
-
import 'chartjs-adapter-
|
|
5
|
+
import 'chartjs-adapter-dayjs';
|
|
6
6
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
7
7
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
8
8
|
import { useCallback, useState } from 'react';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Checkbox, Divider, Grid, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, Typography } from '@mui/material';
|
|
3
3
|
import api from '@cccsaurora/howler-ui/api';
|
|
4
|
-
import 'chartjs-adapter-
|
|
4
|
+
import 'chartjs-adapter-dayjs';
|
|
5
5
|
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
6
6
|
import EditRow from '@cccsaurora/howler-ui/components/elements/EditRow';
|
|
7
7
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Skeleton, Typography } from '@mui/material';
|
|
3
3
|
import api from '@cccsaurora/howler-ui/api';
|
|
4
|
-
import 'chartjs-adapter-
|
|
4
|
+
import 'chartjs-adapter-dayjs';
|
|
5
5
|
import useMyChart from '@cccsaurora/howler-ui/components/hooks/useMyChart';
|
|
6
6
|
import { forwardRef, useEffect, useState } from 'react';
|
|
7
7
|
import { Bar } from 'react-chartjs-2';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Skeleton } from '@mui/material';
|
|
3
3
|
import api from '@cccsaurora/howler-ui/api';
|
|
4
|
-
import 'chartjs-adapter-
|
|
4
|
+
import 'chartjs-adapter-dayjs';
|
|
5
5
|
import useMyChart from '@cccsaurora/howler-ui/components/hooks/useMyChart';
|
|
6
6
|
import { forwardRef, useEffect, useState } from 'react';
|
|
7
7
|
import { Line } from 'react-chartjs-2';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Skeleton, useTheme } from '@mui/material';
|
|
3
3
|
import api from '@cccsaurora/howler-ui/api';
|
|
4
|
-
import 'chartjs-adapter-
|
|
4
|
+
import 'chartjs-adapter-dayjs';
|
|
5
5
|
import useMyChart from '@cccsaurora/howler-ui/components/hooks/useMyChart';
|
|
6
6
|
import { forwardRef, useEffect, useMemo, useState } from 'react';
|
|
7
7
|
import { Doughnut } from 'react-chartjs-2';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Skeleton } from '@mui/material';
|
|
3
3
|
import api from '@cccsaurora/howler-ui/api';
|
|
4
|
-
import 'chartjs-adapter-
|
|
4
|
+
import 'chartjs-adapter-dayjs';
|
|
5
5
|
import useMyChart from '@cccsaurora/howler-ui/components/hooks/useMyChart';
|
|
6
6
|
import sum from 'lodash-es/sum';
|
|
7
7
|
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useTheme } from '@mui/material';
|
|
3
|
-
import 'chartjs-adapter-
|
|
3
|
+
import 'chartjs-adapter-dayjs';
|
|
4
4
|
import { forwardRef } from 'react';
|
|
5
5
|
import { STATUS_COLORS } from '@cccsaurora/howler-ui/utils/constants';
|
|
6
6
|
import Stacked from './Stacked';
|
|
@@ -5,7 +5,7 @@ import Markdown from '@cccsaurora/howler-ui/components/elements/display/Markdown
|
|
|
5
5
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
6
6
|
import DefaultOutline from '@cccsaurora/howler-ui/components/elements/hit/outlines/DefaultOutline';
|
|
7
7
|
import { useScrollRestoration } from '@cccsaurora/howler-ui/components/hooks/useScrollRestoration';
|
|
8
|
-
import
|
|
8
|
+
import dayjs from 'dayjs';
|
|
9
9
|
import { useMemo } from 'react';
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import TEMPLATES_EN from './markdown/en/templates.md';
|
|
@@ -14,8 +14,8 @@ const ALERTS = [
|
|
|
14
14
|
{
|
|
15
15
|
howler: { id: 'hit1', analytic: 'Cat Checker', detection: 'Listening for Meows' },
|
|
16
16
|
event: {
|
|
17
|
-
start:
|
|
18
|
-
end:
|
|
17
|
+
start: dayjs().subtract(4, 'hour').toString(),
|
|
18
|
+
end: dayjs().subtract(3, 'hour').toString(),
|
|
19
19
|
kind: 'Loud meow',
|
|
20
20
|
outcome: 'Food provided'
|
|
21
21
|
}
|
|
@@ -23,8 +23,8 @@ const ALERTS = [
|
|
|
23
23
|
{
|
|
24
24
|
howler: { id: 'hit2', analytic: 'Cat Checker', detection: 'Looking for paw prints' },
|
|
25
25
|
event: {
|
|
26
|
-
start:
|
|
27
|
-
end:
|
|
26
|
+
start: dayjs().subtract(6, 'hour').toString(),
|
|
27
|
+
end: dayjs().subtract(5, 'hour').toString(),
|
|
28
28
|
provider: "The neighbour's cat (probably)",
|
|
29
29
|
reason: 'There was some fish we forgot to put away in the kitchen'
|
|
30
30
|
}
|
|
@@ -10,8 +10,8 @@ import FlexPort from '@cccsaurora/howler-ui/components/elements/addons/layout/Fl
|
|
|
10
10
|
import HitSummary from '@cccsaurora/howler-ui/components/elements/hit/HitSummary';
|
|
11
11
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
12
12
|
import ErrorBoundary from '@cccsaurora/howler-ui/components/routes/ErrorBoundary';
|
|
13
|
+
import dayjs from 'dayjs';
|
|
13
14
|
import { has, isNull } from 'lodash-es';
|
|
14
|
-
import moment from 'moment';
|
|
15
15
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
16
16
|
import { Trans, useTranslation } from 'react-i18next';
|
|
17
17
|
import { useLocation, useParams, useSearchParams } from 'react-router-dom';
|
|
@@ -88,7 +88,7 @@ const HitBrowser = () => {
|
|
|
88
88
|
useEffect(() => {
|
|
89
89
|
// On load check to filter out any queries older than one month
|
|
90
90
|
setQueryHistory(_queryHistory => {
|
|
91
|
-
const filterQueryTime =
|
|
91
|
+
const filterQueryTime = dayjs().subtract(1, 'month').toISOString();
|
|
92
92
|
return Object.fromEntries(Object.entries(_queryHistory).filter(([_, value]) => value > filterQueryTime));
|
|
93
93
|
});
|
|
94
94
|
}, [setQueryHistory]);
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Stack } from '@mui/material';
|
|
3
3
|
import { LocalizationProvider } from '@mui/x-date-pickers';
|
|
4
|
-
import {
|
|
4
|
+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
5
5
|
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
|
|
6
6
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
7
|
-
import
|
|
7
|
+
import dayjs from 'dayjs';
|
|
8
8
|
import { memo, useEffect } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
import { useContextSelector } from 'use-context-selector';
|
|
11
11
|
const CustomSpan = () => {
|
|
12
12
|
const { t } = useTranslation();
|
|
13
13
|
const span = useContextSelector(ParameterContext, ctx => ctx.span);
|
|
14
|
-
const startDate = useContextSelector(ParameterContext, ctx => (ctx.startDate ?
|
|
14
|
+
const startDate = useContextSelector(ParameterContext, ctx => (ctx.startDate ? dayjs(ctx.startDate) : null));
|
|
15
15
|
const setCustomSpan = useContextSelector(ParameterContext, ctx => ctx.setCustomSpan);
|
|
16
|
-
const endDate = useContextSelector(ParameterContext, ctx => (ctx.endDate ?
|
|
16
|
+
const endDate = useContextSelector(ParameterContext, ctx => (ctx.endDate ? dayjs(ctx.endDate) : null));
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
if (span?.endsWith('custom') && (!startDate || !endDate)) {
|
|
19
|
-
setCustomSpan(
|
|
19
|
+
setCustomSpan(dayjs().subtract(3, 'days').toISOString(), dayjs().toISOString());
|
|
20
20
|
}
|
|
21
21
|
}, [endDate, setCustomSpan, span, startDate]);
|
|
22
|
-
return span?.endsWith('custom') ? (_jsx(LocalizationProvider, { dateAdapter:
|
|
22
|
+
return span?.endsWith('custom') ? (_jsx(LocalizationProvider, { dateAdapter: AdapterDayjs, children: _jsxs(Stack, { direction: "row", spacing: 1, useFlexGap: true, flexWrap: "wrap", children: [_jsx(DateTimePicker, { sx: { minWidth: '175px', flexGrow: 1, marginTop: 1 }, slotProps: { textField: { size: 'small' } }, label: t('date.select.start'), value: startDate ? dayjs(startDate) : dayjs().subtract(1, 'days'), maxDate: endDate, onChange: (newStartDate) => setCustomSpan(newStartDate.toISOString(), endDate.toISOString()), ampm: false, disableFuture: true }), _jsx(DateTimePicker, { sx: { minWidth: '175px', flexGrow: 1, marginTop: 1 }, slotProps: { textField: { size: 'small' } }, label: t('date.select.end'), value: endDate, minDate: startDate, onChange: (newEndDate) => setCustomSpan(startDate.toISOString(), newEndDate.toISOString()), ampm: false, disableFuture: true })] }) })) : null;
|
|
23
23
|
};
|
|
24
24
|
export default memo(CustomSpan);
|
|
@@ -10,8 +10,8 @@ import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCente
|
|
|
10
10
|
import CustomButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomButton';
|
|
11
11
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
12
12
|
import useMyUserFunctions from '@cccsaurora/howler-ui/components/hooks/useMyUserFunctions';
|
|
13
|
+
import dayjs from 'dayjs';
|
|
13
14
|
import isEqual from 'lodash-es/isEqual';
|
|
14
|
-
import moment from 'moment';
|
|
15
15
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
16
16
|
import { useTranslation } from 'react-i18next';
|
|
17
17
|
import { Link } from 'react-router-dom';
|
|
@@ -26,7 +26,7 @@ const Home = () => {
|
|
|
26
26
|
const { user, setUser } = useAppUser();
|
|
27
27
|
const { setDashboard } = useMyUserFunctions();
|
|
28
28
|
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
29
|
-
const [lastViewed, setLastViewed] = useMyLocalStorageItem(StorageKey.LAST_VIEW,
|
|
29
|
+
const [lastViewed, setLastViewed] = useMyLocalStorageItem(StorageKey.LAST_VIEW, dayjs().utc().format(LUCENE_DATE_FMT));
|
|
30
30
|
const [loading, setLoading] = useState(false);
|
|
31
31
|
const [isEditing, setIsEditing] = useState(false);
|
|
32
32
|
const [updatedHitTotal, setUpdatedHitTotal] = useState(0);
|
|
@@ -81,8 +81,8 @@ const Home = () => {
|
|
|
81
81
|
})
|
|
82
82
|
.then(result => setUpdatedHitTotal(result.total));
|
|
83
83
|
}, [updateQuery]);
|
|
84
|
-
return (_jsx(PageCenter, { maxWidth: "1800px", textAlign: "left", height: "100%", children: _jsxs(Stack, { direction: "column", spacing: 1, sx: { height: '100%' }, children: [_jsxs(Stack, { direction: "row", justifyContent: "end", spacing: 1, children: [isEditing && (_jsx(CustomButton, { variant: "outlined", size: "small", color: "error", startIcon: _jsx(Cancel, {}), onClick: discardChanges, children: t('cancel') })), _jsx(CustomButton, { variant: "outlined", size: "small", disabled: isEditing && isEqual(dashboard, user.dashboard), color: isEditing ? 'success' : 'primary', startIcon: isEditing ? loading ? _jsx(CircularProgress, { size: 20 }) : _jsx(Check, {}) : _jsx(Edit, {}), onClick: () => (!isEditing ? setIsEditing(true) : saveChanges()), children: t(isEditing ? 'save' : 'edit') })] }), updatedHitTotal > 0 && (_jsxs(Alert, { severity: "info", variant: "outlined", action: _jsxs(Stack, { spacing: 1, direction: "row", children: [_jsx(IconButton, { color: "info", component: Link, to: `/hits?query=${encodeURIComponent(updateQuery)}`, onClick: () => setLastViewed(
|
|
85
|
-
setLastViewed(
|
|
84
|
+
return (_jsx(PageCenter, { maxWidth: "1800px", textAlign: "left", height: "100%", children: _jsxs(Stack, { direction: "column", spacing: 1, sx: { height: '100%' }, children: [_jsxs(Stack, { direction: "row", justifyContent: "end", spacing: 1, children: [isEditing && (_jsx(CustomButton, { variant: "outlined", size: "small", color: "error", startIcon: _jsx(Cancel, {}), onClick: discardChanges, children: t('cancel') })), _jsx(CustomButton, { variant: "outlined", size: "small", disabled: isEditing && isEqual(dashboard, user.dashboard), color: isEditing ? 'success' : 'primary', startIcon: isEditing ? loading ? _jsx(CircularProgress, { size: 20 }) : _jsx(Check, {}) : _jsx(Edit, {}), onClick: () => (!isEditing ? setIsEditing(true) : saveChanges()), children: t(isEditing ? 'save' : 'edit') })] }), updatedHitTotal > 0 && (_jsxs(Alert, { severity: "info", variant: "outlined", action: _jsxs(Stack, { spacing: 1, direction: "row", children: [_jsx(IconButton, { color: "info", component: Link, to: `/hits?query=${encodeURIComponent(updateQuery)}`, onClick: () => setLastViewed(dayjs().utc().format(LUCENE_DATE_FMT)), children: _jsx(OpenInNew, {}) }), _jsx(IconButton, { color: "info", onClick: () => {
|
|
85
|
+
setLastViewed(dayjs().utc().format(LUCENE_DATE_FMT));
|
|
86
86
|
setUpdatedHitTotal(0);
|
|
87
87
|
}, children: _jsx(Close, {}) })] }), children: [_jsx(AlertTitle, { children: t('route.home.alert.updated.title') }), t('route.home.alert.updated.description', { count: updatedHitTotal })] })), _jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: (dashboard ?? []).map(entry => getIdFromEntry(entry)), children: _jsxs(Grid, { container: true, spacing: 1, alignItems: "stretch", sx: [
|
|
88
88
|
theme => ({
|
|
@@ -3,7 +3,7 @@ import { Add } from '@mui/icons-material';
|
|
|
3
3
|
import { Chip, Grid, IconButton, TableCell, TableRow } from '@mui/material';
|
|
4
4
|
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
5
5
|
import useMyLocalStorage from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
6
|
-
import
|
|
6
|
+
import dayjs from 'dayjs';
|
|
7
7
|
import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
|
|
8
8
|
import { useContext, useMemo } from 'react';
|
|
9
9
|
import { Trans, useTranslation } from 'react-i18next';
|
|
@@ -26,6 +26,6 @@ const SecuritySection = ({ user, editPassword, addApiKey, removeApiKey, editQuot
|
|
|
26
26
|
return (_jsxs(SettingsSection, { title: t('page.settings.security.title'), colSpan: 3, children: [!isOAuth && editPassword && (_jsx(EditRow, { titleKey: "page.settings.security.table.password", value: "\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF\u25CF", onEdit: editPassword, type: "password" })), config?.configuration.auth.allow_apikeys && (_jsxs(TableRow, { sx: { cursor: 'pointer' }, children: [_jsx(TableCell, { style: { whiteSpace: 'nowrap' }, children: t('page.settings.security.table.apikeys') }), _jsx(TableCell, { width: "100%", children: _jsxs(Grid, { container: true, spacing: 1, children: [user?.apikeys?.map(apiKey => (_jsx(Grid, { item: true, children: _jsx(Chip, { label: apiKey[0].toLocaleLowerCase() +
|
|
27
27
|
(apiKey[1].length > 0
|
|
28
28
|
? ` (${apiKey[1].map(permission => t(APIKEY_LABELS[permission])).join(', ')})`
|
|
29
|
-
: ''), style: { backgroundColor:
|
|
29
|
+
: ''), style: { backgroundColor: dayjs.utc(apiKey[2]).isBefore(dayjs().utc()) ? 'orange' : 'default' }, onDelete: removeApiKey ? () => removeApiKey(apiKey) : null }) }, apiKey[0]))), user?.apikeys?.length < 1 && (_jsx(Grid, { item: true, children: _jsx(Trans, { i18nKey: "none" }) }))] }) }), _jsx(TableCell, { align: "right", children: addApiKey && (_jsx(IconButton, { onClick: addApiKey, children: _jsx(Add, { fontSize: "small" }) })) })] })), _jsx(EditRow, { titleKey: "page.settings.security.table.apiquota", descriptionKey: "page.settings.security.table.apiquota.description", value: user?.api_quota, validate: value => value && /^[0-9]*$/m.test(value.toString()), type: "number", onEdit: editQuota }), howlerPluginStore.plugins.map(plugin => pluginStore.executeFunction(`${plugin}.settings`, 'security'))] }));
|
|
30
30
|
};
|
|
31
31
|
export default SecuritySection;
|
package/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import '@fontsource/roboto';
|
|
3
3
|
import App from '@cccsaurora/howler-ui/components/app/App';
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import duration from 'dayjs/plugin/duration';
|
|
6
|
+
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
7
|
+
import utc from 'dayjs/plugin/utc';
|
|
4
8
|
import '@cccsaurora/howler-ui/i18n';
|
|
5
9
|
import 'index.css';
|
|
6
10
|
// import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
|
|
7
11
|
import * as ReactDOM from 'react-dom/client';
|
|
12
|
+
dayjs.extend(utc);
|
|
13
|
+
dayjs.extend(duration);
|
|
14
|
+
dayjs.extend(relativeTime);
|
|
8
15
|
// This is where you can inject UI plugins to modify Howler's interface.
|
|
9
16
|
// howlerPluginStore.install(new ExamplePlugin());
|
|
10
17
|
ReactDOM.createRoot(document.getElementById('root')).render(_jsx(App, {}));
|
package/package.json
CHANGED
|
@@ -31,10 +31,11 @@
|
|
|
31
31
|
"axios-retry": "^3.9.1",
|
|
32
32
|
"borealis-ui": "0.11.0",
|
|
33
33
|
"chart.js": "^4.5.0",
|
|
34
|
-
"chartjs-adapter-
|
|
34
|
+
"chartjs-adapter-dayjs": "^1.0.0",
|
|
35
35
|
"chartjs-plugin-zoom": "^2.2.0",
|
|
36
36
|
"clsx": "^2.1.1",
|
|
37
37
|
"csstype": "^3.1.3",
|
|
38
|
+
"dayjs": "^1.11.13",
|
|
38
39
|
"dompurify": "^3.2.6",
|
|
39
40
|
"express": "^4.21.2",
|
|
40
41
|
"flat": "^6.0.1",
|
|
@@ -47,7 +48,6 @@
|
|
|
47
48
|
"lodash-es": "^4.17.21",
|
|
48
49
|
"md5": "^2.3.0",
|
|
49
50
|
"mermaid": "^11.10.0",
|
|
50
|
-
"moment": "^2.30.1",
|
|
51
51
|
"monaco-editor": "^0.49.0",
|
|
52
52
|
"notistack": "^3.0.2",
|
|
53
53
|
"react": "^18.3.1",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"internal-slot": "1.0.7"
|
|
97
97
|
},
|
|
98
98
|
"type": "module",
|
|
99
|
-
"version": "2.13.0-dev.
|
|
99
|
+
"version": "2.13.0-dev.176",
|
|
100
100
|
"exports": {
|
|
101
101
|
"./i18n": "./i18n.js",
|
|
102
102
|
"./index.css": "./index.css",
|
package/utils/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { LocalPolice, MoodBad, NewReleases, PsychologyAlt, Star, Timeline } from '@mui/icons-material';
|
|
3
3
|
import { blue, grey, indigo, orange, pink, red, teal, yellow } from '@mui/material/colors';
|
|
4
|
-
import
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
5
|
export const HOWLER_API = import.meta.env.VITE_API;
|
|
6
6
|
export const LOCAL = HOWLER_API === 'MOCK';
|
|
7
7
|
export const VERSION = import.meta.env.VITE_VERSION;
|
|
@@ -66,7 +66,7 @@ export var StorageKey;
|
|
|
66
66
|
export const MOCK_SEARCH_QUERY_STORE = `${MY_LOCAL_STORAGE_PREFIX}.${StorageKey.MOCK_SEARCH_QUERY_STORE}`;
|
|
67
67
|
export const MOCK_FAVOURITES_STORE = `${MY_LOCAL_STORAGE_PREFIX}.${StorageKey.MOCK_FAVOURITES_STORE}`;
|
|
68
68
|
export const VALID_ACTION_TRIGGERS = ['create', 'promote', 'demote'];
|
|
69
|
-
const CURRENT_TIME =
|
|
69
|
+
const CURRENT_TIME = dayjs();
|
|
70
70
|
export const minutes = [CURRENT_TIME.get('minute'), CURRENT_TIME.add(30, 'minute').get('minute')].sort();
|
|
71
71
|
export const hours = [
|
|
72
72
|
CURRENT_TIME.get('hour'),
|
package/utils/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as colors from '@mui/material/colors';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
2
3
|
import { flatten, unflatten } from 'flat';
|
|
3
4
|
import { isArray, isEmpty, isNil, isObject, isPlainObject } from 'lodash-es';
|
|
4
|
-
import moment from 'moment';
|
|
5
5
|
export const bytesToSize = (bytes) => {
|
|
6
6
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
7
7
|
if (bytes === 0 || bytes === null)
|
|
@@ -39,7 +39,7 @@ export const formatDate = (date) => {
|
|
|
39
39
|
if (!date) {
|
|
40
40
|
return '?';
|
|
41
41
|
}
|
|
42
|
-
return
|
|
42
|
+
return dayjs(date).utc().format(DATE_FORMAT);
|
|
43
43
|
};
|
|
44
44
|
export const compareTimestamp = (a, b) => {
|
|
45
45
|
return (new Date(a).getTime() - new Date(b).getTime()) / 1000;
|
|
@@ -48,7 +48,7 @@ export const twitterShort = (date) => {
|
|
|
48
48
|
if (!date || date === '?') {
|
|
49
49
|
return '?';
|
|
50
50
|
}
|
|
51
|
-
return
|
|
51
|
+
return dayjs(date).fromNow();
|
|
52
52
|
};
|
|
53
53
|
export const hashCode = (s) => s.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
|
|
54
54
|
export const stringToColor = (string) => {
|