tycho-components 0.0.10-SNAPSHOT-8 → 0.0.11-SNAPSHOT
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/dist/AppLoading/AppLoading.d.ts +3 -0
- package/dist/AppLoading/AppLoading.js +7 -0
- package/dist/AppLoading/index.d.ts +2 -0
- package/dist/AppLoading/index.js +2 -0
- package/dist/AppLoading/style.scss +8 -0
- package/dist/AppPlaceholder/AppPlaceholder.d.ts +8 -0
- package/dist/AppPlaceholder/AppPlaceholder.js +5 -0
- package/dist/AppPlaceholder/index.d.ts +2 -0
- package/dist/AppPlaceholder/index.js +2 -0
- package/dist/AppPlaceholder/style.scss +18 -0
- package/dist/Comments/Comments.d.ts +9 -0
- package/dist/Comments/Comments.js +110 -0
- package/dist/Comments/HeaderNotifications/HeaderNotifications.d.ts +6 -0
- package/dist/Comments/HeaderNotifications/HeaderNotifications.js +39 -0
- package/dist/Comments/HeaderNotifications/index.d.ts +2 -0
- package/dist/Comments/HeaderNotifications/index.js +2 -0
- package/dist/Comments/HeaderNotifications/style.scss +104 -0
- package/dist/Comments/index.d.ts +2 -0
- package/dist/Comments/index.js +2 -0
- package/dist/Comments/style.scss +136 -0
- package/dist/Comments/types/Comment.d.ts +28 -0
- package/dist/Comments/types/Comment.js +13 -0
- package/dist/Comments/types/CommentService.d.ts +21 -0
- package/dist/Comments/types/CommentService.js +44 -0
- package/dist/configs/Localization.d.ts +56 -0
- package/dist/configs/Localization.js +4 -1
- package/dist/configs/User.d.ts +16 -0
- package/dist/configs/User.js +7 -0
- package/dist/configs/localization/CommentsTexts.d.ts +58 -0
- package/dist/configs/localization/CommentsTexts.js +58 -0
- package/dist/configs/store/actions.d.ts +2 -0
- package/dist/configs/store/actions.js +4 -0
- package/dist/configs/store/reducer.js +5 -0
- package/dist/configs/store/store.js +1 -0
- package/dist/configs/store/types.d.ts +4 -1
- package/dist/configs/store/types.js +1 -0
- package/dist/functions/DateUtils.d.ts +9 -0
- package/dist/functions/DateUtils.js +42 -0
- package/dist/functions/FormUtils.d.ts +11 -0
- package/dist/functions/FormUtils.js +56 -0
- package/dist/functions/SecurityUtils.d.ts +6 -0
- package/dist/functions/SecurityUtils.js +28 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/package.json +2 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import ReactLoading from 'react-loading';
|
|
3
|
+
import './style.scss';
|
|
4
|
+
function AppLoading() {
|
|
5
|
+
return (_jsx("div", { className: "loading-container", children: _jsx(ReactLoading, { type: "spinningBubbles", color: "blue", height: 50, width: 50 }) }));
|
|
6
|
+
}
|
|
7
|
+
export default AppLoading;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import './style.scss';
|
|
3
|
+
export default function AppPlaceholder({ children, onClick }) {
|
|
4
|
+
return (_jsx("div", { role: "presentation", className: "placeholder-container", onClick: () => onClick && onClick(), onKeyDown: () => onClick && onClick(), children: children }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.placeholder-container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
cursor: pointer;
|
|
7
|
+
|
|
8
|
+
margin: 32px 16px;
|
|
9
|
+
font-size: var(--font-size-xlarge);
|
|
10
|
+
|
|
11
|
+
height: 75%;
|
|
12
|
+
padding: var(--spacing-small);
|
|
13
|
+
background-color: var(--opacity-black-08);
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
font-weight: var(--font-weight-large);
|
|
17
|
+
color: var(--color-primary-alternative);
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import './style.scss';
|
|
2
|
+
type Props = {
|
|
3
|
+
lexicon: string;
|
|
4
|
+
entry: string;
|
|
5
|
+
keywords: Record<string, string | number | boolean>;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
};
|
|
8
|
+
export default function Comments({ lexicon, entry, keywords, onClose }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
3
|
+
import { Drawer } from '@mui/material';
|
|
4
|
+
import { useContext, useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { useForm } from 'react-hook-form';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Avatar, Button, IconButton, SelectField, TextField, } from 'tycho-storybook';
|
|
8
|
+
import * as yup from 'yup';
|
|
9
|
+
import AppLoading from '../AppLoading';
|
|
10
|
+
import AppPlaceholder from '../AppPlaceholder';
|
|
11
|
+
import CommonContext from '../configs/CommonContext';
|
|
12
|
+
import { useMessageUtils } from '../configs/useMessageUtils';
|
|
13
|
+
import DateUtils from '../functions/DateUtils';
|
|
14
|
+
import FormUtils from '../functions/FormUtils';
|
|
15
|
+
import SecurityUtils from '../functions/SecurityUtils';
|
|
16
|
+
import './style.scss';
|
|
17
|
+
import { EMPTY_COMMENT_REQUEST, } from './types/Comment';
|
|
18
|
+
import CommentService from './types/CommentService';
|
|
19
|
+
export default function Comments({ lexicon, entry, keywords, onClose }) {
|
|
20
|
+
const { t } = useTranslation('comments');
|
|
21
|
+
const { state } = useContext(CommonContext);
|
|
22
|
+
const { isLoading, dispatchLoading } = useMessageUtils();
|
|
23
|
+
const [openAddComment, setOpenAddComment] = useState(false);
|
|
24
|
+
const [comment, setComment] = useState();
|
|
25
|
+
const [comments, setComments] = useState();
|
|
26
|
+
const [users, setUsers] = useState([]);
|
|
27
|
+
const createdForm = useForm({
|
|
28
|
+
resolver: yupResolver(getFormSchema(t)),
|
|
29
|
+
mode: 'onChange',
|
|
30
|
+
});
|
|
31
|
+
const load = async () => {
|
|
32
|
+
try {
|
|
33
|
+
const [commentsResponse, usersResponse] = await Promise.all([
|
|
34
|
+
CommentService.find(lexicon, entry),
|
|
35
|
+
CommentService.findAvailableUsers(lexicon),
|
|
36
|
+
]);
|
|
37
|
+
setComments(commentsResponse.data);
|
|
38
|
+
setUsers(usersResponse.data);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Error loading comments or users', error);
|
|
42
|
+
// optionally handle specific error state here
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const handleAdd = () => {
|
|
46
|
+
if (isLoading())
|
|
47
|
+
return;
|
|
48
|
+
dispatchLoading(true);
|
|
49
|
+
CommentService.add(lexicon, entry, createdForm.getValues(), keywords).then((r) => {
|
|
50
|
+
dispatchLoading(false);
|
|
51
|
+
setOpenAddComment(false);
|
|
52
|
+
setComment(undefined);
|
|
53
|
+
setComments((prev) => [...(prev || []), r.data]);
|
|
54
|
+
createdForm.reset(EMPTY_COMMENT_REQUEST);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const handleMarkRead = (thisComment) => {
|
|
58
|
+
CommentService.markRead(thisComment.id).then(() => {
|
|
59
|
+
const updated = { ...thisComment, read: true };
|
|
60
|
+
setComments(comments?.map((c) => c.id === thisComment.id ? { ...c, ...updated } : c));
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const handleRemove = (thisComment) => {
|
|
64
|
+
CommentService.remove(thisComment.id).then(() => {
|
|
65
|
+
setComments(comments?.filter((c) => c.id !== thisComment.id));
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const handleEdit = (thisComment) => {
|
|
69
|
+
setComment(thisComment);
|
|
70
|
+
setOpenAddComment(true);
|
|
71
|
+
createdForm.reset({
|
|
72
|
+
value: thisComment.value,
|
|
73
|
+
title: thisComment.title,
|
|
74
|
+
requestedUser: thisComment.requested?.uid,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const handleUpdate = () => {
|
|
78
|
+
if (!comment)
|
|
79
|
+
return;
|
|
80
|
+
CommentService.update(comment.id, createdForm.getValues()).then((r) => {
|
|
81
|
+
setComments((prev) => prev?.map((c) => (c.id === comment.id ? r.data : c)));
|
|
82
|
+
setComment(undefined);
|
|
83
|
+
setOpenAddComment(false);
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const hasEditAccess = useMemo(() => {
|
|
87
|
+
return SecurityUtils.hasAccess(lexicon, ['ADMIN', 'EDITOR']);
|
|
88
|
+
}, [lexicon]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
load();
|
|
91
|
+
}, []);
|
|
92
|
+
if (!comments)
|
|
93
|
+
return _jsx(AppLoading, {});
|
|
94
|
+
return (_jsx(Drawer, { anchor: "right", open: true, onClose: onClose, children: _jsxs("div", { className: "comments-container", children: [_jsxs("div", { className: "header", children: [_jsx(IconButton, { name: "close", size: "small", mode: "ghost", onClick: onClose }), _jsx("span", { className: "title", children: t('label.title.comments') }), _jsx("div", { className: "actions", children: hasEditAccess && (_jsx(Button, { text: t('button.add'), icon: "add", mode: "outlined", size: "small", className: "edit-button", onClick: () => {
|
|
95
|
+
setComment(undefined);
|
|
96
|
+
setOpenAddComment(!openAddComment);
|
|
97
|
+
} })) })] }), _jsxs("div", { className: "body", children: [openAddComment && (_jsxs("div", { className: "form", children: [_jsx(TextField, { label: t('input.value'), attr: "value", createdForm: createdForm, showEndAdornment: false, placeholder: t('common:generic.placeholder'), required: true, multiline: true }), _jsx(TextField, { label: t('input.title'), attr: "title", createdForm: createdForm, showEndAdornment: false, placeholder: t('common:generic.placeholder') }), _jsx(SelectField, { label: t('input.user'), attr: "requestedUser", createdForm: createdForm, options: [
|
|
98
|
+
{ label: t('common:generic.placeholder.select'), value: '' },
|
|
99
|
+
...FormUtils.convertList(users, 'name', 'uid'),
|
|
100
|
+
], showEndAdornment: false }), _jsxs("div", { className: "buttons", children: [comment && (_jsx(Button, { onClick: comment?.id ? handleUpdate : handleAdd, text: t('button.discard'), color: "danger" })), _jsx(Button, { onClick: comment ? handleUpdate : handleAdd, text: comment ? t('label.button.update') : t('label.button.add'), disabled: !createdForm.formState.isValid })] })] })), comments?.length === 0 && (_jsx(AppPlaceholder, { children: _jsx("span", { children: t('placeholder.comments.empty') }) })), comments?.map((el, idx) => (_jsxs("div", { className: "comment", children: [_jsxs("div", { className: "top", children: [_jsx(Avatar, { title: el.name, src: el.picture, size: "medium" }), _jsx("div", { className: "name", children: el.name }), _jsxs("div", { className: "buttons", children: [state.logged?.uid === el.user && (_jsxs(_Fragment, { children: [_jsx(IconButton, { size: "small", onClick: () => handleEdit(el), title: t('tooltip.edit'), name: "edit" }), _jsx(IconButton, { size: "small", onClick: () => handleRemove(el), title: t('tooltip.delete'), name: "delete" })] })), el.requested &&
|
|
101
|
+
!el.read &&
|
|
102
|
+
state.logged?.uid === el.requested.uid && (_jsx(IconButton, { size: "small", onClick: () => {
|
|
103
|
+
handleMarkRead(el);
|
|
104
|
+
}, title: t('tooltip.read'), name: "check_circle" }))] })] }), _jsxs("div", { className: "content", children: [el.title && _jsx("div", { className: "title", children: el.title }), _jsx("div", { className: "text", children: el.value }), el.requested && (_jsxs("div", { className: "date", children: [_jsx("span", { className: "me-1", children: t('label.requested.to') }), _jsx("span", { children: el.requested.name })] })), _jsxs("div", { className: "date", children: [_jsx("span", { className: "me-1", children: t('label.posted.on') }), _jsx("span", { className: "me-1", children: DateUtils.formatDateTime(el.edited ? el.edited : el.date, 'MM/dd/yyyy - HH:mm') }), el.edited && _jsx("i", { children: "edited" })] })] })] }, idx.valueOf())))] })] }) }));
|
|
105
|
+
}
|
|
106
|
+
const getFormSchema = (t) => yup.object().shape({
|
|
107
|
+
title: yup.string().optional(),
|
|
108
|
+
value: yup.string().required(t('common:validation.required')),
|
|
109
|
+
requestedUser: yup.string().optional(),
|
|
110
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Trans, useTranslation } from 'react-i18next';
|
|
4
|
+
import { useNavigate } from 'react-router-dom';
|
|
5
|
+
import { Avatar, IconButton } from 'tycho-storybook';
|
|
6
|
+
import DateUtils from '../../functions/DateUtils';
|
|
7
|
+
import CommentService from '../types/CommentService';
|
|
8
|
+
import './style.scss';
|
|
9
|
+
export default function HeaderNotifications({ lexicon }) {
|
|
10
|
+
const navigate = useNavigate();
|
|
11
|
+
const { t } = useTranslation('comments');
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
const [notifications, setNotifications] = useState();
|
|
14
|
+
const [comments, setComments] = useState();
|
|
15
|
+
const [tab, setTab] = useState('unread');
|
|
16
|
+
const handleOpen = (notification) => {
|
|
17
|
+
navigate(`/edit/${notification.entry}`);
|
|
18
|
+
setOpen(false);
|
|
19
|
+
};
|
|
20
|
+
const load = () => {
|
|
21
|
+
const serviceMethod = tab === 'read'
|
|
22
|
+
? CommentService.findReadNotifications
|
|
23
|
+
: CommentService.findNotifications;
|
|
24
|
+
serviceMethod(lexicon).then((r) => {
|
|
25
|
+
if (tab !== 'read') {
|
|
26
|
+
setComments(r.data);
|
|
27
|
+
}
|
|
28
|
+
setNotifications(r.data);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
load();
|
|
33
|
+
}, [lexicon, tab]);
|
|
34
|
+
return (_jsxs("div", { className: "ds-dropdown-container notifications-container", children: [_jsx(IconButton, { name: "notifications_active", className: comments && comments.length > 0 ? 'shake' : '', size: "medium", onClick: () => setOpen(!open) }), open && (_jsx("div", { className: "ds-dropdown-list", children: _jsxs("div", { className: "notifications-panel", children: [_jsxs("div", { className: "header", children: [_jsx("span", { children: t('notification.title') }), _jsx(IconButton, { name: "close", size: "small", mode: "ghost", onClick: () => setOpen(false) })] }), _jsxs("div", { className: "tabs", children: [_jsxs("button", { className: tab === 'unread' ? 'active-tab' : '', onClick: () => setTab('unread'), children: [t('notification.unread'), " (", comments?.length || 0, ")"] }), _jsx("button", { className: tab === 'read' ? 'active-tab' : '', onClick: () => setTab('read'), children: t('notification.all') })] }), _jsxs("div", { className: "content", children: [notifications &&
|
|
35
|
+
notifications.map((not, idx) => (_jsxs("div", { className: "item", onClick: () => handleOpen(not), children: [_jsx(Avatar, { src: not.picture, size: "small" }), _jsxs("div", { className: "message", children: [_jsx("span", { className: "text", children: _jsx(Trans, { t: t, i18nKey: "notification.text", values: {
|
|
36
|
+
user: not.name,
|
|
37
|
+
entry: not.keywords ? not.keywords['entry'] : '',
|
|
38
|
+
} }) }), _jsx("span", { className: "date", children: DateUtils.formatDateTime(not.edited ? not.edited : not.date, 'MM/dd/yyyy - HH:mm') })] })] }, idx.valueOf()))), notifications && notifications.length === 0 && (_jsx("div", { className: "empty-notifications", children: t('notification.empty') }))] })] }) }))] }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
@use 'tycho-storybook/dist/styles/main' as *;
|
|
2
|
+
|
|
3
|
+
.notifications-container {
|
|
4
|
+
@keyframes shake {
|
|
5
|
+
0% {
|
|
6
|
+
transform: rotate(0deg);
|
|
7
|
+
}
|
|
8
|
+
20% {
|
|
9
|
+
transform: rotate(-15deg);
|
|
10
|
+
}
|
|
11
|
+
40% {
|
|
12
|
+
transform: rotate(15deg);
|
|
13
|
+
}
|
|
14
|
+
60% {
|
|
15
|
+
transform: rotate(-10deg);
|
|
16
|
+
}
|
|
17
|
+
80% {
|
|
18
|
+
transform: rotate(10deg);
|
|
19
|
+
}
|
|
20
|
+
100% {
|
|
21
|
+
transform: rotate(0deg);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.shake {
|
|
26
|
+
animation: shake 0.5s ease-in-out infinite;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
> .ds-dropdown-list {
|
|
30
|
+
right: 0;
|
|
31
|
+
width: auto;
|
|
32
|
+
|
|
33
|
+
.notifications-panel {
|
|
34
|
+
width: 320px;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
background: #fff;
|
|
37
|
+
|
|
38
|
+
.header {
|
|
39
|
+
display: flex;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
align-items: center;
|
|
42
|
+
padding: 10px 12px;
|
|
43
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
44
|
+
font-weight: bold;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.tabs {
|
|
48
|
+
display: flex;
|
|
49
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
50
|
+
|
|
51
|
+
> button {
|
|
52
|
+
flex: 1;
|
|
53
|
+
padding: 8px;
|
|
54
|
+
border: none;
|
|
55
|
+
background: none;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.active-tab {
|
|
61
|
+
border-bottom: 2.5px solid var(--border-hover);
|
|
62
|
+
color: var(--icon-accent-hover);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
> .content {
|
|
67
|
+
.item {
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: flex-start;
|
|
70
|
+
gap: 8px;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
padding: 16px;
|
|
73
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
74
|
+
|
|
75
|
+
&:hover {
|
|
76
|
+
background-color: var(--layer-hover-1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.message {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: 8px;
|
|
83
|
+
|
|
84
|
+
.text {
|
|
85
|
+
font-size: 14px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.date {
|
|
89
|
+
@include helper-small-1;
|
|
90
|
+
color: var(--text-tertiary);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.empty-notifications {
|
|
97
|
+
text-align: center;
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
color: #666;
|
|
100
|
+
padding: 20px 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
@use 'tycho-storybook/dist/styles/main' as *;
|
|
2
|
+
|
|
3
|
+
.comments-container {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
width: 480px;
|
|
7
|
+
|
|
8
|
+
> .header {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
padding: var(--spacing-100);
|
|
12
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
13
|
+
|
|
14
|
+
> .title {
|
|
15
|
+
@include tag-medium-1;
|
|
16
|
+
color: var(--text-secondary);
|
|
17
|
+
margin-left: 8px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
> .actions {
|
|
21
|
+
display: flex;
|
|
22
|
+
margin-left: auto;
|
|
23
|
+
|
|
24
|
+
.edit-button {
|
|
25
|
+
margin-right: var(--spacing-100);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
> .body {
|
|
31
|
+
padding: var(--spacing-0) var(--spacing-250);
|
|
32
|
+
background-color: #fff;
|
|
33
|
+
padding: 16px;
|
|
34
|
+
|
|
35
|
+
.comment {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
padding: var(--spacing-150) var(--spacing-100);
|
|
39
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
40
|
+
|
|
41
|
+
.top {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
|
|
45
|
+
> .ds-avatar {
|
|
46
|
+
margin-right: 8px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.buttons {
|
|
50
|
+
display: flex;
|
|
51
|
+
margin-left: auto;
|
|
52
|
+
gap: 8px;
|
|
53
|
+
|
|
54
|
+
button {
|
|
55
|
+
border: none;
|
|
56
|
+
border-radius: var(--border-radius-button);
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.content {
|
|
63
|
+
margin-top: 16px;
|
|
64
|
+
|
|
65
|
+
.title {
|
|
66
|
+
@include tag-small-1;
|
|
67
|
+
color: var(--text-accent);
|
|
68
|
+
text-transform: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.text {
|
|
72
|
+
@include body-medium-1;
|
|
73
|
+
color: var(--text-primary);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.date {
|
|
77
|
+
@include helper-small-1;
|
|
78
|
+
color: var(--text-tertiary);
|
|
79
|
+
margin-top: 8px;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.form {
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
padding-bottom: 16px;
|
|
88
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
89
|
+
|
|
90
|
+
> .ds-input-text,
|
|
91
|
+
> .ds-select-text {
|
|
92
|
+
margin-bottom: 8px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
> .buttons {
|
|
96
|
+
display: flex;
|
|
97
|
+
gap: 8px;
|
|
98
|
+
|
|
99
|
+
> .ds-button {
|
|
100
|
+
width: 100%;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.placeholder-container {
|
|
106
|
+
margin: 0px;
|
|
107
|
+
font-size: var(--font-size-medium);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.modal-comment-info {
|
|
113
|
+
.modal-body {
|
|
114
|
+
> textarea {
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 20vh;
|
|
117
|
+
padding: 8px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.footnote-actions {
|
|
121
|
+
text-align: center;
|
|
122
|
+
|
|
123
|
+
> button {
|
|
124
|
+
font-family: var(--font-family-regular);
|
|
125
|
+
font-style: var(--font-weight-medium);
|
|
126
|
+
font-weight: var(--font-weight-medium);
|
|
127
|
+
font-size: var(--font-size-button);
|
|
128
|
+
color: var(--color-background);
|
|
129
|
+
width: 200px;
|
|
130
|
+
border-radius: 8px;
|
|
131
|
+
padding: 12px;
|
|
132
|
+
border: none;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CommentRequest = {
|
|
2
|
+
title?: string;
|
|
3
|
+
value: string;
|
|
4
|
+
requestedUser?: string;
|
|
5
|
+
};
|
|
6
|
+
export type Comment = {
|
|
7
|
+
id: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
value: string;
|
|
10
|
+
date: string;
|
|
11
|
+
edited?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
user: string;
|
|
14
|
+
picture: string;
|
|
15
|
+
lexicon: string;
|
|
16
|
+
entry: string;
|
|
17
|
+
requested?: UserComment;
|
|
18
|
+
read?: boolean;
|
|
19
|
+
keywords?: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
export type UserComment = {
|
|
22
|
+
uid: string;
|
|
23
|
+
name: string;
|
|
24
|
+
picture: string;
|
|
25
|
+
email: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const EMPTY_COMMENT: Comment;
|
|
28
|
+
export declare const EMPTY_COMMENT_REQUEST: CommentRequest;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import User from '../../configs/User';
|
|
2
|
+
import { Comment, CommentRequest } from './Comment';
|
|
3
|
+
declare function markRead(id: string): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
4
|
+
declare function update(id: string, comment: CommentRequest): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
5
|
+
declare function remove(id: string): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
6
|
+
declare function add(lexicon: string, entry: string, comment: CommentRequest, keywords: Record<string, string | number | boolean>): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
7
|
+
declare function find(lexicon: string, entry?: string): Promise<import("axios").AxiosResponse<Comment[], any>>;
|
|
8
|
+
declare function findNotifications(lexicon: string): Promise<import("axios").AxiosResponse<Comment[], any>>;
|
|
9
|
+
declare function findReadNotifications(lexicon: string): Promise<import("axios").AxiosResponse<Comment[], any>>;
|
|
10
|
+
declare function findAvailableUsers(lexicon: string): Promise<import("axios").AxiosResponse<User[], any>>;
|
|
11
|
+
declare const CommentService: {
|
|
12
|
+
add: typeof add;
|
|
13
|
+
remove: typeof remove;
|
|
14
|
+
update: typeof update;
|
|
15
|
+
markRead: typeof markRead;
|
|
16
|
+
find: typeof find;
|
|
17
|
+
findNotifications: typeof findNotifications;
|
|
18
|
+
findReadNotifications: typeof findReadNotifications;
|
|
19
|
+
findAvailableUsers: typeof findAvailableUsers;
|
|
20
|
+
};
|
|
21
|
+
export default CommentService;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import api from '../../configs/api';
|
|
2
|
+
function markRead(id) {
|
|
3
|
+
return api.put(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${id}`);
|
|
4
|
+
}
|
|
5
|
+
function update(id, comment) {
|
|
6
|
+
return api.patch(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/${id}`, { ...comment });
|
|
7
|
+
}
|
|
8
|
+
function remove(id) {
|
|
9
|
+
return api.delete(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${id}`);
|
|
10
|
+
}
|
|
11
|
+
function add(lexicon, entry, comment, keywords) {
|
|
12
|
+
return api.post(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/${lexicon}`, {
|
|
13
|
+
...comment,
|
|
14
|
+
entry,
|
|
15
|
+
keywords,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function find(lexicon, entry) {
|
|
19
|
+
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/${lexicon}`, {
|
|
20
|
+
params: {
|
|
21
|
+
entry,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function findNotifications(lexicon) {
|
|
26
|
+
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/notifications/${lexicon}`);
|
|
27
|
+
}
|
|
28
|
+
function findReadNotifications(lexicon) {
|
|
29
|
+
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/notifications/read/${lexicon}`);
|
|
30
|
+
}
|
|
31
|
+
function findAvailableUsers(lexicon) {
|
|
32
|
+
return api.get(`${import.meta.env.VITE_APP_AUTH_API}/lexicon/${lexicon}`);
|
|
33
|
+
}
|
|
34
|
+
const CommentService = {
|
|
35
|
+
add,
|
|
36
|
+
remove,
|
|
37
|
+
update,
|
|
38
|
+
markRead,
|
|
39
|
+
find,
|
|
40
|
+
findNotifications,
|
|
41
|
+
findReadNotifications,
|
|
42
|
+
findAvailableUsers,
|
|
43
|
+
};
|
|
44
|
+
export default CommentService;
|
|
@@ -39,6 +39,34 @@ export declare const commonResources: {
|
|
|
39
39
|
'modal.select.transfer': string;
|
|
40
40
|
'participant.code.exists': string;
|
|
41
41
|
};
|
|
42
|
+
comments: {
|
|
43
|
+
'label.title.comments': string;
|
|
44
|
+
'placeholder.comment.text': string;
|
|
45
|
+
'placeholder.comment.title': string;
|
|
46
|
+
'placeholder.comment.person': string;
|
|
47
|
+
'label.button.add': string;
|
|
48
|
+
'label.button.update': string;
|
|
49
|
+
'label.requested.to': string;
|
|
50
|
+
'label.posted.on': string;
|
|
51
|
+
'placeholder.comments.empty': string;
|
|
52
|
+
'tooltip.edit': string;
|
|
53
|
+
'tooltip.delete': string;
|
|
54
|
+
'tooltip.read': string;
|
|
55
|
+
'modal.title.footnote': string;
|
|
56
|
+
'placeholder.footnote.text': string;
|
|
57
|
+
'modal.title.footnote.remove': string;
|
|
58
|
+
'modal.text.footnote.remove': string;
|
|
59
|
+
'button.add': string;
|
|
60
|
+
'input.value': string;
|
|
61
|
+
'input.title': string;
|
|
62
|
+
'input.user': string;
|
|
63
|
+
'button.discard': string;
|
|
64
|
+
'notification.title': string;
|
|
65
|
+
'notification.unread': string;
|
|
66
|
+
'notification.all': string;
|
|
67
|
+
'notification.empty': string;
|
|
68
|
+
'notification.text': string;
|
|
69
|
+
};
|
|
42
70
|
};
|
|
43
71
|
'pt-BR': {
|
|
44
72
|
common: {
|
|
@@ -78,6 +106,34 @@ export declare const commonResources: {
|
|
|
78
106
|
'modal.input.name': string;
|
|
79
107
|
'participant.code.exists': string;
|
|
80
108
|
};
|
|
109
|
+
comments: {
|
|
110
|
+
'label.title.comments': string;
|
|
111
|
+
'placeholder.comment.text': string;
|
|
112
|
+
'placeholder.comment.title': string;
|
|
113
|
+
'placeholder.comment.person': string;
|
|
114
|
+
'label.button.add': string;
|
|
115
|
+
'label.button.update': string;
|
|
116
|
+
'label.requested.to': string;
|
|
117
|
+
'label.posted.on': string;
|
|
118
|
+
'placeholder.comments.empty': string;
|
|
119
|
+
'tooltip.edit': string;
|
|
120
|
+
'tooltip.delete': string;
|
|
121
|
+
'tooltip.read': string;
|
|
122
|
+
'modal.title.footnote': string;
|
|
123
|
+
'placeholder.footnote.text': string;
|
|
124
|
+
'modal.title.footnote.remove': string;
|
|
125
|
+
'modal.text.footnote.remove': string;
|
|
126
|
+
'button.add': string;
|
|
127
|
+
'input.value': string;
|
|
128
|
+
'input.title': string;
|
|
129
|
+
'input.user': string;
|
|
130
|
+
'button.discard': string;
|
|
131
|
+
'notification.title': string;
|
|
132
|
+
'notification.unread': string;
|
|
133
|
+
'notification.all': string;
|
|
134
|
+
'notification.empty': string;
|
|
135
|
+
'notification.text': string;
|
|
136
|
+
};
|
|
81
137
|
};
|
|
82
138
|
};
|
|
83
139
|
export default function commonLocalization(): void;
|
|
@@ -3,14 +3,17 @@ import languageDetector from 'i18next-browser-languagedetector';
|
|
|
3
3
|
import { initReactI18next } from 'react-i18next';
|
|
4
4
|
import { ParticipantsTexts } from './localization/ParticipantsTexts';
|
|
5
5
|
import { CommonTexts } from './localization/CommonTexts';
|
|
6
|
+
import { CommentsTexts } from './localization/CommentsTexts';
|
|
6
7
|
export const commonResources = {
|
|
7
8
|
en: {
|
|
8
9
|
common: CommonTexts.en,
|
|
9
10
|
participants: ParticipantsTexts.en,
|
|
11
|
+
comments: CommentsTexts.en,
|
|
10
12
|
},
|
|
11
13
|
'pt-BR': {
|
|
12
14
|
common: CommonTexts['pt-BR'],
|
|
13
15
|
participants: ParticipantsTexts['pt-BR'],
|
|
16
|
+
comments: CommentsTexts['pt-BR'],
|
|
14
17
|
},
|
|
15
18
|
};
|
|
16
19
|
export default function commonLocalization() {
|
|
@@ -20,7 +23,7 @@ export default function commonLocalization() {
|
|
|
20
23
|
.init({
|
|
21
24
|
resources: commonResources,
|
|
22
25
|
fallbackLng: 'en',
|
|
23
|
-
defaultNS: '
|
|
26
|
+
defaultNS: 'common',
|
|
24
27
|
interpolation: {
|
|
25
28
|
escapeValue: false,
|
|
26
29
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare enum UserStatus {
|
|
2
|
+
ACTIVE = "ACTIVE",
|
|
3
|
+
INACTIVE = "INACTIVE",
|
|
4
|
+
SUPER = "SUPER",
|
|
5
|
+
VISITOR = "VISITOR"
|
|
6
|
+
}
|
|
7
|
+
type User = {
|
|
8
|
+
uid: string;
|
|
9
|
+
name: string;
|
|
10
|
+
sub: string;
|
|
11
|
+
picture: string;
|
|
12
|
+
lang: string;
|
|
13
|
+
status: UserStatus;
|
|
14
|
+
permissions: string[];
|
|
15
|
+
};
|
|
16
|
+
export default User;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const CommentsTexts: {
|
|
2
|
+
en: {
|
|
3
|
+
'label.title.comments': string;
|
|
4
|
+
'placeholder.comment.text': string;
|
|
5
|
+
'placeholder.comment.title': string;
|
|
6
|
+
'placeholder.comment.person': string;
|
|
7
|
+
'label.button.add': string;
|
|
8
|
+
'label.button.update': string;
|
|
9
|
+
'label.requested.to': string;
|
|
10
|
+
'label.posted.on': string;
|
|
11
|
+
'placeholder.comments.empty': string;
|
|
12
|
+
'tooltip.edit': string;
|
|
13
|
+
'tooltip.delete': string;
|
|
14
|
+
'tooltip.read': string;
|
|
15
|
+
'modal.title.footnote': string;
|
|
16
|
+
'placeholder.footnote.text': string;
|
|
17
|
+
'modal.title.footnote.remove': string;
|
|
18
|
+
'modal.text.footnote.remove': string;
|
|
19
|
+
'button.add': string;
|
|
20
|
+
'input.value': string;
|
|
21
|
+
'input.title': string;
|
|
22
|
+
'input.user': string;
|
|
23
|
+
'button.discard': string;
|
|
24
|
+
'notification.title': string;
|
|
25
|
+
'notification.unread': string;
|
|
26
|
+
'notification.all': string;
|
|
27
|
+
'notification.empty': string;
|
|
28
|
+
'notification.text': string;
|
|
29
|
+
};
|
|
30
|
+
'pt-BR': {
|
|
31
|
+
'label.title.comments': string;
|
|
32
|
+
'placeholder.comment.text': string;
|
|
33
|
+
'placeholder.comment.title': string;
|
|
34
|
+
'placeholder.comment.person': string;
|
|
35
|
+
'label.button.add': string;
|
|
36
|
+
'label.button.update': string;
|
|
37
|
+
'label.requested.to': string;
|
|
38
|
+
'label.posted.on': string;
|
|
39
|
+
'placeholder.comments.empty': string;
|
|
40
|
+
'tooltip.edit': string;
|
|
41
|
+
'tooltip.delete': string;
|
|
42
|
+
'tooltip.read': string;
|
|
43
|
+
'modal.title.footnote': string;
|
|
44
|
+
'placeholder.footnote.text': string;
|
|
45
|
+
'modal.title.footnote.remove': string;
|
|
46
|
+
'modal.text.footnote.remove': string;
|
|
47
|
+
'button.add': string;
|
|
48
|
+
'input.value': string;
|
|
49
|
+
'input.title': string;
|
|
50
|
+
'input.user': string;
|
|
51
|
+
'button.discard': string;
|
|
52
|
+
'notification.title': string;
|
|
53
|
+
'notification.unread': string;
|
|
54
|
+
'notification.all': string;
|
|
55
|
+
'notification.empty': string;
|
|
56
|
+
'notification.text': string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const CommentsTexts = {
|
|
2
|
+
en: {
|
|
3
|
+
'label.title.comments': 'Comments',
|
|
4
|
+
'placeholder.comment.text': 'Your comment',
|
|
5
|
+
'placeholder.comment.title': 'Title (optional)',
|
|
6
|
+
'placeholder.comment.person': 'Select person',
|
|
7
|
+
'label.button.add': 'Add comment',
|
|
8
|
+
'label.button.update': 'Update comment',
|
|
9
|
+
'label.requested.to': 'Sent to:',
|
|
10
|
+
'label.posted.on': 'Posted on:',
|
|
11
|
+
'placeholder.comments.empty': 'No comments available.',
|
|
12
|
+
'tooltip.edit': 'Edit comment',
|
|
13
|
+
'tooltip.delete': 'Remove comment',
|
|
14
|
+
'tooltip.read': 'Mark as read',
|
|
15
|
+
'modal.title.footnote': 'Edit footnote',
|
|
16
|
+
'placeholder.footnote.text': 'Enter footnote',
|
|
17
|
+
'modal.title.footnote.remove': 'Confirm footnote removal',
|
|
18
|
+
'modal.text.footnote.remove': 'Are you sure you want to remove this footnote? This action is irreversible.',
|
|
19
|
+
'button.add': 'Add comment',
|
|
20
|
+
'input.value': 'Comment',
|
|
21
|
+
'input.title': 'Title',
|
|
22
|
+
'input.user': 'Send to user',
|
|
23
|
+
'button.discard': 'Discard',
|
|
24
|
+
'notification.title': 'Notifications',
|
|
25
|
+
'notification.unread': 'Unread',
|
|
26
|
+
'notification.all': 'Read',
|
|
27
|
+
'notification.empty': 'No more notifications',
|
|
28
|
+
'notification.text': '{{user}} added a comment to entry {{entry}}.',
|
|
29
|
+
},
|
|
30
|
+
'pt-BR': {
|
|
31
|
+
'label.title.comments': 'Comentários',
|
|
32
|
+
'placeholder.comment.text': 'Seu comentário',
|
|
33
|
+
'placeholder.comment.title': 'Título (opcional)',
|
|
34
|
+
'placeholder.comment.person': 'Selecione a pessoa',
|
|
35
|
+
'label.button.add': 'Adicionar comentário',
|
|
36
|
+
'label.button.update': 'Atualizar comentário',
|
|
37
|
+
'label.requested.to': 'Enviado para:',
|
|
38
|
+
'label.posted.on': 'Postado em:',
|
|
39
|
+
'placeholder.comments.empty': 'Nenhum comentário disponível.',
|
|
40
|
+
'tooltip.edit': 'Editar commentário',
|
|
41
|
+
'tooltip.delete': 'Remover commentário',
|
|
42
|
+
'tooltip.read': 'Marcar como lido',
|
|
43
|
+
'modal.title.footnote': 'Editar nota de rodapé',
|
|
44
|
+
'placeholder.footnote.text': 'Entre o texto da nota de rodapé',
|
|
45
|
+
'modal.title.footnote.remove': 'Confirmação de exclusão de nota de rodapé',
|
|
46
|
+
'modal.text.footnote.remove': 'Tem certeza de que deseja remover esta nota de rodapé? Esta ação é irreversível.',
|
|
47
|
+
'button.add': 'Adicionar comentário',
|
|
48
|
+
'input.value': 'Comentário',
|
|
49
|
+
'input.title': 'Título',
|
|
50
|
+
'input.user': 'Enviar para usuário',
|
|
51
|
+
'button.discard': 'Descartar',
|
|
52
|
+
'notification.title': 'Notificações',
|
|
53
|
+
'notification.unread': 'Não lidas',
|
|
54
|
+
'notification.all': 'Lidas',
|
|
55
|
+
'notification.empty': 'Não há notificações',
|
|
56
|
+
'notification.text': '{{user}} adicionou um comentário na entrada {{entry}}.',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import ToastMessage from '../../AppToast/ToastMessage';
|
|
2
|
+
import User from '../User';
|
|
2
3
|
import { StoreAction } from './types';
|
|
4
|
+
export declare const logged: (data: User | undefined) => StoreAction;
|
|
3
5
|
export declare const message: (data: ToastMessage) => StoreAction;
|
|
4
6
|
export declare const toastLoading: (data: boolean) => StoreAction;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import ToastMessage from '../../AppToast/ToastMessage';
|
|
2
|
+
import User from '../User';
|
|
2
3
|
export type UserStore = {
|
|
4
|
+
logged: User | undefined;
|
|
3
5
|
message: ToastMessage;
|
|
4
6
|
toastLoading: boolean;
|
|
5
7
|
};
|
|
6
8
|
export type StoreAction = {
|
|
7
9
|
type: string;
|
|
8
|
-
payload?: ToastMessage | boolean;
|
|
10
|
+
payload?: ToastMessage | boolean | User;
|
|
9
11
|
};
|
|
10
12
|
export declare const types: {
|
|
11
13
|
MESSAGE: string;
|
|
12
14
|
TOAST_LOADING: string;
|
|
15
|
+
LOGGED: string;
|
|
13
16
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function extractDateFromMongoId(id: string, time?: boolean): string;
|
|
2
|
+
declare const DateUtils: {
|
|
3
|
+
millis: () => number;
|
|
4
|
+
formatDate: (date: Date | null, fmt: string) => string;
|
|
5
|
+
parseTime: (time: number) => string;
|
|
6
|
+
extractDateFromMongoId: typeof extractDateFromMongoId;
|
|
7
|
+
formatDateTime: (isoDateStr: string, outputFormat: string) => string;
|
|
8
|
+
};
|
|
9
|
+
export default DateUtils;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { addSeconds, parseISO } from 'date-fns';
|
|
2
|
+
import { format } from 'date-fns-tz';
|
|
3
|
+
const millis = () => new Date().getTime();
|
|
4
|
+
const formatDateTime = (isoDateStr, outputFormat) => {
|
|
5
|
+
try {
|
|
6
|
+
const parsedDate = parseISO(isoDateStr);
|
|
7
|
+
return format(parsedDate, outputFormat);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
console.error('Invalid date or format:', error);
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const formatDate = (date, fmt) => {
|
|
15
|
+
console.log(typeof date);
|
|
16
|
+
if (!date)
|
|
17
|
+
return '';
|
|
18
|
+
return format(date, fmt);
|
|
19
|
+
};
|
|
20
|
+
const parseTime = (time) => {
|
|
21
|
+
if (time == null || time <= 0)
|
|
22
|
+
return '00:00:00.000';
|
|
23
|
+
return new Date(time * 1000).toISOString().slice(11, 23);
|
|
24
|
+
};
|
|
25
|
+
function extractDateFromMongoId(id, time) {
|
|
26
|
+
const DATE_PATTERN = 'yyyy-MM-dd';
|
|
27
|
+
const TIME_PATTERN = 'HH:mm:ss';
|
|
28
|
+
const hexTimestamp = id.substring(0, 8);
|
|
29
|
+
const timestampInMilliseconds = parseInt(hexTimestamp, 16) * 1000;
|
|
30
|
+
const dtf = time ? `${DATE_PATTERN} ${TIME_PATTERN}` : DATE_PATTERN;
|
|
31
|
+
const date = addSeconds(new Date(0), timestampInMilliseconds / 1000);
|
|
32
|
+
const formattedDate = format(date, dtf);
|
|
33
|
+
return formattedDate;
|
|
34
|
+
}
|
|
35
|
+
const DateUtils = {
|
|
36
|
+
millis,
|
|
37
|
+
formatDate,
|
|
38
|
+
parseTime,
|
|
39
|
+
extractDateFromMongoId,
|
|
40
|
+
formatDateTime,
|
|
41
|
+
};
|
|
42
|
+
export default DateUtils;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { SelectItem } from 'tycho-storybook/dist/SelectField/SelectField';
|
|
3
|
+
declare const FormUtils: {
|
|
4
|
+
convertEnum: (values: Record<string, string>, t?: TFunction, prefix?: string) => SelectItem[];
|
|
5
|
+
convertList: (values: any, labelAttr: string, valueAttr: string) => SelectItem[];
|
|
6
|
+
convertStringArray: (values: string[]) => SelectItem[];
|
|
7
|
+
booleanOptions: (t: TFunction) => SelectItem[];
|
|
8
|
+
convertMap: (values: Record<string, any> | undefined) => SelectItem[];
|
|
9
|
+
sortByLabel: (items: SelectItem[]) => SelectItem[];
|
|
10
|
+
};
|
|
11
|
+
export default FormUtils;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const convertEnum = (values, t, prefix) => {
|
|
2
|
+
if (!t)
|
|
3
|
+
return Object.entries(values).map(([key, label]) => ({
|
|
4
|
+
value: key,
|
|
5
|
+
label,
|
|
6
|
+
}));
|
|
7
|
+
return Object.entries(values).map(([key, label]) => ({
|
|
8
|
+
value: key,
|
|
9
|
+
label: t(`${prefix}.${label}`),
|
|
10
|
+
}));
|
|
11
|
+
};
|
|
12
|
+
const sortByLabel = (items) => {
|
|
13
|
+
return [...items].sort((a, b) => a.label.localeCompare(b.label));
|
|
14
|
+
};
|
|
15
|
+
const convertList = (values, labelAttr, valueAttr) => {
|
|
16
|
+
if (!values)
|
|
17
|
+
return [];
|
|
18
|
+
let fields = [];
|
|
19
|
+
values.map((value) => {
|
|
20
|
+
fields = [...fields, { label: value[labelAttr], value: value[valueAttr] }];
|
|
21
|
+
});
|
|
22
|
+
return fields;
|
|
23
|
+
};
|
|
24
|
+
const convertMap = (values) => {
|
|
25
|
+
if (!values)
|
|
26
|
+
return [];
|
|
27
|
+
let fields = [];
|
|
28
|
+
Object.keys(values).map((key) => {
|
|
29
|
+
fields = [...fields, { label: values[key], value: key }];
|
|
30
|
+
});
|
|
31
|
+
return fields;
|
|
32
|
+
};
|
|
33
|
+
const convertStringArray = (values) => {
|
|
34
|
+
if (!values)
|
|
35
|
+
return [];
|
|
36
|
+
let fields = [];
|
|
37
|
+
values.map((value) => {
|
|
38
|
+
fields = [...fields, { label: value, value }];
|
|
39
|
+
});
|
|
40
|
+
return fields;
|
|
41
|
+
};
|
|
42
|
+
const booleanOptions = (t) => {
|
|
43
|
+
return [
|
|
44
|
+
{ label: t('message:generic.option.true'), value: 'true' },
|
|
45
|
+
{ label: t('message:generic.option.false'), value: 'false' },
|
|
46
|
+
];
|
|
47
|
+
};
|
|
48
|
+
const FormUtils = {
|
|
49
|
+
convertEnum,
|
|
50
|
+
convertList,
|
|
51
|
+
convertStringArray,
|
|
52
|
+
booleanOptions,
|
|
53
|
+
convertMap,
|
|
54
|
+
sortByLabel,
|
|
55
|
+
};
|
|
56
|
+
export default FormUtils;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import CookieStorage from '../configs/CookieStorage';
|
|
2
|
+
import { UserStatus } from '../configs/User';
|
|
3
|
+
const hasAccess = (uid, roles) => {
|
|
4
|
+
const token = CookieStorage.getJwtToken();
|
|
5
|
+
if (!token)
|
|
6
|
+
return false;
|
|
7
|
+
const logged = parseJwt(token);
|
|
8
|
+
if (logged.status && logged.status === UserStatus.SUPER)
|
|
9
|
+
return true;
|
|
10
|
+
return roles.some((role) => logged.permissions.includes(`ROLE_${role}_LEXICON_${uid}`));
|
|
11
|
+
};
|
|
12
|
+
const parseJwt = (token) => {
|
|
13
|
+
const base64Url = token.split('.')[1];
|
|
14
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
15
|
+
const payload = decodeURIComponent(window
|
|
16
|
+
.atob(base64)
|
|
17
|
+
.split('')
|
|
18
|
+
.map(function (c) {
|
|
19
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
20
|
+
})
|
|
21
|
+
.join(''));
|
|
22
|
+
return JSON.parse(payload);
|
|
23
|
+
};
|
|
24
|
+
const SecurityUtils = {
|
|
25
|
+
hasAccess,
|
|
26
|
+
parseJwt,
|
|
27
|
+
};
|
|
28
|
+
export default SecurityUtils;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,17 @@ export { default as AppModal } from './AppModal';
|
|
|
6
6
|
export { default as AppModalConfirm } from './AppModal/AppModalConfirm';
|
|
7
7
|
export { default as AppModalRemove } from './AppModal/AppModalRemove';
|
|
8
8
|
export { default as VirtualKeyboard } from './VirtualKeyboard';
|
|
9
|
+
export { default as CommentComponent } from './Comments';
|
|
10
|
+
export { default as HeaderNotifications } from './Comments/HeaderNotifications';
|
|
9
11
|
export { CommonProvider } from './configs/CommonContext';
|
|
10
12
|
export { useMessageUtils } from './configs/useMessageUtils';
|
|
11
13
|
export { validateFormField } from './AppEditable/FormField';
|
|
12
14
|
export { convertEnum, convertList } from './AppEditable/FormFieldOption';
|
|
13
15
|
export { commonResources } from './configs/Localization';
|
|
14
16
|
export { KeyboardCustomLayouts } from './VirtualKeyboard/KeyboardCustomLayout';
|
|
17
|
+
export { default as DateUtils } from './functions/DateUtils';
|
|
18
|
+
export { default as FormUtils } from './functions/FormUtils';
|
|
19
|
+
export { default as SecurityUtils } from './functions/SecurityUtils';
|
|
15
20
|
export type { FormFieldOption } from './AppEditable/FormFieldOption';
|
|
16
21
|
export type { AppEditableField } from './AppEditable/AppEditableField';
|
|
17
22
|
export type { FieldOperations, FormField } from './AppEditable/FormField';
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,14 @@ export { default as AppModal } from './AppModal';
|
|
|
6
6
|
export { default as AppModalConfirm } from './AppModal/AppModalConfirm';
|
|
7
7
|
export { default as AppModalRemove } from './AppModal/AppModalRemove';
|
|
8
8
|
export { default as VirtualKeyboard } from './VirtualKeyboard';
|
|
9
|
+
export { default as CommentComponent } from './Comments';
|
|
10
|
+
export { default as HeaderNotifications } from './Comments/HeaderNotifications';
|
|
9
11
|
export { CommonProvider } from './configs/CommonContext';
|
|
10
12
|
export { useMessageUtils } from './configs/useMessageUtils';
|
|
11
13
|
export { validateFormField } from './AppEditable/FormField';
|
|
12
14
|
export { convertEnum, convertList } from './AppEditable/FormFieldOption';
|
|
13
15
|
export { commonResources } from './configs/Localization';
|
|
14
16
|
export { KeyboardCustomLayouts } from './VirtualKeyboard/KeyboardCustomLayout';
|
|
17
|
+
export { default as DateUtils } from './functions/DateUtils';
|
|
18
|
+
export { default as FormUtils } from './functions/FormUtils';
|
|
19
|
+
export { default as SecurityUtils } from './functions/SecurityUtils';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tycho-components",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.11-SNAPSHOT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"react-dom": ">=17 <19",
|
|
48
48
|
"react-hook-form": "^7.45.2",
|
|
49
49
|
"react-i18next": "^13.0.2",
|
|
50
|
+
"react-router-dom": "^6.14.2",
|
|
50
51
|
"tycho-storybook": "0.1.5-SNAPSHOT",
|
|
51
52
|
"yup": "^1.2.0"
|
|
52
53
|
},
|