mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
ref: Little refactoring
This commit is contained in:
parent
70cadcd974
commit
3aba4d4a56
14 changed files with 123 additions and 52 deletions
|
@ -214,19 +214,21 @@ oidc:
|
||||||
##
|
##
|
||||||
extraEnv: []
|
extraEnv: []
|
||||||
|
|
||||||
## Example extraEnv for configure SMTP
|
## Example extraEnv for configuring SMTP
|
||||||
## extraEnv:
|
## extraEnv:
|
||||||
## - name: SMTP_HOST
|
## - name: SMTP_HOST
|
||||||
## value: "your_smtp_server"
|
## value: "smtp.example.com"
|
||||||
## - name: SMTP_PORT
|
## - name: SMTP_PORT
|
||||||
## value: "587"
|
## value: "587"
|
||||||
|
## - name: SMTP_NAME
|
||||||
|
## value: "Your Name"
|
||||||
|
## - name: SMTP_SECURE
|
||||||
|
## value: "true"
|
||||||
## - name: SMTP_USER
|
## - name: SMTP_USER
|
||||||
## value: "your_email@example.com"
|
## value: "your_email@example.com"
|
||||||
## - name: SMTP_PASSWORD
|
## - name: SMTP_PASSWORD
|
||||||
## value: "your_password"
|
## value: "your_password"
|
||||||
## - name: SMTP_FROM
|
## - name: SMTP_FROM
|
||||||
## value: "your_email@example.com"
|
## value: "your_email@example.com"
|
||||||
## - name: SMTP_NAME
|
## - name: SMTP_TLS_REJECT_UNAUTHORIZED
|
||||||
## value: "your_name_or_login"
|
|
||||||
## - name: SMTP_SECURE
|
|
||||||
## value: "false"
|
## value: "false"
|
||||||
|
|
|
@ -20,14 +20,14 @@ const TaskList = React.memo(({ id }) => {
|
||||||
|
|
||||||
const tasks = useSelector((state) => selectTasksByTaskListId(state, id));
|
const tasks = useSelector((state) => selectTasksByTaskListId(state, id));
|
||||||
|
|
||||||
|
const [isOpened, toggleOpened] = useToggle();
|
||||||
|
|
||||||
// TODO: move to selector?
|
// TODO: move to selector?
|
||||||
const completedTasksTotal = useMemo(
|
const completedTasksTotal = useMemo(
|
||||||
() => tasks.reduce((result, task) => (task.isCompleted ? result + 1 : result), 0),
|
() => tasks.reduce((result, task) => (task.isCompleted ? result + 1 : result), 0),
|
||||||
[tasks],
|
[tasks],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isOpened, toggleOpened] = useToggle();
|
|
||||||
|
|
||||||
const handleToggleClick = useCallback(
|
const handleToggleClick = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
|
@ -71,6 +71,11 @@ export const ListTypes = {
|
||||||
TRASH: 'trash',
|
TRASH: 'trash',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ListTypeStates = {
|
||||||
|
OPENED: 'opened',
|
||||||
|
CLOSED: 'closed',
|
||||||
|
};
|
||||||
|
|
||||||
export const ListSortFieldNames = {
|
export const ListSortFieldNames = {
|
||||||
NAME: 'name',
|
NAME: 'name',
|
||||||
DUE_DATE: 'dueDate',
|
DUE_DATE: 'dueDate',
|
||||||
|
|
11
client/src/constants/ListTypeStateByType.js
Normal file
11
client/src/constants/ListTypeStateByType.js
Normal file
|
@ -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
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ListTypes, ListTypeStates } from './Enums';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
[ListTypes.ACTIVE]: ListTypeStates.OPENED,
|
||||||
|
[ListTypes.CLOSED]: ListTypeStates.CLOSED,
|
||||||
|
};
|
|
@ -10,7 +10,8 @@ import buildSearchParts from '../utils/build-search-parts';
|
||||||
import { isListFinite } from '../utils/record-helpers';
|
import { isListFinite } from '../utils/record-helpers';
|
||||||
import ActionTypes from '../constants/ActionTypes';
|
import ActionTypes from '../constants/ActionTypes';
|
||||||
import Config from '../constants/Config';
|
import Config from '../constants/Config';
|
||||||
import { ListSortFieldNames, ListTypes, SortOrders } from '../constants/Enums';
|
import { ListSortFieldNames, ListTypes, ListTypeStates, SortOrders } from '../constants/Enums';
|
||||||
|
import LIST_TYPE_STATE_BY_TYPE from '../constants/ListTypeStateByType';
|
||||||
|
|
||||||
const POSITION_BY_LIST_TYPE = {
|
const POSITION_BY_LIST_TYPE = {
|
||||||
[ListTypes.ARCHIVE]: Number.MAX_SAFE_INTEGER - 1,
|
[ListTypes.ARCHIVE]: Number.MAX_SAFE_INTEGER - 1,
|
||||||
|
@ -28,6 +29,21 @@ const prepareList = (list) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getChangedTypeState = (prevList, list) => {
|
||||||
|
const prevTypeState = LIST_TYPE_STATE_BY_TYPE[prevList.type];
|
||||||
|
const typeState = LIST_TYPE_STATE_BY_TYPE[list.type];
|
||||||
|
|
||||||
|
if (prevTypeState === ListTypeStates.OPENED && typeState === ListTypeStates.CLOSED) {
|
||||||
|
return ListTypeStates.CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevTypeState === ListTypeStates.CLOSED && typeState === ListTypeStates.OPENED) {
|
||||||
|
return ListTypeStates.OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export default class extends BaseModel {
|
export default class extends BaseModel {
|
||||||
static modelName = 'List';
|
static modelName = 'List';
|
||||||
|
|
||||||
|
@ -121,12 +137,12 @@ export default class extends BaseModel {
|
||||||
|
|
||||||
let isClosed;
|
let isClosed;
|
||||||
if (payload.data.type) {
|
if (payload.data.type) {
|
||||||
if (payload.data.type === ListTypes.CLOSED) {
|
const changedTypeState = getChangedTypeState(listModel, payload.data);
|
||||||
if (listModel.type === ListTypes.ACTIVE) {
|
|
||||||
isClosed = true;
|
if (changedTypeState === ListTypeStates.OPENED) {
|
||||||
}
|
|
||||||
} else if (listModel.type === ListTypes.CLOSED) {
|
|
||||||
isClosed = false;
|
isClosed = false;
|
||||||
|
} else if (changedTypeState === ListTypeStates.CLOSED) {
|
||||||
|
isClosed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,13 +166,13 @@ export default class extends BaseModel {
|
||||||
const listModel = List.withId(payload.list.id);
|
const listModel = List.withId(payload.list.id);
|
||||||
|
|
||||||
if (listModel) {
|
if (listModel) {
|
||||||
|
const changedTypeState = getChangedTypeState(listModel, payload.list);
|
||||||
|
|
||||||
let isClosed;
|
let isClosed;
|
||||||
if (payload.list.type === ListTypes.CLOSED) {
|
if (changedTypeState === ListTypeStates.OPENED) {
|
||||||
if (listModel.type === ListTypes.ACTIVE) {
|
|
||||||
isClosed = true;
|
|
||||||
}
|
|
||||||
} else if (listModel.type === ListTypes.CLOSED) {
|
|
||||||
isClosed = false;
|
isClosed = false;
|
||||||
|
} else if (changedTypeState === ListTypeStates.CLOSED) {
|
||||||
|
isClosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
listModel.update(prepareList(payload.list));
|
listModel.update(prepareList(payload.list));
|
||||||
|
|
|
@ -14,7 +14,8 @@ import api from '../../../api';
|
||||||
import { createLocalId } from '../../../utils/local-id';
|
import { createLocalId } from '../../../utils/local-id';
|
||||||
import { isListArchiveOrTrash, isListFinite } from '../../../utils/record-helpers';
|
import { isListArchiveOrTrash, isListFinite } from '../../../utils/record-helpers';
|
||||||
import ActionTypes from '../../../constants/ActionTypes';
|
import ActionTypes from '../../../constants/ActionTypes';
|
||||||
import { BoardViews, ListTypes } from '../../../constants/Enums';
|
import { BoardViews, ListTypes, ListTypeStates } from '../../../constants/Enums';
|
||||||
|
import LIST_TYPE_STATE_BY_TYPE from '../../../constants/ListTypeStateByType';
|
||||||
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
const _preloadImage = (url) =>
|
const _preloadImage = (url) =>
|
||||||
|
@ -137,7 +138,7 @@ export function* createCard(listId, data, autoOpen) {
|
||||||
id: localId,
|
id: localId,
|
||||||
boardId: list.boardId,
|
boardId: list.boardId,
|
||||||
creatorUserId: currentUserMembership.userId,
|
creatorUserId: currentUserMembership.userId,
|
||||||
isClosed: list.type === ListTypes.CLOSED,
|
isClosed: LIST_TYPE_STATE_BY_TYPE[list.type] === ListTypeStates.CLOSED,
|
||||||
},
|
},
|
||||||
autoOpen,
|
autoOpen,
|
||||||
),
|
),
|
||||||
|
@ -242,11 +243,13 @@ export function* updateCard(id, data) {
|
||||||
prevListId = null;
|
prevListId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeState = LIST_TYPE_STATE_BY_TYPE[list.type];
|
||||||
|
|
||||||
if (card.isClosed) {
|
if (card.isClosed) {
|
||||||
if (list.type === ListTypes.ACTIVE) {
|
if (typeState === ListTypeStates.OPENED) {
|
||||||
isClosed = false;
|
isClosed = false;
|
||||||
}
|
}
|
||||||
} else if (list.type === ListTypes.CLOSED) {
|
} else if (typeState === ListTypeStates.CLOSED) {
|
||||||
isClosed = true;
|
isClosed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ const Errors = {
|
||||||
LINKED_CARD_NOT_FOUND: {
|
LINKED_CARD_NOT_FOUND: {
|
||||||
linkedCardNotFound: 'Linked card not found',
|
linkedCardNotFound: 'Linked card not found',
|
||||||
},
|
},
|
||||||
|
LINKED_CARD_OR_NAME_MUST_BE_PRESENT: {
|
||||||
|
linkedCardOrNameMustBePresent: 'Linked card or name must be present',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -31,8 +34,9 @@ module.exports = {
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
isNotEmptyString: true,
|
||||||
maxLength: 1024,
|
maxLength: 1024,
|
||||||
// required: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
isCompleted: {
|
isCompleted: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
@ -49,6 +53,9 @@ module.exports = {
|
||||||
linkedCardNotFound: {
|
linkedCardNotFound: {
|
||||||
responseType: 'notFound',
|
responseType: 'notFound',
|
||||||
},
|
},
|
||||||
|
linkedCardOrNameMustBePresent: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
|
@ -100,19 +107,24 @@ module.exports = {
|
||||||
|
|
||||||
const values = _.pick(inputs, ['position', 'name', 'isCompleted']);
|
const values = _.pick(inputs, ['position', 'name', 'isCompleted']);
|
||||||
|
|
||||||
const task = await sails.helpers.tasks.createOne.with({
|
const task = await sails.helpers.tasks.createOne
|
||||||
project,
|
.with({
|
||||||
board,
|
project,
|
||||||
list,
|
board,
|
||||||
card,
|
list,
|
||||||
values: {
|
card,
|
||||||
...values,
|
values: {
|
||||||
taskList,
|
...values,
|
||||||
linkedCard,
|
taskList,
|
||||||
},
|
linkedCard,
|
||||||
actorUser: currentUser,
|
},
|
||||||
request: this.req,
|
actorUser: currentUser,
|
||||||
});
|
request: this.req,
|
||||||
|
})
|
||||||
|
.intercept(
|
||||||
|
'linkedCardOrNameMustBeInValues',
|
||||||
|
() => Errors.LINKED_CARD_OR_NAME_MUST_BE_PRESENT,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
item: task,
|
item: task,
|
||||||
|
|
|
@ -67,7 +67,7 @@ module.exports = {
|
||||||
delete values.position;
|
delete values.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.list.type === List.Types.CLOSED) {
|
if (List.TYPE_STATE_BY_TYPE[values.list.type] === List.TypeStates.CLOSED) {
|
||||||
values.isClosed = true;
|
values.isClosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -386,11 +386,13 @@ module.exports = {
|
||||||
values.prevListId = null;
|
values.prevListId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeState = List.TYPE_STATE_BY_TYPE[values.list.type];
|
||||||
|
|
||||||
if (inputs.record.isClosed) {
|
if (inputs.record.isClosed) {
|
||||||
if (values.list.type === List.Types.ACTIVE) {
|
if (typeState === List.TypeStates.OPENED) {
|
||||||
values.isClosed = false;
|
values.isClosed = false;
|
||||||
}
|
}
|
||||||
} else if (values.list.type === List.Types.CLOSED) {
|
} else if (typeState === List.TypeStates.CLOSED) {
|
||||||
values.isClosed = true;
|
values.isClosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,17 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
exists: {
|
||||||
|
linkedCardOrNameMustBeInValues: {},
|
||||||
|
},
|
||||||
|
|
||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
const { values } = inputs;
|
const { values } = inputs;
|
||||||
|
|
||||||
|
if (!values.linkedCard && !values.name) {
|
||||||
|
throw 'linkedCardOrNameMustBeInValues';
|
||||||
|
}
|
||||||
|
|
||||||
const tasks = await Task.qm.getByTaskListId(values.taskList.id);
|
const tasks = await Task.qm.getByTaskListId(values.taskList.id);
|
||||||
|
|
||||||
const { position, repositions } = sails.helpers.utils.insertToPositionables(
|
const { position, repositions } = sails.helpers.utils.insertToPositionables(
|
||||||
|
|
|
@ -58,10 +58,16 @@ const updateOne = async (criteria, values) => {
|
||||||
let tasks = [];
|
let tasks = [];
|
||||||
|
|
||||||
if (list) {
|
if (list) {
|
||||||
|
const prevTypeState = List.TYPE_STATE_BY_TYPE[prevList.type];
|
||||||
|
const typeState = List.TYPE_STATE_BY_TYPE[list.type];
|
||||||
|
|
||||||
let isClosed;
|
let isClosed;
|
||||||
if (list.type === List.Types.ACTIVE) {
|
if (prevTypeState === List.TypeStates.CLOSED && typeState === List.TypeStates.OPENED) {
|
||||||
isClosed = false;
|
isClosed = false;
|
||||||
} else if (list.type === List.Types.CLOSED) {
|
} else if (
|
||||||
|
prevTypeState === List.TypeStates.OPENED &&
|
||||||
|
typeState === List.TypeStates.CLOSED
|
||||||
|
) {
|
||||||
isClosed = true;
|
isClosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,6 @@ const getByTaskListIds = async (taskListIds, { sort = ['position', 'id'] } = {})
|
||||||
{ sort },
|
{ sort },
|
||||||
);
|
);
|
||||||
|
|
||||||
const getByLinkedCardId = (linkedCardId) =>
|
|
||||||
defaultFind({
|
|
||||||
linkedCardId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getOneById = (id, { taskListId } = {}) => {
|
const getOneById = (id, { taskListId } = {}) => {
|
||||||
const criteria = {
|
const criteria = {
|
||||||
id,
|
id,
|
||||||
|
@ -68,7 +63,6 @@ module.exports = {
|
||||||
getByIds,
|
getByIds,
|
||||||
getByTaskListId,
|
getByTaskListId,
|
||||||
getByTaskListIds,
|
getByTaskListIds,
|
||||||
getByLinkedCardId,
|
|
||||||
getOneById,
|
getOneById,
|
||||||
update,
|
update,
|
||||||
updateOne,
|
updateOne,
|
||||||
|
|
|
@ -48,16 +48,16 @@ module.exports = {
|
||||||
stopwatch: {
|
stopwatch: {
|
||||||
type: 'json',
|
type: 'json',
|
||||||
},
|
},
|
||||||
isClosed: {
|
|
||||||
type: 'boolean',
|
|
||||||
defaultsTo: false,
|
|
||||||
columnName: 'is_closed',
|
|
||||||
},
|
|
||||||
commentsTotal: {
|
commentsTotal: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
defaultsTo: 0,
|
defaultsTo: 0,
|
||||||
columnName: 'comments_total',
|
columnName: 'comments_total',
|
||||||
},
|
},
|
||||||
|
isClosed: {
|
||||||
|
type: 'boolean',
|
||||||
|
defaultsTo: false,
|
||||||
|
columnName: 'is_closed',
|
||||||
|
},
|
||||||
listChangedAt: {
|
listChangedAt: {
|
||||||
type: 'ref',
|
type: 'ref',
|
||||||
columnName: 'list_changed_at',
|
columnName: 'list_changed_at',
|
||||||
|
|
|
@ -17,6 +17,11 @@ const Types = {
|
||||||
TRASH: 'trash',
|
TRASH: 'trash',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TypeStates = {
|
||||||
|
OPENED: 'opened',
|
||||||
|
CLOSED: 'closed',
|
||||||
|
};
|
||||||
|
|
||||||
const SortFieldNames = {
|
const SortFieldNames = {
|
||||||
NAME: 'name',
|
NAME: 'name',
|
||||||
DUE_DATE: 'dueDate',
|
DUE_DATE: 'dueDate',
|
||||||
|
@ -31,6 +36,11 @@ const SortOrders = {
|
||||||
|
|
||||||
const FINITE_TYPES = [Types.ACTIVE, Types.CLOSED];
|
const FINITE_TYPES = [Types.ACTIVE, Types.CLOSED];
|
||||||
|
|
||||||
|
const TYPE_STATE_BY_TYPE = {
|
||||||
|
[Types.ACTIVE]: TypeStates.OPENED,
|
||||||
|
[Types.CLOSED]: Types.CLOSED,
|
||||||
|
};
|
||||||
|
|
||||||
const COLORS = [
|
const COLORS = [
|
||||||
'berry-red',
|
'berry-red',
|
||||||
'pumpkin-orange',
|
'pumpkin-orange',
|
||||||
|
@ -46,9 +56,11 @@ const COLORS = [
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Types,
|
Types,
|
||||||
|
TypeStates,
|
||||||
SortFieldNames,
|
SortFieldNames,
|
||||||
SortOrders,
|
SortOrders,
|
||||||
FINITE_TYPES,
|
FINITE_TYPES,
|
||||||
|
TYPE_STATE_BY_TYPE,
|
||||||
COLORS,
|
COLORS,
|
||||||
|
|
||||||
attributes: {
|
attributes: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue