41
client/.gitignore
vendored
|
@ -1,23 +1,24 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# Planka client
|
|
@ -1,58 +0,0 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const BASE_URL_PLACEHOLDER = 'BASE_URL_PLACEHOLDER';
|
||||
|
||||
const replaceInFile = (file, search, replace) => {
|
||||
fs.readFile(file, 'utf8', (readError, data) => {
|
||||
if (readError) {
|
||||
throw new Error(`${readError}`);
|
||||
}
|
||||
const res = data.replaceAll(search, replace);
|
||||
fs.writeFile(file, res, 'utf8', (writeError) => {
|
||||
if (writeError) {
|
||||
throw new Error(`${writeError}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const replaceBaseUrl = (compiler) => {
|
||||
compiler.hooks.assetEmitted.tap('ReplaceBaseUrlPlaceholder', (file, info) => {
|
||||
if (info.content.indexOf(BASE_URL_PLACEHOLDER) >= 0) {
|
||||
if (/\.css$/.exec(info.targetPath)) {
|
||||
// For CSS 'url(...)' import we can use relative import
|
||||
const relPath = path
|
||||
.relative(path.dirname(info.targetPath), info.outputPath)
|
||||
.replace(/\\/g, '/');
|
||||
replaceInFile(info.targetPath, BASE_URL_PLACEHOLDER, `${relPath}/`);
|
||||
} else if (/\.js$/.exec(info.targetPath)) {
|
||||
// For JS 'import ... from "some-asset"' we can get the variable injected in the window object
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
replaceInFile(info.targetPath, `"${BASE_URL_PLACEHOLDER}"`, '`${window.BASE_URL}/`');
|
||||
} else if (/index\.html$/.exec(info.targetPath)) {
|
||||
// For the main html file, we set a placeholder for sails to inject the correct value as runtime
|
||||
replaceInFile(info.targetPath, BASE_URL_PLACEHOLDER, '<%= BASE_URL %>');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = function override(config, env) {
|
||||
if (env === 'production') {
|
||||
const plugins = config.plugins.map((plugin) => {
|
||||
if (plugin.constructor.name === 'InterpolateHtmlPlugin') {
|
||||
const newPlugin = plugin;
|
||||
newPlugin.replacements.PUBLIC_URL = BASE_URL_PLACEHOLDER;
|
||||
return newPlugin;
|
||||
}
|
||||
return plugin;
|
||||
});
|
||||
return {
|
||||
...config,
|
||||
output: { ...config.output, publicPath: BASE_URL_PLACEHOLDER },
|
||||
plugins: [...plugins, { apply: replaceBaseUrl }],
|
||||
};
|
||||
}
|
||||
return config;
|
||||
};
|
18
client/index.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="PLANKA is the kanban-style project mastering tool for everyone" />
|
||||
<title>PLANKA</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
</head>
|
||||
<body id="app"></body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.js"></script>
|
||||
</body>
|
||||
</html>
|
21148
client/package-lock.json
generated
160
client/package.json
Normal file → Executable file
|
@ -1,24 +1,18 @@
|
|||
{
|
||||
"name": "planka-client",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "react-app-rewired build",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint --ext js,jsx src config-overrides.js",
|
||||
"start": "react-app-rewired start",
|
||||
"test": "react-app-rewired test",
|
||||
"test:acceptance": "cucumber-js --require tests/acceptance/cucumber.conf.js --require tests/acceptance/stepDefinitions/**/*.js --format @cucumber/pretty-formatter"
|
||||
"build": "vite build",
|
||||
"postinstall": "patch-package",
|
||||
"lint": "eslint --ext js,jsx src --report-unused-disable-directives",
|
||||
"start": "vite",
|
||||
"test": "jest",
|
||||
"test:acceptance": "cucumber-js --import tests/acceptance/cucumber.conf.js --import tests/acceptance/steps/**/*.js --format @cucumber/pretty-formatter tests"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/preset-env"
|
||||
]
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
@ -35,96 +29,154 @@
|
|||
},
|
||||
"requireConfigFile": false
|
||||
},
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"airbnb/hooks",
|
||||
"../package.json"
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "warn",
|
||||
"import/no-extraneous-dependencies": [
|
||||
"import/no-unresolved": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": [
|
||||
"src/setupTests.js",
|
||||
"**/*.test.js"
|
||||
"ignore": [
|
||||
"\\.svg\\?react$"
|
||||
]
|
||||
}
|
||||
],
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"endOfLine": "auto"
|
||||
"endOfLine": "auto",
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"tests/acceptance/**/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"import/extensions": "off"
|
||||
},
|
||||
"globals": {
|
||||
"browser": "readonly",
|
||||
"context": "readonly",
|
||||
"page": "readonly"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx)$": "babel-jest"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ballerina/highlightjs-ballerina": "^1.0.1",
|
||||
"@diplodoc/cut-extension": "^0.7.3",
|
||||
"@diplodoc/transform": "^4.57.2",
|
||||
"@gravity-ui/markdown-editor": "^15.11.0",
|
||||
"@gravity-ui/uikit": "^7.11.0",
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"browserslist-to-esbuild": "^2.1.1",
|
||||
"classnames": "^2.5.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"dequal": "^2.0.3",
|
||||
"easymde": "^2.18.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"highlightjs-4d": "^1.0.6",
|
||||
"highlightjs-alan": "^0.0.2",
|
||||
"highlightjs-apex": "^1.5.0",
|
||||
"highlightjs-blade": "^0.1.0",
|
||||
"highlightjs-cobol": "^0.3.3",
|
||||
"highlightjs-cshtml-razor": "^2.1.1",
|
||||
"highlightjs-gf": "^1.0.1",
|
||||
"highlightjs-jolie": "^0.1.8",
|
||||
"highlightjs-lean": "^1.2.0",
|
||||
"highlightjs-lookml": "^1.0.2",
|
||||
"highlightjs-macaulay2": "^0.2.5",
|
||||
"highlightjs-mlir": "^0.0.1",
|
||||
"highlightjs-qsharp": "^1.0.2",
|
||||
"highlightjs-redbol": "^2.1.2",
|
||||
"highlightjs-rpm-specfile": "^1.0.0",
|
||||
"highlightjs-sap-abap": "^0.3.0",
|
||||
"highlightjs-solidity": "^2.0.6",
|
||||
"highlightjs-supercollider": "^1.0.0",
|
||||
"highlightjs-svelte": "^1.0.6",
|
||||
"highlightjs-xsharp": "^1.0.0",
|
||||
"highlightjs-zenscript": "^2.0.0",
|
||||
"hightlightjs-papyrus": "^0.0.4",
|
||||
"history": "^5.3.0",
|
||||
"i18next": "23.15.2",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next": "^23.16.8",
|
||||
"i18next-browser-languagedetector": "^8.1.0",
|
||||
"initials": "^3.1.2",
|
||||
"javascript-time-ago": "^2.5.11",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"linkify-react": "^4.1.4",
|
||||
"linkifyjs": "^4.1.4",
|
||||
"linkify-react": "^4.3.1",
|
||||
"linkifyjs": "^4.3.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^5.0.9",
|
||||
"node-sass": "^9.0.0",
|
||||
"lowlight": "^3.3.0",
|
||||
"markdown-it": "^13.0.2",
|
||||
"nanoid": "^5.1.5",
|
||||
"patch-package": "^8.0.0",
|
||||
"photoswipe": "^5.4.4",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "18.2.0",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-datepicker": "^4.25.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-dropzone": "^14.3.5",
|
||||
"react-i18next": "^15.1.1",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-frame-component": "^5.2.7",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-intersection-observer": "^9.16.0",
|
||||
"react-photoswipe-gallery": "^2.2.7",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-simplemde-editor": "^5.2.0",
|
||||
"react-textarea-autosize": "^8.5.5",
|
||||
"react-router-dom": "^6.30.0",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"react-time-ago": "^7.3.3",
|
||||
"redux": "^4.2.1",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-orm": "^0.16.2",
|
||||
"redux-saga": "^1.3.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"reselect": "^4.1.8",
|
||||
"sails.io.js": "^1.2.1",
|
||||
"sass-embedded": "^1.87.0",
|
||||
"semantic-ui-react": "^2.1.5",
|
||||
"socket.io-client": "^2.5.0",
|
||||
"validator": "^13.12.0",
|
||||
"whatwg-fetch": "^3.6.20",
|
||||
"validator": "^13.15.0",
|
||||
"vite": "^5.4.19",
|
||||
"vite-plugin-commonjs": "^0.10.4",
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@cucumber/cucumber": "^7.3.2",
|
||||
"@babel/eslint-parser": "^7.27.1",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@cucumber/cucumber": "^11.2.0",
|
||||
"@cucumber/pretty-formatter": "^1.0.1",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"axios": "^1.8.2",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"babel-jest": "^29.7.0",
|
||||
"babel-preset-airbnb": "^5.0.0",
|
||||
"chai": "^4.5.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-prettier": "^5.4.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"playwright": "^1.49.0",
|
||||
"react-test-renderer": "18.2.0"
|
||||
"jest": "^29.7.0",
|
||||
"playwright": "^1.52.0",
|
||||
"prettier": "3.3.3"
|
||||
}
|
||||
}
|
||||
|
|
53
client/patches/@gravity-ui+markdown-editor+15.11.0.patch
Normal file
|
@ -0,0 +1,53 @@
|
|||
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js b/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
|
||||
index 2152fd6..ceda0c1 100644
|
||||
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
|
||||
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
|
||||
@@ -101,7 +101,6 @@ export const BundlePreset = (builder, opts) => {
|
||||
enableNewImageSizeCalculation: opts.enableNewImageSizeCalculation,
|
||||
...opts.imgSize,
|
||||
},
|
||||
- checkbox: { checkboxLabelPlaceholder: () => i18nPlaceholder('checkbox'), ...opts.checkbox },
|
||||
deflist: {
|
||||
deflistTermPlaceholder: () => i18nPlaceholder('deflist_term'),
|
||||
deflistDescPlaceholder: () => i18nPlaceholder('deflist_desc'),
|
||||
@@ -118,11 +117,6 @@ export const BundlePreset = (builder, opts) => {
|
||||
...opts.yfmNote,
|
||||
},
|
||||
yfmTable: { yfmTableCellPlaceholder: () => i18nPlaceholder('table_cell'), ...opts.yfmTable },
|
||||
- yfmFile: {
|
||||
- fileUploadHandler: opts.fileUploadHandler,
|
||||
- needToSetDimensionsForUploadedImages: opts.needToSetDimensionsForUploadedImages,
|
||||
- ...opts.yfmFile,
|
||||
- },
|
||||
yfmHeading: {
|
||||
h1Key: f.toPM(A.Heading1),
|
||||
h2Key: f.toPM(A.Heading2),
|
||||
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js b/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js
|
||||
index ed2a9db..f95b693 100644
|
||||
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js
|
||||
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Deflist, Subscript, Superscript, Underline, } from "../extensions/markdown/index.js";
|
||||
-import { Checkbox, ImgSize, Monospace, Video, YfmConfigs, YfmCut, YfmFile, YfmHeading, YfmNote, YfmTable, YfmTabs, } from "../extensions/yfm/index.js";
|
||||
+import { ImgSize, Monospace, Video, YfmConfigs, YfmCut, YfmHeading, YfmNote, YfmTable } from "../extensions/yfm/index.js";
|
||||
import { DefaultPreset } from "./default.js";
|
||||
export const YfmPreset = (builder, opts) => {
|
||||
builder.use(DefaultPreset, { ...opts, image: false, heading: false });
|
||||
@@ -8,16 +8,13 @@ export const YfmPreset = (builder, opts) => {
|
||||
.use(Subscript)
|
||||
.use(Superscript)
|
||||
.use(Underline, opts.underline ?? {})
|
||||
- .use(Checkbox, opts.checkbox ?? {})
|
||||
.use(ImgSize, opts.imgSize ?? {})
|
||||
.use(Monospace)
|
||||
.use(Video, opts.video ?? {})
|
||||
.use(YfmConfigs, opts.yfmConfigs ?? {})
|
||||
.use(YfmCut, opts.yfmCut ?? {})
|
||||
.use(YfmNote, opts.yfmNote ?? {})
|
||||
- .use(YfmFile, opts.yfmFile ?? {})
|
||||
.use(YfmHeading, opts.yfmHeading ?? {})
|
||||
- .use(YfmTable, opts.yfmTable ?? {})
|
||||
- .use(YfmTabs);
|
||||
+ .use(YfmTable, opts.yfmTable ?? {});
|
||||
};
|
||||
//# sourceMappingURL=yfm.js.map
|
2543
client/patches/redux-orm+0.16.2.patch
Normal file
37
client/patches/semantic-ui-react+2.1.5.patch
Normal file
|
@ -0,0 +1,37 @@
|
|||
diff --git a/node_modules/semantic-ui-react/dist/es/lib/doesNodeContainClick.js b/node_modules/semantic-ui-react/dist/es/lib/doesNodeContainClick.js
|
||||
index 6d06078..fb7534d 100644
|
||||
--- a/node_modules/semantic-ui-react/dist/es/lib/doesNodeContainClick.js
|
||||
+++ b/node_modules/semantic-ui-react/dist/es/lib/doesNodeContainClick.js
|
||||
@@ -17,13 +17,7 @@ var doesNodeContainClick = function doesNodeContainClick(node, e) {
|
||||
if (_some([e, node], _isNil)) return false; // if there is an e.target and it is in the document, use a simple node.contains() check
|
||||
|
||||
if (e.target) {
|
||||
- _invoke(e.target, 'setAttribute', 'data-suir-click-target', true);
|
||||
-
|
||||
- if (document.querySelector('[data-suir-click-target=true]')) {
|
||||
- _invoke(e.target, 'removeAttribute', 'data-suir-click-target');
|
||||
-
|
||||
- return node.contains(e.target);
|
||||
- }
|
||||
+ return node.contains(e.target);
|
||||
} // Below logic handles cases where the e.target is no longer in the document.
|
||||
// The result of the click likely has removed the e.target node.
|
||||
// Instead of node.contains(), we'll identify the click by X/Y position.
|
||||
diff --git a/node_modules/semantic-ui-react/src/lib/doesNodeContainClick.js b/node_modules/semantic-ui-react/src/lib/doesNodeContainClick.js
|
||||
index d1ae271..43e1170 100644
|
||||
--- a/node_modules/semantic-ui-react/src/lib/doesNodeContainClick.js
|
||||
+++ b/node_modules/semantic-ui-react/src/lib/doesNodeContainClick.js
|
||||
@@ -14,12 +14,7 @@ const doesNodeContainClick = (node, e) => {
|
||||
|
||||
// if there is an e.target and it is in the document, use a simple node.contains() check
|
||||
if (e.target) {
|
||||
- _.invoke(e.target, 'setAttribute', 'data-suir-click-target', true)
|
||||
-
|
||||
- if (document.querySelector('[data-suir-click-target=true]')) {
|
||||
- _.invoke(e.target, 'removeAttribute', 'data-suir-click-target')
|
||||
- return node.contains(e.target)
|
||||
- }
|
||||
+ return node.contains(e.target)
|
||||
}
|
||||
|
||||
// Below logic handles cases where the e.target is no longer in the document.
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 143 KiB |
|
@ -1,44 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Planka is an open source project management software"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Planka</title>
|
||||
</head>
|
||||
<script>window.BASE_URL = "%PUBLIC_URL%";</script>
|
||||
<body id="app">
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
BIN
client/public/logo192.png
Executable file → Normal file
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 9.4 KiB |
BIN
client/public/logo512.png
Executable file → Normal file
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 25 KiB |
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "Planka",
|
||||
"name": "PLANKA",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const fetchActivities = (cardId) => ({
|
||||
|
@ -24,31 +29,6 @@ fetchActivities.failure = (cardId, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const toggleActivitiesDetails = (cardId, isVisible) => ({
|
||||
type: ActionTypes.ACTIVITIES_DETAILS_TOGGLE,
|
||||
payload: {
|
||||
cardId,
|
||||
isVisible,
|
||||
},
|
||||
});
|
||||
|
||||
toggleActivitiesDetails.success = (cardId, activities, users) => ({
|
||||
type: ActionTypes.ACTIVITIES_DETAILS_TOGGLE__SUCCESS,
|
||||
payload: {
|
||||
cardId,
|
||||
activities,
|
||||
users,
|
||||
},
|
||||
});
|
||||
|
||||
toggleActivitiesDetails.failure = (cardId, error) => ({
|
||||
type: ActionTypes.ACTIVITIES_DETAILS_TOGGLE__FAILURE,
|
||||
payload: {
|
||||
cardId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleActivityCreate = (activity) => ({
|
||||
type: ActionTypes.ACTIVITY_CREATE_HANDLE,
|
||||
payload: {
|
||||
|
@ -56,24 +36,7 @@ const handleActivityCreate = (activity) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleActivityUpdate = (activity) => ({
|
||||
type: ActionTypes.ACTIVITY_UPDATE_HANDLE,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
const handleActivityDelete = (activity) => ({
|
||||
type: ActionTypes.ACTIVITY_DELETE_HANDLE,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
fetchActivities,
|
||||
toggleActivitiesDetails,
|
||||
handleActivityCreate,
|
||||
handleActivityUpdate,
|
||||
handleActivityDelete,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createAttachment = (attachment) => ({
|
||||
|
|
72
client/src/actions/background-images.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBackgroundImage = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
createBackgroundImage.success = (localId, backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
createBackgroundImage.failure = (localId, error) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBackgroundImageCreate = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_CREATE_HANDLE,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBackgroundImage = (id) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBackgroundImage.success = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE__SUCCESS,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBackgroundImage.failure = (id, error) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBackgroundImageDelete = (backgroundImage) => ({
|
||||
type: ActionTypes.BACKGROUND_IMAGE_DELETE_HANDLE,
|
||||
payload: {
|
||||
backgroundImage,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createBackgroundImage,
|
||||
handleBackgroundImageCreate,
|
||||
deleteBackgroundImage,
|
||||
handleBackgroundImageDelete,
|
||||
};
|
104
client/src/actions/base-custom-field-groups.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBaseCustomFieldGroup = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createBaseCustomFieldGroup.success = (localId, baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createBaseCustomFieldGroup.failure = (localId, error) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBaseCustomFieldGroupCreate = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_CREATE_HANDLE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const updateBaseCustomFieldGroup = (id, data) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateBaseCustomFieldGroup.success = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
updateBaseCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBaseCustomFieldGroupUpdate = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_UPDATE_HANDLE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBaseCustomFieldGroup = (id) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBaseCustomFieldGroup.success = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE__SUCCESS,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBaseCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleBaseCustomFieldGroupDelete = (baseCustomFieldGroup) => ({
|
||||
type: ActionTypes.BASE_CUSTOM_FIELD_GROUP_DELETE_HANDLE,
|
||||
payload: {
|
||||
baseCustomFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createBaseCustomFieldGroup,
|
||||
handleBaseCustomFieldGroupCreate,
|
||||
updateBaseCustomFieldGroup,
|
||||
handleBaseCustomFieldGroupUpdate,
|
||||
deleteBaseCustomFieldGroup,
|
||||
handleBaseCustomFieldGroupDelete,
|
||||
};
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBoardMembership = (boardMembership) => ({
|
||||
|
@ -25,10 +30,13 @@ createBoardMembership.failure = (localId, error) => ({
|
|||
|
||||
const handleBoardMembershipCreate = (
|
||||
boardMembership,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -36,17 +44,25 @@ const handleBoardMembershipCreate = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE,
|
||||
payload: {
|
||||
boardMembership,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -54,18 +70,14 @@ const handleBoardMembershipCreate = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
},
|
||||
});
|
||||
|
||||
handleBoardMembershipCreate.fetchProject = (id, currentUserId, currentBoardId) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH,
|
||||
payload: {
|
||||
id,
|
||||
currentUserId,
|
||||
currentBoardId,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -99,17 +111,19 @@ const handleBoardMembershipUpdate = (boardMembership) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const deleteBoardMembership = (id) => ({
|
||||
const deleteBoardMembership = (id, isCurrentUser) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
isCurrentUser,
|
||||
},
|
||||
});
|
||||
|
||||
deleteBoardMembership.success = (boardMembership) => ({
|
||||
deleteBoardMembership.success = (boardMembership, isCurrentUser) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_DELETE__SUCCESS,
|
||||
payload: {
|
||||
boardMembership,
|
||||
isCurrentUser,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -121,10 +135,11 @@ deleteBoardMembership.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleBoardMembershipDelete = (boardMembership) => ({
|
||||
const handleBoardMembershipDelete = (boardMembership, isCurrentUser) => ({
|
||||
type: ActionTypes.BOARD_MEMBERSHIP_DELETE_HANDLE,
|
||||
payload: {
|
||||
boardMembership,
|
||||
isCurrentUser,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createBoard = (board) => ({
|
||||
|
@ -24,10 +29,11 @@ createBoard.failure = (localId, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleBoardCreate = (board) => ({
|
||||
const handleBoardCreate = (board, boardMemberships) => ({
|
||||
type: ActionTypes.BOARD_CREATE_HANDLE,
|
||||
payload: {
|
||||
board,
|
||||
boardMemberships,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -48,8 +54,12 @@ fetchBoard.success = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.BOARD_FETCH__SUCCESS,
|
||||
payload: {
|
||||
|
@ -62,8 +72,12 @@ fetchBoard.success = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -105,6 +119,23 @@ const handleBoardUpdate = (board) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const updateBoardContext = (id, value) => ({
|
||||
type: ActionTypes.BOARD_CONTEXT_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const searchInBoard = (id, value, currentListId) => ({
|
||||
type: ActionTypes.IN_BOARD_SEARCH,
|
||||
payload: {
|
||||
id,
|
||||
value,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteBoard = (id) => ({
|
||||
type: ActionTypes.BOARD_DELETE,
|
||||
payload: {
|
||||
|
@ -140,6 +171,8 @@ export default {
|
|||
fetchBoard,
|
||||
updateBoard,
|
||||
handleBoardUpdate,
|
||||
updateBoardContext,
|
||||
searchInBoard,
|
||||
deleteBoard,
|
||||
handleBoardDelete,
|
||||
};
|
||||
|
|
|
@ -1,9 +1,67 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCard = (card) => ({
|
||||
const fetchCards = (listId) => ({
|
||||
type: ActionTypes.CARDS_FETCH,
|
||||
payload: {
|
||||
listId,
|
||||
},
|
||||
});
|
||||
|
||||
fetchCards.success = (
|
||||
listId,
|
||||
cards,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARDS_FETCH__SUCCESS,
|
||||
payload: {
|
||||
listId,
|
||||
cards,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
fetchCards.failure = (listId, error) => ({
|
||||
type: ActionTypes.CARDS_FETCH__FAILURE,
|
||||
payload: {
|
||||
listId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCardsUpdate = (cards, activities) => ({
|
||||
type: ActionTypes.CARDS_UPDATE_HANDLE,
|
||||
payload: {
|
||||
cards,
|
||||
activities,
|
||||
},
|
||||
});
|
||||
|
||||
const createCard = (card, autoOpen) => ({
|
||||
type: ActionTypes.CARD_CREATE,
|
||||
payload: {
|
||||
card,
|
||||
autoOpen,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -23,14 +81,30 @@ createCard.failure = (localId, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({
|
||||
const handleCardCreate = (
|
||||
card,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARD_CREATE_HANDLE,
|
||||
payload: {
|
||||
card,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -57,42 +131,75 @@ updateCard.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleCardUpdate = (card, isFetched, cardMemberships, cardLabels, tasks, attachments) => ({
|
||||
const handleCardUpdate = (
|
||||
card,
|
||||
isFetched,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARD_UPDATE_HANDLE,
|
||||
payload: {
|
||||
card,
|
||||
isFetched,
|
||||
users,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
const duplicateCard = (id, card, taskIds) => ({
|
||||
const duplicateCard = (id, localId, data) => ({
|
||||
type: ActionTypes.CARD_DUPLICATE,
|
||||
payload: {
|
||||
id,
|
||||
card,
|
||||
taskIds,
|
||||
localId,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({
|
||||
duplicateCard.success = (
|
||||
localId,
|
||||
card,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
) => ({
|
||||
type: ActionTypes.CARD_DUPLICATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
card,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
},
|
||||
});
|
||||
|
||||
duplicateCard.failure = (id, error) => ({
|
||||
duplicateCard.failure = (localId, error) => ({
|
||||
type: ActionTypes.CARD_DUPLICATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
@ -126,15 +233,9 @@ const handleCardDelete = (card) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const filterText = (boardId, text) => ({
|
||||
type: ActionTypes.TEXT_FILTER_IN_CURRENT_BOARD,
|
||||
payload: {
|
||||
boardId,
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
fetchCards,
|
||||
handleCardsUpdate,
|
||||
createCard,
|
||||
handleCardCreate,
|
||||
updateCard,
|
||||
|
@ -142,5 +243,4 @@ export default {
|
|||
duplicateCard,
|
||||
deleteCard,
|
||||
handleCardDelete,
|
||||
filterText,
|
||||
};
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCommentActivity = (activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_CREATE,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
createCommentActivity.success = (localId, activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
createCommentActivity.failure = (localId, error) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const updateCommentActivity = (id, data) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateCommentActivity.success = (activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
updateCommentActivity.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCommentActivity = (id) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCommentActivity.success = (activity) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_DELETE__SUCCESS,
|
||||
payload: {
|
||||
activity,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCommentActivity.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_ACTIVITY_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createCommentActivity,
|
||||
updateCommentActivity,
|
||||
deleteCommentActivity,
|
||||
};
|
130
client/src/actions/comments.js
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const fetchComments = (cardId) => ({
|
||||
type: ActionTypes.COMMENTS_FETCH,
|
||||
payload: {
|
||||
cardId,
|
||||
},
|
||||
});
|
||||
|
||||
fetchComments.success = (cardId, comments, users) => ({
|
||||
type: ActionTypes.COMMENTS_FETCH__SUCCESS,
|
||||
payload: {
|
||||
cardId,
|
||||
comments,
|
||||
users,
|
||||
},
|
||||
});
|
||||
|
||||
fetchComments.failure = (cardId, error) => ({
|
||||
type: ActionTypes.COMMENTS_FETCH__FAILURE,
|
||||
payload: {
|
||||
cardId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const createComment = (comment) => ({
|
||||
type: ActionTypes.COMMENT_CREATE,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
createComment.success = (localId, comment) => ({
|
||||
type: ActionTypes.COMMENT_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
createComment.failure = (localId, error) => ({
|
||||
type: ActionTypes.COMMENT_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCommentCreate = (comment, users) => ({
|
||||
type: ActionTypes.COMMENT_CREATE_HANDLE,
|
||||
payload: {
|
||||
comment,
|
||||
users,
|
||||
},
|
||||
});
|
||||
|
||||
const updateComment = (id, data) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateComment.success = (comment) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
updateComment.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCommentUpdate = (comment) => ({
|
||||
type: ActionTypes.COMMENT_UPDATE_HANDLE,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteComment = (id) => ({
|
||||
type: ActionTypes.COMMENT_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteComment.success = (comment) => ({
|
||||
type: ActionTypes.COMMENT_DELETE__SUCCESS,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
deleteComment.failure = (id, error) => ({
|
||||
type: ActionTypes.COMMENT_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCommentDelete = (comment) => ({
|
||||
type: ActionTypes.COMMENT_DELETE_HANDLE,
|
||||
payload: {
|
||||
comment,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
fetchComments,
|
||||
createComment,
|
||||
handleCommentCreate,
|
||||
updateComment,
|
||||
handleCommentUpdate,
|
||||
deleteComment,
|
||||
handleCommentDelete,
|
||||
};
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeCore = (
|
||||
|
@ -6,6 +11,8 @@ const initializeCore = (
|
|||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -13,10 +20,14 @@ const initializeCore = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE,
|
||||
payload: {
|
||||
|
@ -25,6 +36,8 @@ const initializeCore = (
|
|||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -32,14 +45,17 @@ const initializeCore = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: with success?
|
||||
initializeCore.fetchConfig = (config) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE__CONFIG_FETCH,
|
||||
payload: {
|
||||
|
@ -47,13 +63,32 @@ initializeCore.fetchConfig = (config) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const logout = (invalidateAccessToken) => ({
|
||||
type: ActionTypes.LOGOUT,
|
||||
const toggleFavorites = (isEnabled) => ({
|
||||
type: ActionTypes.FAVORITES_TOGGLE,
|
||||
payload: {
|
||||
invalidateAccessToken,
|
||||
isEnabled,
|
||||
},
|
||||
});
|
||||
|
||||
const toggleEditMode = (isEnabled) => ({
|
||||
type: ActionTypes.EDIT_MODE_TOGGLE,
|
||||
payload: {
|
||||
isEnabled,
|
||||
},
|
||||
});
|
||||
|
||||
const updateHomeView = (value) => ({
|
||||
type: ActionTypes.HOME_VIEW_UPDATE,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const logout = () => ({
|
||||
type: ActionTypes.LOGOUT,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
logout.invalidateAccessToken = () => ({
|
||||
type: ActionTypes.LOGOUT__ACCESS_TOKEN_INVALIDATE,
|
||||
payload: {},
|
||||
|
@ -61,5 +96,8 @@ logout.invalidateAccessToken = () => ({
|
|||
|
||||
export default {
|
||||
initializeCore,
|
||||
toggleFavorites,
|
||||
toggleEditMode,
|
||||
updateHomeView,
|
||||
logout,
|
||||
};
|
||||
|
|
104
client/src/actions/custom-field-groups.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCustomFieldGroup = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomFieldGroup.success = (localId, customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomFieldGroup.failure = (localId, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldGroupCreate = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_CREATE_HANDLE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const updateCustomFieldGroup = (id, data) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldGroup.success = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldGroupUpdate = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_UPDATE_HANDLE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCustomFieldGroup = (id) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldGroup.success = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE__SUCCESS,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldGroup.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldGroupDelete = (customFieldGroup) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_GROUP_DELETE_HANDLE,
|
||||
payload: {
|
||||
customFieldGroup,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createCustomFieldGroup,
|
||||
handleCustomFieldGroupCreate,
|
||||
updateCustomFieldGroup,
|
||||
handleCustomFieldGroupUpdate,
|
||||
deleteCustomFieldGroup,
|
||||
handleCustomFieldGroupDelete,
|
||||
};
|
72
client/src/actions/custom-field-values.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const updateCustomFieldValue = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldValue.success = (localId, customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomFieldValue.failure = (localId, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldValueUpdate = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_UPDATE_HANDLE,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCustomFieldValue = (id) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldValue.success = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE__SUCCESS,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomFieldValue.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldValueDelete = (customFieldValue) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_VALUE_DELETE_HANDLE,
|
||||
payload: {
|
||||
customFieldValue,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
updateCustomFieldValue,
|
||||
handleCustomFieldValueUpdate,
|
||||
deleteCustomFieldValue,
|
||||
handleCustomFieldValueDelete,
|
||||
};
|
104
client/src/actions/custom-fields.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createCustomField = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomField.success = (localId, customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
createCustomField.failure = (localId, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldCreate = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_CREATE_HANDLE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
const updateCustomField = (id, data) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomField.success = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
updateCustomField.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldUpdate = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_UPDATE_HANDLE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteCustomField = (id) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomField.success = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE__SUCCESS,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
deleteCustomField.failure = (id, error) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleCustomFieldDelete = (customField) => ({
|
||||
type: ActionTypes.CUSTOM_FIELD_DELETE_HANDLE,
|
||||
payload: {
|
||||
customField,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createCustomField,
|
||||
handleCustomFieldCreate,
|
||||
updateCustomField,
|
||||
handleCustomFieldUpdate,
|
||||
deleteCustomField,
|
||||
handleCustomFieldDelete,
|
||||
};
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import router from './router';
|
||||
import socket from './socket';
|
||||
import login from './login';
|
||||
|
@ -6,16 +11,23 @@ import modals from './modals';
|
|||
import users from './users';
|
||||
import projects from './projects';
|
||||
import projectManagers from './project-managers';
|
||||
import backgroundImages from './background-images';
|
||||
import baseCustomFieldGroups from './base-custom-field-groups';
|
||||
import boards from './boards';
|
||||
import boardMemberships from './board-memberships';
|
||||
import labels from './labels';
|
||||
import lists from './lists';
|
||||
import cards from './cards';
|
||||
import taskLists from './task-lists';
|
||||
import tasks from './tasks';
|
||||
import attachments from './attachments';
|
||||
import customFieldGroups from './custom-field-groups';
|
||||
import customFields from './custom-fields';
|
||||
import customFieldValues from './custom-field-values';
|
||||
import comments from './comments';
|
||||
import activities from './activities';
|
||||
import commentActivities from './comment-activities';
|
||||
import notifications from './notifications';
|
||||
import notificationServices from './notification-services';
|
||||
|
||||
export default {
|
||||
...router,
|
||||
|
@ -26,14 +38,21 @@ export default {
|
|||
...users,
|
||||
...projects,
|
||||
...projectManagers,
|
||||
...backgroundImages,
|
||||
...baseCustomFieldGroups,
|
||||
...boards,
|
||||
...boardMemberships,
|
||||
...labels,
|
||||
...lists,
|
||||
...cards,
|
||||
...taskLists,
|
||||
...tasks,
|
||||
...attachments,
|
||||
...customFieldGroups,
|
||||
...customFields,
|
||||
...customFieldValues,
|
||||
...comments,
|
||||
...activities,
|
||||
...commentActivities,
|
||||
...notifications,
|
||||
...notificationServices,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createLabel = (label) => ({
|
||||
|
@ -23,6 +28,32 @@ createLabel.failure = (localId, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const createLabelFromCard = (cardId, label) => ({
|
||||
type: ActionTypes.LABEL_FROM_CARD_CREATE,
|
||||
payload: {
|
||||
cardId,
|
||||
label,
|
||||
},
|
||||
});
|
||||
|
||||
createLabelFromCard.success = (localId, label, cardLabel) => ({
|
||||
type: ActionTypes.LABEL_FROM_CARD_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
label,
|
||||
cardLabel,
|
||||
},
|
||||
});
|
||||
|
||||
createLabelFromCard.failure = (cardId, localId, error) => ({
|
||||
type: ActionTypes.LABEL_FROM_CARD_CREATE__FAILURE,
|
||||
payload: {
|
||||
cardId,
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleLabelCreate = (label) => ({
|
||||
type: ActionTypes.LABEL_CREATE_HANDLE,
|
||||
payload: {
|
||||
|
@ -151,24 +182,27 @@ const handleLabelFromCardRemove = (cardLabel) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const addLabelToBoardFilter = (id, boardId) => ({
|
||||
const addLabelToBoardFilter = (id, boardId, currentListId) => ({
|
||||
type: ActionTypes.LABEL_TO_BOARD_FILTER_ADD,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
const removeLabelFromBoardFilter = (id, boardId) => ({
|
||||
const removeLabelFromBoardFilter = (id, boardId, currentListId) => ({
|
||||
type: ActionTypes.LABEL_FROM_BOARD_FILTER_REMOVE,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createLabel,
|
||||
createLabelFromCard,
|
||||
handleLabelCreate,
|
||||
updateLabel,
|
||||
handleLabelUpdate,
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createList = (list) => ({
|
||||
|
@ -84,25 +89,75 @@ sortList.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleListSort = (list, cards) => ({
|
||||
type: ActionTypes.LIST_SORT_HANDLE,
|
||||
const moveListCards = (id, nextId, cardIds) => ({
|
||||
type: ActionTypes.LIST_CARDS_MOVE,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
id,
|
||||
nextId,
|
||||
cardIds,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteList = (id) => ({
|
||||
type: ActionTypes.LIST_DELETE,
|
||||
moveListCards.success = (list, cards, activities) => ({
|
||||
type: ActionTypes.LIST_CARDS_MOVE__SUCCESS,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
activities,
|
||||
},
|
||||
});
|
||||
|
||||
moveListCards.failure = (id, error) => ({
|
||||
type: ActionTypes.LIST_CARDS_MOVE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const clearList = (id) => ({
|
||||
type: ActionTypes.LIST_CLEAR,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteList.success = (list) => ({
|
||||
clearList.success = (list) => ({
|
||||
type: ActionTypes.LIST_CLEAR__SUCCESS,
|
||||
payload: {
|
||||
list,
|
||||
},
|
||||
});
|
||||
|
||||
clearList.failure = (id, error) => ({
|
||||
type: ActionTypes.LIST_CLEAR__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleListClear = (list) => ({
|
||||
type: ActionTypes.LIST_CLEAR_HANDLE,
|
||||
payload: {
|
||||
list,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteList = (id, trashId, cardIds) => ({
|
||||
type: ActionTypes.LIST_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
trashId,
|
||||
cardIds,
|
||||
},
|
||||
});
|
||||
|
||||
deleteList.success = (list, cards) => ({
|
||||
type: ActionTypes.LIST_DELETE__SUCCESS,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -114,10 +169,11 @@ deleteList.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleListDelete = (list) => ({
|
||||
const handleListDelete = (list, cards) => ({
|
||||
type: ActionTypes.LIST_DELETE_HANDLE,
|
||||
payload: {
|
||||
list,
|
||||
cards,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -127,7 +183,9 @@ export default {
|
|||
updateList,
|
||||
handleListUpdate,
|
||||
sortList,
|
||||
handleListSort,
|
||||
moveListCards,
|
||||
clearList,
|
||||
handleListClear,
|
||||
deleteList,
|
||||
handleListDelete,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeLogin = (config) => ({
|
||||
|
@ -28,20 +33,20 @@ authenticate.failure = (error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const authenticateUsingOidc = () => ({
|
||||
type: ActionTypes.USING_OIDC_AUTHENTICATE,
|
||||
const authenticateWithOidc = () => ({
|
||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
authenticateUsingOidc.success = (accessToken) => ({
|
||||
type: ActionTypes.USING_OIDC_AUTHENTICATE__SUCCESS,
|
||||
authenticateWithOidc.success = (accessToken) => ({
|
||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS,
|
||||
payload: {
|
||||
accessToken,
|
||||
},
|
||||
});
|
||||
|
||||
authenticateUsingOidc.failure = (error) => ({
|
||||
type: ActionTypes.USING_OIDC_AUTHENTICATE__FAILURE,
|
||||
authenticateWithOidc.failure = (error) => ({
|
||||
type: ActionTypes.WITH_OIDC_AUTHENTICATE__FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
|
@ -55,6 +60,6 @@ const clearAuthenticateError = () => ({
|
|||
export default {
|
||||
initializeLogin,
|
||||
authenticate,
|
||||
authenticateUsingOidc,
|
||||
authenticateWithOidc,
|
||||
clearAuthenticateError,
|
||||
};
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const openModal = (type) => ({
|
||||
const openModal = (type, params = {}) => ({
|
||||
type: ActionTypes.MODAL_OPEN,
|
||||
payload: {
|
||||
type,
|
||||
params,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
127
client/src/actions/notification-services.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createNotificationService = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
createNotificationService.success = (localId, notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
createNotificationService.failure = (localId, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationServiceCreate = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_CREATE_HANDLE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
const updateNotificationService = (id, data) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateNotificationService.success = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
updateNotificationService.failure = (id, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationServiceUpdate = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_UPDATE_HANDLE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
const testNotificationService = (id) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_TEST,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
testNotificationService.success = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_TEST__SUCCESS,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
testNotificationService.failure = (id, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_TEST__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteNotificationService = (id) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteNotificationService.success = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE__SUCCESS,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
deleteNotificationService.failure = (id, error) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationServiceDelete = (notificationService) => ({
|
||||
type: ActionTypes.NOTIFICATION_SERVICE_DELETE_HANDLE,
|
||||
payload: {
|
||||
notificationService,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createNotificationService,
|
||||
handleNotificationServiceCreate,
|
||||
updateNotificationService,
|
||||
handleNotificationServiceUpdate,
|
||||
testNotificationService,
|
||||
deleteNotificationService,
|
||||
handleNotificationServiceDelete,
|
||||
};
|
|
@ -1,12 +1,34 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const handleNotificationCreate = (notification, users, cards, activities) => ({
|
||||
const deleteAllNotifications = () => ({
|
||||
type: ActionTypes.ALL_NOTIFICATIONS_DELETE,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
deleteAllNotifications.success = (notifications) => ({
|
||||
type: ActionTypes.ALL_NOTIFICATIONS_DELETE__SUCCESS,
|
||||
payload: {
|
||||
notifications,
|
||||
},
|
||||
});
|
||||
|
||||
deleteAllNotifications.failure = (error) => ({
|
||||
type: ActionTypes.ALL_NOTIFICATIONS_DELETE__FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleNotificationCreate = (notification, users) => ({
|
||||
type: ActionTypes.NOTIFICATION_CREATE_HANDLE,
|
||||
payload: {
|
||||
notification,
|
||||
users,
|
||||
cards,
|
||||
activities,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -40,6 +62,7 @@ const handleNotificationDelete = (notification) => ({
|
|||
});
|
||||
|
||||
export default {
|
||||
deleteAllNotifications,
|
||||
handleNotificationCreate,
|
||||
deleteNotification,
|
||||
handleNotificationDelete,
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createProjectManager = (projectManager) => ({
|
||||
|
@ -25,10 +30,15 @@ createProjectManager.failure = (localId, error) => ({
|
|||
|
||||
const handleProjectManagerCreate = (
|
||||
projectManager,
|
||||
boardIds,
|
||||
isCurrentUser,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -36,17 +46,27 @@ const handleProjectManagerCreate = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_CREATE_HANDLE,
|
||||
payload: {
|
||||
projectManager,
|
||||
boardIds,
|
||||
isCurrentUser,
|
||||
isProjectAvailable,
|
||||
project,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -54,27 +74,21 @@ const handleProjectManagerCreate = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
handleProjectManagerCreate.fetchProject = (id, currentUserId, currentBoardId) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH,
|
||||
payload: {
|
||||
id,
|
||||
currentUserId,
|
||||
currentBoardId,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteProjectManager = (id, isCurrentUser, isCurrentProject) => ({
|
||||
const deleteProjectManager = (id) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
isCurrentUser,
|
||||
isCurrentProject,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -93,12 +107,10 @@ deleteProjectManager.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleProjectManagerDelete = (projectManager, isCurrentUser, isCurrentProject) => ({
|
||||
const handleProjectManagerDelete = (projectManager) => ({
|
||||
type: ActionTypes.PROJECT_MANAGER_DELETE_HANDLE,
|
||||
payload: {
|
||||
projectManager,
|
||||
isCurrentUser,
|
||||
isCurrentProject,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,31 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const searchProjects = (value) => ({
|
||||
type: ActionTypes.PROJECTS_SEARCH,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const updateProjectsOrder = (value) => ({
|
||||
type: ActionTypes.PROJECTS_ORDER_UPDATE,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
const toggleHiddenProjects = (isVisible) => ({
|
||||
type: ActionTypes.HIDDEN_PROJECTS_TOGGLE,
|
||||
payload: {
|
||||
isVisible,
|
||||
},
|
||||
});
|
||||
|
||||
const createProject = (data) => ({
|
||||
type: ActionTypes.PROJECT_CREATE,
|
||||
payload: {
|
||||
|
@ -22,14 +48,28 @@ createProject.failure = (error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleProjectCreate = (project, users, projectManagers, boards, boardMemberships) => ({
|
||||
const handleProjectCreate = (
|
||||
project,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
customFields,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.PROJECT_CREATE_HANDLE,
|
||||
payload: {
|
||||
project,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
customFields,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -56,32 +96,56 @@ updateProject.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleProjectUpdate = (project) => ({
|
||||
const handleProjectUpdate = (
|
||||
project,
|
||||
boardIds,
|
||||
isAvailable,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.PROJECT_UPDATE_HANDLE,
|
||||
payload: {
|
||||
project,
|
||||
},
|
||||
});
|
||||
|
||||
const updateProjectBackgroundImage = (id) => ({
|
||||
type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
updateProjectBackgroundImage.success = (project) => ({
|
||||
type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
project,
|
||||
},
|
||||
});
|
||||
|
||||
updateProjectBackgroundImage.failure = (id, error) => ({
|
||||
type: ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
boardIds,
|
||||
isAvailable,
|
||||
board,
|
||||
users,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -115,11 +179,13 @@ const handleProjectDelete = (project) => ({
|
|||
});
|
||||
|
||||
export default {
|
||||
searchProjects,
|
||||
updateProjectsOrder,
|
||||
toggleHiddenProjects,
|
||||
createProject,
|
||||
handleProjectCreate,
|
||||
updateProject,
|
||||
handleProjectUpdate,
|
||||
updateProjectBackgroundImage,
|
||||
deleteProject,
|
||||
handleProjectDelete,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const handleLocationChange = (
|
||||
pathname,
|
||||
currentBoardId,
|
||||
currentCardId,
|
||||
isEditModeEnabled,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
|
@ -10,12 +19,20 @@ const handleLocationChange = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
) => ({
|
||||
type: ActionTypes.LOCATION_CHANGE_HANDLE,
|
||||
payload: {
|
||||
pathname,
|
||||
currentBoardId,
|
||||
currentCardId,
|
||||
isEditModeEnabled,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
|
@ -25,12 +42,21 @@ const handleLocationChange = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
deletedNotifications,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
},
|
||||
});
|
||||
|
||||
handleLocationChange.fetchContent = () => ({
|
||||
type: ActionTypes.LOCATION_CHANGE_HANDLE__CONTENT_FETCH,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
handleLocationChange.fetchBoard = (id) => ({
|
||||
type: ActionTypes.LOCATION_CHANGE_HANDLE__BOARD_FETCH,
|
||||
payload: {
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const handleSocketDisconnect = () => ({
|
||||
|
@ -6,11 +11,14 @@ const handleSocketDisconnect = () => ({
|
|||
});
|
||||
|
||||
const handleSocketReconnect = (
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -18,18 +26,25 @@ const handleSocketReconnect = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.SOCKET_RECONNECT_HANDLE,
|
||||
payload: {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
|
@ -37,10 +52,14 @@ const handleSocketReconnect = (
|
|||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
activities,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notifications,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
104
client/src/actions/task-lists.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createTaskList = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
createTaskList.success = (localId, taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE__SUCCESS,
|
||||
payload: {
|
||||
localId,
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
createTaskList.failure = (localId, error) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE__FAILURE,
|
||||
payload: {
|
||||
localId,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTaskListCreate = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_CREATE_HANDLE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
const updateTaskList = (id, data) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE,
|
||||
payload: {
|
||||
id,
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateTaskList.success = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
updateTaskList.failure = (id, error) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTaskListUpdate = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_UPDATE_HANDLE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteTaskList = (id) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
deleteTaskList.success = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE__SUCCESS,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
deleteTaskList.failure = (id, error) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE__FAILURE,
|
||||
payload: {
|
||||
id,
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTaskListDelete = (taskList) => ({
|
||||
type: ActionTypes.TASK_LIST_DELETE_HANDLE,
|
||||
payload: {
|
||||
taskList,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
createTaskList,
|
||||
handleTaskListCreate,
|
||||
updateTaskList,
|
||||
handleTaskListUpdate,
|
||||
deleteTaskList,
|
||||
handleTaskListDelete,
|
||||
};
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createTask = (task) => ({
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const createUser = (data) => ({
|
||||
|
@ -56,12 +61,60 @@ updateUser.failure = (id, error) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const handleUserUpdate = (user, users, isCurrent) => ({
|
||||
const handleUserUpdate = (
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
config,
|
||||
board,
|
||||
users,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
) => ({
|
||||
type: ActionTypes.USER_UPDATE_HANDLE,
|
||||
payload: {
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
config,
|
||||
board,
|
||||
users,
|
||||
isCurrent,
|
||||
projects,
|
||||
projectManagers,
|
||||
backgroundImages,
|
||||
baseCustomFieldGroups,
|
||||
boards,
|
||||
boardMemberships,
|
||||
labels,
|
||||
lists,
|
||||
cards,
|
||||
cardMemberships,
|
||||
cardLabels,
|
||||
taskLists,
|
||||
tasks,
|
||||
attachments,
|
||||
customFieldGroups,
|
||||
customFields,
|
||||
customFieldValues,
|
||||
notificationsToDelete,
|
||||
notificationServices,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -270,19 +323,22 @@ const handleUserFromCardRemove = (cardMembership) => ({
|
|||
},
|
||||
});
|
||||
|
||||
const addUserToBoardFilter = (id, boardId) => ({
|
||||
const addUserToBoardFilter = (id, boardId, replace, currentListId) => ({
|
||||
type: ActionTypes.USER_TO_BOARD_FILTER_ADD,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
replace,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
const removeUserFromBoardFilter = (id, boardId) => ({
|
||||
const removeUserFromBoardFilter = (id, boardId, currentListId) => ({
|
||||
type: ActionTypes.USER_FROM_BOARD_FILTER_REMOVE,
|
||||
payload: {
|
||||
id,
|
||||
boardId,
|
||||
currentListId,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
|
@ -5,13 +10,13 @@ import http from './http';
|
|||
const createAccessToken = (data, headers) =>
|
||||
http.post('/access-tokens?withHttpOnlyToken=true', data, headers);
|
||||
|
||||
const exchangeForAccessTokenUsingOidc = (data, headers) =>
|
||||
http.post('/access-tokens/exchange-using-oidc?withHttpOnlyToken=true', data, headers);
|
||||
const exchangeForAccessTokenWithOidc = (data, headers) =>
|
||||
http.post('/access-tokens/exchange-with-oidc?withHttpOnlyToken=true', data, headers);
|
||||
|
||||
const deleteCurrentAccessToken = (headers) => http.delete('/access-tokens/me', undefined, headers);
|
||||
|
||||
export default {
|
||||
createAccessToken,
|
||||
exchangeForAccessTokenUsingOidc,
|
||||
exchangeForAccessTokenWithOidc,
|
||||
deleteCurrentAccessToken,
|
||||
};
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
import { transformUser } from './users';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformActivity = (activity) => ({
|
||||
...activity,
|
||||
createdAt: new Date(activity.createdAt),
|
||||
...(activity.createdAt && {
|
||||
createdAt: new Date(activity.createdAt),
|
||||
}),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
@ -14,10 +20,6 @@ const getActivities = (cardId, data, headers) =>
|
|||
socket.get(`/cards/${cardId}/actions`, data, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformActivity),
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
},
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
@ -29,13 +31,7 @@ const makeHandleActivityCreate = (next) => (body) => {
|
|||
});
|
||||
};
|
||||
|
||||
const makeHandleActivityUpdate = makeHandleActivityCreate;
|
||||
|
||||
const makeHandleActivityDelete = makeHandleActivityCreate;
|
||||
|
||||
export default {
|
||||
getActivities,
|
||||
makeHandleActivityCreate,
|
||||
makeHandleActivityUpdate,
|
||||
makeHandleActivityDelete,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
|
@ -5,17 +10,34 @@ import socket from './socket';
|
|||
|
||||
export const transformAttachment = (attachment) => ({
|
||||
...attachment,
|
||||
createdAt: new Date(attachment.createdAt),
|
||||
...(attachment.createdAt && {
|
||||
createdAt: new Date(attachment.createdAt),
|
||||
}),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createAttachment = (cardId, data, requestId, headers) =>
|
||||
http.post(`/cards/${cardId}/attachments?requestId=${requestId}`, data, headers).then((body) => ({
|
||||
const createAttachment = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/attachments`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformAttachment(body.item),
|
||||
}));
|
||||
|
||||
const createAttachmentWithFile = (cardId, { file, ...data }, requestId, headers) =>
|
||||
http
|
||||
.post(
|
||||
`/cards/${cardId}/attachments?requestId=${requestId}`,
|
||||
{
|
||||
...data,
|
||||
file,
|
||||
},
|
||||
headers,
|
||||
)
|
||||
.then((body) => ({
|
||||
...body,
|
||||
item: transformAttachment(body.item),
|
||||
}));
|
||||
|
||||
const updateAttachment = (id, data, headers) =>
|
||||
socket.patch(`/attachments/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
|
@ -43,6 +65,7 @@ const makeHandleAttachmentDelete = makeHandleAttachmentCreate;
|
|||
|
||||
export default {
|
||||
createAttachment,
|
||||
createAttachmentWithFile,
|
||||
updateAttachment,
|
||||
deleteAttachment,
|
||||
makeHandleAttachmentCreate,
|
||||
|
|
27
client/src/api/background-images.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBackgroundImage = (projectId, { file, ...data }, requestId, headers) =>
|
||||
http.post(
|
||||
`/projects/${projectId}/background-images?requestId=${requestId}`,
|
||||
{
|
||||
...data,
|
||||
file,
|
||||
},
|
||||
headers,
|
||||
);
|
||||
|
||||
const deleteBackgroundImage = (id, headers) =>
|
||||
socket.delete(`/background-images/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createBackgroundImage,
|
||||
deleteBackgroundImage,
|
||||
};
|
23
client/src/api/base-custom-field-groups.js
Executable file
|
@ -0,0 +1,23 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBaseCustomFieldGroup = (projectId, data, headers) =>
|
||||
socket.post(`/projects/${projectId}/base-custom-field-groups`, data, headers);
|
||||
|
||||
const updateBaseCustomFieldGroup = (id, data, headers) =>
|
||||
socket.patch(`/base-custom-field-groups/${id}`, data, headers);
|
||||
|
||||
const deleteBaseCustomFieldGroup = (id, headers) =>
|
||||
socket.delete(`/base-custom-field-groups/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createBaseCustomFieldGroup,
|
||||
updateBaseCustomFieldGroup,
|
||||
deleteBaseCustomFieldGroup,
|
||||
};
|
|
@ -1,50 +1,23 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformBoardMembership = (boardMembership) => ({
|
||||
...boardMembership,
|
||||
createdAt: new Date(boardMembership.createdAt),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBoardMembership = (boardId, data, headers) =>
|
||||
socket.post(`/boards/${boardId}/memberships`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
}));
|
||||
socket.post(`/boards/${boardId}/board-memberships`, data, headers);
|
||||
|
||||
const updateBoardMembership = (id, data, headers) =>
|
||||
socket.patch(`/board-memberships/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
}));
|
||||
socket.patch(`/board-memberships/${id}`, data, headers);
|
||||
|
||||
const deleteBoardMembership = (id, headers) =>
|
||||
socket.delete(`/board-memberships/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleBoardMembershipCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformBoardMembership(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleBoardMembershipUpdate = makeHandleBoardMembershipCreate;
|
||||
|
||||
const makeHandleBoardMembershipDelete = makeHandleBoardMembershipCreate;
|
||||
socket.delete(`/board-memberships/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createBoardMembership,
|
||||
updateBoardMembership,
|
||||
deleteBoardMembership,
|
||||
makeHandleBoardMembershipCreate,
|
||||
makeHandleBoardMembershipUpdate,
|
||||
makeHandleBoardMembershipDelete,
|
||||
};
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
import socket from './socket';
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import { transformUser } from './users';
|
||||
import { transformBoardMembership } from './board-memberships';
|
||||
import socket from './socket';
|
||||
import { transformCard } from './cards';
|
||||
import { transformAttachment } from './attachments';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createBoard = (projectId, data, headers) =>
|
||||
socket.post(`/projects/${projectId}/boards`, data, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
},
|
||||
}));
|
||||
socket.post(`/projects/${projectId}/boards`, data, headers);
|
||||
|
||||
const createBoardWithImport = (projectId, data, requestId, headers) =>
|
||||
http.post(`/projects/${projectId}/boards?requestId=${requestId}`, data, headers);
|
||||
|
@ -26,8 +23,6 @@ const getBoard = (id, subscribe, headers) =>
|
|||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCardLabel = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/labels`, data, headers);
|
||||
socket.post(`/cards/${cardId}/card-labels`, data, headers);
|
||||
|
||||
const deleteCardLabel = (cardId, labelId, headers) =>
|
||||
socket.delete(`/cards/${cardId}/labels/${labelId}`, undefined, headers);
|
||||
socket.delete(`/cards/${cardId}/card-labels/labelId:${labelId}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCardLabel,
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCardMembership = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/memberships`, data, headers);
|
||||
socket.post(`/cards/${cardId}/card-memberships`, data, headers);
|
||||
|
||||
const deleteCardMembership = (cardId, userId, headers) =>
|
||||
socket.delete(`/cards/${cardId}/memberships?userId=${userId}`, undefined, headers);
|
||||
socket.delete(`/cards/${cardId}/card-memberships/userId:${userId}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCardMembership,
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import socket from './socket';
|
||||
import { transformAttachment } from './attachments';
|
||||
import { transformActivity } from './activities';
|
||||
import { transformNotification } from './notifications';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
|
@ -16,6 +25,12 @@ export const transformCard = (card) => ({
|
|||
}),
|
||||
},
|
||||
}),
|
||||
...(card.createdAt && {
|
||||
createdAt: new Date(card.createdAt),
|
||||
}),
|
||||
...(card.listChangedAt && {
|
||||
listChangedAt: new Date(card.listChangedAt),
|
||||
}),
|
||||
});
|
||||
|
||||
export const transformCardData = (data) => ({
|
||||
|
@ -35,6 +50,16 @@ export const transformCardData = (data) => ({
|
|||
|
||||
/* Actions */
|
||||
|
||||
const getCards = (listId, data, headers) =>
|
||||
socket.get(`/lists/${listId}/cards`, data, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformCard),
|
||||
included: {
|
||||
...body.included,
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
}));
|
||||
|
||||
const createCard = (listId, data, headers) =>
|
||||
socket.post(`/lists/${listId}/cards`, transformCardData(data), headers).then((body) => ({
|
||||
...body,
|
||||
|
@ -61,6 +86,20 @@ const duplicateCard = (id, data, headers) =>
|
|||
socket.post(`/cards/${id}/duplicate`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformCard(body.item),
|
||||
included: {
|
||||
...body.included,
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
}));
|
||||
|
||||
const readCardNotifications = (id, headers) =>
|
||||
socket.post(`/cards/${id}/read-notifications`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformCard(body.item),
|
||||
included: {
|
||||
...body.included,
|
||||
notifications: body.included.notifications.map(transformNotification),
|
||||
},
|
||||
}));
|
||||
|
||||
const deleteCard = (id, headers) =>
|
||||
|
@ -71,6 +110,17 @@ const deleteCard = (id, headers) =>
|
|||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleCardsUpdate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
items: body.items.map(transformCard),
|
||||
included: body.included && {
|
||||
...omit(body.included, 'actions'),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleCardCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
|
@ -80,14 +130,17 @@ const makeHandleCardCreate = (next) => (body) => {
|
|||
|
||||
const makeHandleCardUpdate = makeHandleCardCreate;
|
||||
|
||||
const makeHandleCardDelete = makeHandleCardCreate;
|
||||
const makeHandleCardDelete = makeHandleCardUpdate;
|
||||
|
||||
export default {
|
||||
getCards,
|
||||
createCard,
|
||||
getCard,
|
||||
updateCard,
|
||||
deleteCard,
|
||||
duplicateCard,
|
||||
readCardNotifications,
|
||||
deleteCard,
|
||||
makeHandleCardsUpdate,
|
||||
makeHandleCardCreate,
|
||||
makeHandleCardUpdate,
|
||||
makeHandleCardDelete,
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import socket from './socket';
|
||||
import { transformActivity } from './activities';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCommentActivity = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/comment-actions`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformActivity(body.item),
|
||||
}));
|
||||
|
||||
const updateCommentActivity = (id, data, headers) =>
|
||||
socket.patch(`/comment-actions/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformActivity(body.item),
|
||||
}));
|
||||
|
||||
const deleteCommentActivity = (id, headers) =>
|
||||
socket.delete(`/comment-actions/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformActivity(body.item),
|
||||
}));
|
||||
|
||||
export default {
|
||||
createCommentActivity,
|
||||
updateCommentActivity,
|
||||
deleteCommentActivity,
|
||||
};
|
64
client/src/api/comments.js
Normal file
|
@ -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 socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformComment = (comment) => ({
|
||||
...comment,
|
||||
...(comment.createdAt && {
|
||||
createdAt: new Date(comment.createdAt),
|
||||
}),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getComments = (cardId, data, headers) =>
|
||||
socket.get(`/cards/${cardId}/comments`, data, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformComment),
|
||||
}));
|
||||
|
||||
const createComment = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/comments`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
}));
|
||||
|
||||
const updateComment = (id, data, headers) =>
|
||||
socket.patch(`/comments/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
}));
|
||||
|
||||
const deleteComment = (id, headers) =>
|
||||
socket.delete(`/comments/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleCommentCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformComment(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleCommentUpdate = makeHandleCommentCreate;
|
||||
|
||||
const makeHandleCommentDelete = makeHandleCommentUpdate;
|
||||
|
||||
export default {
|
||||
getComments,
|
||||
createComment,
|
||||
updateComment,
|
||||
deleteComment,
|
||||
makeHandleCommentCreate,
|
||||
makeHandleCommentUpdate,
|
||||
makeHandleCommentDelete,
|
||||
};
|
14
client/src/api/config.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getConfig = (headers) => http.get('/config', undefined, headers);
|
||||
|
||||
export default {
|
||||
getConfig,
|
||||
};
|
31
client/src/api/custom-field-groups.js
Executable file
|
@ -0,0 +1,31 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCustomFieldGroupInBoard = (cardId, data, headers) =>
|
||||
socket.post(`/boards/${cardId}/custom-field-groups`, data, headers);
|
||||
|
||||
const createCustomFieldGroupInCard = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/custom-field-groups`, data, headers);
|
||||
|
||||
const getCustomFieldGroup = (id, headers) =>
|
||||
socket.get(`/custom-field-groups/${id}`, undefined, headers);
|
||||
|
||||
const updateCustomFieldGroup = (id, data, headers) =>
|
||||
socket.patch(`/custom-field-groups/${id}`, data, headers);
|
||||
|
||||
const deleteCustomFieldGroup = (id, headers) =>
|
||||
socket.delete(`/custom-field-groups/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCustomFieldGroupInBoard,
|
||||
createCustomFieldGroupInCard,
|
||||
getCustomFieldGroup,
|
||||
updateCustomFieldGroup,
|
||||
deleteCustomFieldGroup,
|
||||
};
|
27
client/src/api/custom-field-values.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const updateCustomFieldValue = (cardId, customFieldGroupId, customFieldId, data, headers) =>
|
||||
socket.patch(
|
||||
`/cards/${cardId}/custom-field-values/customFieldGroupId:${customFieldGroupId}:customFieldId:${customFieldId}`,
|
||||
data,
|
||||
headers,
|
||||
);
|
||||
|
||||
const deleteCustomFieldValue = (cardId, customFieldGroupId, customFieldId, headers) =>
|
||||
socket.delete(
|
||||
`/cards/${cardId}/custom-field-values/customFieldGroupId:${customFieldGroupId}:customFieldId:${customFieldId}`,
|
||||
undefined,
|
||||
headers,
|
||||
);
|
||||
|
||||
export default {
|
||||
updateCustomFieldValue,
|
||||
deleteCustomFieldValue,
|
||||
};
|
27
client/src/api/custom-fields.js
Executable file
|
@ -0,0 +1,27 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createCustomFieldInBaseGroup = (baseCustomFieldGroupId, data, headers) =>
|
||||
socket.post(`/base-custom-field-groups/${baseCustomFieldGroupId}/custom-fields`, data, headers);
|
||||
|
||||
const createCustomFieldInGroup = (customFieldGroupId, data, headers) =>
|
||||
socket.post(`/custom-field-groups/${customFieldGroupId}/custom-fields`, data, headers);
|
||||
|
||||
const updateCustomField = (id, data, headers) =>
|
||||
socket.patch(`/custom-fields/${id}`, data, headers);
|
||||
|
||||
const deleteCustomField = (id, headers) =>
|
||||
socket.delete(`/custom-fields/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createCustomFieldInBaseGroup,
|
||||
createCustomFieldInGroup,
|
||||
updateCustomField,
|
||||
deleteCustomField,
|
||||
};
|
|
@ -1,4 +1,7 @@
|
|||
import { fetch } from 'whatwg-fetch';
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import Config from '../constants/Config';
|
||||
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
import root from './root';
|
||||
import config from './config';
|
||||
import accessTokens from './access-tokens';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
import projectManagers from './project-managers';
|
||||
import backgroundImages from './background-images';
|
||||
import baseCustomFieldGroups from './base-custom-field-groups';
|
||||
import boards from './boards';
|
||||
import boardMemberships from './board-memberships';
|
||||
import labels from './labels';
|
||||
|
@ -12,20 +19,27 @@ import lists from './lists';
|
|||
import cards from './cards';
|
||||
import cardMemberships from './card-memberships';
|
||||
import cardLabels from './card-labels';
|
||||
import taskLists from './task-lists';
|
||||
import tasks from './tasks';
|
||||
import attachments from './attachments';
|
||||
import customFieldGroups from './custom-field-groups';
|
||||
import customFields from './custom-fields';
|
||||
import customFieldValues from './custom-field-values';
|
||||
import comments from './comments';
|
||||
import activities from './activities';
|
||||
import commentActivities from './comment-activities';
|
||||
import notifications from './notifications';
|
||||
import notificationServices from './notification-services';
|
||||
|
||||
export { http, socket };
|
||||
|
||||
export default {
|
||||
...root,
|
||||
...config,
|
||||
...accessTokens,
|
||||
...users,
|
||||
...projects,
|
||||
...projectManagers,
|
||||
...backgroundImages,
|
||||
...baseCustomFieldGroups,
|
||||
...boards,
|
||||
...boardMemberships,
|
||||
...labels,
|
||||
|
@ -33,9 +47,14 @@ export default {
|
|||
...cards,
|
||||
...cardMemberships,
|
||||
...cardLabels,
|
||||
...taskLists,
|
||||
...tasks,
|
||||
...attachments,
|
||||
...customFieldGroups,
|
||||
...customFields,
|
||||
...customFieldValues,
|
||||
...comments,
|
||||
...activities,
|
||||
...commentActivities,
|
||||
...notifications,
|
||||
...notificationServices,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
|
|
@ -1,11 +1,30 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import socket from './socket';
|
||||
import { transformCard } from './cards';
|
||||
import { transformAttachment } from './attachments';
|
||||
import { transformActivity } from './activities';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createList = (boardId, data, headers) =>
|
||||
socket.post(`/boards/${boardId}/lists`, data, headers);
|
||||
|
||||
const getList = (id, headers) =>
|
||||
socket.get(`/lists/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
cards: body.included.cards.map(transformCard),
|
||||
attachments: body.included.attachments.map(transformAttachment),
|
||||
},
|
||||
}));
|
||||
|
||||
const updateList = (id, data, headers) => socket.patch(`/lists/${id}`, data, headers);
|
||||
|
||||
const sortList = (id, data, headers) =>
|
||||
|
@ -17,11 +36,30 @@ const sortList = (id, data, headers) =>
|
|||
},
|
||||
}));
|
||||
|
||||
const deleteList = (id, headers) => socket.delete(`/lists/${id}`, undefined, headers);
|
||||
const moveListCards = (id, data, headers) =>
|
||||
socket.post(`/lists/${id}/move-cards`, data, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...omit(body.included, 'actions'),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
}));
|
||||
|
||||
const clearList = (id, headers) => socket.post(`/lists/${id}/clear`, undefined, headers);
|
||||
|
||||
const deleteList = (id, headers) =>
|
||||
socket.delete(`/lists/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
cards: body.included.cards.map(transformCard),
|
||||
},
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleListSort = (next) => (body) => {
|
||||
const makeHandleListDelete = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
included: {
|
||||
|
@ -33,8 +71,11 @@ const makeHandleListSort = (next) => (body) => {
|
|||
|
||||
export default {
|
||||
createList,
|
||||
getList,
|
||||
updateList,
|
||||
sortList,
|
||||
moveListCards,
|
||||
clearList,
|
||||
deleteList,
|
||||
makeHandleListSort,
|
||||
makeHandleListDelete,
|
||||
};
|
||||
|
|
31
client/src/api/notification-services.js
Executable file
|
@ -0,0 +1,31 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createNotificationServiceInUser = (userId, data, headers) =>
|
||||
socket.post(`/users/${userId}/notification-services`, data, headers);
|
||||
|
||||
const createNotificationServiceInBoard = (boardId, data, headers) =>
|
||||
socket.post(`/boards/${boardId}/notification-services`, data, headers);
|
||||
|
||||
const updateNotificationService = (id, data, headers) =>
|
||||
socket.patch(`/notification-services/${id}`, data, headers);
|
||||
|
||||
const testNotificationService = (id, headers) =>
|
||||
socket.post(`/notification-services/${id}/test`, undefined, headers);
|
||||
|
||||
const deleteNotificationService = (id, headers) =>
|
||||
socket.delete(`/notification-services/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createNotificationServiceInUser,
|
||||
createNotificationServiceInBoard,
|
||||
updateNotificationService,
|
||||
testNotificationService,
|
||||
deleteNotificationService,
|
||||
};
|
|
@ -1,15 +1,21 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import socket from './socket';
|
||||
import { transformUser } from './users';
|
||||
import { transformCard } from './cards';
|
||||
import { transformActivity } from './activities';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformNotification = (notification) => ({
|
||||
...omit(notification, 'actionId'),
|
||||
activityId: notification.actionId,
|
||||
...(notification.actionId
|
||||
? {
|
||||
...omit(notification, 'actionId'),
|
||||
activityId: notification.actionId,
|
||||
}
|
||||
: notification),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
@ -18,28 +24,25 @@ const getNotifications = (headers) =>
|
|||
socket.get('/notifications', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformNotification),
|
||||
included: {
|
||||
...omit(body.included, 'actions'),
|
||||
users: body.included.users.map(transformUser),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
}));
|
||||
|
||||
const getNotification = (id, headers) =>
|
||||
/* const getNotification = (id, headers) =>
|
||||
socket.get(`/notifications/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformNotification(body.item),
|
||||
included: {
|
||||
...omit(body.included, 'actions'),
|
||||
users: body.included.users.map(transformUser),
|
||||
cards: body.included.cards.map(transformCard),
|
||||
activities: body.included.actions.map(transformActivity),
|
||||
},
|
||||
})); */
|
||||
|
||||
const updateNotification = (id, data, headers) =>
|
||||
socket.patch(`/notifications/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformNotification(body.item),
|
||||
}));
|
||||
|
||||
const updateNotifications = (ids, data, headers) =>
|
||||
socket.patch(`/notifications/${ids.join(',')}`, data, headers).then((body) => ({
|
||||
const readAllNotifications = (headers) =>
|
||||
socket.post('/notifications/read-all', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformNotification),
|
||||
}));
|
||||
|
@ -57,8 +60,9 @@ const makeHandleNotificationUpdate = makeHandleNotificationCreate;
|
|||
|
||||
export default {
|
||||
getNotifications,
|
||||
getNotification,
|
||||
updateNotifications,
|
||||
// getNotification,
|
||||
updateNotification,
|
||||
readAllNotifications,
|
||||
makeHandleNotificationCreate,
|
||||
makeHandleNotificationUpdate,
|
||||
};
|
||||
|
|
|
@ -1,40 +1,19 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformProjectManager = (projectManager) => ({
|
||||
...projectManager,
|
||||
createdAt: new Date(projectManager.createdAt),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createProjectManager = (projectId, data, headers) =>
|
||||
socket.post(`/projects/${projectId}/managers`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformProjectManager(body.item),
|
||||
}));
|
||||
socket.post(`/projects/${projectId}/project-managers`, data, headers);
|
||||
|
||||
const deleteProjectManager = (id, headers) =>
|
||||
socket.delete(`/project-managers/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformProjectManager(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleProjectManagerCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformProjectManager(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleProjectManagerDelete = makeHandleProjectManagerCreate;
|
||||
socket.delete(`/project-managers/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createProjectManager,
|
||||
deleteProjectManager,
|
||||
makeHandleProjectManagerCreate,
|
||||
makeHandleProjectManagerDelete,
|
||||
};
|
||||
|
|
|
@ -1,47 +1,20 @@
|
|||
import http from './http';
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
import { transformUser } from './users';
|
||||
import { transformProjectManager } from './project-managers';
|
||||
import { transformBoardMembership } from './board-memberships';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getProjects = (headers) =>
|
||||
socket.get('/projects', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
projectManagers: body.included.projectManagers.map(transformProjectManager),
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
},
|
||||
}));
|
||||
const getProjects = (headers) => socket.get('/projects', undefined, headers);
|
||||
|
||||
const createProject = (data, headers) =>
|
||||
socket.post('/projects', data, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
projectManagers: body.included.projectManagers.map(transformProjectManager),
|
||||
},
|
||||
}));
|
||||
const createProject = (data, headers) => socket.post('/projects', data, headers);
|
||||
|
||||
const getProject = (id, headers) =>
|
||||
socket.get(`/projects/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
included: {
|
||||
...body.included,
|
||||
users: body.included.users.map(transformUser),
|
||||
projectManagers: body.included.projectManagers.map(transformProjectManager),
|
||||
boardMemberships: body.included.boardMemberships.map(transformBoardMembership),
|
||||
},
|
||||
}));
|
||||
const getProject = (id, headers) => socket.get(`/projects/${id}`, undefined, headers);
|
||||
|
||||
const updateProject = (id, data, headers) => socket.patch(`/projects/${id}`, data, headers);
|
||||
|
||||
const updateProjectBackgroundImage = (id, data, headers) =>
|
||||
http.post(`/projects/${id}/background-image`, data, headers);
|
||||
|
||||
const deleteProject = (id, headers) => socket.delete(`/projects/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
|
@ -49,6 +22,5 @@ export default {
|
|||
createProject,
|
||||
getProject,
|
||||
updateProject,
|
||||
updateProjectBackgroundImage,
|
||||
deleteProject,
|
||||
};
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getConfig = (headers) => http.get('/config', undefined, headers);
|
||||
|
||||
export default {
|
||||
getConfig,
|
||||
};
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socketIOClient from 'socket.io-client';
|
||||
import sailsIOClient from 'sails.io.js';
|
||||
|
||||
|
@ -5,15 +10,14 @@ import Config from '../constants/Config';
|
|||
|
||||
const io = sailsIOClient(socketIOClient);
|
||||
|
||||
io.sails.url = Config.SERVER_HOST_NAME;
|
||||
io.sails.url = Config.SERVER_BASE_URL;
|
||||
io.sails.autoConnect = false;
|
||||
io.sails.reconnection = true;
|
||||
io.sails.useCORSRouteToGetCookie = false;
|
||||
io.sails.environment = process.env.NODE_ENV;
|
||||
io.sails.environment = import.meta.env.MODE;
|
||||
|
||||
const { socket } = io;
|
||||
|
||||
socket.path = `${Config.SERVER_BASE_PATH}/socket.io`;
|
||||
socket.connect = socket._connect; // eslint-disable-line no-underscore-dangle
|
||||
|
||||
['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].forEach((method) => {
|
||||
|
|
24
client/src/api/task-lists.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createTaskList = (cardId, data, headers) =>
|
||||
socket.post(`/cards/${cardId}/task-lists`, data, headers);
|
||||
|
||||
const getTaskList = (id, headers) => socket.get(`/task-lists/${id}`, undefined, headers);
|
||||
|
||||
const updateTaskList = (id, data, headers) => socket.patch(`/task-lists/${id}`, data, headers);
|
||||
|
||||
const deleteTaskList = (id, headers) => socket.delete(`/task-lists/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
createTaskList,
|
||||
getTaskList,
|
||||
updateTaskList,
|
||||
deleteTaskList,
|
||||
};
|
|
@ -1,8 +1,14 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const createTask = (cardId, data, headers) => socket.post(`/cards/${cardId}/tasks`, data, headers);
|
||||
const createTask = (taskListId, data, headers) =>
|
||||
socket.post(`/task-lists/${taskListId}/tasks`, data, headers);
|
||||
|
||||
const updateTask = (id, data, headers) => socket.patch(`/tasks/${id}`, data, headers);
|
||||
|
||||
|
|
|
@ -1,92 +1,44 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
/* Transformers */
|
||||
|
||||
export const transformUser = (user) => ({
|
||||
...user,
|
||||
createdAt: new Date(user.createdAt),
|
||||
});
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getUsers = (headers) =>
|
||||
socket.get('/users', undefined, headers).then((body) => ({
|
||||
...body,
|
||||
items: body.items.map(transformUser),
|
||||
}));
|
||||
const getUsers = (headers) => socket.get('/users', undefined, headers);
|
||||
|
||||
const createUser = (data, headers) =>
|
||||
socket.post('/users', data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const createUser = (data, headers) => socket.post('/users', data, headers);
|
||||
|
||||
const getUser = (id, headers) =>
|
||||
/* const getUser = (id, headers) =>
|
||||
socket.get(`/users/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
})); */
|
||||
|
||||
const getCurrentUser = (subscribe, headers) =>
|
||||
socket.get(`/users/me${subscribe ? '?subscribe=true' : ''}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
socket.get(`/users/me${subscribe ? '?subscribe=true' : ''}`, undefined, headers);
|
||||
|
||||
const updateUser = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const updateUser = (id, data, headers) => socket.patch(`/users/${id}`, data, headers);
|
||||
|
||||
const updateUserEmail = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}/email`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const updateUserEmail = (id, data, headers) => socket.patch(`/users/${id}/email`, data, headers);
|
||||
|
||||
const updateUserPassword = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}/password`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
socket.patch(`/users/${id}/password`, data, headers);
|
||||
|
||||
const updateUserUsername = (id, data, headers) =>
|
||||
socket.patch(`/users/${id}/username`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
socket.patch(`/users/${id}/username`, data, headers);
|
||||
|
||||
const updateUserAvatar = (id, data, headers) =>
|
||||
http.post(`/users/${id}/avatar`, data, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
const updateUserAvatar = (id, data, headers) => http.post(`/users/${id}/avatar`, data, headers);
|
||||
|
||||
const deleteUser = (id, headers) =>
|
||||
socket.delete(`/users/${id}`, undefined, headers).then((body) => ({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
}));
|
||||
|
||||
/* Event handlers */
|
||||
|
||||
const makeHandleUserCreate = (next) => (body) => {
|
||||
next({
|
||||
...body,
|
||||
item: transformUser(body.item),
|
||||
});
|
||||
};
|
||||
|
||||
const makeHandleUserUpdate = makeHandleUserCreate;
|
||||
|
||||
const makeHandleUserDelete = makeHandleUserCreate;
|
||||
const deleteUser = (id, headers) => socket.delete(`/users/${id}`, undefined, headers);
|
||||
|
||||
export default {
|
||||
getUsers,
|
||||
createUser,
|
||||
getUser,
|
||||
// getUser,
|
||||
getCurrentUser,
|
||||
updateUser,
|
||||
updateUserEmail,
|
||||
|
@ -94,7 +46,4 @@ export default {
|
|||
updateUserUsername,
|
||||
updateUserAvatar,
|
||||
deleteUser,
|
||||
makeHandleUserCreate,
|
||||
makeHandleUserUpdate,
|
||||
makeHandleUserDelete,
|
||||
};
|
||||
|
|
2337
client/src/assets/css/font-awesome.css
vendored
4
client/src/assets/css/font-awesome.min.css
vendored
Before Width: | Height: | Size: 434 KiB |
Before Width: | Height: | Size: 559 KiB After Width: | Height: | Size: 157 KiB |
BIN
client/src/assets/images/deleted-user.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
@ -1,38 +0,0 @@
|
|||
import upperFirst from 'lodash/upperFirst';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ProjectBackgroundTypes } from '../../constants/Enums';
|
||||
|
||||
import styles from './Background.module.scss';
|
||||
import globalStyles from '../../styles.module.scss';
|
||||
|
||||
function Background({ type, name, imageUrl }) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
styles.wrapper,
|
||||
type === ProjectBackgroundTypes.GRADIENT &&
|
||||
globalStyles[`background${upperFirst(camelCase(name))}`],
|
||||
)}
|
||||
style={{
|
||||
background: type === 'image' && `url("${imageUrl}") center / cover`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Background.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
name: PropTypes.string,
|
||||
imageUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
Background.defaultProps = {
|
||||
name: undefined,
|
||||
imageUrl: undefined,
|
||||
};
|
||||
|
||||
export default Background;
|
|
@ -1,10 +0,0 @@
|
|||
:global(#app) {
|
||||
.wrapper {
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
max-width: 100vw;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import Background from './Background';
|
||||
|
||||
export default Background;
|
|
@ -1,201 +0,0 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
||||
import { closePopup } from '../../lib/popup';
|
||||
|
||||
import DroppableTypes from '../../constants/DroppableTypes';
|
||||
import ListContainer from '../../containers/ListContainer';
|
||||
import CardModalContainer from '../../containers/CardModalContainer';
|
||||
import ListAdd from './ListAdd';
|
||||
import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-icon.svg';
|
||||
|
||||
import styles from './Board.module.scss';
|
||||
import globalStyles from '../../styles.module.scss';
|
||||
|
||||
const parseDndId = (dndId) => dndId.split(':')[1];
|
||||
|
||||
const Board = React.memo(
|
||||
({ listIds, isCardModalOpened, canEdit, onListCreate, onListMove, onCardMove }) => {
|
||||
const [t] = useTranslation();
|
||||
const [isListAddOpened, setIsListAddOpened] = useState(false);
|
||||
|
||||
const wrapper = useRef(null);
|
||||
const prevPosition = useRef(null);
|
||||
|
||||
const handleAddListClick = useCallback(() => {
|
||||
setIsListAddOpened(true);
|
||||
}, []);
|
||||
|
||||
const handleAddListClose = useCallback(() => {
|
||||
setIsListAddOpened(false);
|
||||
}, []);
|
||||
|
||||
const handleDragStart = useCallback(() => {
|
||||
document.body.classList.add(globalStyles.dragging);
|
||||
closePopup();
|
||||
}, []);
|
||||
|
||||
const handleDragEnd = useCallback(
|
||||
({ draggableId, type, source, destination }) => {
|
||||
document.body.classList.remove(globalStyles.dragging);
|
||||
|
||||
if (
|
||||
!destination ||
|
||||
(source.droppableId === destination.droppableId && source.index === destination.index)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = parseDndId(draggableId);
|
||||
|
||||
switch (type) {
|
||||
case DroppableTypes.LIST:
|
||||
onListMove(id, destination.index);
|
||||
|
||||
break;
|
||||
case DroppableTypes.CARD:
|
||||
onCardMove(id, parseDndId(destination.droppableId), destination.index);
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
[onListMove, onCardMove],
|
||||
);
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(event) => {
|
||||
// If button is defined and not equal to 0 (left click)
|
||||
if (event.button) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target !== wrapper.current && !event.target.dataset.dragScroller) {
|
||||
return;
|
||||
}
|
||||
|
||||
prevPosition.current = event.clientX;
|
||||
|
||||
window.getSelection().removeAllRanges();
|
||||
document.body.classList.add(globalStyles.dragScrolling);
|
||||
},
|
||||
[wrapper],
|
||||
);
|
||||
|
||||
const handleWindowMouseMove = useCallback(
|
||||
(event) => {
|
||||
if (prevPosition.current === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
window.scrollBy({
|
||||
left: prevPosition.current - event.clientX,
|
||||
});
|
||||
|
||||
prevPosition.current = event.clientX;
|
||||
},
|
||||
[prevPosition],
|
||||
);
|
||||
|
||||
const handleWindowMouseRelease = useCallback(() => {
|
||||
if (prevPosition.current === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
prevPosition.current = null;
|
||||
document.body.classList.remove(globalStyles.dragScrolling);
|
||||
}, [prevPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.overflowX = 'auto';
|
||||
|
||||
return () => {
|
||||
document.body.style.overflowX = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isListAddOpened) {
|
||||
window.scroll(document.body.scrollWidth, 0);
|
||||
}
|
||||
}, [listIds, isListAddOpened]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('mousemove', handleWindowMouseMove);
|
||||
|
||||
window.addEventListener('mouseup', handleWindowMouseRelease);
|
||||
window.addEventListener('blur', handleWindowMouseRelease);
|
||||
window.addEventListener('contextmenu', handleWindowMouseRelease);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleWindowMouseMove);
|
||||
|
||||
window.removeEventListener('mouseup', handleWindowMouseRelease);
|
||||
window.removeEventListener('blur', handleWindowMouseRelease);
|
||||
window.removeEventListener('contextmenu', handleWindowMouseRelease);
|
||||
};
|
||||
}, [handleWindowMouseMove, handleWindowMouseRelease]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div ref={wrapper} className={styles.wrapper} onMouseDown={handleMouseDown}>
|
||||
<div>
|
||||
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="board" type={DroppableTypes.LIST} direction="horizontal">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<div
|
||||
{...droppableProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||
data-drag-scroller
|
||||
ref={innerRef}
|
||||
className={styles.lists}
|
||||
>
|
||||
{listIds.map((listId, index) => (
|
||||
<ListContainer key={listId} id={listId} index={index} />
|
||||
))}
|
||||
{placeholder}
|
||||
{canEdit && (
|
||||
<div data-drag-scroller className={styles.list}>
|
||||
{isListAddOpened ? (
|
||||
<ListAdd onCreate={onListCreate} onClose={handleAddListClose} />
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.addListButton}
|
||||
onClick={handleAddListClick}
|
||||
>
|
||||
<PlusMathIcon className={styles.addListButtonIcon} />
|
||||
<span className={styles.addListButtonText}>
|
||||
{listIds.length > 0
|
||||
? t('action.addAnotherList')
|
||||
: t('action.addList')}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</div>
|
||||
{isCardModalOpened && <CardModalContainer />}
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Board.propTypes = {
|
||||
listIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
isCardModalOpened: PropTypes.bool.isRequired,
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onListCreate: PropTypes.func.isRequired,
|
||||
onListMove: PropTypes.func.isRequired,
|
||||
onCardMove: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Board;
|
|
@ -1,89 +0,0 @@
|
|||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Input } from 'semantic-ui-react';
|
||||
import { useDidUpdate, useToggle } from '../../lib/hooks';
|
||||
|
||||
import { useClosableForm, useForm } from '../../hooks';
|
||||
|
||||
import styles from './ListAdd.module.scss';
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
name: '',
|
||||
};
|
||||
|
||||
const ListAdd = React.memo(({ onCreate, onClose }) => {
|
||||
const [t] = useTranslation();
|
||||
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
||||
const [focusNameFieldState, focusNameField] = useToggle();
|
||||
|
||||
const nameField = useRef(null);
|
||||
|
||||
const handleFieldKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
[onClose],
|
||||
);
|
||||
|
||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(onClose);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
name: data.name.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.name) {
|
||||
nameField.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
onCreate(cleanData);
|
||||
|
||||
setData(DEFAULT_DATA);
|
||||
focusNameField();
|
||||
}, [onCreate, data, setData, focusNameField]);
|
||||
|
||||
useEffect(() => {
|
||||
nameField.current.focus();
|
||||
}, []);
|
||||
|
||||
useDidUpdate(() => {
|
||||
nameField.current.focus();
|
||||
}, [focusNameFieldState]);
|
||||
|
||||
return (
|
||||
<Form className={styles.wrapper} onSubmit={handleSubmit}>
|
||||
<Input
|
||||
ref={nameField}
|
||||
name="name"
|
||||
value={data.name}
|
||||
placeholder={t('common.enterListTitle')}
|
||||
className={styles.field}
|
||||
onKeyDown={handleFieldKeyDown}
|
||||
onChange={handleFieldChange}
|
||||
onBlur={handleFieldBlur}
|
||||
/>
|
||||
<div className={styles.controls}>
|
||||
{/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
|
||||
<Button
|
||||
positive
|
||||
content={t('action.addList')}
|
||||
className={styles.button}
|
||||
onMouseOver={handleControlMouseOver}
|
||||
onMouseOut={handleControlMouseOut}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
ListAdd.propTypes = {
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ListAdd;
|
|
@ -1,33 +0,0 @@
|
|||
:global(#app) {
|
||||
.button {
|
||||
min-height: 30px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.field {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 #ccc;
|
||||
color: #333;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
border-color: #298fca;
|
||||
box-shadow: 0 0 2px #298fca;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #e2e4e6;
|
||||
border-radius: 3px;
|
||||
padding: 4px;
|
||||
transition: opacity 40ms ease-in;
|
||||
width: 272px;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import Board from './Board';
|
||||
|
||||
export default Board;
|
|
@ -1,97 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Filters from './Filters';
|
||||
import Memberships from '../Memberships';
|
||||
import BoardMembershipPermissionsSelectStep from '../BoardMembershipPermissionsSelectStep';
|
||||
|
||||
import styles from './BoardActions.module.scss';
|
||||
|
||||
const BoardActions = React.memo(
|
||||
({
|
||||
memberships,
|
||||
labels,
|
||||
filterUsers,
|
||||
filterLabels,
|
||||
filterText,
|
||||
allUsers,
|
||||
canEdit,
|
||||
canEditMemberships,
|
||||
onMembershipCreate,
|
||||
onMembershipUpdate,
|
||||
onMembershipDelete,
|
||||
onUserToFilterAdd,
|
||||
onUserFromFilterRemove,
|
||||
onLabelToFilterAdd,
|
||||
onLabelFromFilterRemove,
|
||||
onLabelCreate,
|
||||
onLabelUpdate,
|
||||
onLabelMove,
|
||||
onLabelDelete,
|
||||
onTextFilterUpdate,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.actions}>
|
||||
<div className={styles.action}>
|
||||
<Memberships
|
||||
items={memberships}
|
||||
allUsers={allUsers}
|
||||
permissionsSelectStep={BoardMembershipPermissionsSelectStep}
|
||||
canEdit={canEditMemberships}
|
||||
onCreate={onMembershipCreate}
|
||||
onUpdate={onMembershipUpdate}
|
||||
onDelete={onMembershipDelete}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.action}>
|
||||
<Filters
|
||||
users={filterUsers}
|
||||
labels={filterLabels}
|
||||
filterText={filterText}
|
||||
allBoardMemberships={memberships}
|
||||
allLabels={labels}
|
||||
canEdit={canEdit}
|
||||
onUserAdd={onUserToFilterAdd}
|
||||
onUserRemove={onUserFromFilterRemove}
|
||||
onLabelAdd={onLabelToFilterAdd}
|
||||
onLabelRemove={onLabelFromFilterRemove}
|
||||
onLabelCreate={onLabelCreate}
|
||||
onLabelUpdate={onLabelUpdate}
|
||||
onLabelMove={onLabelMove}
|
||||
onLabelDelete={onLabelDelete}
|
||||
onTextFilterUpdate={onTextFilterUpdate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BoardActions.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
memberships: PropTypes.array.isRequired,
|
||||
labels: PropTypes.array.isRequired,
|
||||
filterUsers: PropTypes.array.isRequired,
|
||||
filterLabels: PropTypes.array.isRequired,
|
||||
filterText: PropTypes.string.isRequired,
|
||||
allUsers: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
canEditMemberships: PropTypes.bool.isRequired,
|
||||
onMembershipCreate: PropTypes.func.isRequired,
|
||||
onMembershipUpdate: PropTypes.func.isRequired,
|
||||
onMembershipDelete: PropTypes.func.isRequired,
|
||||
onUserToFilterAdd: PropTypes.func.isRequired,
|
||||
onUserFromFilterRemove: PropTypes.func.isRequired,
|
||||
onLabelToFilterAdd: PropTypes.func.isRequired,
|
||||
onLabelFromFilterRemove: PropTypes.func.isRequired,
|
||||
onLabelCreate: PropTypes.func.isRequired,
|
||||
onLabelUpdate: PropTypes.func.isRequired,
|
||||
onLabelMove: PropTypes.func.isRequired,
|
||||
onLabelDelete: PropTypes.func.isRequired,
|
||||
onTextFilterUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default BoardActions;
|
|
@ -1,26 +0,0 @@
|
|||
:global(#app) {
|
||||
.action {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.actions {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: flex-start;
|
||||
margin: 20px 20px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from 'semantic-ui-react';
|
||||
import { usePopup } from '../../lib/popup';
|
||||
import { Input } from '../../lib/custom-ui';
|
||||
|
||||
import User from '../User';
|
||||
import Label from '../Label';
|
||||
import BoardMembershipsStep from '../BoardMembershipsStep';
|
||||
import LabelsStep from '../LabelsStep';
|
||||
|
||||
import styles from './Filters.module.scss';
|
||||
|
||||
const Filters = React.memo(
|
||||
({
|
||||
users,
|
||||
labels,
|
||||
filterText,
|
||||
allBoardMemberships,
|
||||
allLabels,
|
||||
canEdit,
|
||||
onUserAdd,
|
||||
onUserRemove,
|
||||
onLabelAdd,
|
||||
onLabelRemove,
|
||||
onLabelCreate,
|
||||
onLabelUpdate,
|
||||
onLabelMove,
|
||||
onLabelDelete,
|
||||
onTextFilterUpdate,
|
||||
}) => {
|
||||
const [t] = useTranslation();
|
||||
const [isSearchFocused, setIsSearchFocused] = useState(false);
|
||||
|
||||
const searchFieldRef = useRef(null);
|
||||
|
||||
const cancelSearch = useCallback(() => {
|
||||
onTextFilterUpdate('');
|
||||
searchFieldRef.current.blur();
|
||||
}, [onTextFilterUpdate]);
|
||||
|
||||
const handleRemoveUserClick = useCallback(
|
||||
(id) => {
|
||||
onUserRemove(id);
|
||||
},
|
||||
[onUserRemove],
|
||||
);
|
||||
|
||||
const handleRemoveLabelClick = useCallback(
|
||||
(id) => {
|
||||
onLabelRemove(id);
|
||||
},
|
||||
[onLabelRemove],
|
||||
);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
(_, { value }) => {
|
||||
onTextFilterUpdate(value);
|
||||
},
|
||||
[onTextFilterUpdate],
|
||||
);
|
||||
|
||||
const handleSearchFocus = useCallback(() => {
|
||||
setIsSearchFocused(true);
|
||||
}, []);
|
||||
|
||||
const handleSearchKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
cancelSearch();
|
||||
}
|
||||
},
|
||||
[cancelSearch],
|
||||
);
|
||||
|
||||
const handleSearchBlur = useCallback(() => {
|
||||
setIsSearchFocused(false);
|
||||
}, []);
|
||||
|
||||
const handleCancelSearchClick = useCallback(() => {
|
||||
cancelSearch();
|
||||
}, [cancelSearch]);
|
||||
|
||||
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
|
||||
const LabelsPopup = usePopup(LabelsStep);
|
||||
|
||||
const isSearchActive = filterText || isSearchFocused;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={styles.filter}>
|
||||
<BoardMembershipsPopup
|
||||
items={allBoardMemberships}
|
||||
currentUserIds={users.map((user) => user.id)}
|
||||
title="common.filterByMembers"
|
||||
onUserSelect={onUserAdd}
|
||||
onUserDeselect={onUserRemove}
|
||||
>
|
||||
<button type="button" className={styles.filterButton}>
|
||||
<span className={styles.filterTitle}>{`${t('common.members')}:`}</span>
|
||||
{users.length === 0 && <span className={styles.filterLabel}>{t('common.all')}</span>}
|
||||
</button>
|
||||
</BoardMembershipsPopup>
|
||||
{users.map((user) => (
|
||||
<span key={user.id} className={styles.filterItem}>
|
||||
<User
|
||||
name={user.name}
|
||||
avatarUrl={user.avatarUrl}
|
||||
size="tiny"
|
||||
onClick={() => handleRemoveUserClick(user.id)}
|
||||
/>
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
<span className={styles.filter}>
|
||||
<LabelsPopup
|
||||
items={allLabels}
|
||||
currentIds={labels.map((label) => label.id)}
|
||||
title="common.filterByLabels"
|
||||
canEdit={canEdit}
|
||||
onSelect={onLabelAdd}
|
||||
onDeselect={onLabelRemove}
|
||||
onCreate={onLabelCreate}
|
||||
onUpdate={onLabelUpdate}
|
||||
onMove={onLabelMove}
|
||||
onDelete={onLabelDelete}
|
||||
>
|
||||
<button type="button" className={styles.filterButton}>
|
||||
<span className={styles.filterTitle}>{`${t('common.labels')}:`}</span>
|
||||
{labels.length === 0 && <span className={styles.filterLabel}>{t('common.all')}</span>}
|
||||
</button>
|
||||
</LabelsPopup>
|
||||
{labels.map((label) => (
|
||||
<span key={label.id} className={styles.filterItem}>
|
||||
<Label
|
||||
name={label.name}
|
||||
color={label.color}
|
||||
size="small"
|
||||
onClick={() => handleRemoveLabelClick(label.id)}
|
||||
/>
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
<span className={styles.filter}>
|
||||
<Input
|
||||
ref={searchFieldRef}
|
||||
value={filterText}
|
||||
placeholder={t('common.searchCards')}
|
||||
icon={
|
||||
isSearchActive ? (
|
||||
<Icon link name="cancel" onClick={handleCancelSearchClick} />
|
||||
) : (
|
||||
'search'
|
||||
)
|
||||
}
|
||||
className={classNames(styles.search, !isSearchActive && styles.searchInactive)}
|
||||
onFocus={handleSearchFocus}
|
||||
onKeyDown={handleSearchKeyDown}
|
||||
onChange={handleSearchChange}
|
||||
onBlur={handleSearchBlur}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Filters.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
users: PropTypes.array.isRequired,
|
||||
labels: PropTypes.array.isRequired,
|
||||
filterText: PropTypes.string.isRequired,
|
||||
allBoardMemberships: PropTypes.array.isRequired,
|
||||
allLabels: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
canEdit: PropTypes.bool.isRequired,
|
||||
onUserAdd: PropTypes.func.isRequired,
|
||||
onUserRemove: PropTypes.func.isRequired,
|
||||
onLabelAdd: PropTypes.func.isRequired,
|
||||
onLabelRemove: PropTypes.func.isRequired,
|
||||
onLabelCreate: PropTypes.func.isRequired,
|
||||
onLabelUpdate: PropTypes.func.isRequired,
|
||||
onLabelMove: PropTypes.func.isRequired,
|
||||
onLabelDelete: PropTypes.func.isRequired,
|
||||
onTextFilterUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Filters;
|
|
@ -1,3 +0,0 @@
|
|||
import BoardActions from './BoardActions';
|
||||
|
||||
export default BoardActions;
|
|
@ -1,107 +0,0 @@
|
|||
import { dequal } from 'dequal';
|
||||
import omit from 'lodash/omit';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Menu, Radio, Segment } from 'semantic-ui-react';
|
||||
import { Popup } from '../../lib/custom-ui';
|
||||
|
||||
import { BoardMembershipRoles } from '../../constants/Enums';
|
||||
|
||||
import styles from './BoardMembershipPermissionsSelectStep.module.scss';
|
||||
|
||||
const BoardMembershipPermissionsSelectStep = React.memo(
|
||||
({ defaultData, title, buttonContent, onSelect, onBack, onClose }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [data, setData] = useState(() => ({
|
||||
role: BoardMembershipRoles.EDITOR,
|
||||
canComment: null,
|
||||
...defaultData,
|
||||
}));
|
||||
|
||||
const handleSelectRoleClick = useCallback((role) => {
|
||||
setData((prevData) => ({
|
||||
...prevData,
|
||||
role,
|
||||
canComment: role === BoardMembershipRoles.VIEWER ? !!prevData.canComment : null,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const handleSettingChange = useCallback((_, { name: fieldName, checked: value }) => {
|
||||
setData((prevData) => ({
|
||||
...prevData,
|
||||
[fieldName]: value,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
if (!dequal(data, defaultData)) {
|
||||
onSelect(data.role === BoardMembershipRoles.VIEWER ? data : omit(data, 'canComment'));
|
||||
}
|
||||
|
||||
onClose();
|
||||
}, [defaultData, onSelect, onClose, data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup.Header onBack={onBack}>
|
||||
{t(title, {
|
||||
context: 'title',
|
||||
})}
|
||||
</Popup.Header>
|
||||
<Popup.Content>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Menu secondary vertical className={styles.menu}>
|
||||
<Menu.Item
|
||||
active={data.role === BoardMembershipRoles.EDITOR}
|
||||
onClick={() => handleSelectRoleClick(BoardMembershipRoles.EDITOR)}
|
||||
>
|
||||
<div className={styles.menuItemTitle}>{t('common.editor')}</div>
|
||||
<div className={styles.menuItemDescription}>
|
||||
{t('common.canEditContentOfBoard')}
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
active={data.role === BoardMembershipRoles.VIEWER}
|
||||
onClick={() => handleSelectRoleClick(BoardMembershipRoles.VIEWER)}
|
||||
>
|
||||
<div className={styles.menuItemTitle}>{t('common.viewer')}</div>
|
||||
<div className={styles.menuItemDescription}>{t('common.canOnlyViewBoard')}</div>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
{data.role === BoardMembershipRoles.VIEWER && (
|
||||
<Segment basic className={styles.settings}>
|
||||
<Radio
|
||||
toggle
|
||||
name="canComment"
|
||||
checked={data.canComment}
|
||||
label={t('common.canComment')}
|
||||
onChange={handleSettingChange}
|
||||
/>
|
||||
</Segment>
|
||||
)}
|
||||
<Button positive content={t(buttonContent)} />
|
||||
</Form>
|
||||
</Popup.Content>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BoardMembershipPermissionsSelectStep.propTypes = {
|
||||
defaultData: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
title: PropTypes.string,
|
||||
buttonContent: PropTypes.string,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
BoardMembershipPermissionsSelectStep.defaultProps = {
|
||||
defaultData: undefined,
|
||||
title: 'common.selectPermissions',
|
||||
buttonContent: 'action.selectPermissions',
|
||||
};
|
||||
|
||||
export default BoardMembershipPermissionsSelectStep;
|
|
@ -1,18 +0,0 @@
|
|||
:global(#app) {
|
||||
.menu {
|
||||
margin: 0 auto 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menuItemDescription {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.menuItemTitle {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import BoardMembershipPermissionsSelectStep from './BoardMembershipPermissionsSelectStep';
|
||||
|
||||
export default BoardMembershipPermissionsSelectStep;
|
|
@ -1,103 +0,0 @@
|
|||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Menu } from 'semantic-ui-react';
|
||||
import { Input, Popup } from '../../lib/custom-ui';
|
||||
|
||||
import { useField } from '../../hooks';
|
||||
import Item from './Item';
|
||||
|
||||
import styles from './BoardMembershipsStep.module.scss';
|
||||
|
||||
const BoardMembershipsStep = React.memo(
|
||||
({ items, currentUserIds, title, onUserSelect, onUserDeselect, onBack }) => {
|
||||
const [t] = useTranslation();
|
||||
const [search, handleSearchChange] = useField('');
|
||||
const cleanSearch = useMemo(() => search.trim().toLowerCase(), [search]);
|
||||
|
||||
const filteredItems = useMemo(
|
||||
() =>
|
||||
items.filter(
|
||||
({ user }) =>
|
||||
user.email.includes(cleanSearch) ||
|
||||
user.name.toLowerCase().includes(cleanSearch) ||
|
||||
(user.username && user.username.includes(cleanSearch)),
|
||||
),
|
||||
[items, cleanSearch],
|
||||
);
|
||||
|
||||
const searchField = useRef(null);
|
||||
|
||||
const handleUserSelect = useCallback(
|
||||
(id) => {
|
||||
onUserSelect(id);
|
||||
},
|
||||
[onUserSelect],
|
||||
);
|
||||
|
||||
const handleUserDeselect = useCallback(
|
||||
(id) => {
|
||||
onUserDeselect(id);
|
||||
},
|
||||
[onUserDeselect],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
searchField.current.focus({
|
||||
preventScroll: true,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup.Header onBack={onBack}>
|
||||
{t(title, {
|
||||
context: 'title',
|
||||
})}
|
||||
</Popup.Header>
|
||||
<Popup.Content>
|
||||
<Input
|
||||
fluid
|
||||
ref={searchField}
|
||||
value={search}
|
||||
placeholder={t('common.searchMembers')}
|
||||
icon="search"
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
{filteredItems.length > 0 && (
|
||||
<Menu secondary vertical className={styles.menu}>
|
||||
{filteredItems.map((item) => (
|
||||
<Item
|
||||
key={item.id}
|
||||
isPersisted={item.isPersisted}
|
||||
isActive={currentUserIds.includes(item.user.id)}
|
||||
user={item.user}
|
||||
onUserSelect={() => handleUserSelect(item.user.id)}
|
||||
onUserDeselect={() => handleUserDeselect(item.user.id)}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
)}
|
||||
</Popup.Content>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BoardMembershipsStep.propTypes = {
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
items: PropTypes.array.isRequired,
|
||||
currentUserIds: PropTypes.array.isRequired,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
title: PropTypes.string,
|
||||
onUserSelect: PropTypes.func.isRequired,
|
||||
onUserDeselect: PropTypes.func.isRequired,
|
||||
onBack: PropTypes.func,
|
||||
};
|
||||
|
||||
BoardMembershipsStep.defaultProps = {
|
||||
title: 'common.members',
|
||||
onBack: undefined,
|
||||
};
|
||||
|
||||
export default BoardMembershipsStep;
|
|
@ -1,21 +0,0 @@
|
|||
:global(#app) {
|
||||
.menu {
|
||||
margin: 8px auto 0;
|
||||
max-height: 60vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Menu } from 'semantic-ui-react';
|
||||
|
||||
import User from '../User';
|
||||
|
||||
import styles from './Item.module.scss';
|
||||
|
||||
const Item = React.memo(({ isPersisted, isActive, user, onUserSelect, onUserDeselect }) => {
|
||||
const handleToggleClick = useCallback(() => {
|
||||
if (isActive) {
|
||||
onUserDeselect();
|
||||
} else {
|
||||
onUserSelect();
|
||||
}
|
||||
}, [isActive, onUserSelect, onUserDeselect]);
|
||||
|
||||
return (
|
||||
<Menu.Item
|
||||
active={isActive}
|
||||
disabled={!isPersisted}
|
||||
className={classNames(styles.menuItem, isActive && styles.menuItemActive)}
|
||||
onClick={handleToggleClick}
|
||||
>
|
||||
<span className={styles.user}>
|
||||
<User name={user.name} avatarUrl={user.avatarUrl} />
|
||||
</span>
|
||||
<div className={classNames(styles.menuItemText, isActive && styles.menuItemTextActive)}>
|
||||
{user.name}
|
||||
</div>
|
||||
</Menu.Item>
|
||||
);
|
||||
});
|
||||
|
||||
Item.propTypes = {
|
||||
isPersisted: PropTypes.bool.isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
onUserSelect: PropTypes.func.isRequired,
|
||||
onUserDeselect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Item;
|
|
@ -1,3 +0,0 @@
|
|||
import BoardMembershipsStep from './BoardMembershipsStep';
|
||||
|
||||
export default BoardMembershipsStep;
|
|
@ -1,3 +0,0 @@
|
|||
import AddStep from './AddStep';
|
||||
|
||||
export default AddStep;
|