mirror of
https://github.com/plankanban/planka.git
synced 2025-12-26 09:15:01 +03:00
188
client/src/components/projects/ProjectCard/ProjectCard.jsx
Normal file
188
client/src/components/projects/ProjectCard/ProjectCard.jsx
Normal file
@@ -0,0 +1,188 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Icon } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import Paths from '../../../constants/Paths';
|
||||
import { ProjectBackgroundTypes } from '../../../constants/Enums';
|
||||
import UserAvatar from '../../users/UserAvatar';
|
||||
|
||||
import styles from './ProjectCard.module.scss';
|
||||
import globalStyles from '../../../styles.module.scss';
|
||||
|
||||
const Sizes = {
|
||||
SMALL: 'small',
|
||||
LARGE: 'large',
|
||||
};
|
||||
|
||||
const ProjectCard = React.memo(
|
||||
({ id, size, isActive, withDescription, withTypeIndicator, withFavoriteButton, className }) => {
|
||||
const selectProjectById = useMemo(() => selectors.makeSelectProjectById(), []);
|
||||
|
||||
const selectFirstBoardIdByProjectId = useMemo(
|
||||
() => selectors.makeSelectFirstBoardIdByProjectId(),
|
||||
[],
|
||||
);
|
||||
|
||||
const selectNotificationsTotalByProjectId = useMemo(
|
||||
() => selectors.makeSelectNotificationsTotalByProjectId(),
|
||||
[],
|
||||
);
|
||||
|
||||
const selectProjectManagerById = useMemo(() => selectors.makeSelectProjectManagerById(), []);
|
||||
const selectBackgroundImageById = useMemo(() => selectors.makeSelectBackgroundImageById(), []);
|
||||
|
||||
const project = useSelector((state) => selectProjectById(state, id));
|
||||
const firstBoardId = useSelector((state) => selectFirstBoardIdByProjectId(state, id));
|
||||
|
||||
const notificationsTotal = useSelector((state) =>
|
||||
selectNotificationsTotalByProjectId(state, id),
|
||||
);
|
||||
|
||||
const ownerProjectManager = useSelector(
|
||||
(state) =>
|
||||
project.ownerProjectManagerId &&
|
||||
selectProjectManagerById(state, project.ownerProjectManagerId),
|
||||
);
|
||||
|
||||
const backgroundImageUrl = useSelector((state) => {
|
||||
if (!project.backgroundType || project.backgroundType !== ProjectBackgroundTypes.IMAGE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const backgroundImage = selectBackgroundImageById(state, project.backgroundImageId);
|
||||
|
||||
if (!backgroundImage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return backgroundImage.thumbnailUrls.outside360;
|
||||
});
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleToggleFavoriteClick = useCallback(() => {
|
||||
dispatch(
|
||||
entryActions.updateProject(project.id, {
|
||||
isFavorite: !project.isFavorite,
|
||||
}),
|
||||
);
|
||||
}, [project, dispatch]);
|
||||
|
||||
const withSidebar = withTypeIndicator || (withFavoriteButton && !project.isHidden);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
styles.wrapper,
|
||||
styles[`wrapper${upperFirst(size)}`],
|
||||
project.isHidden && styles.wrapperHidden,
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
to={
|
||||
firstBoardId
|
||||
? Paths.BOARDS.replace(':id', firstBoardId)
|
||||
: Paths.PROJECTS.replace(':id', id)
|
||||
}
|
||||
className={styles.content}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.cover,
|
||||
project.backgroundType === ProjectBackgroundTypes.GRADIENT &&
|
||||
globalStyles[`background${upperFirst(camelCase(project.backgroundGradient))}`],
|
||||
)}
|
||||
style={{
|
||||
background: backgroundImageUrl && `url("${backgroundImageUrl}") center / cover`,
|
||||
}}
|
||||
/>
|
||||
{notificationsTotal > 0 && (
|
||||
<span className={styles.notifications}>{notificationsTotal}</span>
|
||||
)}
|
||||
<div
|
||||
className={classNames(styles.information, withSidebar && styles.informationWithSidebar)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.title,
|
||||
isActive !== undefined && styles.titleActivatable,
|
||||
isActive && styles.titleActive,
|
||||
)}
|
||||
>
|
||||
{project.name}
|
||||
</div>
|
||||
{withDescription && project.description && (
|
||||
<div className={styles.description}>{project.description}</div>
|
||||
)}
|
||||
</div>
|
||||
{withTypeIndicator && (
|
||||
<div
|
||||
className={classNames(
|
||||
styles.typeIndicator,
|
||||
ownerProjectManager && styles.typeIndicatorWithUser,
|
||||
)}
|
||||
>
|
||||
{ownerProjectManager ? (
|
||||
<UserAvatar id={ownerProjectManager.userId} size="small" />
|
||||
) : (
|
||||
<Icon
|
||||
fitted
|
||||
name="group"
|
||||
className={classNames(styles.icon, styles.typeIndicatorIcon)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
{withFavoriteButton && !project.isHidden && (
|
||||
<Button
|
||||
className={classNames(
|
||||
styles.favoriteButton,
|
||||
!project.isFavorite && styles.favoriteButtonAppearable,
|
||||
)}
|
||||
onClick={handleToggleFavoriteClick}
|
||||
>
|
||||
<Icon
|
||||
fitted
|
||||
name={project.isFavorite ? 'star' : 'star outline'}
|
||||
className={classNames(styles.icon, styles.favoriteButtonIcon)}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ProjectCard.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
size: PropTypes.oneOf(Object.values(Sizes)),
|
||||
isActive: PropTypes.bool,
|
||||
withDescription: PropTypes.bool,
|
||||
withTypeIndicator: PropTypes.bool,
|
||||
withFavoriteButton: PropTypes.bool,
|
||||
className: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
ProjectCard.defaultProps = {
|
||||
size: Sizes.LARGE,
|
||||
isActive: undefined,
|
||||
withDescription: false,
|
||||
withTypeIndicator: false,
|
||||
withFavoriteButton: false,
|
||||
};
|
||||
|
||||
export default ProjectCard;
|
||||
Reference in New Issue
Block a user