diff --git a/client/src/components/cards/Card/ProjectContent.jsx b/client/src/components/cards/Card/ProjectContent.jsx
index 0f74693a..2cf7fac1 100755
--- a/client/src/components/cards/Card/ProjectContent.jsx
+++ b/client/src/components/cards/Card/ProjectContent.jsx
@@ -77,6 +77,13 @@ const ProjectContent = React.memo(({ cardId }) => {
const { listName, withCreator } = useSelector((state) => {
const board = selectors.selectCurrentBoard(state);
+
+ if (!board) {
+ return {
+ listName: null,
+ withCreator: false,
+ };
+ }
return {
listName: list.name && (board.view === BoardViews.KANBAN ? null : list.name),
diff --git a/client/src/components/cards/CardModal/CardModal.jsx b/client/src/components/cards/CardModal/CardModal.jsx
index 3d86c94c..ec5a4bfc 100755
--- a/client/src/components/cards/CardModal/CardModal.jsx
+++ b/client/src/components/cards/CardModal/CardModal.jsx
@@ -45,7 +45,7 @@ const CardModal = React.memo(() => {
const dispatch = useDispatch();
const handleClose = useCallback(() => {
- dispatch(push(Paths.BOARDS.replace(':id', card.boardId)));
+ window.history.back();
}, [card.boardId, dispatch]);
const [ClosableModal, isClosableActiveRef] = useClosableModal();
diff --git a/client/src/components/common/Root.jsx b/client/src/components/common/Root.jsx
index 03ea0e0f..516f6eed 100755
--- a/client/src/components/common/Root.jsx
+++ b/client/src/components/common/Root.jsx
@@ -16,6 +16,7 @@ import Paths from '../../constants/Paths';
import Login from './Login';
import Core from './Core';
import NotFound from './NotFound';
+import UserCardsPage from "../users/UserCardsPage";
import 'react-datepicker/dist/react-datepicker.css';
import 'photoswipe/dist/photoswipe.css';
@@ -37,6 +38,7 @@ function Root({ store, history }) {
} />
} />
} />
+ } />
} />
diff --git a/client/src/components/users/UserCardsPage/UserCardsPage.jsx b/client/src/components/users/UserCardsPage/UserCardsPage.jsx
new file mode 100755
index 00000000..1a8dfd5b
--- /dev/null
+++ b/client/src/components/users/UserCardsPage/UserCardsPage.jsx
@@ -0,0 +1,136 @@
+/*!
+ * Copyright (c) 2024 PLANKA Software GmbH
+ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
+ */
+
+import React, { useEffect, useRef } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import { useTranslation } from 'react-i18next';
+import { Loader } from 'semantic-ui-react';
+import { DragDropContext, Droppable } from 'react-beautiful-dnd';
+
+import DroppableTypes from '../../../constants/DroppableTypes';
+import DraggableCard from '../../cards/DraggableCard';
+import Header from '../../common/Header';
+import selectors from '../../../selectors';
+import entryActions from '../../../entry-actions';
+
+import styles from './UserCardsPage.module.scss';
+
+function UserCardsPage() {
+ const [t] = useTranslation();
+ const dispatch = useDispatch();
+ const loading = useRef(false);
+
+ const currentUserId = useSelector(selectors.selectCurrentUserId);
+ const user = useSelector(selectors.selectCurrentUser);
+ const projectsToCard = useSelector(selectors.selectProjectsToCardsWithEditorRightsForCurrentUser);
+
+ console.log('Projects to Cards Data:', projectsToCard);
+
+ const isDataReady = currentUserId && user && projectsToCard !== null;
+
+ const handleDragEnd = (result) => {
+ if (!result.destination) {
+ return;
+ }
+
+ const { source, destination, draggableId } = result;
+ };
+
+ useEffect(() => {
+ if (!currentUserId) {
+ return;
+ }
+
+ if (loading.current || !isDataReady) return;
+
+ if (projectsToCard && projectsToCard.length > 0) {
+ projectsToCard.forEach((project) => {
+ project.boards?.forEach((board) => {
+ dispatch(entryActions.fetchBoard(board.id));
+ });
+ });
+ }
+
+ loading.current = true;
+ }, [currentUserId, projectsToCard, dispatch, isDataReady]);
+
+ if (!isDataReady) {
+ return (
+ <>
+
+
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+
+
{t('common.myCards')}
+ {projectsToCard && projectsToCard.length > 0 ? (
+
+
+ {projectsToCard.map((project) => (
+
+
{project.name}
+
+ {project.boards && project.boards.length > 0 ? (
+ project.boards.map((board) => (
+
+
{board.name}
+
+ {board.cards && board.cards.length > 0 ? (
+
+ {(provided) => (
+
+ {board.cards.map((card, index) => {
+ const cardId = card.id || card._fields?.id;
+ if (!cardId) {
+ console.warn('Card missing ID:', card);
+ return null;
+ }
+
+ return (
+
+
+
+ );
+ })}
+ {provided.placeholder}
+
+ )}
+
+ ) : (
+
{t('common.noCards')}
+ )}
+
+ ))
+ ) : (
+
{t('common.noBoards')}
+ )}
+
+ ))}
+
+
+ ) : (
+
+
{t('common.noProjects')}
+
+ )}
+
+ >
+ );
+}
+
+export default UserCardsPage;
diff --git a/client/src/components/users/UserCardsPage/UserCardsPage.module.scss b/client/src/components/users/UserCardsPage/UserCardsPage.module.scss
new file mode 100755
index 00000000..f0be4f79
--- /dev/null
+++ b/client/src/components/users/UserCardsPage/UserCardsPage.module.scss
@@ -0,0 +1,96 @@
+/*!
+ * Copyright (c) 2024 PLANKA Software GmbH
+ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
+ */
+
+.container {
+padding: 2rem;
+}
+
+.title {
+ color: #fff;
+ margin-left: 1rem;
+}
+
+.projectsContainer {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.projectSection {
+ background-color: #333333;
+ border-radius: 4px;
+ padding: 2rem;
+ margin: 1rem;
+ font-family: "Nunitoga", "Helvetica Neue", Arial, Helvetica, sans-serif;
+}
+
+.projectTitle {
+ color: #fff;
+}
+
+.boardSection {
+ width: 300px;
+ color: #000;
+ background-color: #dfe3e6;
+ border-radius: 4px;
+ padding: 1rem;
+ margin-top: 1rem;
+}
+
+.boardTitle {
+ color: #000;
+}
+
+.cardsGrid {
+ display: grid;
+}
+
+.card {
+ margin-top: 1rem;
+ align-items: center;
+ border-radius: 4px;
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.card:hover {
+ transform: translateY(-8px);
+ box-shadow: 0 8px 16px rgba(255, 255, 255, 0.562);
+}
+
+.cardName {
+ margin-right: 10px;
+}
+
+.emptyMessage {
+ width: 300px;
+ padding: 1rem;
+ margin-top: 1rem;
+ color: #9e0000;
+ font-weight:bold;
+ font-size:large;
+}
+
+.emptyState {
+ width: 300px;
+ padding: 1rem;
+ margin-top: 1rem;
+ color: #9e0000;
+ font-weight:bold;
+ font-size: xx-large;
+}
+
+/* Media Queries for Responsiveness */
+/* tablet */
+@media (max-width: 768px) {
+
+ .projectsContainer{
+ justify-content: center;
+ }
+
+}
+
+/* phone */
+@media (max-width: 480px) {
+
+}
diff --git a/client/src/components/users/UserCardsPage/index.js b/client/src/components/users/UserCardsPage/index.js
new file mode 100755
index 00000000..1d8aa232
--- /dev/null
+++ b/client/src/components/users/UserCardsPage/index.js
@@ -0,0 +1,8 @@
+/*!
+ * Copyright (c) 2024 PLANKA Software GmbH
+ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
+ */
+
+import UserCards from './UserCardsPage';
+
+export default UserCards;
diff --git a/client/src/components/users/UserStep/UserStep.jsx b/client/src/components/users/UserStep/UserStep.jsx
index 1d943c19..11d79913 100755
--- a/client/src/components/users/UserStep/UserStep.jsx
+++ b/client/src/components/users/UserStep/UserStep.jsx
@@ -7,6 +7,7 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
+import { Link } from 'react-router-dom';
import { Button, Menu } from 'semantic-ui-react';
import { Popup } from '../../../lib/custom-ui';
@@ -40,6 +41,10 @@ const UserStep = React.memo(({ onClose }) => {
onClose();
}, [onClose, dispatch]);
+ const handleCardsClick = useCallback(() => {
+ onClose();
+ }, [onClose]);
+
let logoutMenuItemProps;
if (isLogouting) {
logoutMenuItemProps = {
@@ -60,6 +65,16 @@ const UserStep = React.memo(({ onClose }) => {