mirror of
https://github.com/plankanban/planka.git
synced 2025-12-25 01:11:49 +03:00
@@ -0,0 +1,69 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tab } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import { useClosableModal } from '../../../hooks';
|
||||
import GeneralPane from './GeneralPane';
|
||||
import PreferencesPane from './PreferencesPane';
|
||||
import NotificationsPane from './NotificationsPane';
|
||||
|
||||
const BoardSettingsModal = React.memo(() => {
|
||||
const openPreferences = useSelector(
|
||||
(state) => selectors.selectCurrentModal(state).params.openPreferences,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
dispatch(entryActions.closeModal());
|
||||
}, [dispatch]);
|
||||
|
||||
const [ClosableModal] = useClosableModal();
|
||||
|
||||
const panes = [
|
||||
{
|
||||
menuItem: t('common.general', {
|
||||
context: 'title',
|
||||
}),
|
||||
render: () => <GeneralPane />,
|
||||
},
|
||||
{
|
||||
menuItem: t('common.preferences', {
|
||||
context: 'title',
|
||||
}),
|
||||
render: () => <PreferencesPane />,
|
||||
},
|
||||
{
|
||||
menuItem: t('common.notifications', {
|
||||
context: 'title',
|
||||
}),
|
||||
render: () => <NotificationsPane />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ClosableModal closeIcon size="small" centered={false} onClose={handleClose}>
|
||||
<ClosableModal.Content>
|
||||
<Tab
|
||||
menu={{
|
||||
secondary: true,
|
||||
pointing: true,
|
||||
}}
|
||||
panes={panes}
|
||||
defaultActiveIndex={openPreferences ? 1 : undefined}
|
||||
/>
|
||||
</ClosableModal.Content>
|
||||
</ClosableModal>
|
||||
);
|
||||
});
|
||||
|
||||
export default BoardSettingsModal;
|
||||
@@ -0,0 +1,75 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { dequal } from 'dequal';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Input } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import entryActions from '../../../../entry-actions';
|
||||
import { useForm, useNestedRef } from '../../../../hooks';
|
||||
|
||||
import styles from './EditInformation.module.scss';
|
||||
|
||||
const EditInformation = React.memo(() => {
|
||||
const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []);
|
||||
|
||||
const boardId = useSelector((state) => selectors.selectCurrentModal(state).params.id);
|
||||
const board = useSelector((state) => selectBoardById(state, boardId));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const defaultData = useMemo(
|
||||
() => ({
|
||||
name: board.name,
|
||||
}),
|
||||
[board.name],
|
||||
);
|
||||
|
||||
const [data, handleFieldChange] = useForm(() => ({
|
||||
name: '',
|
||||
...defaultData,
|
||||
}));
|
||||
|
||||
const cleanData = useMemo(
|
||||
() => ({
|
||||
...data,
|
||||
name: data.name.trim(),
|
||||
}),
|
||||
[data],
|
||||
);
|
||||
|
||||
const [nameFieldRef, handleNameFieldRef] = useNestedRef('inputRef');
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
if (!cleanData.name) {
|
||||
nameFieldRef.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(entryActions.updateBoard(boardId, cleanData));
|
||||
}, [boardId, dispatch, cleanData, nameFieldRef]);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<div className={styles.text}>{t('common.title')}</div>
|
||||
<Input
|
||||
fluid
|
||||
ref={handleNameFieldRef}
|
||||
name="name"
|
||||
value={data.name}
|
||||
maxLength={128}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<Button positive disabled={dequal(cleanData, defaultData)} content={t('action.save')} />
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
export default EditInformation;
|
||||
@@ -0,0 +1,17 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.field {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #444444;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Divider, Header, Tab } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import entryActions from '../../../../entry-actions';
|
||||
import { usePopupInClosableContext } from '../../../../hooks';
|
||||
import EditInformation from './EditInformation';
|
||||
import ConfirmationStep from '../../../common/ConfirmationStep';
|
||||
|
||||
import styles from './GeneralPane.module.scss';
|
||||
|
||||
const GeneralPane = React.memo(() => {
|
||||
const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []);
|
||||
|
||||
const boardId = useSelector((state) => selectors.selectCurrentModal(state).params.id);
|
||||
const board = useSelector((state) => selectBoardById(state, boardId));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleDeleteConfirm = useCallback(() => {
|
||||
dispatch(entryActions.deleteBoard(boardId));
|
||||
}, [boardId, dispatch]);
|
||||
|
||||
const ConfirmationPopup = usePopupInClosableContext(ConfirmationStep);
|
||||
|
||||
return (
|
||||
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||
<EditInformation />
|
||||
<Divider horizontal section>
|
||||
<Header as="h4">
|
||||
{t('common.dangerZone', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
</Divider>
|
||||
<div className={styles.action}>
|
||||
<ConfirmationPopup
|
||||
title="common.deleteBoard"
|
||||
content="common.areYouSureYouWantToDeleteThisBoard"
|
||||
buttonContent="action.deleteBoard"
|
||||
typeValue={board.name}
|
||||
typeContent="common.typeTitleToConfirm"
|
||||
onConfirm={handleDeleteConfirm}
|
||||
>
|
||||
<Button className={styles.actionButton}>
|
||||
{t(`action.deleteBoard`, {
|
||||
context: 'title',
|
||||
})}
|
||||
</Button>
|
||||
</ConfirmationPopup>
|
||||
</div>
|
||||
</Tab.Pane>
|
||||
);
|
||||
});
|
||||
|
||||
export default GeneralPane;
|
||||
@@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.action {
|
||||
border: none;
|
||||
border-radius: 0.28571429rem;
|
||||
display: inline-block;
|
||||
height: 36px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: background 0.3s ease;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background: #e9e9e9;
|
||||
}
|
||||
}
|
||||
|
||||
.actionButton {
|
||||
background: transparent;
|
||||
color: #6b808c;
|
||||
font-weight: normal;
|
||||
height: 36px;
|
||||
line-height: 24px;
|
||||
padding: 6px 12px;
|
||||
text-align: left;
|
||||
text-decoration: underline;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -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 GeneralPane from './GeneralPane';
|
||||
|
||||
export default GeneralPane;
|
||||
@@ -0,0 +1,44 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Tab } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import NotificationServices from '../../notification-services/NotificationServices';
|
||||
|
||||
import styles from './NotificationsPane.module.scss';
|
||||
|
||||
const NotificationsPane = React.memo(() => {
|
||||
const selectNotificationServiceIdsByBoardId = useMemo(
|
||||
() => selectors.makeSelectNotificationServiceIdsByBoardId(),
|
||||
[],
|
||||
);
|
||||
|
||||
const boardId = useSelector((state) => selectors.selectCurrentModal(state).params.id);
|
||||
|
||||
const notificationServiceIds = useSelector((state) =>
|
||||
selectNotificationServiceIdsByBoardId(state, boardId),
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleCreate = useCallback(
|
||||
(data) => {
|
||||
dispatch(entryActions.createNotificationServiceInBoard(boardId, data));
|
||||
},
|
||||
[boardId, dispatch],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||
<NotificationServices ids={notificationServiceIds} onCreate={handleCreate} />
|
||||
</Tab.Pane>
|
||||
);
|
||||
});
|
||||
|
||||
export default NotificationsPane;
|
||||
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.wrapper {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Radio, Segment } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import entryActions from '../../../../entry-actions';
|
||||
import SelectCardType from '../../../cards/SelectCardType';
|
||||
|
||||
import styles from './DefaultCardType.module.scss';
|
||||
|
||||
const DefaultCardType = React.memo(() => {
|
||||
const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []);
|
||||
|
||||
const boardId = useSelector((state) => selectors.selectCurrentModal(state).params.id);
|
||||
const board = useSelector((state) => selectBoardById(state, boardId));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(defaultCardType) => {
|
||||
dispatch(
|
||||
entryActions.updateBoard(boardId, {
|
||||
defaultCardType,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[boardId, dispatch],
|
||||
);
|
||||
|
||||
const handleToggleChange = useCallback(
|
||||
(_, { name: fieldName, checked }) => {
|
||||
dispatch(
|
||||
entryActions.updateBoard(boardId, {
|
||||
[fieldName]: checked,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[boardId, dispatch],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SelectCardType value={board.defaultCardType} onSelect={handleSelect} />
|
||||
<Segment basic className={styles.settings}>
|
||||
<Radio
|
||||
toggle
|
||||
name="limitCardTypesToDefaultOne"
|
||||
checked={board.limitCardTypesToDefaultOne}
|
||||
label={t('common.limitCardTypesToDefaultOne')}
|
||||
className={styles.radio}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Segment>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default DefaultCardType;
|
||||
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.radio {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, Menu } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import entryActions from '../../../../entry-actions';
|
||||
import { BoardViews } from '../../../../constants/Enums';
|
||||
import { BoardViewIcons } from '../../../../constants/Icons';
|
||||
|
||||
import styles from './DefaultView.module.scss';
|
||||
|
||||
const DESCRIPTION_BY_VIEW = {
|
||||
[BoardViews.KANBAN]: 'common.visualTaskManagementWithLists',
|
||||
[BoardViews.GRID]: 'common.dynamicAndUnevenlySpacedLayout',
|
||||
[BoardViews.LIST]: 'common.sequentialDisplayOfCards',
|
||||
};
|
||||
|
||||
const DefaultView = React.memo(() => {
|
||||
const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []);
|
||||
|
||||
const boardId = useSelector((state) => selectors.selectCurrentModal(state).params.id);
|
||||
const board = useSelector((state) => selectBoardById(state, boardId));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleSelectClick = useCallback(
|
||||
(_, { value: defaultView }) => {
|
||||
dispatch(
|
||||
entryActions.updateBoard(boardId, {
|
||||
defaultView,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[boardId, dispatch],
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu secondary vertical className={styles.menu}>
|
||||
{[BoardViews.KANBAN, BoardViews.GRID, BoardViews.LIST].map((view) => (
|
||||
<Menu.Item
|
||||
key={view}
|
||||
value={view}
|
||||
active={view === board.defaultView}
|
||||
className={styles.menuItem}
|
||||
onClick={handleSelectClick}
|
||||
>
|
||||
<Icon name={BoardViewIcons[view]} className={styles.menuItemIcon} />
|
||||
<div className={styles.menuItemTitle}>{t(`common.${view}`)}</div>
|
||||
<p className={styles.menuItemDescription}>{t(DESCRIPTION_BY_VIEW[view])}</p>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
});
|
||||
|
||||
export default DefaultView;
|
||||
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.menu {
|
||||
margin: 0 auto 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menuItem:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.menuItemDescription {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.menuItemIcon {
|
||||
float: left;
|
||||
margin: 0 0.35714286em 0 0;
|
||||
}
|
||||
|
||||
.menuItemTitle {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Radio, Segment } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import entryActions from '../../../../entry-actions';
|
||||
|
||||
import styles from './Others.module.scss';
|
||||
|
||||
const Others = React.memo(() => {
|
||||
const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []);
|
||||
|
||||
const boardId = useSelector((state) => selectors.selectCurrentModal(state).params.id);
|
||||
const board = useSelector((state) => selectBoardById(state, boardId));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleChange = useCallback(
|
||||
(_, { name: fieldName, checked }) => {
|
||||
dispatch(
|
||||
entryActions.updateBoard(boardId, {
|
||||
[fieldName]: checked,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[boardId, dispatch],
|
||||
);
|
||||
|
||||
return (
|
||||
<Segment basic>
|
||||
<Radio
|
||||
toggle
|
||||
name="alwaysDisplayCardCreator"
|
||||
checked={board.alwaysDisplayCardCreator}
|
||||
label={t('common.alwaysDisplayCardCreator')}
|
||||
className={styles.radio}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Segment>
|
||||
);
|
||||
});
|
||||
|
||||
export default Others;
|
||||
@@ -0,0 +1,15 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.radio {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, Header, Tab } from 'semantic-ui-react';
|
||||
|
||||
import DefaultView from './DefaultView';
|
||||
import DefaultCardType from './DefaultCardType';
|
||||
import Others from './Others';
|
||||
|
||||
import styles from './PreferencesPane.module.scss';
|
||||
|
||||
const PreferencesPane = React.memo(() => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
return (
|
||||
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||
<Divider horizontal className={styles.firstDivider}>
|
||||
<Header as="h4">
|
||||
{t('common.defaultView', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
</Divider>
|
||||
<DefaultView />
|
||||
<Divider horizontal>
|
||||
<Header as="h4">
|
||||
{t('common.defaultCardType', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
</Divider>
|
||||
<DefaultCardType />
|
||||
<Divider horizontal>
|
||||
<Header as="h4">
|
||||
{t('common.others', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
</Divider>
|
||||
<Others />
|
||||
</Tab.Pane>
|
||||
);
|
||||
});
|
||||
|
||||
export default PreferencesPane;
|
||||
@@ -0,0 +1,15 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.firstDivider {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -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 PreferencesPane from './PreferencesPane';
|
||||
|
||||
export default PreferencesPane;
|
||||
8
client/src/components/boards/BoardSettingsModal/index.js
Normal file
8
client/src/components/boards/BoardSettingsModal/index.js
Normal file
@@ -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 BoardSettingsModal from './BoardSettingsModal';
|
||||
|
||||
export default BoardSettingsModal;
|
||||
Reference in New Issue
Block a user