1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-08-10 16:05:35 +02:00

feat: client implementation of list sorting

This commit is contained in:
Samuel 2024-04-16 15:19:48 +02:00
parent 03825c3bed
commit 1f62f20350
14 changed files with 190 additions and 12 deletions

View file

@ -89,6 +89,36 @@ const handleListDelete = (list) => ({
},
});
const sortList = (id, data) => ({
type: ActionTypes.LIST_SORT,
payload: {
id,
data,
},
});
sortList.success = (list) => ({
type: ActionTypes.LIST_SORT__SUCCESS,
payload: {
list,
},
});
sortList.failure = (id, error) => ({
type: ActionTypes.LIST_SORT__FAILURE,
payload: {
id,
error,
},
});
const handleListSort = (list) => ({
type: ActionTypes.LIST_SORT_HANDLE,
payload: {
list,
},
});
export default {
createList,
handleListCreate,
@ -96,4 +126,6 @@ export default {
handleListUpdate,
deleteList,
handleListDelete,
sortList,
handleListSort
};

View file

@ -7,10 +7,13 @@ const createList = (boardId, data, headers) =>
const updateList = (id, data, headers) => socket.patch(`/lists/${id}`, data, headers);
const sortList = (id, data, headers) => socket.post(`/lists/${id}/sort`, data, headers);
const deleteList = (id, headers) => socket.delete(`/lists/${id}`, undefined, headers);
export default {
createList,
updateList,
sortList,
deleteList,
};

View file

@ -8,12 +8,14 @@ import { useSteps } from '../../hooks';
import DeleteStep from '../DeleteStep';
import styles from './ActionsStep.module.scss';
import SortStep from "../SortStep";
const StepTypes = {
DELETE: 'DELETE',
SORT: 'SORT',
};
const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) => {
const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onSort, onClose }) => {
const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps();
@ -31,16 +33,32 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
openStep(StepTypes.DELETE);
}, [openStep]);
if (step && step.type === StepTypes.DELETE) {
return (
<DeleteStep
title="common.deleteList"
content="common.areYouSureYouWantToDeleteThisList"
buttonContent="action.deleteList"
onConfirm={onDelete}
onBack={handleBack}
/>
);
const handleSortClick = useCallback(() => {
openStep(StepTypes.SORT);
}, [openStep]);
if (step && step.type) {
switch (step.type){
case StepTypes.DELETE:
return (
<DeleteStep
title="common.deleteList"
content="common.areYouSureYouWantToDeleteThisList"
buttonContent="action.deleteList"
onConfirm={onDelete}
onBack={handleBack}
/>
);
case StepTypes.SORT:
return (
<SortStep
title="common.sortList"
onSort={onSort}
onBack={handleBack}
/>
);
default:
}
}
return (
@ -52,6 +70,11 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
</Popup.Header>
<Popup.Content>
<Menu secondary vertical className={styles.menu}>
<Menu.Item className={styles.menuItem} onClick={handleSortClick}>
{t('action.sort', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
{t('action.editTitle', {
context: 'title',
@ -77,6 +100,7 @@ ActionsStep.propTypes = {
onNameEdit: PropTypes.func.isRequired,
onCardAdd: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
onSort: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};

View file

@ -16,7 +16,7 @@ import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-ic
import styles from './List.module.scss';
const List = React.memo(
({ id, index, name, isPersisted, cardIds, canEdit, onUpdate, onDelete, onCardCreate }) => {
({ id, index, name, isPersisted, cardIds, canEdit, onUpdate, onDelete, onSort, onCardCreate }) => {
const [t] = useTranslation();
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
@ -114,6 +114,7 @@ const List = React.memo(
onNameEdit={handleNameEdit}
onCardAdd={handleCardAdd}
onDelete={onDelete}
onSort={onSort}
>
<Button className={classNames(styles.headerButton, styles.target)}>
<Icon fitted name="pencil" size="small" />
@ -160,6 +161,7 @@ List.propTypes = {
canEdit: PropTypes.bool.isRequired,
onUpdate: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
onSort: PropTypes.func.isRequired,
onCardCreate: PropTypes.func.isRequired,
};

View file

@ -0,0 +1,59 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import {Button, Menu} from 'semantic-ui-react';
import { Popup } from '../../lib/custom-ui';
import styles from './SortStep.module.scss';
const SortStep = React.memo(({ title, onSort, onBack }) => {
const [t] = useTranslation();
const handeClick = (sortType) => onSort({sortType})
return (
<>
<Popup.Header onBack={onBack}>
{t(title, {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
<Menu secondary vertical className={styles.menu}>
<Menu.Item className={styles.menuItem} onClick={() => handeClick('createdat_asc')}>
{t('action.sort.createdFirst', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={() => handeClick('createdat_desc')}>
{t('action.sort.createdLast', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={() => handeClick('name_asc')}>
{t('action.sort.name', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={() => handeClick('duedate_asc')}>
{t('action.sort.due', {
context: 'title',
})}
</Menu.Item>
</Menu>
</Popup.Content>
</>
);
});
SortStep.propTypes = {
title: PropTypes.string.isRequired,
onSort: PropTypes.func.isRequired,
onBack: PropTypes.func,
};
SortStep.defaultProps = {
onBack: undefined,
};
export default SortStep;

View file

@ -0,0 +1,3 @@
import SortStep from './SortStep';
export default SortStep;

View file

@ -173,6 +173,10 @@ export default {
LIST_UPDATE__SUCCESS: 'LIST_UPDATE__SUCCESS',
LIST_UPDATE__FAILURE: 'LIST_UPDATE__FAILURE',
LIST_UPDATE_HANDLE: 'LIST_UPDATE_HANDLE',
LIST_SORT: 'LIST_SORT',
LIST_SORT__SUCCESS: 'LIST_SORT__SUCCESS',
LIST_SORT__FAILURE: 'LIST_SORT__FAILURE',
LIST_SORT_HANDLE: 'LIST_SORT_HANDLE',
LIST_DELETE: 'LIST_DELETE',
LIST_DELETE__SUCCESS: 'LIST_DELETE__SUCCESS',
LIST_DELETE__FAILURE: 'LIST_DELETE__FAILURE',

View file

@ -120,6 +120,8 @@ export default {
LIST_MOVE: `${PREFIX}/LIST_MOVE`,
LIST_DELETE: `${PREFIX}/LIST_DELETE`,
LIST_DELETE_HANDLE: `${PREFIX}/LIST_DELETE_HANDLE`,
LIST_SORT: `${PREFIX}/LIST_SORT`,
LIST_SORT_HANDLE: `${PREFIX}/LIST_SORT_HANDLE`,
/* Cards */

View file

@ -34,6 +34,7 @@ const mapDispatchToProps = (dispatch, { id }) =>
{
onUpdate: (data) => entryActions.updateList(id, data),
onDelete: () => entryActions.deleteList(id),
onSort: (data) => entryActions.sortList(id, data),
onCardCreate: (data, autoOpen) => entryActions.createCard(id, data, autoOpen),
},
dispatch,

View file

@ -51,6 +51,23 @@ const handleListDelete = (list) => ({
},
});
const sortList = (id, data) => {
return ({
type: EntryActionTypes.LIST_SORT,
payload: {
id,
data
},
})};
const handleListSort = (list) => ({
type: EntryActionTypes.LIST_SORT_HANDLE,
payload: {
list,
},
});
export default {
createListInCurrentBoard,
handleListCreate,
@ -59,4 +76,6 @@ export default {
moveList,
deleteList,
handleListDelete,
sortList,
handleListSort,
};

View file

@ -66,6 +66,9 @@ export default class extends BaseModel {
List.withId(payload.id).deleteWithRelated();
break;
// Possible improved flow for updating
// case ActionTypes.LIST_SORT__SUCCESS:
// case ActionTypes.LIST_SORT_HANDLE:
case ActionTypes.LIST_DELETE__SUCCESS:
case ActionTypes.LIST_DELETE_HANDLE: {
const listModel = List.withId(payload.list.id);

View file

@ -61,6 +61,24 @@ export function* handleListUpdate(list) {
yield put(actions.handleListUpdate(list));
}
export function* sortList(id, data) {
yield put(actions.sortList(id, data));
let list;
try {
({ item: list } = yield call(request, api.sortList, id, data));
} catch (error) {
yield put(actions.sortList.failure(id, error));
return;
}
yield put(actions.sortList.success(list));
}
export function* handleListSort(list) {
yield put(actions.handleListSort(list));
}
export function* moveList(id, index) {
const { boardId } = yield select(selectors.selectListById, id);
const position = yield select(selectors.selectNextListPosition, boardId, index, id);
@ -94,6 +112,8 @@ export default {
handleListCreate,
updateList,
handleListUpdate,
sortList,
handleListSort,
moveList,
deleteList,
handleListDelete,

View file

@ -17,6 +17,12 @@ export default function* listsWatchers() {
takeEvery(EntryActionTypes.LIST_UPDATE_HANDLE, ({ payload: { list } }) =>
services.handleListUpdate(list),
),
takeEvery(EntryActionTypes.LIST_SORT, ({ payload: { id, data } }) =>
services.sortList(id, data),
),
takeEvery(EntryActionTypes.LIST_SORT_HANDLE, ({ payload: { list } }) =>
services.handleListSort(list),
),
takeEvery(EntryActionTypes.LIST_MOVE, ({ payload: { id, index } }) =>
services.moveList(id, index),
),