mirror of
https://github.com/plankanban/planka.git
synced 2025-07-23 15:19:44 +02:00
Initial commit
This commit is contained in:
commit
36fe34e8e1
583 changed files with 91539 additions and 0 deletions
88
client/src/models/Action.js
Executable file
88
client/src/models/Action.js
Executable file
|
@ -0,0 +1,88 @@
|
|||
import { Model, attr, fk } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Action';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
type: attr(),
|
||||
data: attr(),
|
||||
createdAt: attr({
|
||||
getDefault: () => new Date(),
|
||||
}),
|
||||
isInCard: attr({
|
||||
getDefault: () => true,
|
||||
}),
|
||||
cardId: fk({
|
||||
to: 'Card',
|
||||
as: 'card',
|
||||
relatedName: 'actions',
|
||||
}),
|
||||
userId: fk({
|
||||
to: 'User',
|
||||
as: 'user',
|
||||
relatedName: 'actions',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Action) {
|
||||
switch (type) {
|
||||
case ActionTypes.ACTIONS_FETCH_SUCCEEDED:
|
||||
payload.actions.forEach((action) => {
|
||||
Action.upsert(action);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.ACTION_CREATE_RECEIVED:
|
||||
case ActionTypes.COMMENT_ACTION_CREATE:
|
||||
Action.upsert(payload.action);
|
||||
|
||||
break;
|
||||
case ActionTypes.ACTION_UPDATE_RECEIVED:
|
||||
Action.withId(payload.action.id).update(payload.action);
|
||||
|
||||
break;
|
||||
case ActionTypes.ACTION_DELETE_RECEIVED:
|
||||
Action.withId(payload.action.id).delete();
|
||||
|
||||
break;
|
||||
case ActionTypes.COMMENT_ACTION_UPDATE:
|
||||
Action.withId(payload.id).update({
|
||||
data: payload.data,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.COMMENT_ACTION_DELETE:
|
||||
Action.withId(payload.id).delete();
|
||||
|
||||
break;
|
||||
case ActionTypes.COMMENT_ACTION_CREATE_SUCCEEDED:
|
||||
Action.withId(payload.localId).delete();
|
||||
Action.upsert(payload.action);
|
||||
|
||||
break;
|
||||
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
|
||||
payload.actions.forEach((action) => {
|
||||
Action.upsert({
|
||||
...action,
|
||||
isInCard: false,
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.NOTIFICATION_CREATE_RECEIVED: {
|
||||
const actionModel = Action.withId(payload.action.id);
|
||||
|
||||
Action.upsert({
|
||||
...payload.action,
|
||||
isInCard: actionModel ? actionModel.isInCard : false,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
109
client/src/models/Board.js
Executable file
109
client/src/models/Board.js
Executable file
|
@ -0,0 +1,109 @@
|
|||
import {
|
||||
Model, attr, fk, many,
|
||||
} from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Board';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
position: attr(),
|
||||
name: attr(),
|
||||
isFetching: attr({
|
||||
getDefault: () => null,
|
||||
}),
|
||||
projectId: fk({
|
||||
to: 'Project',
|
||||
as: 'project',
|
||||
relatedName: 'boards',
|
||||
}),
|
||||
filterUsers: many('User', 'filterBoards'),
|
||||
filterLabels: many('Label', 'filterBoards'),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Board) {
|
||||
switch (type) {
|
||||
case ActionTypes.USER_TO_BOARD_FILTER_ADD:
|
||||
Board.withId(payload.boardId).filterUsers.add(payload.id);
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_FROM_BOARD_FILTER_REMOVE:
|
||||
Board.withId(payload.boardId).filterUsers.remove(payload.id);
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_RECEIVED:
|
||||
payload.boards.forEach((board) => {
|
||||
Board.upsert(board);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_CREATE:
|
||||
case ActionTypes.BOARD_CREATE_RECEIVED:
|
||||
Board.upsert(payload.board);
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_UPDATE:
|
||||
Board.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_DELETE:
|
||||
Board.withId(payload.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_CREATE_SUCCEEDED:
|
||||
Board.withId(payload.localId).delete();
|
||||
Board.upsert({
|
||||
...payload.board,
|
||||
isFetching: false,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_FETCH_REQUESTED:
|
||||
Board.withId(payload.id).update({
|
||||
isFetching: true,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_FETCH_SUCCEEDED:
|
||||
Board.withId(payload.board.id).update({
|
||||
...payload.board,
|
||||
isFetching: false,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_UPDATE_RECEIVED:
|
||||
Board.withId(payload.board.id).update(payload.board);
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_DELETE_RECEIVED:
|
||||
Board.withId(payload.board.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_TO_BOARD_FILTER_ADD:
|
||||
Board.withId(payload.boardId).filterLabels.add(payload.id);
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_FROM_BOARD_FILTER_REMOVE:
|
||||
Board.withId(payload.boardId).filterLabels.remove(payload.id);
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
getOrderedListsQuerySet() {
|
||||
return this.lists.orderBy('position');
|
||||
}
|
||||
|
||||
deleteWithRelated() {
|
||||
this.cards.toModelArray().forEach((cardModel) => {
|
||||
cardModel.deleteWithRelated();
|
||||
});
|
||||
|
||||
this.delete();
|
||||
}
|
||||
}
|
166
client/src/models/Card.js
Executable file
166
client/src/models/Card.js
Executable file
|
@ -0,0 +1,166 @@
|
|||
import {
|
||||
Model, attr, fk, many,
|
||||
} from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
import Config from '../constants/Config';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Card';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
position: attr(),
|
||||
name: attr(),
|
||||
description: attr(),
|
||||
deadline: attr(),
|
||||
timer: attr(),
|
||||
isSubscribed: attr({
|
||||
getDefault: () => false,
|
||||
}),
|
||||
isActionsFetching: attr({
|
||||
getDefault: () => false,
|
||||
}),
|
||||
isAllActionsFetched: attr({
|
||||
getDefault: () => false,
|
||||
}),
|
||||
listId: fk({
|
||||
to: 'List',
|
||||
as: 'list',
|
||||
relatedName: 'cards',
|
||||
}),
|
||||
boardId: fk({
|
||||
to: 'Board',
|
||||
as: 'board',
|
||||
relatedName: 'cards',
|
||||
}),
|
||||
users: many('User', 'cards'),
|
||||
labels: many('Label', 'cards'),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Card) {
|
||||
switch (type) {
|
||||
case ActionTypes.USER_TO_CARD_ADD: {
|
||||
const cardModel = Card.withId(payload.cardId);
|
||||
cardModel.users.add(payload.id);
|
||||
|
||||
if (payload.isCurrent) {
|
||||
cardModel.isSubscribed = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ActionTypes.USER_FROM_CARD_REMOVE:
|
||||
Card.withId(payload.cardId).users.remove(payload.id);
|
||||
|
||||
break;
|
||||
case ActionTypes.BOARD_FETCH_SUCCEEDED:
|
||||
payload.cards.forEach((card) => {
|
||||
Card.upsert(card);
|
||||
});
|
||||
|
||||
payload.cardMemberships.forEach(({ cardId, userId }) => {
|
||||
Card.withId(cardId).users.add(userId);
|
||||
});
|
||||
|
||||
payload.cardLabels.forEach(({ cardId, labelId }) => {
|
||||
Card.withId(cardId).labels.add(labelId);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_TO_CARD_ADD:
|
||||
Card.withId(payload.cardId).labels.add(payload.id);
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_FROM_CARD_REMOVE:
|
||||
Card.withId(payload.cardId).labels.remove(payload.id);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_CREATE:
|
||||
case ActionTypes.CARD_CREATE_RECEIVED:
|
||||
case ActionTypes.CARD_FETCH_SUCCEEDED:
|
||||
case ActionTypes.NOTIFICATION_CREATE_RECEIVED:
|
||||
Card.upsert(payload.card);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_UPDATE:
|
||||
Card.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_DELETE:
|
||||
Card.withId(payload.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_CREATE_SUCCEEDED:
|
||||
Card.withId(payload.localId).delete();
|
||||
Card.upsert(payload.card);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_UPDATE_RECEIVED:
|
||||
Card.withId(payload.card.id).update(payload.card);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_DELETE_RECEIVED:
|
||||
Card.withId(payload.card.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_MEMBERSHIP_CREATE_RECEIVED:
|
||||
Card.withId(payload.cardMembership.cardId).users.add(payload.cardMembership.userId);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_MEMBERSHIP_DELETE_RECEIVED:
|
||||
Card.withId(payload.cardMembership.cardId).users.remove(payload.cardMembership.userId);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_LABEL_CREATE_RECEIVED:
|
||||
Card.withId(payload.cardLabel.cardId).labels.add(payload.cardLabel.labelId);
|
||||
|
||||
break;
|
||||
case ActionTypes.CARD_LABEL_DELETE_RECEIVED:
|
||||
Card.withId(payload.cardLabel.cardId).labels.remove(payload.cardLabel.labelId);
|
||||
|
||||
break;
|
||||
case ActionTypes.ACTIONS_FETCH_REQUESTED:
|
||||
Card.withId(payload.cardId).update({
|
||||
isActionsFetching: true,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.ACTIONS_FETCH_SUCCEEDED:
|
||||
Card.withId(payload.cardId).update({
|
||||
isActionsFetching: false,
|
||||
isAllActionsFetched: payload.actions.length < Config.ACTIONS_LIMIT,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
|
||||
payload.cards.forEach((card) => {
|
||||
Card.upsert(card);
|
||||
});
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
getOrderedTasksQuerySet() {
|
||||
return this.tasks.orderBy('id');
|
||||
}
|
||||
|
||||
getOrderedInCardActionsQuerySet() {
|
||||
return this.actions.orderBy('id', 'desc');
|
||||
}
|
||||
|
||||
getUnreadNotificationsQuerySet() {
|
||||
return this.notifications.filter({
|
||||
isRead: false,
|
||||
});
|
||||
}
|
||||
|
||||
deleteWithRelated() {
|
||||
this.tasks.delete();
|
||||
this.actions.delete();
|
||||
|
||||
this.delete();
|
||||
}
|
||||
}
|
58
client/src/models/Label.js
Executable file
58
client/src/models/Label.js
Executable file
|
@ -0,0 +1,58 @@
|
|||
import { Model, attr, fk } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Label';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
name: attr(),
|
||||
color: attr(),
|
||||
boardId: fk({
|
||||
to: 'Board',
|
||||
as: 'board',
|
||||
relatedName: 'labels',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Label) {
|
||||
switch (type) {
|
||||
case ActionTypes.BOARD_CREATE_SUCCEEDED:
|
||||
case ActionTypes.BOARD_CREATE_RECEIVED:
|
||||
case ActionTypes.BOARD_FETCH_SUCCEEDED:
|
||||
payload.labels.forEach((label) => {
|
||||
Label.upsert(label);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_CREATE:
|
||||
case ActionTypes.LABEL_CREATE_RECEIVED:
|
||||
Label.upsert(payload.label);
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_UPDATE:
|
||||
Label.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_DELETE:
|
||||
Label.withId(payload.id).delete();
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_CREATE_SUCCEEDED:
|
||||
Label.withId(payload.localId).delete();
|
||||
Label.upsert(payload.label);
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_UPDATE_RECEIVED:
|
||||
Label.withId(payload.label.id).update(payload.label);
|
||||
|
||||
break;
|
||||
case ActionTypes.LABEL_DELETE_RECEIVED:
|
||||
Label.withId(payload.label.id).delete();
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
95
client/src/models/List.js
Executable file
95
client/src/models/List.js
Executable file
|
@ -0,0 +1,95 @@
|
|||
import { Model, attr, fk } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'List';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
position: attr(),
|
||||
name: attr(),
|
||||
boardId: fk({
|
||||
to: 'Board',
|
||||
as: 'board',
|
||||
relatedName: 'lists',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, List) {
|
||||
switch (type) {
|
||||
case ActionTypes.BOARD_CREATE_SUCCEEDED:
|
||||
case ActionTypes.BOARD_CREATE_RECEIVED:
|
||||
case ActionTypes.BOARD_FETCH_SUCCEEDED:
|
||||
payload.lists.forEach((list) => {
|
||||
List.upsert(list);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.LIST_CREATE:
|
||||
case ActionTypes.LIST_CREATE_RECEIVED:
|
||||
List.upsert(payload.list);
|
||||
|
||||
break;
|
||||
case ActionTypes.LIST_UPDATE:
|
||||
List.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.LIST_DELETE:
|
||||
List.withId(payload.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.LIST_CREATE_SUCCEEDED:
|
||||
List.withId(payload.localId).delete();
|
||||
List.upsert(payload.list);
|
||||
|
||||
break;
|
||||
case ActionTypes.LIST_UPDATE_RECEIVED:
|
||||
List.withId(payload.list.id).update(payload.list);
|
||||
|
||||
break;
|
||||
case ActionTypes.LIST_DELETE_RECEIVED:
|
||||
List.withId(payload.list.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
getOrderedCardsQuerySet() {
|
||||
return this.cards.orderBy('position');
|
||||
}
|
||||
|
||||
getOrderedFilteredCardsModelArray() {
|
||||
let cardModels = this.getOrderedCardsQuerySet().toModelArray();
|
||||
|
||||
const filterUserIds = this.board.filterUsers.toRefArray().map((user) => user.id);
|
||||
const filterLabelIds = this.board.filterLabels.toRefArray().map((label) => label.id);
|
||||
|
||||
if (filterUserIds.length > 0) {
|
||||
cardModels = cardModels.filter((cardModel) => {
|
||||
const users = cardModel.users.toRefArray();
|
||||
|
||||
return users.some((user) => filterUserIds.includes(user.id));
|
||||
});
|
||||
}
|
||||
|
||||
if (filterLabelIds.length > 0) {
|
||||
cardModels = cardModels.filter((cardModel) => {
|
||||
const labels = cardModel.labels.toRefArray();
|
||||
|
||||
return labels.some((label) => filterLabelIds.includes(label.id));
|
||||
});
|
||||
}
|
||||
|
||||
return cardModels;
|
||||
}
|
||||
|
||||
deleteWithRelated() {
|
||||
this.cards.toModelArray().forEach((cardModel) => {
|
||||
cardModel.deleteWithRelated();
|
||||
});
|
||||
|
||||
this.delete();
|
||||
}
|
||||
}
|
60
client/src/models/Notification.js
Executable file
60
client/src/models/Notification.js
Executable file
|
@ -0,0 +1,60 @@
|
|||
import { Model, attr, fk } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Notification';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
type: attr(),
|
||||
data: attr(),
|
||||
isRead: attr(),
|
||||
userId: fk({
|
||||
to: 'User',
|
||||
as: 'user',
|
||||
relatedName: 'notifications',
|
||||
}),
|
||||
actionId: fk({
|
||||
to: 'Action',
|
||||
as: 'action',
|
||||
relatedName: 'notifications',
|
||||
}),
|
||||
cardId: fk({
|
||||
to: 'Card',
|
||||
as: 'card',
|
||||
relatedName: 'notifications',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Notification) {
|
||||
switch (type) {
|
||||
case ActionTypes.NOTIFICATIONS_DELETE:
|
||||
payload.ids.forEach((id) => {
|
||||
Notification.withId(id).delete();
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
|
||||
payload.notifications.forEach((notification) => {
|
||||
Notification.upsert(notification);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.NOTIFICATION_CREATE_RECEIVED:
|
||||
Notification.upsert(payload.notification);
|
||||
|
||||
break;
|
||||
case ActionTypes.NOTIFICATION_DELETE_RECEIVED: {
|
||||
const notificationModel = Notification.withId(payload.notification.id);
|
||||
|
||||
if (notificationModel) {
|
||||
notificationModel.delete();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
66
client/src/models/Project.js
Executable file
66
client/src/models/Project.js
Executable file
|
@ -0,0 +1,66 @@
|
|||
import { Model, attr, many } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Project';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
name: attr(),
|
||||
users: many({
|
||||
to: 'User',
|
||||
through: 'ProjectMembership',
|
||||
relatedName: 'projects',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Project) {
|
||||
switch (type) {
|
||||
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
|
||||
payload.projects.forEach((project) => {
|
||||
Project.upsert(project);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_UPDATE:
|
||||
Project.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_DELETE:
|
||||
Project.withId(payload.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_RECEIVED:
|
||||
Project.upsert(payload.project);
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_UPDATE_RECEIVED:
|
||||
Project.withId(payload.project.id).update(payload.project);
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_DELETE_RECEIVED:
|
||||
Project.withId(payload.project.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
getOrderedMembershipsQuerySet() {
|
||||
return this.memberships.orderBy('id');
|
||||
}
|
||||
|
||||
getOrderedBoardsQuerySet() {
|
||||
return this.boards.orderBy('position');
|
||||
}
|
||||
|
||||
deleteWithRelated() {
|
||||
this.boards.toModelArray().forEach((boardModel) => {
|
||||
boardModel.deleteWithRelated();
|
||||
});
|
||||
|
||||
this.delete();
|
||||
}
|
||||
}
|
69
client/src/models/ProjectMembership.js
Executable file
69
client/src/models/ProjectMembership.js
Executable file
|
@ -0,0 +1,69 @@
|
|||
import { Model, attr, fk } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'ProjectMembership';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
projectId: fk({
|
||||
to: 'Project',
|
||||
as: 'project',
|
||||
relatedName: 'memberships',
|
||||
}),
|
||||
userId: fk({
|
||||
to: 'User',
|
||||
as: 'user',
|
||||
relatedName: 'projectMemberships',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, ProjectMembership) {
|
||||
switch (type) {
|
||||
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_RECEIVED:
|
||||
payload.projectMemberships.forEach((projectMembership) => {
|
||||
ProjectMembership.upsert(projectMembership);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_MEMBERSHIP_CREATE:
|
||||
case ActionTypes.PROJECT_MEMBERSHIP_CREATE_RECEIVED:
|
||||
ProjectMembership.upsert(payload.projectMembership);
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_MEMBERSHIP_CREATE_SUCCEEDED:
|
||||
ProjectMembership.withId(payload.localId).delete();
|
||||
ProjectMembership.upsert(payload.projectMembership);
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_MEMBERSHIP_DELETE:
|
||||
ProjectMembership.withId(payload.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.PROJECT_MEMBERSHIP_DELETE_RECEIVED:
|
||||
ProjectMembership.withId(payload.projectMembership.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
deleteWithRelated() {
|
||||
this.project.boards.toModelArray().forEach((boardModel) => {
|
||||
boardModel.cards.toModelArray().forEach((cardModel) => {
|
||||
try {
|
||||
cardModel.users.remove(this.userId);
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
});
|
||||
|
||||
try {
|
||||
boardModel.filterUsers.remove(this.userId);
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
});
|
||||
|
||||
this.delete();
|
||||
}
|
||||
}
|
58
client/src/models/Task.js
Executable file
58
client/src/models/Task.js
Executable file
|
@ -0,0 +1,58 @@
|
|||
import { Model, attr, fk } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'Task';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
name: attr(),
|
||||
isCompleted: attr({
|
||||
getDefault: () => false,
|
||||
}),
|
||||
cardId: fk({
|
||||
to: 'Card',
|
||||
as: 'card',
|
||||
relatedName: 'tasks',
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, Task) {
|
||||
switch (type) {
|
||||
case ActionTypes.BOARD_FETCH_SUCCEEDED:
|
||||
payload.tasks.forEach((task) => {
|
||||
Task.upsert(task);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.TASK_CREATE:
|
||||
case ActionTypes.TASK_CREATE_RECEIVED:
|
||||
Task.upsert(payload.task);
|
||||
|
||||
break;
|
||||
case ActionTypes.TASK_UPDATE:
|
||||
Task.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.TASK_DELETE:
|
||||
Task.withId(payload.id).delete();
|
||||
|
||||
break;
|
||||
case ActionTypes.TASK_CREATE_SUCCEEDED:
|
||||
Task.withId(payload.localId).delete();
|
||||
Task.upsert(payload.task);
|
||||
|
||||
break;
|
||||
case ActionTypes.TASK_UPDATE_RECEIVED:
|
||||
Task.withId(payload.task.id).update(payload.task);
|
||||
|
||||
break;
|
||||
case ActionTypes.TASK_DELETE_RECEIVED:
|
||||
Task.withId(payload.task.id).delete();
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
112
client/src/models/User.js
Executable file
112
client/src/models/User.js
Executable file
|
@ -0,0 +1,112 @@
|
|||
import { Model, attr } from 'redux-orm';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
export default class extends Model {
|
||||
static modelName = 'User';
|
||||
|
||||
static fields = {
|
||||
id: attr(),
|
||||
email: attr(),
|
||||
name: attr(),
|
||||
avatar: attr(),
|
||||
deletedAt: attr(),
|
||||
isAdmin: attr({
|
||||
getDefault: () => false,
|
||||
}),
|
||||
isAvatarUploading: attr({
|
||||
getDefault: () => false,
|
||||
}),
|
||||
};
|
||||
|
||||
static reducer({ type, payload }, User) {
|
||||
switch (type) {
|
||||
case ActionTypes.USER_CREATE_SUCCEEDED:
|
||||
case ActionTypes.USER_CREATE_RECEIVED:
|
||||
case ActionTypes.CURRENT_USER_FETCH_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_MEMBERSHIP_CREATE_RECEIVED:
|
||||
case ActionTypes.NOTIFICATION_CREATE_RECEIVED:
|
||||
User.upsert(payload.user);
|
||||
|
||||
break;
|
||||
case ActionTypes.USERS_FETCH_SUCCEEDED:
|
||||
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
|
||||
case ActionTypes.PROJECT_CREATE_RECEIVED:
|
||||
case ActionTypes.ACTIONS_FETCH_SUCCEEDED:
|
||||
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
|
||||
payload.users.forEach((user) => {
|
||||
User.upsert(user);
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_UPDATE:
|
||||
User.withId(payload.id).update(payload.data);
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_DELETE:
|
||||
User.withId(payload.id).deleteWithRelated();
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_UPDATE_RECEIVED:
|
||||
User.withId(payload.user.id).update(payload.user);
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_AVATAR_UPLOAD_REQUESTED:
|
||||
User.withId(payload.id).update({
|
||||
isAvatarUploading: true,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_AVATAR_UPLOAD_SUCCEEDED:
|
||||
User.withId(payload.user.id).update({
|
||||
...payload.user,
|
||||
isAvatarUploading: false,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_AVATAR_UPLOAD_FAILED:
|
||||
User.withId(payload.id).update({
|
||||
isAvatarUploading: false,
|
||||
});
|
||||
|
||||
break;
|
||||
case ActionTypes.USER_DELETE_SUCCEEDED:
|
||||
case ActionTypes.USER_DELETE_RECEIVED:
|
||||
User.withId(payload.user.id).deleteWithRelated(payload.user);
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
static getOrderedUndeletedQuerySet() {
|
||||
return this.filter({
|
||||
deletedAt: null,
|
||||
}).orderBy('id');
|
||||
}
|
||||
|
||||
getOrderedProjectMembershipsQuerySet() {
|
||||
return this.projectMemberships.orderBy('id');
|
||||
}
|
||||
|
||||
getOrderedUnreadNotificationsQuerySet() {
|
||||
return this.notifications
|
||||
.filter({
|
||||
isRead: false,
|
||||
})
|
||||
.orderBy('id', 'desc');
|
||||
}
|
||||
|
||||
deleteWithRelated(user) {
|
||||
this.projectMemberships.toModelArray().forEach((projectMembershipModel) => {
|
||||
projectMembershipModel.deleteWithRelated();
|
||||
});
|
||||
|
||||
this.update(
|
||||
user || {
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
14
client/src/models/index.js
Executable file
14
client/src/models/index.js
Executable file
|
@ -0,0 +1,14 @@
|
|||
import User from './User';
|
||||
import Project from './Project';
|
||||
import ProjectMembership from './ProjectMembership';
|
||||
import Board from './Board';
|
||||
import List from './List';
|
||||
import Label from './Label';
|
||||
import Card from './Card';
|
||||
import Task from './Task';
|
||||
import Action from './Action';
|
||||
import Notification from './Notification';
|
||||
|
||||
export {
|
||||
User, Project, ProjectMembership, Board, List, Label, Card, Task, Action, Notification,
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue