mirror of
https://github.com/plankanban/planka.git
synced 2025-08-02 03:55:26 +02:00
ref: Refactoring
This commit is contained in:
parent
aa4723d7fe
commit
3f8216dca8
189 changed files with 3781 additions and 3486 deletions
112
client/src/components/CardModal/Activities/Activities.jsx
Executable file
112
client/src/components/CardModal/Activities/Activities.jsx
Executable file
|
@ -0,0 +1,112 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Comment, Icon, Loader, Visibility } from 'semantic-ui-react';
|
||||
|
||||
import { ActivityTypes } from '../../../constants/Enums';
|
||||
import CommentAdd from './CommentAdd';
|
||||
import Item from './Item';
|
||||
|
||||
import styles from './Activities.module.scss';
|
||||
|
||||
const Activities = React.memo(
|
||||
({
|
||||
items,
|
||||
isFetching,
|
||||
isAllFetched,
|
||||
isDetailsVisible,
|
||||
isDetailsFetching,
|
||||
canEdit,
|
||||
canEditAllComments,
|
||||
onFetch,
|
||||
onDetailsToggle,
|
||||
onCommentCreate,
|
||||
onCommentUpdate,
|
||||
onCommentDelete,
|
||||
}) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleToggleDetailsClick = useCallback(() => {
|
||||
onDetailsToggle(!isDetailsVisible);
|
||||
}, [isDetailsVisible, onDetailsToggle]);
|
||||
|
||||
const handleCommentUpdate = useCallback(
|
||||
(id, data) => {
|
||||
onCommentUpdate(id, data);
|
||||
},
|
||||
[onCommentUpdate],
|
||||
);
|
||||
|
||||
const handleCommentDelete = useCallback(
|
||||
(id) => {
|
||||
onCommentDelete(id);
|
||||
},
|
||||
[onCommentDelete],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.contentModule}>
|
||||
<div className={styles.moduleWrapper}>
|
||||
<Icon name="list ul" className={styles.moduleIcon} />
|
||||
<div className={styles.moduleHeader}>
|
||||
{t('common.actions')}
|
||||
<Button
|
||||
content={isDetailsVisible ? t('action.hideDetails') : t('action.showDetails')}
|
||||
className={styles.toggleButton}
|
||||
onClick={handleToggleDetailsClick}
|
||||
/>
|
||||
</div>
|
||||
{canEdit && <CommentAdd onCreate={onCommentCreate} />}
|
||||
<div className={styles.wrapper}>
|
||||
<Comment.Group>
|
||||
{items.map((item) =>
|
||||
item.type === ActivityTypes.COMMENT_CARD ? (
|
||||
<Item.Comment
|
||||
key={item.id}
|
||||
data={item.data}
|
||||
createdAt={item.createdAt}
|
||||
isPersisted={item.isPersisted}
|
||||
user={item.user}
|
||||
canEdit={(item.user.isCurrent && canEdit) || canEditAllComments}
|
||||
onUpdate={(data) => handleCommentUpdate(item.id, data)}
|
||||
onDelete={() => handleCommentDelete(item.id)}
|
||||
/>
|
||||
) : (
|
||||
<Item
|
||||
key={item.id}
|
||||
type={item.type}
|
||||
data={item.data}
|
||||
createdAt={item.createdAt}
|
||||
user={item.user}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</Comment.Group>
|
||||
</div>
|
||||
{isFetching || isDetailsFetching ? (
|
||||
<Loader active inverted inline="centered" size="small" className={styles.loader} />
|
||||
) : (
|
||||
!isAllFetched && <Visibility fireOnMount onOnScreen={onFetch} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Activities.propTypes = {
|
||||
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isAllFetched: PropTypes.bool.isRequired,
|
||||
isDetailsVisible: PropTypes.bool.isRequired,
|
||||
isDetailsFetching: PropTypes.bool.isRequired,
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
canEditAllComments: PropTypes.bool.isRequired,
|
||||
onFetch: PropTypes.func.isRequired,
|
||||
onDetailsToggle: PropTypes.func.isRequired,
|
||||
onCommentCreate: PropTypes.func.isRequired,
|
||||
onCommentUpdate: PropTypes.func.isRequired,
|
||||
onCommentDelete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Activities;
|
|
@ -0,0 +1,60 @@
|
|||
:global(#app) {
|
||||
.contentModule {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.moduleHeader {
|
||||
align-items: center;
|
||||
color: #17394d;
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
height: 36px;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.moduleIcon {
|
||||
color: #17394d;
|
||||
font-size: 17px;
|
||||
height: 32px;
|
||||
left: -40px;
|
||||
line-height: 32px;
|
||||
margin-right: 0;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.moduleWrapper {
|
||||
margin: 0 0 0 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toggleButton {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
color: #6b808c;
|
||||
float: right;
|
||||
font-weight: normal;
|
||||
margin-right: 0;
|
||||
padding: 6px 11px;
|
||||
text-align: left;
|
||||
text-decoration: underline;
|
||||
transition: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba(9, 30, 66, 0.08);
|
||||
color: #092d42;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin-left: -40px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
102
client/src/components/CardModal/Activities/CommentAdd.jsx
Executable file
102
client/src/components/CardModal/Activities/CommentAdd.jsx
Executable file
|
@ -0,0 +1,102 @@
|
|||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||
import { useDidUpdate, useToggle } from '../../../lib/hooks';
|
||||
|
||||
import { useClosableForm, useForm } from '../../../hooks';
|
||||
|
||||
import styles from './CommentAdd.module.scss';
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
text: '',
|
||||
};
|
||||
|
||||
const CommentAdd = React.memo(({ onCreate }) => {
|
||||
const [t] = useTranslation();
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
||||
const [selectTextFieldState, selectTextField] = useToggle();
|
||||
|
||||
const textField = useRef(null);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpened(false);
|
||||
}, []);
|
||||
|
||||
const submit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
text: data.text.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.text) {
|
||||
textField.current.ref.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
onCreate(cleanData);
|
||||
|
||||
setData(DEFAULT_DATA);
|
||||
selectTextField();
|
||||
}, [onCreate, data, setData, selectTextField]);
|
||||
|
||||
const handleFieldFocus = useCallback(() => {
|
||||
setIsOpened(true);
|
||||
}, []);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.ctrlKey && event.key === 'Enter') {
|
||||
submit();
|
||||
}
|
||||
},
|
||||
[submit],
|
||||
);
|
||||
|
||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(close);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
submit();
|
||||
}, [submit]);
|
||||
|
||||
useDidUpdate(() => {
|
||||
textField.current.ref.current.focus();
|
||||
}, [selectTextFieldState]);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<TextArea
|
||||
ref={textField}
|
||||
as={TextareaAutosize}
|
||||
name="text"
|
||||
value={data.text}
|
||||
placeholder={t('common.writeComment')}
|
||||
minRows={isOpened ? 3 : 1}
|
||||
spellCheck={false}
|
||||
className={styles.field}
|
||||
onFocus={handleFieldFocus}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
{isOpened && (
|
||||
<div className={styles.controls}>
|
||||
<Button
|
||||
positive
|
||||
content={t('action.addComment')}
|
||||
onMouseOver={handleControlMouseOver}
|
||||
onMouseOut={handleControlMouseOut}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
CommentAdd.propTypes = {
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default CommentAdd;
|
|
@ -0,0 +1,25 @@
|
|||
:global(#app) {
|
||||
.controls {
|
||||
clear: both;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.field {
|
||||
background: #fff;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
padding: 8px 12px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
120
client/src/components/CardModal/Activities/CommentEdit.jsx
Executable file
120
client/src/components/CardModal/Activities/CommentEdit.jsx
Executable file
|
@ -0,0 +1,120 @@
|
|||
import { dequal } from 'dequal';
|
||||
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||
|
||||
import { useClosableForm, useForm } from '../../../hooks';
|
||||
|
||||
import styles from './CommentEdit.module.scss';
|
||||
|
||||
const CommentEdit = React.forwardRef(({ children, defaultData, onUpdate }, ref) => {
|
||||
const [t] = useTranslation();
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const [data, handleFieldChange, setData] = useForm(null);
|
||||
|
||||
const textField = useRef(null);
|
||||
|
||||
const open = useCallback(() => {
|
||||
setIsOpened(true);
|
||||
setData({
|
||||
text: '',
|
||||
...defaultData,
|
||||
});
|
||||
}, [defaultData, setData]);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpened(false);
|
||||
setData(null);
|
||||
}, [setData]);
|
||||
|
||||
const submit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
text: data.text.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.text) {
|
||||
textField.current.ref.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dequal(cleanData, defaultData)) {
|
||||
onUpdate(cleanData);
|
||||
}
|
||||
|
||||
close();
|
||||
}, [defaultData, onUpdate, data, close]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
open,
|
||||
close,
|
||||
}),
|
||||
[open, close],
|
||||
);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.ctrlKey && event.key === 'Enter') {
|
||||
submit();
|
||||
}
|
||||
},
|
||||
[submit],
|
||||
);
|
||||
|
||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||
close,
|
||||
isOpened,
|
||||
);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
submit();
|
||||
}, [submit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpened) {
|
||||
textField.current.ref.current.focus();
|
||||
}
|
||||
}, [isOpened]);
|
||||
|
||||
if (!isOpened) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<TextArea
|
||||
ref={textField}
|
||||
as={TextareaAutosize}
|
||||
name="text"
|
||||
value={data.text}
|
||||
minRows={3}
|
||||
spellCheck={false}
|
||||
className={styles.field}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
<div className={styles.controls}>
|
||||
{/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
|
||||
<Button
|
||||
positive
|
||||
content={t('action.save')}
|
||||
onMouseOver={handleControlMouseOver}
|
||||
onMouseOut={handleControlMouseOut}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
CommentEdit.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(CommentEdit);
|
|
@ -0,0 +1,26 @@
|
|||
:global(#app) {
|
||||
.controls {
|
||||
clear: both;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.field {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(9, 30, 66, 0.13);
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
padding: 8px 12px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
89
client/src/components/CardModal/Activities/Item.jsx
Executable file
89
client/src/components/CardModal/Activities/Item.jsx
Executable file
|
@ -0,0 +1,89 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { Comment } from 'semantic-ui-react';
|
||||
|
||||
import { ActivityTypes } from '../../../constants/Enums';
|
||||
import ItemComment from './ItemComment';
|
||||
import User from '../../User';
|
||||
|
||||
import styles from './Item.module.scss';
|
||||
|
||||
const Item = React.memo(({ type, data, createdAt, user }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
let contentNode;
|
||||
|
||||
switch (type) {
|
||||
case ActivityTypes.CREATE_CARD:
|
||||
contentNode = (
|
||||
<Trans
|
||||
i18nKey="common.userAddedThisCardToList"
|
||||
values={{
|
||||
user: user.name,
|
||||
list: data.list.name,
|
||||
}}
|
||||
>
|
||||
<span className={styles.author}>{user.name}</span>
|
||||
<span className={styles.text}>
|
||||
{' added this card to '}
|
||||
{data.list.name}
|
||||
</span>
|
||||
</Trans>
|
||||
);
|
||||
|
||||
break;
|
||||
case ActivityTypes.MOVE_CARD:
|
||||
contentNode = (
|
||||
<Trans
|
||||
i18nKey="common.userMovedThisCardFromListToList"
|
||||
values={{
|
||||
user: user.name,
|
||||
fromList: data.fromList.name,
|
||||
toList: data.toList.name,
|
||||
}}
|
||||
>
|
||||
<span className={styles.author}>{user.name}</span>
|
||||
<span className={styles.text}>
|
||||
{' moved this card from '}
|
||||
{data.fromList.name}
|
||||
{' to '}
|
||||
{data.toList.name}
|
||||
</span>
|
||||
</Trans>
|
||||
);
|
||||
|
||||
break;
|
||||
default:
|
||||
contentNode = null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Comment>
|
||||
<span className={styles.user}>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} />
|
||||
</span>
|
||||
<div className={classNames(styles.content)}>
|
||||
<div>{contentNode}</div>
|
||||
<span className={styles.date}>
|
||||
{t('format:longDateTime', {
|
||||
postProcess: 'formatDate',
|
||||
value: createdAt,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</Comment>
|
||||
);
|
||||
});
|
||||
|
||||
Item.Comment = ItemComment;
|
||||
|
||||
Item.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
createdAt: PropTypes.instanceOf(Date).isRequired,
|
||||
user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
export default Item;
|
33
client/src/components/CardModal/Activities/Item.module.scss
Normal file
33
client/src/components/CardModal/Activities/Item.module.scss
Normal file
|
@ -0,0 +1,33 @@
|
|||
:global(#app) {
|
||||
.author {
|
||||
color: #17394d;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
border-bottom: 1px solid #092d4221;
|
||||
display: inline-block;
|
||||
padding-bottom: 14px;
|
||||
vertical-align: top;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #6b808c;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.text {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: inline-block;
|
||||
padding: 4px 8px 0 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
86
client/src/components/CardModal/Activities/ItemComment.jsx
Executable file
86
client/src/components/CardModal/Activities/ItemComment.jsx
Executable file
|
@ -0,0 +1,86 @@
|
|||
import React, { useCallback, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment } from 'semantic-ui-react';
|
||||
import { Markdown } from '../../../lib/custom-ui';
|
||||
|
||||
import CommentEdit from './CommentEdit';
|
||||
import User from '../../User';
|
||||
import DeletePopup from '../../DeletePopup';
|
||||
|
||||
import styles from './ItemComment.module.scss';
|
||||
|
||||
const ItemComment = React.memo(
|
||||
({ data, createdAt, isPersisted, user, canEdit, onUpdate, onDelete }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const commentEdit = useRef(null);
|
||||
|
||||
const handleEditClick = useCallback(() => {
|
||||
commentEdit.current.open();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Comment>
|
||||
<span className={styles.user}>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} />
|
||||
</span>
|
||||
<div className={classNames(styles.content)}>
|
||||
<div className={styles.title}>
|
||||
<span className={styles.author}>{user.name}</span>
|
||||
<span className={styles.date}>
|
||||
{t('format:longDateTime', {
|
||||
postProcess: 'formatDate',
|
||||
value: createdAt,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<CommentEdit ref={commentEdit} defaultData={data} onUpdate={onUpdate}>
|
||||
<>
|
||||
<div className={styles.text}>
|
||||
<Markdown linkTarget="_blank">{data.text}</Markdown>
|
||||
</div>
|
||||
{canEdit && (
|
||||
<Comment.Actions>
|
||||
<Comment.Action
|
||||
as="button"
|
||||
content={t('action.edit')}
|
||||
disabled={!isPersisted}
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
<DeletePopup
|
||||
title={t('common.deleteComment', {
|
||||
context: 'title',
|
||||
})}
|
||||
content={t('common.areYouSureYouWantToDeleteThisComment')}
|
||||
buttonContent={t('action.deleteComment')}
|
||||
onConfirm={onDelete}
|
||||
>
|
||||
<Comment.Action
|
||||
as="button"
|
||||
content={t('action.delete')}
|
||||
disabled={!isPersisted}
|
||||
/>
|
||||
</DeletePopup>
|
||||
</Comment.Actions>
|
||||
)}
|
||||
</>
|
||||
</CommentEdit>
|
||||
</div>
|
||||
</Comment>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ItemComment.propTypes = {
|
||||
data: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
createdAt: PropTypes.instanceOf(Date).isRequired,
|
||||
isPersisted: PropTypes.bool.isRequired,
|
||||
user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ItemComment;
|
|
@ -0,0 +1,48 @@
|
|||
:global(#app) {
|
||||
.author {
|
||||
color: #17394d;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
border-bottom: 1px solid #092d4221;
|
||||
display: inline-block;
|
||||
padding-bottom: 14px;
|
||||
vertical-align: top;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #6b808c;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.text {
|
||||
background: #fff;
|
||||
border-radius: 0px 8px 8px;
|
||||
box-shadow: 0 1px 2px -1px rgba(9, 30, 66, 0.25),
|
||||
0 0 0 1px rgba(9, 30, 66, 0.08);
|
||||
box-sizing: border-box;
|
||||
color: #17394d;
|
||||
display: inline-block;
|
||||
margin: 1px 2px 4px 1px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: inline-block;
|
||||
padding: 4px 8px 0 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
3
client/src/components/CardModal/Activities/index.js
Executable file
3
client/src/components/CardModal/Activities/index.js
Executable file
|
@ -0,0 +1,3 @@
|
|||
import Activities from './Activities';
|
||||
|
||||
export default Activities;
|
Loading…
Add table
Add a link
Reference in a new issue