feat: Version 2

Closes #627, closes #1047
This commit is contained in:
Maksim Eltyshev
2025-05-10 02:09:06 +02:00
parent ad7fb51cfa
commit 2ee1166747
1557 changed files with 76832 additions and 47042 deletions
+140 -75
View File
@@ -1,8 +1,13 @@
import { attr, many } from 'redux-orm';
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { attr, many, oneToOne } from 'redux-orm';
import BaseModel from './BaseModel';
import ActionTypes from '../constants/ActionTypes';
import { ProjectBackgroundTypes } from '../constants/Enums';
import { UserRoles } from '../constants/Enums';
export default class extends BaseModel {
static modelName = 'Project';
@@ -10,15 +15,27 @@ export default class extends BaseModel {
static fields = {
id: attr(),
name: attr(),
background: attr(),
backgroundImage: attr(),
isBackgroundImageUpdating: attr({
description: attr(),
backgroundType: attr(),
backgroundGradient: attr(),
isHidden: attr(),
isFavorite: attr({
getDefault: () => false,
}),
ownerProjectManagerId: oneToOne({
to: 'ProjectManager',
as: 'ownerProjectManager',
relatedName: 'ownedProject',
}),
backgroundImageId: oneToOne({
to: 'BackgroundImage',
as: 'backgroundImage',
relatedName: 'backgroundedProject', // TODO: rename?
}),
managerUsers: many({
to: 'User',
through: 'ProjectManager',
relatedName: 'projects',
relatedName: 'managerProjects',
}),
};
@@ -47,46 +64,51 @@ export default class extends BaseModel {
});
break;
case ActionTypes.PROJECT_CREATE__SUCCESS:
case ActionTypes.PROJECT_CREATE_HANDLE:
case ActionTypes.PROJECT_UPDATE__SUCCESS:
case ActionTypes.PROJECT_UPDATE_HANDLE:
Project.upsert(payload.project);
case ActionTypes.USER_UPDATE_HANDLE:
Project.all()
.toModelArray()
.forEach((projectModel) => {
if (!payload.projectIds.includes(projectModel.id)) {
projectModel.deleteWithRelated();
}
});
break;
case ActionTypes.PROJECT_UPDATE: {
const project = Project.withId(payload.id);
project.update(payload.data);
if (
payload.data.backgroundImage === null &&
project.background &&
project.background.type === ProjectBackgroundTypes.IMAGE
) {
project.background = null;
if (payload.projects) {
payload.projects.forEach((project) => {
Project.upsert(project);
});
}
break;
case ActionTypes.PROJECT_CREATE__SUCCESS:
case ActionTypes.PROJECT_CREATE_HANDLE:
case ActionTypes.PROJECT_UPDATE__SUCCESS:
Project.upsert(payload.project);
break;
case ActionTypes.PROJECT_UPDATE:
Project.withId(payload.id).update(payload.data);
break;
case ActionTypes.PROJECT_UPDATE_HANDLE: {
const projectModel = Project.withId(payload.project.id);
if (projectModel) {
if (payload.isAvailable) {
projectModel.boards.toModelArray().forEach((boardModel) => {
if (!payload.boardIds.includes(boardModel.id)) {
boardModel.deleteWithRelated();
}
});
} else {
projectModel.deleteWithRelated();
}
}
Project.upsert(payload.project);
break;
}
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE:
Project.withId(payload.id).update({
isBackgroundImageUpdating: true,
});
break;
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS:
Project.withId(payload.project.id).update({
...payload.project,
isBackgroundImageUpdating: false,
});
break;
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE:
Project.withId(payload.id).update({
isBackgroundImageUpdating: false,
});
break;
case ActionTypes.PROJECT_DELETE:
Project.withId(payload.id).deleteWithRelated();
@@ -101,64 +123,86 @@ export default class extends BaseModel {
break;
}
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.project) {
const projectModel = Project.withId(payload.project.id);
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: {
const projectModel = Project.withId(payload.projectManager.projectId);
if (projectModel) {
if (projectModel) {
if (payload.isProjectAvailable) {
projectModel.boards.toModelArray().forEach((boardModel) => {
if (payload.boardIds.includes(boardModel.id)) {
if (payload.isCurrentUser) {
boardModel.notificationServices.delete();
}
} else {
boardModel.deleteWithRelated();
}
});
} else {
projectModel.deleteWithRelated();
}
}
if (payload.project) {
Project.upsert(payload.project);
}
break;
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH: {
const projectModel = Project.withId(payload.id);
}
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (!payload.isProjectAvailable) {
const projectModel = Project.withId(payload.boardMembership.projectId);
if (projectModel) {
projectModel.boards.toModelArray().forEach((boardModel) => {
if (boardModel.id !== payload.currentBoardId) {
boardModel.update({
isFetching: null,
});
if (projectModel) {
projectModel.deleteWithRelated();
}
}
boardModel.deleteRelated(payload.currentUserId);
}
});
if (payload.project) {
Project.upsert(payload.project);
}
break;
}
default:
}
}
getOrderedManagersQuerySet() {
return this.managers.orderBy('createdAt');
static getSharedQuerySet() {
return this.filter({
ownerProjectManagerId: null,
}).orderBy(['id.length', 'id']);
}
getOrderedBoardsQuerySet() {
return this.boards.orderBy('position');
getManagersQuerySet() {
return this.managers.orderBy(['id.length', 'id']);
}
getOrderedBoardsModelArrayForUser(userId) {
return this.getOrderedBoardsQuerySet()
getBackgroundImagesQuerySet() {
return this.backgroundImages.orderBy(['id.length', 'id']);
}
getBaseCustomFieldGroupsQuerySet() {
return this.baseCustomFieldGroups.orderBy(['id.length', 'id']);
}
getBoardsQuerySet() {
return this.boards.orderBy(['position', 'id.length', 'id']);
}
getBoardsModelArrayForUserWithId(userId) {
return this.getBoardsQuerySet()
.toModelArray()
.filter((boardModel) => boardModel.hasMembershipForUser(userId));
.filter((boardModel) => boardModel.hasMembershipWithUserId(userId));
}
getOrderedBoardsModelArrayAvailableForUser(userId) {
if (this.hasManagerForUser(userId)) {
return this.getOrderedBoardsQuerySet().toModelArray();
getBoardsModelArrayAvailableForUser(userModel) {
if (this.isExternalAccessibleForUser(userModel)) {
return this.getBoardsQuerySet().toModelArray();
}
return this.getOrderedBoardsModelArrayForUser(userId);
return this.getBoardsModelArrayForUserWithId(userModel.id);
}
hasManagerForUser(userId) {
hasManagerWithUserId(userId) {
return this.managers
.filter({
userId,
@@ -166,17 +210,38 @@ export default class extends BaseModel {
.exists();
}
hasMembershipInAnyBoardForUser(userId) {
return this.boards.toModelArray().some((boardModel) => boardModel.hasMembershipForUser(userId));
hasMembershipWithUserIdInAnyBoard(userId) {
return this.boards
.toModelArray()
.some((boardModel) => boardModel.hasMembershipWithUserId(userId));
}
isAvailableForUser(userId) {
return this.hasManagerForUser(userId) || this.hasMembershipInAnyBoardForUser(userId);
isExternalAccessibleForUser(userModel) {
if (!this.ownerProjectManagerId && userModel.role === UserRoles.ADMIN) {
return true;
}
return this.hasManagerWithUserId(userModel.id);
}
isAvailableForUser(userModel) {
return (
this.isExternalAccessibleForUser(userModel) ||
this.hasMembershipWithUserIdInAnyBoard(userModel.id)
);
}
deleteRelated() {
this.managers.delete();
this.backgroundImages.toModelArray().forEach((backgroundImageModel) => {
backgroundImageModel.deleteWithRelated();
});
this.baseCustomFieldGroups.toModelArray().forEach((baseCustomFieldGroupModel) => {
baseCustomFieldGroupModel.deleteWithRelated();
});
this.boards.toModelArray().forEach((boardModel) => {
boardModel.deleteWithRelated();
});