Compare commits

...

30 Commits

Author SHA1 Message Date
dependabot[bot]
d472da7523 build(deps): Bump jws from 3.2.2 to 3.2.3 in /server (#1449)
Bumps [jws](https://github.com/brianloveswords/node-jws) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/brianloveswords/node-jws/releases)
- [Changelog](https://github.com/auth0/node-jws/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianloveswords/node-jws/compare/v3.2.2...v3.2.3)

---
updated-dependencies:
- dependency-name: jws
  dependency-version: 3.2.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-04 18:16:18 +01:00
Maksim Eltyshev
f030b78f82 feat: Add object-path support to OIDC attribute mapping
Closes #1359
2025-12-04 17:38:39 +01:00
dependabot[bot]
b94759d399 build(deps): Bump nodemailer from 7.0.10 to 7.0.11 in /server (#1448)
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 7.0.10 to 7.0.11.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v7.0.10...v7.0.11)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 7.0.11
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-04 16:23:31 +01:00
Maksim Eltyshev
fedee157c7 build: Refine server build steps 2025-11-28 17:45:31 +01:00
Maksim Eltyshev
68e5c33418 fix: Improve browser caching for public files 2025-11-28 13:14:25 +01:00
Maksim Eltyshev
f7a09b2eca license: Improve clarity of commercial license terms 2025-11-28 00:18:29 +01:00
Maksim Eltyshev
c058df8fc4 fix: Rename GIN indexes 2025-11-27 19:31:11 +01:00
Maksim Eltyshev
0023c63be8 fix: Optimize query methods 2025-11-27 19:09:10 +01:00
Maksim Eltyshev
26b3cffdab fix: Rename getCards inputs for consistency 2025-11-27 18:41:50 +01:00
Maksim Eltyshev
bf2ab4649e fix: Create isolated i18n instances to prevent locale collision 2025-11-27 18:28:25 +01:00
Maksim Eltyshev
54e230d4c1 ref: Refactoring 2025-11-27 18:24:55 +01:00
Maksim Eltyshev
7be2343076 chore: Add Swagger generation script 2025-11-25 22:08:37 +01:00
Maksim Eltyshev
c646f0f5b3 fix: Increase max username length
Closes #1441
2025-11-25 15:03:35 +01:00
Maksim Eltyshev
8a288d1816 fix: Prevent shortcuts from triggering in actions popup 2025-11-24 21:46:32 +01:00
Maksim Eltyshev
197ebc16db chore: Bump package lock files 2025-11-24 19:48:30 +01:00
Maksim Eltyshev
5c787d65a9 fix: Properly prevent shortcut events, update dependencies
Closes #1440
2025-11-24 19:21:07 +01:00
Samar Khajuria
7e41a0167d feat: Add create-board button to open-board screen (#1438)
Closes #1435
2025-11-24 15:45:50 +01:00
Maksim Eltyshev
c68ab99bfe feat: Extend card action shortcuts 2025-11-24 15:02:26 +01:00
Serge Zaitsev
561d2c77d4 feat: Add basic shortcuts (#1436) 2025-11-24 12:35:31 +01:00
Maksim Eltyshev
6e44b2ad19 chore: Restore license header 2025-11-19 18:20:54 +01:00
Maksim Eltyshev
cd5685049e fix: Log errors when sending notifications via Apprise 2025-11-19 18:19:31 +01:00
dependabot[bot]
015562d8da build(deps): Bump glob from 10.4.5 to 10.5.0 in /client (#1432) 2025-11-18 21:23:34 +01:00
dependabot[bot]
7da8d8219b build(deps-dev): Bump js-yaml from 3.14.1 to 3.14.2 in /client (#1431)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 21:11:18 +01:00
dependabot[bot]
4ed0f02657 build(deps-dev): Bump js-yaml from 4.1.0 to 4.1.1 in /server (#1429)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 21:11:08 +01:00
Shanick
0736b8fea0 fix: Make date command cross-platform in backup script (#1409) 2025-11-18 21:10:24 +01:00
Roger Clotet
7a05bf2a30 feat: Add Catalan (ca-ES) translation (#1424) 2025-11-12 19:19:29 +01:00
Maksim Eltyshev
d7cb2087e5 fix: Adjust margins in menu-styled popups 2025-11-06 23:15:57 +01:00
Maksim Eltyshev
22baf5ab12 feat: Ghost error page for improved error display 2025-11-06 23:11:06 +01:00
Steve Váradi
33a7a6ccb1 chore: Add chart to feature request template (#1414) 2025-11-06 21:03:43 +01:00
Samuel
b4cbd32bf2 feat: Add API key authentication (#1254)
Closes #945
2025-11-06 20:56:48 +01:00
262 changed files with 5564 additions and 2091 deletions

View File

@@ -1,33 +1,34 @@
*/README.md
*/.gitignore
*/node_modules
**/.DS_Store
server/**/.gitkeep
*/node_modules
server/swagger.json
server/.env
server/.editorconfig
server/.eslintignore
server/.npmrc
server/test
server/dist
server/logs
server/.venv
server/test
server/.tmp
server/.venv
server/views/*
!server/views/.gitkeep
server/public/*
!server/public/preloaded-favicons
!server/public/favicons
server/public/favicons/*
!server/public/favicons/.gitkeep
!server/public/user-avatars
server/public/user-avatars/*
!server/public/user-avatars/.gitkeep
!server/public/background-images
server/public/background-images/*
!server/public/background-images/.gitkeep
server/private/*
!server/private/attachments
server/private/attachments/*
!server/private/attachments/.gitkeep
client/dist

View File

@@ -5,11 +5,12 @@ body:
- type: dropdown
id: idea-type
attributes:
label: Is this a feature for the backend or frontend?
label: Which part of the project does this feature apply to?
multiple: true
options:
- Backend
- Frontend
- Chart
validations:
required: true
- type: textarea

View File

@@ -21,6 +21,14 @@ jobs:
- name: Update npm
run: npm install npm --global
- name: Install server dependencies
run: npm install --omit=prod --ignore-scripts
working-directory: ./server
- name: Build server
run: npm run build
working-directory: ./server
- name: Install client dependencies
run: npm install --omit=dev
working-directory: ./client
@@ -29,24 +37,25 @@ jobs:
run: DISABLE_ESLINT_PLUGIN=true npm run build
working-directory: ./client
- name: Include server into dist
run: mv server dist
- name: Include licenses into dist
run: |
mv LICENSE.md server/dist
mv "LICENSES/PLANKA Community License DE.md" server/dist/LICENSE_DE.md
- name: Include built client into dist
run: |
mv dist/* ../dist/public
cp ../dist/public/index.html ../dist/views
working-directory: ./client
- name: Include LICENSE.md, README.md, SECURITY.md into dist
run: mv LICENSE.md README.md SECURITY.md dist
mv ../../client/dist/* public
cp public/index.html views
working-directory: ./server/dist
- name: Create release package
run: |
mv dist planka
zip -r planka-prebuild.zip planka
working-directory: ./server
- name: Publish release package
run: gh release upload ${{ github.event.release.tag_name }} planka-prebuild.zip
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload ${{ github.event.release.tag_name }} planka-prebuild.zip
working-directory: ./server

4
.gitignore vendored
View File

@@ -1,9 +1,9 @@
node_modules
docker-compose.override.yml
.idea
.DS_Store
node_modules
# Prevent another lockfile than package-lock.json (npm) from being created
# If some case you are using pnpm or yarn, don't forget to generate npm lockfile
# before commiting your code by running:

View File

@@ -1,16 +1,19 @@
FROM node:22-alpine AS server-dependencies
# Stage 1: Server build
FROM node:22-alpine AS server
RUN apk -U upgrade \
&& apk add build-base python3 --no-cache
WORKDIR /app
COPY server/package.json server/package-lock.json server/requirements.txt ./
COPY server/patches ./patches
COPY server .
RUN npm install npm --global \
&& npm install --omit=dev
&& npm install \
&& npm run build \
&& npm prune --production
# Stage 2: Client build
FROM node:22 AS client
WORKDIR /app
@@ -18,10 +21,10 @@ WORKDIR /app
COPY client .
RUN npm install npm --global \
&& npm install --omit=dev
RUN DISABLE_ESLINT_PLUGIN=true npm run build
&& npm install --omit=dev \
&& DISABLE_ESLINT_PLUGIN=true npm run build
# Stage 3: Final image
FROM node:22-alpine
RUN apk -U upgrade \
@@ -31,18 +34,20 @@ RUN apk -U upgrade \
USER node
WORKDIR /app
COPY --chown=node:node server .
COPY --chown=node:node LICENSE.md .
COPY --chown=node:node ["LICENSES/PLANKA Community License DE.md", "LICENSE_DE.md"]
COPY --from=server --chown=node:node /app/node_modules node_modules
COPY --from=server --chown=node:node /app/dist .
COPY --from=client --chown=node:node /app/dist public
COPY --from=client --chown=node:node /app/dist/index.html views
RUN python3 -m venv .venv \
&& .venv/bin/pip3 install -r requirements.txt --no-cache-dir \
&& mv .env.sample .env \
&& npm config set update-notifier false
COPY --from=server-dependencies --chown=node:node /app/node_modules node_modules
COPY --from=client --chown=node:node /app/dist public
COPY --from=client --chown=node:node /app/dist/index.html views
VOLUME /app/public/favicons
VOLUME /app/public/user-avatars
VOLUME /app/public/background-images

View File

@@ -1,6 +1,6 @@
**PLANKA Commercial License**
Version 1.1 - Zuletzt aktualisiert: 20. Mai 2025
Version 1.2 - Zuletzt aktualisiert: 28. Nov 2025
Zugehörige Dateien in Englisch:
@@ -20,7 +20,7 @@ Zugehörige Dateien in Deutsch:
Copyright (c) 2025 bis heute von PLANKA Software GmbH.
Unsere Software und zugehörige Dokumentationsdateien (die "Software") dürfen nur dann produktiv genutzt werden, wenn Sie (und jede juristische Person, die Sie vertreten) eine gültige "PLANKA Pro/Enterprise-Lizenz" besitzen, die Ihrer Nutzung entspricht. Vorbehaltlich des vorstehenden Satzes steht es Ihnen frei, unsere Software zu modifizieren und Patches dafür zu veröffentlichen. Sie stimmen zu, dass die PLANKA Software GmbH und/oder ihre Lizenzgeber (falls zutreffend) alle Rechte, Titel und Ansprüche an und auf alle solche Modifikationen und/oder Patches behalten, und alle solche Modifikationen und/oder Patches dürfen nur mit einer gültigen "PLANKA Pro/Enterprise-Lizenz" für die entsprechende Nutzung verwendet, kopiert, modifiziert, angezeigt, verteilt oder anderweitig genutzt werden. Ungeachtet des Vorstehenden dürfen Sie die Software für Entwicklungs- und Testzwecke ohne Abonnement kopieren und modifizieren. Sie stimmen zu, dass PLANKA Software GmbH und/oder ihre Lizenzgeber (falls zutreffend) alle Rechte, Titel und Ansprüche an und auf alle solche Modifikationen behalten. Es werden Ihnen keine anderen Rechte gewährt als die, die hier ausdrücklich genannt sind. Vorbehaltlich des Vorstehenden ist es verboten, die Software zu kopieren, zusammenzuführen, zu veröffentlichen, zu verteilen, zu unterlizenzieren und/oder zu verkaufen.
Unsere Software und zugehörige Dokumentationsdateien (die "Software") dürfen nur dann produktiv genutzt werden, wenn Sie (und jede juristische Person, die Sie vertreten) eine gültige "PLANKA Pro/Enterprise-Lizenz" besitzen, die Ihrer Nutzung entspricht. Sie stimmen zu, dass die PLANKA Software GmbH und/oder ihre Lizenzgeber (falls zutreffend) alle Rechte, Titel und Ansprüche an und auf alle solche Modifikationen und/oder Patches behalten, und alle solche Modifikationen und/oder Patches dürfen nur mit einer gültigen "PLANKA Pro/Enterprise-Lizenz" für die entsprechende Nutzung verwendet, kopiert, modifiziert, angezeigt, verteilt oder anderweitig genutzt werden. Ungeachtet des Vorstehenden dürfen Sie die Software für Entwicklungs- und Testzwecke ohne Abonnement kopieren und modifizieren. Sie stimmen zu, dass PLANKA Software GmbH und/oder ihre Lizenzgeber (falls zutreffend) alle Rechte, Titel und Ansprüche an und auf alle solche Modifikationen behalten. Es werden Ihnen keine anderen Rechte gewährt als die, die hier ausdrücklich genannt sind. Vorbehaltlich des Vorstehenden ist es verboten, die Software zu kopieren, zusammenzuführen, zu veröffentlichen, zu verteilen, zu unterlizenzieren und/oder zu verkaufen.
#### Komponenten von Drittanbietern
@@ -28,7 +28,11 @@ Für alle Komponenten von Drittanbietern, die in unsere Software integriert sind
## PLANKA Pro/Enterprise Repositories
Nach dem Kauf unserer "PLANKA Pro/Enterprise-Lizenz" erhalten Sie Zugang zu unseren "PLANKA Pro/Enterprise"-Repositories. Hier finden Sie unsere neuesten stabilen Builds, die umfangreiche, eingehende Tests bestanden haben und als kampferprobt gelten. Unter keinen Umständen dürfen Sie Dateien, Quellcode oder Teile aus unseren "PLANKA Pro/Enterprise"-Repositories ohne vorherige Genehmigung der PLANKA Software GmbH an Personen weitergeben, die nicht zugangsberechtigt sind.
Nach dem Erwerb einer "PLANKA Pro/Enterprise-Lizenz" erhalten Sie Zugang zu unseren "PLANKA Pro/Enterprise"-Repositories. Dort finden Sie unsere neuesten stabilen Builds, die umfangreiche Tests durchlaufen haben und als produktionsreif gelten.
Wichtiger Hinweis zum Zugriffsumfang: Der Standardzugang umfasst ausschließlich die vorkompilierten Versionen unserer Software. Zugang zum Quellcode wird nur in Ausnahmefällen und nach gesonderter Vereinbarung mit PLANKA Software GmbH gewährt.
Unabhängig vom Zugriffsumfang gilt: Die Weitergabe von Dateien, Quellcode oder Teilen davon aus unseren "PLANKA Pro/Enterprise"-Repositories an Dritte, die nicht zugriffsberechtigt sind, ist ohne vorherige schriftliche Genehmigung von PLANKA Software GmbH untersagt.
## Eingeschränkte Garantie

View File

@@ -1,6 +1,6 @@
**PLANKA Commercial License**
Version 1.1 - Last updated: May 20, 2025
Version 1.2 - Last updated: Nov 28, 2025
Related files in English:
@@ -20,7 +20,7 @@ Related files in German:
Copyright (c) 2025 to present by PLANKA Software GmbH.
Our software and associated documentation files (the "Software") may only be used in production if you (and any entity that you represent) hold a valid "PLANKA Pro/Enterprise License" corresponding to your usage. Subject to the foregoing sentence, you are free to modify our Software and publish patches for it. You agree that PLANKA Software GmbH and/or its licensors (as applicable) retain all right, title, and interest in and to all such modifications and/or patches, and all such modifications and/or patches may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid "PLANKA Pro/Enterprise License" for the corresponding usage. Notwithstanding the foregoing, you may copy and modify the Software for development and testing purposes without requiring a subscription. You agree that PLANKA Software GmbH and/or its licensors (as applicable) retain all right, title, and interest in and to all such modifications. You are not granted any other rights beyond what is expressly stated herein. Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, and/or sell the Software.
Our software and associated documentation files (the "Software") may only be used in production if you (and any entity that you represent) hold a valid "PLANKA Pro/Enterprise License" corresponding to your usage. You agree that PLANKA Software GmbH and/or its licensors (as applicable) retain all right, title, and interest in and to all such modifications and/or patches, and all such modifications and/or patches may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid "PLANKA Pro/Enterprise License" for the corresponding usage. Notwithstanding the foregoing, you may copy and modify the Software for development and testing purposes without requiring a subscription. You agree that PLANKA Software GmbH and/or its licensors (as applicable) retain all right, title, and interest in and to all such modifications. You are not granted any other rights beyond what is expressly stated herein. Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, and/or sell the Software.
#### Third-Party Components
@@ -28,7 +28,11 @@ For all third-party components incorporated into our Software, those components
## PLANKA Pro/Enterprise Repositories
After purchasing our "PLANKA Pro/Enterprise License", you get access to our "PLANKA Pro/Enterprise" repositories. Here you find our latest stable builds, which have passed extensive in-depth tests and are considered battle-proof. Under no circumstances are you allowed to pass files, source code, or any part of it from our "PLANKA Pro/Enterprise" repositories to anyone not eligible for access without prior permission from PLANKA Software GmbH.
After purchasing a "PLANKA Pro/Enterprise License", you will receive access to our "PLANKA Pro/Enterprise" repositories. There you will find our latest stable builds, which have undergone extensive testing and are considered production-ready.
Important note on access scope: Standard access includes only the precompiled versions of our software. Access to the source code is granted only in exceptional cases and requires a separate agreement with PLANKA Software GmbH.
Regardless of access scope: The distribution of files, source code, or any parts thereof from our "PLANKA Pro/Enterprise" repositories to third parties who are not authorized for access is prohibited without prior written permission from PLANKA Software GmbH.
## Limited Warranty

1759
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -80,10 +80,10 @@
"dependencies": {
"@ballerina/highlightjs-ballerina": "^1.0.1",
"@diplodoc/cut-extension": "^0.7.4",
"@diplodoc/transform": "^4.61.0",
"@gravity-ui/components": "^4.12.0",
"@gravity-ui/markdown-editor": "^15.19.1",
"@gravity-ui/uikit": "^7.20.0",
"@diplodoc/transform": "^4.64.1",
"@gravity-ui/components": "^4.16.0",
"@gravity-ui/markdown-editor": "^15.25.0",
"@gravity-ui/uikit": "^7.26.1",
"@juggle/resize-observer": "^3.4.0",
"@vitejs/plugin-react": "^4.7.0",
"browserslist-to-esbuild": "^2.1.1",
@@ -114,10 +114,10 @@
"highlightjs-zenscript": "^2.0.0",
"hightlightjs-papyrus": "^0.0.4",
"history": "^5.3.0",
"i18next": "^25.4.2",
"i18next": "^25.6.3",
"i18next-browser-languagedetector": "^8.2.0",
"initials": "^3.1.2",
"javascript-time-ago": "^2.5.11",
"javascript-time-ago": "^2.5.12",
"js-cookie": "^3.0.5",
"jwt-decode": "^4.0.0",
"linkify-react": "^4.3.2",
@@ -125,7 +125,7 @@
"lodash": "^4.17.21",
"lowlight": "^3.3.0",
"markdown-it": "^13.0.2",
"nanoid": "^5.1.5",
"nanoid": "^5.1.6",
"papaparse": "^5.5.3",
"patch-package": "^8.0.1",
"photoswipe": "^5.4.4",
@@ -137,25 +137,25 @@
"react-dropzone": "^14.3.8",
"react-frame-component": "^5.2.7",
"react-hot-toast": "^2.6.0",
"react-i18next": "^15.7.3",
"react-i18next": "^15.7.4",
"react-input-mask": "^2.0.4",
"react-intersection-observer": "^9.16.0",
"react-mentions": "^4.4.10",
"react-photoswipe-gallery": "^2.2.7",
"react-redux": "^8.1.3",
"react-router-dom": "^6.30.1",
"react-router-dom": "^6.30.2",
"react-textarea-autosize": "^8.5.9",
"react-time-ago": "^7.3.3",
"react-time-ago": "^7.3.5",
"redux": "^4.2.1",
"redux-logger": "^3.0.6",
"redux-orm": "^0.16.2",
"redux-saga": "^1.3.0",
"redux-saga": "^1.4.2",
"reselect": "^4.1.8",
"sails.io.js": "^1.2.1",
"sass-embedded": "^1.91.0",
"sass-embedded": "^1.93.3",
"semantic-ui-react": "^2.1.5",
"socket.io-client": "^2.5.0",
"validator": "^13.15.20",
"validator": "^13.15.23",
"vite": "^6.4.1",
"vite-plugin-commonjs": "^0.10.4",
"vite-plugin-node-polyfills": "^0.23.0",
@@ -163,8 +163,8 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@babel/eslint-parser": "^7.28.0",
"@babel/preset-env": "^7.28.3",
"@babel/eslint-parser": "^7.28.5",
"@babel/preset-env": "^7.28.5",
"@cucumber/cucumber": "^11.3.0",
"@cucumber/pretty-formatter": "^1.0.1",
"@playwright/test": "^1.56.1",

View File

@@ -1,5 +1,5 @@
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 0e615c4..383338d 100644
index 84c7781..3b2f51c 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) => {
@@ -10,10 +10,10 @@ index 0e615c4..383338d 100644
deflist: {
deflistTermPlaceholder: () => i18nPlaceholder('deflist_term'),
deflistDescPlaceholder: () => i18nPlaceholder('deflist_desc'),
@@ -118,11 +117,6 @@ export const BundlePreset = (builder, opts) => {
...opts.yfmNote,
@@ -122,11 +121,6 @@ export const BundlePreset = (builder, opts) => {
...opts.yfmTable,
controls: opts.mobile ? false : opts.yfmTable?.controls,
},
yfmTable: { yfmTableCellPlaceholder: () => i18nPlaceholder('table_cell'), ...opts.yfmTable },
- yfmFile: {
- fileUploadHandler: opts.fileUploadHandler,
- needToSetDimensionsForUploadedImages: opts.needToSetDimensionsForUploadedImages,

View File

@@ -235,6 +235,58 @@ updateUserAvatar.failure = (id, error) => ({
},
});
const createUserApiKey = (id) => ({
type: ActionTypes.USER_API_KEY_CREATE,
payload: {
id,
},
});
createUserApiKey.success = (user, apiKey) => ({
type: ActionTypes.USER_API_KEY_CREATE__SUCCESS,
payload: {
user,
apiKey,
},
});
createUserApiKey.failure = (id, error) => ({
type: ActionTypes.USER_API_KEY_CREATE__FAILURE,
payload: {
id,
error,
},
});
const deleteUserApiKey = (id) => ({
type: ActionTypes.USER_API_KEY_DELETE,
payload: {
id,
},
});
deleteUserApiKey.success = (user) => ({
type: ActionTypes.USER_API_KEY_DELETE__SUCCESS,
payload: {
user,
},
});
deleteUserApiKey.failure = (id, error) => ({
type: ActionTypes.USER_API_KEY_DELETE__FAILURE,
payload: {
id,
error,
},
});
const clearUserApiKeyValue = (id) => ({
type: ActionTypes.USER_API_KEY_VALUE_CLEAR,
payload: {
id,
},
});
const deleteUser = (id) => ({
type: ActionTypes.USER_DELETE,
payload: {
@@ -359,6 +411,9 @@ export default {
updateUserUsername,
clearUserUsernameUpdateError,
updateUserAvatar,
createUserApiKey,
deleteUserApiKey,
clearUserApiKeyValue,
deleteUser,
handleUserDelete,
addUserToCard,

View File

@@ -33,6 +33,9 @@ const updateUserUsername = (id, data, headers) =>
const updateUserAvatar = (id, data, headers) => http.post(`/users/${id}/avatar`, data, headers);
const createUserApiKey = (userId, headers) =>
socket.post(`/users/${userId}/api-key`, undefined, headers);
const deleteUser = (id, headers) => socket.delete(`/users/${id}`, undefined, headers);
export default {
@@ -45,5 +48,6 @@ export default {
updateUserPassword,
updateUserUsername,
updateUserAvatar,
createUserApiKey,
deleteUser,
};

View File

@@ -11,8 +11,8 @@ import { Link } from 'react-router-dom';
import { Comment } from 'semantic-ui-react';
import selectors from '../../../selectors';
import { isUserStatic } from '../../../utils/record-helpers';
import Paths from '../../../constants/Paths';
import { StaticUserIds } from '../../../constants/StaticUsers';
import { ActivityTypes } from '../../../constants/Enums';
import TimeAgo from '../../common/TimeAgo';
import UserAvatar from '../../users/UserAvatar';
@@ -30,12 +30,11 @@ const Item = React.memo(({ id }) => {
const [t] = useTranslation();
const userName =
user.id === StaticUserIds.DELETED
? t(`common.${user.name}`, {
context: 'title',
})
: user.name;
const userName = isUserStatic(user)
? t(`common.${user.name}`, {
context: 'title',
})
: user.name;
const cardName = card ? card.name : activity.data.card.name;

View File

@@ -10,7 +10,7 @@ import { useTranslation, Trans } from 'react-i18next';
import { Comment } from 'semantic-ui-react';
import selectors from '../../../selectors';
import { StaticUserIds } from '../../../constants/StaticUsers';
import { isUserStatic } from '../../../utils/record-helpers';
import { ActivityTypes } from '../../../constants/Enums';
import TimeAgo from '../../common/TimeAgo';
import UserAvatar from '../../users/UserAvatar';
@@ -26,12 +26,11 @@ const Item = React.memo(({ id }) => {
const [t] = useTranslation();
const userName =
user.id === StaticUserIds.DELETED
? t(`common.${user.name}`, {
context: 'title',
})
: user.name;
const userName = isUserStatic(user)
? t(`common.${user.name}`, {
context: 'title',
})
: user.name;
let contentNode;
switch (activity.type) {

View File

@@ -12,7 +12,7 @@
}
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -121,7 +121,7 @@ const ActionsStep = React.memo(({ boardMembershipId, title, onBack, onClose }) =
)}
{user.organization && (
<div className={styles.information}>
<Icon name="briefcase" className={styles.informationIcon} />
<Icon name="building" className={styles.informationIcon} />
{user.organization}
</div>
)}

View File

@@ -9,20 +9,20 @@ import classNames from 'classnames';
import { useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Button, Form, Icon } from 'semantic-ui-react';
import { useDidUpdate, useToggle } from '../../../../lib/hooks';
import { Input, Popup } from '../../../../lib/custom-ui';
import { useDidUpdate, useToggle } from '../../../lib/hooks';
import { Input, Popup } from '../../../lib/custom-ui';
import entryActions from '../../../../entry-actions';
import { useForm, useNestedRef, useSteps } from '../../../../hooks';
import entryActions from '../../../entry-actions';
import { useForm, useNestedRef, useSteps } from '../../../hooks';
import ImportStep from './ImportStep';
import styles from './AddStep.module.scss';
import styles from './AddBoardStep.module.scss';
const StepTypes = {
IMPORT: 'IMPORT',
};
const AddStep = React.memo(({ onClose }) => {
const AddBoardStep = React.memo(({ onClose }) => {
const dispatch = useDispatch();
const [t] = useTranslation();
@@ -123,8 +123,8 @@ const AddStep = React.memo(({ onClose }) => {
);
});
AddStep.propTypes = {
AddBoardStep.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default AddStep;
export default AddBoardStep;

View File

@@ -7,7 +7,7 @@ import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react';
import { FilePicker, Popup } from '../../../../lib/custom-ui';
import { FilePicker, Popup } from '../../../lib/custom-ui';
import styles from './ImportStep.module.scss';

View File

@@ -3,6 +3,6 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import StoryContent from './StoryContent';
import AddBoardStep from './AddBoardStep';
export default StoryContent;
export default AddBoardStep;

View File

@@ -12,6 +12,7 @@ import { BoardContexts, BoardViews } from '../../../constants/Enums';
import KanbanContent from './KanbanContent';
import FiniteContent from './FiniteContent';
import EndlessContent from './EndlessContent';
import ShortcutsProvider from './ShortcutsProvider';
import CardModal from '../../cards/CardModal';
import BoardActivitiesModal from '../../activities/BoardActivitiesModal';
@@ -53,7 +54,9 @@ const Board = React.memo(() => {
return (
<>
<Content />
<ShortcutsProvider>
<Content />
</ShortcutsProvider>
{modalNode}
</>
);

View File

@@ -15,13 +15,13 @@ import ListView from './ListView';
const FiniteContent = React.memo(() => {
const board = useSelector(selectors.selectCurrentBoard);
const cardIds = useSelector(selectors.selectFilteredCardIdsForCurrentBoard);
const hasAnyFiniteList = useSelector((state) => !!selectors.selectFirstFiniteListId(state));
const canAddCard = useSelector((state) => !!selectors.selectFirstKanbanListId(state));
const dispatch = useDispatch();
const handleCardCreate = useCallback(
(data, autoOpen) => {
dispatch(entryActions.createCardInFirstFiniteList(data, undefined, autoOpen));
dispatch(entryActions.createCardInCurrentContext(data, undefined, autoOpen));
},
[dispatch],
);
@@ -39,7 +39,7 @@ const FiniteContent = React.memo(() => {
default:
}
return <View cardIds={cardIds} onCardCreate={hasAnyFiniteList ? handleCardCreate : undefined} />;
return <View cardIds={cardIds} onCardCreate={canAddCard ? handleCardCreate : undefined} />;
});
export default FiniteContent;

View File

@@ -23,7 +23,7 @@ import styles from './KanbanContent.module.scss';
import globalStyles from '../../../../styles.module.scss';
const KanbanContent = React.memo(() => {
const listIds = useSelector(selectors.selectFiniteListIdsForCurrentBoard);
const listIds = useSelector(selectors.selectKanbanListIdsForCurrentBoard);
const canAddList = useSelector((state) => {
const isEditModeEnabled = selectors.selectIsEditModeEnabled(state); // TODO: move out?

View File

@@ -0,0 +1,295 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { push } from '../../../lib/redux-router';
import { useDidUpdate } from '../../../lib/hooks';
import { closePopup } from '../../../lib/popup';
import store from '../../../store';
import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
import { isListArchiveOrTrash } from '../../../utils/record-helpers';
import { isActiveTextElement } from '../../../utils/element-helpers';
import { isModifierKeyPressed } from '../../../utils/event-helpers';
import { BoardShortcutsContext } from '../../../contexts';
import Paths from '../../../constants/Paths';
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
import CardActionsStep from '../../cards/CardActionsStep';
const canEditCardName = (boardMembership, list) => {
if (isListArchiveOrTrash(list)) {
return false;
}
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
};
const canArchiveCard = (boardMembership, list) => {
if (list.type === ListTypes.ARCHIVE) {
return false;
}
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
};
const canUseCardMembers = (boardMembership, list) => {
if (isListArchiveOrTrash(list)) {
return false;
}
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
};
const canUseCardLabels = (boardMembership, list) => {
if (isListArchiveOrTrash(list)) {
return false;
}
return boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
};
const ShortcutsProvider = React.memo(({ children }) => {
const { cardId, boardId } = useSelector(selectors.selectPath);
const dispatch = useDispatch();
const selectedCardRef = useRef(null);
const handleCardMouseEnter = useCallback((id, editName, openActions) => {
selectedCardRef.current = {
id,
editName,
openActions,
};
}, []);
const handleCardMouseLeave = useCallback(() => {
selectedCardRef.current = null;
}, []);
const contextValue = useMemo(
() => [handleCardMouseEnter, handleCardMouseLeave],
[handleCardMouseEnter, handleCardMouseLeave],
);
useDidUpdate(() => {
selectedCardRef.current = null;
}, [cardId, boardId]);
useEffect(() => {
const handleCardOpen = (event) => {
if (!selectedCardRef.current) {
return;
}
const state = store.getState();
const card = selectors.selectCardById(state, selectedCardRef.current.id);
if (!card || !card.isPersisted) {
return;
}
event.preventDefault();
closePopup();
dispatch(push(Paths.CARDS.replace(':id', card.id)));
};
const handleCardNameEdit = (event) => {
if (!selectedCardRef.current) {
return;
}
const state = store.getState();
const card = selectors.selectCardById(state, selectedCardRef.current.id);
if (!card || !card.isPersisted) {
return;
}
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const list = selectors.selectListById(state, card.listId);
if (!canEditCardName(boardMembership, list)) {
return;
}
event.preventDefault();
selectedCardRef.current.editName();
};
const handleCardArchive = (event) => {
if (!selectedCardRef.current) {
return;
}
const state = store.getState();
const card = selectors.selectCardById(state, selectedCardRef.current.id);
if (!card || !card.isPersisted) {
return;
}
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const list = selectors.selectListById(state, card.listId);
if (!canArchiveCard(boardMembership, list)) {
return;
}
event.preventDefault();
selectedCardRef.current.openActions(CardActionsStep.StepTypes.ARCHIVE);
};
const handleCardMembers = (event) => {
if (!selectedCardRef.current) {
return;
}
const state = store.getState();
const card = selectors.selectCardById(state, selectedCardRef.current.id);
if (!card || !card.isPersisted) {
return;
}
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const list = selectors.selectListById(state, card.listId);
if (!canUseCardMembers(boardMembership, list)) {
return;
}
event.preventDefault();
selectedCardRef.current.openActions(CardActionsStep.StepTypes.MEMBERS);
};
const handleCardLabels = (event) => {
if (!selectedCardRef.current) {
return;
}
const state = store.getState();
const card = selectors.selectCardById(state, selectedCardRef.current.id);
if (!card || !card.isPersisted) {
return;
}
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const list = selectors.selectListById(state, card.listId);
if (!canUseCardLabels(boardMembership, list)) {
return;
}
event.preventDefault();
selectedCardRef.current.openActions(CardActionsStep.StepTypes.LABELS);
};
const handleLabelToCardAdd = (event) => {
if (!selectedCardRef.current) {
return;
}
const state = store.getState();
const card = selectors.selectCardById(state, selectedCardRef.current.id);
if (!card || !card.isPersisted) {
return;
}
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const list = selectors.selectListById(state, card.listId);
if (!canUseCardLabels(boardMembership, list)) {
return;
}
const index = event.code === 'Digit0' ? 10 : parseInt(event.code.slice(-1), 10) - 1;
const label = selectors.selectLabelsForCurrentBoard(state)[index];
if (!label) {
return;
}
event.preventDefault();
const labelIds = selectors.selectLabelIdsByCardId(state, card.id);
if (labelIds.includes(label.id)) {
dispatch(entryActions.removeLabelFromCard(label.id, card.id));
} else {
dispatch(entryActions.addLabelToCard(label.id, card.id));
}
};
const handleKeyDown = (event) => {
if (isActiveTextElement(event.target)) {
return;
}
if (isModifierKeyPressed(event)) {
return;
}
switch (event.code) {
case 'KeyE':
case 'Enter':
handleCardOpen(event);
break;
case 'KeyL':
handleCardLabels(event);
break;
case 'KeyM':
handleCardMembers(event);
break;
case 'KeyT':
handleCardNameEdit(event);
break;
case 'KeyV':
handleCardArchive(event);
break;
case 'Digit1':
case 'Digit2':
case 'Digit3':
case 'Digit4':
case 'Digit5':
case 'Digit6':
case 'Digit7':
case 'Digit8':
case 'Digit9':
case 'Digit0':
handleLabelToCardAdd(event);
break;
default:
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [dispatch]);
return (
<BoardShortcutsContext.Provider value={contextValue}>{children}</BoardShortcutsContext.Provider>
);
});
ShortcutsProvider.propTypes = {
children: PropTypes.element.isRequired,
};
export default ShortcutsProvider;

View File

@@ -12,7 +12,7 @@
}
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -13,7 +13,7 @@ import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
import DroppableTypes from '../../../constants/DroppableTypes';
import Item from './Item';
import AddStep from './AddStep';
import AddBoardStep from '../AddBoardStep';
import styles from './Boards.module.scss';
import globalStyles from '../../../styles.module.scss';
@@ -59,7 +59,7 @@ const Boards = React.memo(() => {
});
}, []);
const AddPopup = usePopup(AddStep);
const AddBoardPopup = usePopup(AddBoardStep);
return (
<div className={styles.wrapper} onWheel={handleWheel}>
@@ -74,9 +74,9 @@ const Boards = React.memo(() => {
))}
{placeholder}
{canAdd && (
<AddPopup>
<AddBoardPopup>
<Button icon="plus" className={styles.addButton} />
</AddPopup>
</AddBoardPopup>
)}
</div>
)}

View File

@@ -5,7 +5,7 @@
import upperFirst from 'lodash/upperFirst';
import camelCase from 'lodash/camelCase';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
@@ -14,13 +14,14 @@ import { push } from '../../../lib/redux-router';
import { closePopup, usePopup } from '../../../lib/popup';
import selectors from '../../../selectors';
import { BoardShortcutsContext } from '../../../contexts';
import Paths from '../../../constants/Paths';
import { BoardMembershipRoles, CardTypes } from '../../../constants/Enums';
import ProjectContent from './ProjectContent';
import StoryContent from './StoryContent';
import InlineContent from './InlineContent';
import EditName from './EditName';
import ActionsStep from './ActionsStep';
import CardActionsStep from '../CardActionsStep';
import styles from './Card.module.scss';
import globalStyles from '../../../styles.module.scss';
@@ -50,6 +51,7 @@ const Card = React.memo(({ id, isInline }) => {
const dispatch = useDispatch();
const [isEditNameOpened, setIsEditNameOpened] = useState(false);
const [handleCardMouseEnter, handleCardMouseLeave] = useContext(BoardShortcutsContext);
const actionsPopupRef = useRef(null);
@@ -61,6 +63,22 @@ const Card = React.memo(({ id, isInline }) => {
dispatch(push(Paths.CARDS.replace(':id', id)));
}, [id, dispatch]);
const handleMouseEnter = useCallback(() => {
handleCardMouseEnter(
id,
() => {
setIsEditNameOpened(true);
},
(step) => {
closePopup();
actionsPopupRef.current.open({
defaultStep: step,
});
},
);
}, [id, handleCardMouseEnter]);
const handleContextMenu = useCallback((event) => {
if (!actionsPopupRef.current) {
return;
@@ -80,7 +98,7 @@ const Card = React.memo(({ id, isInline }) => {
setIsEditNameOpened(false);
}, []);
const ActionsPopup = usePopup(ActionsStep);
const CardActionsPopup = usePopup(CardActionsStep);
if (isEditNameOpened) {
return <EditName cardId={id} onClose={handleEditNameClose} />;
@@ -122,6 +140,8 @@ const Card = React.memo(({ id, isInline }) => {
jsx-a11y/no-static-element-interactions */}
<div
className={classNames(styles.content, card.isClosed && styles.contentDisabled)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleCardMouseLeave}
onClick={handleClick}
onContextMenu={handleContextMenu}
>
@@ -129,11 +149,11 @@ const Card = React.memo(({ id, isInline }) => {
{colorLineNode}
</div>
{canUseActions && (
<ActionsPopup ref={actionsPopupRef} cardId={id} onNameEdit={handleNameEdit}>
<CardActionsPopup ref={actionsPopupRef} cardId={id} onNameEdit={handleNameEdit}>
<Button className={styles.actionsButton}>
<Icon fitted name="pencil" size="small" />
</Button>
</ActionsPopup>
</CardActionsPopup>
)}
</>
) : (

View File

@@ -21,6 +21,7 @@
top: 2px;
transition: background 85ms ease;
width: 20px;
z-index: 1000;
&:hover {
background: #ebeef0;
@@ -52,6 +53,7 @@
background: #fff;
border-radius: 3px;
box-shadow: 0 1px 0 #ccc;
overflow: hidden;
position: relative;
&:hover {

View File

@@ -23,12 +23,12 @@ import ConfirmationStep from '../../common/ConfirmationStep';
import BoardMembershipsStep from '../../board-memberships/BoardMembershipsStep';
import LabelsStep from '../../labels/LabelsStep';
import styles from './ActionsStep.module.scss';
import styles from './CardActionsStep.module.scss';
const StepTypes = {
EDIT_TYPE: 'EDIT_TYPE',
USERS: 'USERS',
MEMBERS: 'MEMBERS',
LABELS: 'LABELS',
EDIT_TYPE: 'EDIT_TYPE',
EDIT_DUE_DATE: 'EDIT_DUE_DATE',
EDIT_STOPWATCH: 'EDIT_STOPWATCH',
MOVE: 'MOVE',
@@ -36,7 +36,7 @@ const StepTypes = {
DELETE: 'DELETE',
};
const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
const CardActionsStep = React.memo(({ cardId, defaultStep, onNameEdit, onClose }) => {
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
const selectPrevListById = useMemo(() => selectors.makeSelectListById(), []);
@@ -104,7 +104,7 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
const dispatch = useDispatch();
const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps();
const [step, openStep, handleBack] = useSteps(defaultStep || null);
const handleTypeSelect = useCallback(
(type) => {
@@ -180,18 +180,18 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
onClose();
}, [onNameEdit, onClose]);
const handleEditTypeClick = useCallback(() => {
openStep(StepTypes.EDIT_TYPE);
}, [openStep]);
const handleUsersClick = useCallback(() => {
openStep(StepTypes.USERS);
const handleMembersClick = useCallback(() => {
openStep(StepTypes.MEMBERS);
}, [openStep]);
const handleLabelsClick = useCallback(() => {
openStep(StepTypes.LABELS);
}, [openStep]);
const handleEditTypeClick = useCallback(() => {
openStep(StepTypes.EDIT_TYPE);
}, [openStep]);
const handleEditDueDateClick = useCallback(() => {
openStep(StepTypes.EDIT_DUE_DATE);
}, [openStep]);
@@ -214,19 +214,7 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
if (step) {
switch (step.type) {
case StepTypes.EDIT_TYPE:
return (
<SelectCardTypeStep
withButton
defaultValue={card.type}
title="common.editType"
buttonContent="action.save"
onSelect={handleTypeSelect}
onBack={handleBack}
onClose={onClose}
/>
);
case StepTypes.USERS:
case StepTypes.MEMBERS:
return (
<BoardMembershipsStep
currentUserIds={userIds}
@@ -245,6 +233,18 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
onBack={handleBack}
/>
);
case StepTypes.EDIT_TYPE:
return (
<SelectCardTypeStep
withButton
defaultValue={card.type}
title="common.editType"
buttonContent="action.save"
onSelect={handleTypeSelect}
onBack={handleBack}
onClose={onClose}
/>
);
case StepTypes.EDIT_DUE_DATE:
return <EditDueDateStep cardId={cardId} onBack={handleBack} onClose={onClose} />;
case StepTypes.EDIT_STOPWATCH:
@@ -305,7 +305,7 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
</Menu.Item>
)}
{card.type === CardTypes.PROJECT && canUseMembers && (
<Menu.Item className={styles.menuItem} onClick={handleUsersClick}>
<Menu.Item className={styles.menuItem} onClick={handleMembersClick}>
<Icon name="user outline" className={styles.menuItemIcon} />
{t('common.members', {
context: 'title',
@@ -321,7 +321,7 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
</Menu.Item>
)}
{card.type === CardTypes.STORY && canUseMembers && (
<Menu.Item className={styles.menuItem} onClick={handleUsersClick}>
<Menu.Item className={styles.menuItem} onClick={handleMembersClick}>
<Icon name="user outline" className={styles.menuItemIcon} />
{t('common.members', {
context: 'title',
@@ -395,10 +395,17 @@ const ActionsStep = React.memo(({ cardId, onNameEdit, onClose }) => {
);
});
ActionsStep.propTypes = {
CardActionsStep.propTypes = {
cardId: PropTypes.string.isRequired,
defaultStep: PropTypes.string,
onNameEdit: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
export default ActionsStep;
CardActionsStep.defaultProps = {
defaultStep: undefined,
};
CardActionsStep.StepTypes = StepTypes;
export default CardActionsStep;

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

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

View File

@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { Icon } from 'semantic-ui-react';
import selectors from '../../../selectors';
import { StaticUserIds } from '../../../constants/StaticUsers';
import { isUserStatic } from '../../../utils/record-helpers';
import TimeAgo from '../../common/TimeAgo';
import UserAvatar from '../../users/UserAvatar';
@@ -32,7 +32,7 @@ const CreationDetailsStep = React.memo(({ userId }) => {
</span>
<span className={styles.content}>
<div className={styles.name}>
{user.id === StaticUserIds.DELETED
{isUserStatic(user)
? t(`common.${user.name}`, {
context: 'title',
})

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -9,32 +9,32 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Gallery, Item as GalleryItem } from 'react-photoswipe-gallery';
import { Button, Grid, Icon } from 'semantic-ui-react';
import { useDidUpdate } from '../../../../lib/hooks';
import { useDidUpdate } from '../../../lib/hooks';
import selectors from '../../../../selectors';
import entryActions from '../../../../entry-actions';
import { usePopupInClosableContext } from '../../../../hooks';
import { isUsableMarkdownElement } from '../../../../utils/element-helpers';
import { BoardMembershipRoles, CardTypes, ListTypes } from '../../../../constants/Enums';
import { CardTypeIcons } from '../../../../constants/Icons';
import { ClosableContext } from '../../../../contexts';
import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
import { usePopupInClosableContext } from '../../../hooks';
import { isUsableMarkdownElement } from '../../../utils/element-helpers';
import { BoardMembershipRoles, CardTypes, ListTypes } from '../../../constants/Enums';
import { CardTypeIcons } from '../../../constants/Icons';
import { ClosableContext } from '../../../contexts';
import NameField from './NameField';
import Thumbnail from './Thumbnail';
import NameField from '../NameField';
import CustomFieldGroups from '../CustomFieldGroups';
import Communication from '../Communication';
import CreationDetailsStep from '../CreationDetailsStep';
import MoreActionsStep from '../MoreActionsStep';
import Markdown from '../../../common/Markdown';
import EditMarkdown from '../../../common/EditMarkdown';
import ConfirmationStep from '../../../common/ConfirmationStep';
import UserAvatar from '../../../users/UserAvatar';
import BoardMembershipsStep from '../../../board-memberships/BoardMembershipsStep';
import LabelChip from '../../../labels/LabelChip';
import LabelsStep from '../../../labels/LabelsStep';
import ListsStep from '../../../lists/ListsStep';
import Attachments from '../../../attachments/Attachments';
import AddAttachmentStep from '../../../attachments/AddAttachmentStep';
import AddCustomFieldGroupStep from '../../../custom-field-groups/AddCustomFieldGroupStep';
import CustomFieldGroups from './CustomFieldGroups';
import Communication from './Communication';
import CreationDetailsStep from './CreationDetailsStep';
import MoreActionsStep from './MoreActionsStep';
import Markdown from '../../common/Markdown';
import EditMarkdown from '../../common/EditMarkdown';
import ConfirmationStep from '../../common/ConfirmationStep';
import UserAvatar from '../../users/UserAvatar';
import BoardMembershipsStep from '../../board-memberships/BoardMembershipsStep';
import LabelChip from '../../labels/LabelChip';
import LabelsStep from '../../labels/LabelsStep';
import ListsStep from '../../lists/ListsStep';
import Attachments from '../../attachments/Attachments';
import AddAttachmentStep from '../../attachments/AddAttachmentStep';
import AddCustomFieldGroupStep from '../../custom-field-groups/AddCustomFieldGroupStep';
import styles from './StoryContent.module.scss';

View File

@@ -99,7 +99,7 @@
}
.coverWrapper {
padding-bottom: 20px;
padding-bottom: 10px;
}
.cursorPointer {
@@ -283,7 +283,7 @@
}
.moduleWrapperAttachments {
margin-bottom: 20px;
margin-bottom: 10px;
}
.moreActionsButton {

View File

@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { Item as GalleryItem } from 'react-photoswipe-gallery';
import selectors from '../../../../selectors';
import selectors from '../../../selectors';
import styles from './Thumbnail.module.scss';

View File

@@ -6,5 +6,6 @@
:global(#app) {
.wrapper {
cursor: auto;
outline: none;
}
}

View File

@@ -14,8 +14,7 @@ import { useDidUpdate } from '../../../lib/hooks';
import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
import { usePopupInClosableContext } from '../../../hooks';
import { isListArchiveOrTrash } from '../../../utils/record-helpers';
import { StaticUserIds } from '../../../constants/StaticUsers';
import { isListArchiveOrTrash, isUserStatic } from '../../../utils/record-helpers';
import { BoardMembershipRoles } from '../../../constants/Enums';
import { ClosableContext } from '../../../contexts';
import Edit from './Edit';
@@ -107,7 +106,7 @@ const Item = React.memo(({ id }) => {
) : (
<div className={classNames(styles.bubble, isCurrentUser && styles.bubbleRight)}>
<div className={styles.header}>
{user.id === StaticUserIds.DELETED
{isUserStatic(user)
? t(`common.${user.name}`, {
context: 'title',
})

View File

@@ -20,7 +20,7 @@ import styles from './SmtpPane.module.scss';
const SmtpPane = React.memo(() => {
const config = useSelector(selectors.selectConfig);
const smtpTest = useSelector(selectors.selectSmtpTest);
const smtpTestState = useSelector(selectors.selectSmtpTestState);
const dispatch = useDispatch();
const [t] = useTranslation();
@@ -196,14 +196,14 @@ const SmtpPane = React.memo(() => {
<Button
type="button"
content={t('action.sendTestEmail')}
loading={smtpTest.isLoading}
disabled={smtpTest.isLoading}
loading={smtpTestState.isLoading}
disabled={smtpTestState.isLoading}
onClick={handleTestClick}
/>
)}
</div>
</Form>
{smtpTest.logs && (
{smtpTestState.logs && (
<>
<Divider horizontal>
<Header as="h4">
@@ -215,7 +215,7 @@ const SmtpPane = React.memo(() => {
<TextArea
readOnly
as={TextareaAutosize}
value={smtpTest.logs.join('\n')}
value={smtpTestState.logs.join('\n')}
className={styles.testLog}
/>
</>

View File

@@ -14,6 +14,7 @@ import selectors from '../../../../selectors';
import entryActions from '../../../../entry-actions';
import { useSteps } from '../../../../hooks';
import SelectRoleStep from './SelectRoleStep';
import ApiKeyStep from './ApiKeyStep';
import ConfirmationStep from '../../ConfirmationStep';
import EditUserInformationStep from '../../../users/EditUserInformationStep';
import EditUserUsernameStep from '../../../users/EditUserUsernameStep';
@@ -28,6 +29,7 @@ const StepTypes = {
EDIT_EMAIL: 'EDIT_EMAIL',
EDIT_PASSWORD: 'EDIT_PASSWORD',
EDIT_ROLE: 'EDIT_ROLE',
API_KEY: 'API_KEY',
ACTIVATE: 'ACTIVATE',
DEACTIVATE: 'DEACTIVATE',
DELETE: 'DELETE',
@@ -39,6 +41,7 @@ const ActionsStep = React.memo(({ userId, onClose }) => {
const activeUsersLimit = useSelector(selectors.selectActiveUsersLimit);
const activeUsersTotal = useSelector(selectors.selectActiveUsersTotal);
const user = useSelector((state) => selectUserById(state, userId));
const isCurrentUser = useSelector((state) => user.id === selectors.selectCurrentUserId(state));
const dispatch = useDispatch();
const [t] = useTranslation();
@@ -99,6 +102,10 @@ const ActionsStep = React.memo(({ userId, onClose }) => {
openStep(StepTypes.EDIT_ROLE);
}, [openStep]);
const handleApiKeyClick = useCallback(() => {
openStep(StepTypes.API_KEY);
}, [openStep]);
const handleActivateClick = useCallback(() => {
openStep(StepTypes.ACTIVATE);
}, [openStep]);
@@ -133,6 +140,8 @@ const ActionsStep = React.memo(({ userId, onClose }) => {
onClose={onClose}
/>
);
case StepTypes.API_KEY:
return <ApiKeyStep userId={userId} onBack={handleBack} onClose={onClose} />;
case StepTypes.ACTIVATE:
return (
<ConfirmationStep
@@ -209,7 +218,7 @@ const ActionsStep = React.memo(({ userId, onClose }) => {
})}
</Menu.Item>
)}
{!user.lockedFieldNames.includes('role') && (
{!user.lockedFieldNames.includes('role') && !isCurrentUser && (
<Menu.Item className={styles.menuItem} onClick={handleEditRoleClick}>
<Icon name="sun outline" className={styles.menuItemIcon} />
{t('action.editRole', {
@@ -217,31 +226,44 @@ const ActionsStep = React.memo(({ userId, onClose }) => {
})}
</Menu.Item>
)}
<Menu.Item
disabled={
user.isDeactivated &&
activeUsersLimit !== null &&
activeUsersTotal >= activeUsersLimit
}
className={styles.menuItem}
onClick={user.isDeactivated ? handleActivateClick : handleDeactivateClick}
>
<Icon name={user.isDeactivated ? 'plus' : 'close'} className={styles.menuItemIcon} />
{user.isDeactivated
? t('action.activateUser', {
context: 'title',
})
: t('action.deactivateUser', {
context: 'title',
})}
<Menu.Item className={styles.menuItem} onClick={handleApiKeyClick}>
<Icon name="key" className={styles.menuItemIcon} />
{t('common.apiKey', {
context: 'title',
})}
</Menu.Item>
{user.isDeactivated && !user.isDefaultAdmin && (
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
<Icon name="trash alternate outline" className={styles.menuItemIcon} />
{t('action.deleteUser', {
context: 'title',
})}
</Menu.Item>
{!isCurrentUser && (
<>
<Menu.Item
disabled={
user.isDeactivated &&
activeUsersLimit !== null &&
activeUsersTotal >= activeUsersLimit
}
className={styles.menuItem}
onClick={user.isDeactivated ? handleActivateClick : handleDeactivateClick}
>
<Icon
name={user.isDeactivated ? 'plus' : 'close'}
className={styles.menuItemIcon}
/>
{user.isDeactivated
? t('action.activateUser', {
context: 'title',
})
: t('action.deactivateUser', {
context: 'title',
})}
</Menu.Item>
{user.isDeactivated && !user.isDefaultAdmin && (
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
<Icon name="trash alternate outline" className={styles.menuItemIcon} />
{t('action.deleteUser', {
context: 'title',
})}
</Menu.Item>
)}
</>
)}
</Menu>
</Popup.Content>

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -221,7 +221,7 @@ const AddStep = React.memo(({ onClose }) => {
ref={handleUsernameFieldRef}
name="username"
value={data.username}
maxLength={16}
maxLength={32}
readOnly={isSubmitting}
className={styles.field}
onChange={handleFieldChange}

View File

@@ -0,0 +1,169 @@
/*!
* Copyright (c) 2025 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Button, Icon, Input, Message } from 'semantic-ui-react';
import { Popup } from '../../../../lib/custom-ui';
import selectors from '../../../../selectors';
import entryActions from '../../../../entry-actions';
import { useSteps } from '../../../../hooks';
import ConfirmationStep from '../../ConfirmationStep';
import styles from './ApiKeyStep.module.scss';
const StepTypes = {
REGENERATE: 'REGENERATE',
DELETE: 'DELETE',
};
const ApiKeyStep = React.memo(({ userId, onBack, onClose }) => {
const selectUserById = useMemo(() => selectors.makeSelectUserById(), []);
const user = useSelector((state) => selectUserById(state, userId));
const dispatch = useDispatch();
const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps();
const [isCopied, setIsCopied] = useState(false);
const handleGenerateClick = useCallback(() => {
if (user.apiKeyPrefix) {
openStep(StepTypes.REGENERATE);
} else {
dispatch(entryActions.createUserApiKey(userId));
}
}, [userId, user.apiKeyPrefix, dispatch, openStep]);
const handleRegenerateConfirm = useCallback(() => {
dispatch(entryActions.createUserApiKey(userId));
handleBack();
}, [userId, dispatch, handleBack]);
const handleDeleteConfirm = useCallback(() => {
dispatch(entryActions.deleteUserApiKey(userId));
onClose();
}, [userId, onClose, dispatch]);
const handleDeleteClick = useCallback(() => {
openStep(StepTypes.DELETE);
}, [openStep]);
const handleCopyClick = useCallback(() => {
if (isCopied) {
return;
}
navigator.clipboard.writeText(user.apiKeyState.value);
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1000);
}, [user.apiKeyState.value, isCopied]);
if (step) {
switch (step.type) {
case StepTypes.REGENERATE:
return (
<ConfirmationStep
title="common.regenerateApiKey"
content="common.areYouSureYouWantToRegenerateThisApiKey"
buttonContent="action.regenerateApiKey"
onConfirm={handleRegenerateConfirm}
onBack={handleBack}
/>
);
case StepTypes.DELETE:
return (
<ConfirmationStep
title="common.deleteApiKey"
content="common.areYouSureYouWantToDeleteThisApiKey"
buttonContent="action.deleteApiKey"
onConfirm={handleDeleteConfirm}
onBack={handleBack}
/>
);
default:
}
}
return (
<>
<Popup.Header onBack={onBack}>
{t('common.apiKey', {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
{user.apiKeyPrefix ? (
<>
{!user.apiKeyState.isCreating &&
(user.apiKeyState.value ? (
<>
<Message
positive
header={t('common.apiKeyCreated', {
context: 'title',
})}
content={t('common.saveThisKeyItWillNotBeShownAgain')}
/>
<div className={styles.valueWrapper}>
<Input fluid readOnly value={user.apiKeyState.value} className={styles.value} />
<Button className={styles.copyButton} onClick={handleCopyClick}>
<Icon fitted name={isCopied ? 'check' : 'copy'} />
</Button>
</div>
</>
) : (
<Message
warning
header={`${user.apiKeyPrefix}_...`}
content={t('common.fullKeyIsHiddenForSecurityReasons')}
/>
))}
<Button
fluid
content={t('action.regenerateApiKey')}
loading={user.apiKeyState.isCreating}
disabled={user.apiKeyState.isCreating}
className={styles.actionButton}
onClick={handleGenerateClick}
/>
<Button
fluid
content={t('action.deleteApiKey')}
className={styles.actionButton}
onClick={handleDeleteClick}
/>
</>
) : (
<>
<div className={styles.content}>{t('common.noApiKeyCreated')}</div>
<Button
fluid
positive
content={t('action.createApiKey')}
loading={user.apiKeyState.isCreating}
disabled={user.apiKeyState.isCreating}
onClick={handleGenerateClick}
/>
</>
)}
</Popup.Content>
</>
);
});
ApiKeyStep.propTypes = {
userId: PropTypes.string.isRequired,
onBack: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
export default ApiKeyStep;

View File

@@ -0,0 +1,65 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
:global(#app) {
.actionButton {
background: transparent;
box-shadow: none;
color: #6b808c;
font-weight: normal;
margin-top: 8px;
padding: 6px 11px;
text-align: left;
text-decoration: underline;
transition: background 0.3s ease;
&:hover {
background: #e9e9e9;
}
}
.content {
margin-bottom: 6px;
}
.copyButton {
background: #ebeef0;
box-shadow: none;
border-radius: 3px;
box-sizing: content-box;
color: #516b7a;
display: none;
height: 30px;
margin: 0;
min-height: auto;
outline: none;
padding: 4px;
position: absolute;
right: 0;
top: 0;
transition: background 85ms ease;
width: 20px;
&:hover {
background: #dfe3e6;
color: #4c4c4c;
}
}
.value input {
overflow: hidden;
text-overflow: ellipsis;
}
.valueWrapper {
position: relative;
&:hover:not(:has(input:focus)) {
.copyButton {
display: block;
}
}
}
}

View File

@@ -6,11 +6,13 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Button, Icon, Table } from 'semantic-ui-react';
import { useEventCallback } from '../../../../lib/hooks';
import selectors from '../../../../selectors';
import entryActions from '../../../../entry-actions';
import { usePopupInClosableContext } from '../../../../hooks';
import { UserRoleIcons } from '../../../../constants/Icons';
import ActionsStep from './ActionsStep';
@@ -22,21 +24,62 @@ const Item = React.memo(({ id }) => {
const selectUserById = useMemo(() => selectors.makeSelectUserById(), []);
const user = useSelector((state) => selectUserById(state, id));
const currentUserId = useSelector(selectors.selectCurrentUserId);
const dispatch = useDispatch();
const [t] = useTranslation();
const ActionsPopup = usePopupInClosableContext(ActionsStep);
const handleActionsPopupClose = useEventCallback(() => {
if (user.apiKeyState.value) {
dispatch(entryActions.clearUserApiKeyValue(id));
}
}, [id, user.apiKeyState.value, dispatch]);
const ActionsPopup = usePopupInClosableContext(ActionsStep, {
onClose: handleActionsPopupClose,
});
return (
<Table.Row className={classNames(user.isDeactivated && styles.wrapperDeactivated)}>
<Table.Row
className={classNames(styles.wrapper, user.isDeactivated && styles.wrapperDeactivated)}
>
<Table.Cell>
<UserAvatar id={id} />
<div className={styles.user}>
<UserAvatar id={id} />
<div>
{user.name}
{user.id === currentUserId && (
<div className={styles.note}>{t('common.currentUser')}</div>
)}
</div>
</div>
</Table.Cell>
<Table.Cell>
{user.email}
{user.username && <div className={styles.note}>@{user.username}</div>}
</Table.Cell>
<Table.Cell>
{user.phone && (
<div className={styles.information}>
<Icon name="phone" className={styles.icon} />
{user.phone}
</div>
)}
{user.organization && (
<div className={styles.information}>
<Icon name="building" className={styles.icon} />
{user.organization}
</div>
)}
{user.apiKeyPrefix && (
<div className={classNames(styles.information, styles.informationApiKey)}>
<Icon name="key" className={styles.icon} />
{user.apiKeyPrefix}_...
</div>
)}
</Table.Cell>
<Table.Cell>{user.name}</Table.Cell>
<Table.Cell>{user.username || '-'}</Table.Cell>
<Table.Cell>{user.email}</Table.Cell>
<Table.Cell className={styles.roleCell}>
<Icon name={UserRoleIcons[user.role]} className={styles.roleIcon} />
<Icon name={UserRoleIcons[user.role]} className={styles.icon} />
{t(`common.${user.role}`)}
</Table.Cell>
<Table.Cell textAlign="right">

View File

@@ -9,12 +9,43 @@
margin-right: 0;
}
.icon {
color: #888888;
margin: 0 0.35714286em 0 0;
}
.information {
font-size: 13px;
&:not(:last-of-type) {
margin-bottom: 4px;
}
}
.informationApiKey {
color: #cf513d;
.icon {
color: inherit;
}
}
.note {
color: #888888;
}
.roleCell {
white-space: nowrap;
}
.roleIcon {
margin: 0 0.35714286em 0 0;
.user {
align-items: center;
display: flex;
gap: 8px;
}
.wrapper {
color: #212121;
}
.wrapperDeactivated {

View File

@@ -17,9 +17,9 @@ import AddStep from './AddStep';
import styles from './UsersPane.module.scss';
const UsersPane = React.memo(() => {
const activeUsersLimit = useSelector(selectors.selectActiveUsersLimit);
const users = useSelector(selectors.selectUsersExceptCurrent);
const activeUsersTotal = useSelector(selectors.selectActiveUsersTotal);
const activeUsersLimit = useSelector(selectors.selectActiveUsersLimit);
const users = useSelector(selectors.selectUsers);
const canAdd = useSelector((state) => {
const oidcBootstrap = selectors.selectOidcBootstrap(state);
@@ -48,7 +48,9 @@ const UsersPane = React.memo(() => {
return (
user.email.includes(cleanSearch) ||
user.name.toLowerCase().includes(cleanSearch) ||
(user.username && user.username.includes(cleanSearch))
(user.username && user.username.includes(cleanSearch)) ||
(user.organization && user.organization.toLowerCase().includes(cleanSearch)) ||
(user.apiKeyPrefix && user.apiKeyPrefix.toLowerCase().includes(cleanSearch))
);
}),
[users, isDeactivatedVisible, cleanSearch],
@@ -81,9 +83,8 @@ const UsersPane = React.memo(() => {
<Table.Header>
<Table.Row>
<Table.HeaderCell />
<Table.HeaderCell width={4}>{t('common.name')}</Table.HeaderCell>
<Table.HeaderCell width={4}>{t('common.username')}</Table.HeaderCell>
<Table.HeaderCell width={4}>{t('common.email')}</Table.HeaderCell>
<Table.HeaderCell width={4}>{t('common.identity')}</Table.HeaderCell>
<Table.HeaderCell width={4}>{t('common.information')}</Table.HeaderCell>
<Table.HeaderCell>{t('common.role')}</Table.HeaderCell>
<Table.HeaderCell />
</Table.Row>
@@ -101,7 +102,6 @@ const UsersPane = React.memo(() => {
className={styles.toggleDeactivatedButton}
onClick={handleToggleDeactivatedClick}
/>
{canAdd && (
<AddPopup>
<Button

View File

@@ -0,0 +1,134 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, Icon } from 'semantic-ui-react';
import history from '../../../history';
import Paths from '../../../constants/Paths';
import styles from './GhostError.module.scss';
const GhostError = React.memo(({ message }) => {
const [t] = useTranslation();
const eyesRef = useRef(null);
const handleBackClick = useCallback(() => {
history.back();
}, []);
const handleHomeClick = useCallback(() => {
history.push(Paths.ROOT);
}, []);
useEffect(() => {
let pageX = document.documentElement.clientWidth;
let pageY = document.documentElement.clientHeight;
const handleMouseMove = (event) => {
if (!eyesRef.current) {
return;
}
// Vertical axis
const mouseY = event.pageY;
const yAxis = ((pageY / 2 - mouseY) / pageY) * -300;
// Horizontal axis
const mouseX = event.pageX / -pageX;
const xAxis = -mouseX * 100 - 100;
// Apply transform to eyes
eyesRef.current.style.transform = `translate(${xAxis}%, ${yAxis}%)`;
};
const handleResize = () => {
pageX = document.documentElement.clientWidth;
pageY = document.documentElement.clientHeight;
};
document.addEventListener('mousemove', handleMouseMove);
window.addEventListener('resize', handleResize);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div className={styles.wrapper}>
<div className={styles.symbols}>
<div className={styles.symbol} />
<div className={styles.symbol} />
<div className={styles.symbol} />
<div className={styles.symbol} />
<div className={styles.symbol} />
<div className={styles.symbol} />
</div>
<div className={styles.ghost}>
<div ref={eyesRef} className={styles.eyes}>
<div className={styles.eyeLeft} />
<div className={styles.eyeRight} />
</div>
<div className={styles.bottom}>
<div />
<div />
<div />
<div />
<div />
</div>
</div>
<div className={styles.shadow} />
<div className={styles.message}>
<h1 className={styles.title}>
{t('common.whoops', {
context: 'title',
})}
</h1>
<div className={styles.text}>
{t(message, {
context: 'title',
})}
</div>
{window.history.length > 1 && (
<Button
basic
color="yellow"
size="large"
className={styles.button}
onClick={handleBackClick}
>
<Icon name="arrow left" />
{t('action.goBack')}
</Button>
)}
<Button
basic
color="olive"
size="large"
className={styles.button}
onClick={handleHomeClick}
>
<Icon name="home" />
{t('action.goHome')}
</Button>
</div>
</div>
);
});
GhostError.propTypes = {
message: PropTypes.string,
};
GhostError.defaultProps = {
message: 'common.pageNotFound',
};
export default GhostError;

View File

@@ -0,0 +1,298 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
:global(#app) {
.bottom {
display: flex;
left: 0;
position: absolute;
right: 0;
top: 100%;
div {
background-color: #fff;
border-radius: 100%;
flex-grow: 1;
height: 20px;
position: relative;
top: -10px;
&:nth-child(2n) {
background: transparent;
border-top: 15px solid #22252a;
top: -12px;
}
}
}
.button {
padding: 0.78571429em 1.5em 0.78571429em;
}
.eyeLeft {
background: #22252a;
border-radius: 50%;
height: 12px;
left: 10px;
position: absolute;
width: 12px;
}
.eyeRight {
background: #22252a;
border-radius: 50%;
height: 12px;
position: absolute;
right: 10px;
width: 12px;
}
.eyes {
height: 12px;
left: 50%;
position: absolute;
top: 45%;
transform: translateX(-50%);
width: 70px;
}
.ghost {
animation: upndown 3s ease-in-out infinite;
background: #fff;
border-radius: 100px 100px 0 0;
height: 100px;
margin: 0 auto 20px;
position: relative;
width: 100px;
z-index: 1;
}
.message {
max-width: 400px;
text-align: center;
z-index: 1;
}
.shadow {
animation: smallnbig 3s ease-in-out infinite;
background: #000;
border-radius: 50%;
height: 20px;
margin: 0 auto 30px;
width: 80px;
}
.symbol {
&:nth-child(1) {
animation: shine 4s ease-in-out 3s infinite;
left: 20px;
opacity: 0.3;
position: absolute;
top: 20px;
&:before,
&:after {
background: #6b808c;
border-radius: 3px;
content: "";
height: 3px;
position: absolute;
width: 8px;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
&:nth-child(2) {
animation: shine 4s ease-in-out 1.3s infinite;
border: 2px solid;
border-color: #6b808c;
border-radius: 50%;
height: 12px;
opacity: 0.3;
position: absolute;
right: 30px;
top: 40px;
width: 12px;
}
&:nth-child(3) {
animation: shine 3s ease-in-out 0.5s infinite;
bottom: 30px;
left: 30px;
opacity: 0.3;
position: absolute;
&:before,
&:after {
background: #6b808c;
border-radius: 3px;
content: "";
height: 3px;
position: absolute;
width: 8px;
}
&:before {
transform: rotate(90deg);
}
&:after {
transform: rotate(180deg);
}
}
&:nth-child(4) {
animation: shine 6s ease-in-out 1.6s infinite;
opacity: 0.3;
position: absolute;
right: 30px;
top: 10px;
&:before,
&:after {
background: #6b808c;
border-radius: 3px;
content: "";
height: 3px;
position: absolute;
width: 10px;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
&:nth-child(5) {
animation: shine 1.7s ease-in-out 7s infinite;
border: 2px solid;
border-color: #6b808c;
border-radius: 50%;
height: 8px;
opacity: 0.3;
position: absolute;
right: 5px;
top: 60px;
width: 8px;
}
&:nth-child(6) {
animation: shine 2s ease-in-out 6s infinite;
bottom: 10px;
opacity: 0.3;
position: absolute;
right: 10px;
&:before,
&:after {
background: #6b808c;
border-radius: 3px;
content: "";
height: 3px;
position: absolute;
width: 10px;
}
&:before {
transform: rotate(90deg);
}
&:after {
transform: rotate(180deg);
}
}
}
.symbols {
height: 200px;
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 200px;
}
.text {
color: #6b808c;
font-size: 18px;
line-height: 1.4;
margin-bottom: 30px;
}
.title {
font-size: 32px;
letter-spacing: 0.5px;
}
.wrapper {
align-items: center;
color: #fff;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
min-height: 60vh;
padding: 40px 20px;
position: relative;
width: 100%;
}
@keyframes shine {
0% {
opacity: 0.3;
}
25% {
opacity: 0.1;
}
50% {
opacity: 0.3;
}
100% {
opacity: 0.3;
}
}
@keyframes smallnbig {
0% {
width: 80px;
}
50% {
width: 90px;
}
100% {
width: 80px;
}
}
@keyframes upndown {
0% {
transform: translateY(5px);
}
50% {
transform: translateY(15px);
}
100% {
transform: translateY(5px);
}
}
}

View File

@@ -3,6 +3,6 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import AddStep from './AddStep';
import GhostError from './GhostError';
export default AddStep;
export default GhostError;

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -12,7 +12,7 @@ import selectors from '../../../selectors';
import matchPaths from '../../../utils/match-paths';
import Paths from '../../../constants/Paths';
const Linkify = React.memo(({ href, content, stopPropagation, ...props }) => {
const Link = React.memo(({ href, content, stopPropagation, ...props }) => {
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
const url = useMemo(() => {
@@ -68,14 +68,14 @@ const Linkify = React.memo(({ href, content, stopPropagation, ...props }) => {
);
});
Linkify.propTypes = {
Link.propTypes = {
href: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
stopPropagation: PropTypes.bool,
};
Linkify.defaultProps = {
Link.defaultProps = {
stopPropagation: false,
};
export default Linkify;
export default Link;

View File

@@ -1,21 +0,0 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React from 'react';
import { useTranslation } from 'react-i18next';
const NotFound = React.memo(() => {
const [t] = useTranslation();
return (
<h1>
{t('common.pageNotFound', {
context: 'title',
})}
</h1>
);
});
export default NotFound;

View File

@@ -15,7 +15,7 @@ import { ReduxRouter } from '../../lib/redux-router';
import Paths from '../../constants/Paths';
import Login from './Login';
import Core from './Core';
import NotFound from './NotFound';
import GhostError from './GhostError';
import 'react-datepicker/dist/react-datepicker.css';
import 'photoswipe/dist/photoswipe.css';
@@ -37,7 +37,7 @@ function Root({ store, history }) {
<Route path={Paths.PROJECTS} element={<Core />} />
<Route path={Paths.BOARDS} element={<Core />} />
<Route path={Paths.CARDS} element={<Core />} />
<Route path="*" element={<NotFound />} />
<Route path="*" element={<GhostError />} />
</Routes>
</ToasterProvider>
</ThemeProvider>

View File

@@ -7,13 +7,16 @@ import React, { useRef } from 'react';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { useTranslation, Trans } from 'react-i18next';
import { Icon, Loader } from 'semantic-ui-react';
import { Button, Icon, Loader } from 'semantic-ui-react';
import { useTransitioning } from '../../../lib/hooks';
import { usePopup } from '../../../lib/popup';
import selectors from '../../../selectors';
import { BoardViews } from '../../../constants/Enums';
import Home from '../Home';
import GhostError from '../GhostError';
import Board from '../../boards/Board';
import AddBoardStep from '../../boards/AddBoardStep';
import styles from './Static.module.scss';
@@ -23,6 +26,10 @@ const Static = React.memo(() => {
const isFetching = useSelector(selectors.selectIsContentFetching);
const isFavoritesActive = useSelector(selectors.selectIsFavoritesActiveForCurrentUser);
const canAddBoard = useSelector((state) =>
selectors.selectIsCurrentUserManagerForCurrentProject(state),
);
const [t] = useTranslation();
const wrapperRef = useRef(null);
@@ -31,6 +38,8 @@ const Static = React.memo(() => {
isFavoritesActive,
]);
const AddBoardPopup = usePopup(AddBoardStep);
let wrapperClassNames;
let contentNode;
@@ -42,40 +51,13 @@ const Static = React.memo(() => {
contentNode = <Home />;
} else if (cardId === null) {
wrapperClassNames = [isFavoritesActive && styles.wrapperWithFavorites, styles.wrapperFlex];
contentNode = (
<div className={styles.message}>
<h1>
{t('common.cardNotFound', {
context: 'title',
})}
</h1>
</div>
);
contentNode = <GhostError message="common.cardNotFound" />;
} else if (board === null) {
wrapperClassNames = [isFavoritesActive && styles.wrapperWithFavorites, styles.wrapperFlex];
contentNode = (
<div className={styles.message}>
<h1>
{t('common.boardNotFound', {
context: 'title',
})}
</h1>
</div>
);
contentNode = <GhostError message="common.boardNotFound" />;
} else if (projectId === null) {
wrapperClassNames = [isFavoritesActive && styles.wrapperWithFavorites, styles.wrapperFlex];
contentNode = (
<div className={styles.message}>
<h1>
{t('common.projectNotFound', {
context: 'title',
})}
</h1>
</div>
);
contentNode = <GhostError message="common.projectNotFound" />;
} else if (board === undefined) {
wrapperClassNames = [
isFavoritesActive ? styles.wrapperProjectWithFavorites : styles.wrapperProject,
@@ -93,6 +75,14 @@ const Static = React.memo(() => {
<div className={styles.messageContent}>
<Trans i18nKey="common.createNewOneOrSelectExistingOne" />
</div>
{canAddBoard && (
<AddBoardPopup>
<Button basic positive size="large" className={styles.button}>
<Icon name="plus" />
{t('action.createBoard')}
</Button>
</AddBoardPopup>
)}
</div>
);
} else if (board.isFetching) {

View File

@@ -4,6 +4,11 @@
*/
:global(#app) {
.button {
margin-top: 24px;
padding: 0.78571429em 1.5em 0.78571429em;
}
.message {
align-content: space-between;
align-items: center;

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -14,8 +14,8 @@ import { Button } from 'semantic-ui-react';
import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
import { mentionMarkupToText } from '../../../utils/mentions';
import { isUserStatic } from '../../../utils/record-helpers';
import Paths from '../../../constants/Paths';
import { StaticUserIds } from '../../../constants/StaticUsers';
import { NotificationTypes } from '../../../constants/Enums';
import TimeAgo from '../../common/TimeAgo';
import UserAvatar from '../../users/UserAvatar';
@@ -42,12 +42,11 @@ const Item = React.memo(({ id, onClose }) => {
dispatch(entryActions.deleteNotification(id));
}, [id, dispatch]);
const creatorUserName =
creatorUser.id === StaticUserIds.DELETED
? t(`common.${creatorUser.name}`, {
context: 'title',
})
: creatorUser.name;
const creatorUserName = isUserStatic(creatorUser)
? t(`common.${creatorUser.name}`, {
context: 'title',
})
: creatorUser.name;
const cardName = card ? card.name : notification.data.card.name;

View File

@@ -5,7 +5,7 @@
:global(#app) {
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -42,14 +42,19 @@ const createMessage = (error) => {
}
};
const EditUserEmailStep = React.memo(({ id, withPasswordConfirmation, onBack, onClose }) => {
const EditUserEmailStep = React.memo(({ id, onBack, onClose }) => {
const selectUserById = useMemo(() => selectors.makeSelectUserById(), []);
const {
email,
isSsoUser,
emailUpdateForm: { data: defaultData, isSubmitting, error },
} = useSelector((state) => selectUserById(state, id));
const withPasswordConfirmation = useSelector(
(state) => id === selectors.selectCurrentUserId(state) && !isSsoUser,
);
const dispatch = useDispatch();
const [t] = useTranslation();
const wasSubmitting = usePrevious(isSubmitting);
@@ -199,13 +204,11 @@ const EditUserEmailStep = React.memo(({ id, withPasswordConfirmation, onBack, on
EditUserEmailStep.propTypes = {
id: PropTypes.string.isRequired,
withPasswordConfirmation: PropTypes.bool,
onBack: PropTypes.func,
onClose: PropTypes.func.isRequired,
};
EditUserEmailStep.defaultProps = {
withPasswordConfirmation: false,
onBack: undefined,
};

View File

@@ -38,14 +38,17 @@ const createMessage = (error) => {
}
};
const EditUserPasswordStep = React.memo(({ id, withPasswordConfirmation, onBack, onClose }) => {
const EditUserPasswordStep = React.memo(({ id, onBack, onClose }) => {
const selectUserById = useMemo(() => selectors.makeSelectUserById(), []);
const {
data: defaultData,
isSubmitting,
error,
} = useSelector((state) => selectUserById(state, id).passwordUpdateForm);
isSsoUser,
passwordUpdateForm: { data: defaultData, isSubmitting, error },
} = useSelector((state) => selectUserById(state, id));
const withPasswordConfirmation = useSelector(
(state) => id === selectors.selectCurrentUserId(state) && !isSsoUser,
);
const dispatch = useDispatch();
const [t] = useTranslation();
@@ -168,13 +171,11 @@ const EditUserPasswordStep = React.memo(({ id, withPasswordConfirmation, onBack,
EditUserPasswordStep.propTypes = {
id: PropTypes.string.isRequired,
withPasswordConfirmation: PropTypes.bool,
onBack: PropTypes.func,
onClose: PropTypes.func.isRequired,
};
EditUserPasswordStep.defaultProps = {
withPasswordConfirmation: false,
onBack: undefined,
};

View File

@@ -42,14 +42,19 @@ const createMessage = (error) => {
}
};
const EditUserUsernameStep = React.memo(({ id, withPasswordConfirmation, onBack, onClose }) => {
const EditUserUsernameStep = React.memo(({ id, onBack, onClose }) => {
const selectUserById = useMemo(() => selectors.makeSelectUserById(), []);
const {
username,
isSsoUser,
usernameUpdateForm: { data: defaultData, isSubmitting, error },
} = useSelector((state) => selectUserById(state, id));
const withPasswordConfirmation = useSelector(
(state) => id === selectors.selectCurrentUserId(state) && !isSsoUser,
);
const dispatch = useDispatch();
const [t] = useTranslation();
const wasSubmitting = usePrevious(isSubmitting);
@@ -167,7 +172,7 @@ const EditUserUsernameStep = React.memo(({ id, withPasswordConfirmation, onBack,
name="username"
value={data.username}
placeholder={username}
maxLength={16}
maxLength={32}
className={styles.field}
onChange={handleFieldChange}
/>
@@ -199,13 +204,11 @@ const EditUserUsernameStep = React.memo(({ id, withPasswordConfirmation, onBack,
EditUserUsernameStep.propTypes = {
id: PropTypes.string.isRequired,
withPasswordConfirmation: PropTypes.bool,
onBack: PropTypes.func,
onClose: PropTypes.func.isRequired,
};
EditUserUsernameStep.defaultProps = {
withPasswordConfirmation: false,
onBack: undefined,
};

View File

@@ -13,7 +13,7 @@ import { useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import selectors from '../../../selectors';
import { StaticUserIds } from '../../../constants/StaticUsers';
import { isUserStatic } from '../../../utils/record-helpers';
import styles from './UserAvatar.module.scss';
@@ -62,7 +62,7 @@ const UserAvatar = React.memo(
const contentNode = (
<span
title={
user.id === StaticUserIds.DELETED
isUserStatic(user)
? t(`common.${user.name}`, {
context: 'title',
})

View File

@@ -80,7 +80,7 @@ const AccountPane = React.memo(() => {
</Divider>
{isUsernameEditable && (
<div className={styles.action}>
<EditUserUsernamePopup id={user.id} withPasswordConfirmation={!user.isSsoUser}>
<EditUserUsernamePopup id={user.id}>
<Button className={styles.actionButton}>
{t('action.editUsername', {
context: 'title',
@@ -91,7 +91,7 @@ const AccountPane = React.memo(() => {
)}
{isEmailEditable && (
<div className={styles.action}>
<EditUserEmailPopup id={user.id} withPasswordConfirmation={!user.isSsoUser}>
<EditUserEmailPopup id={user.id}>
<Button className={styles.actionButton}>
{t('action.editEmail', {
context: 'title',
@@ -102,7 +102,7 @@ const AccountPane = React.memo(() => {
)}
{isPasswordEditable && (
<div className={styles.action}>
<EditUserPasswordPopup id={user.id} withPasswordConfirmation={!user.isSsoUser}>
<EditUserPasswordPopup id={user.id}>
<Button className={styles.actionButton}>
{t('action.editPassword', {
context: 'title',

View File

@@ -12,7 +12,7 @@
}
.menu {
margin: -7px -12px -5px;
margin: 0 -12px -5px;
width: calc(100% + 24px);
}

View File

@@ -102,6 +102,13 @@ export default {
USER_AVATAR_UPDATE: 'USER_AVATAR_UPDATE',
USER_AVATAR_UPDATE__SUCCESS: 'USER_AVATAR_UPDATE__SUCCESS',
USER_AVATAR_UPDATE__FAILURE: 'USER_AVATAR_UPDATE__FAILURE',
USER_API_KEY_CREATE: 'USER_API_KEY_CREATE',
USER_API_KEY_CREATE__SUCCESS: 'USER_API_KEY_CREATE__SUCCESS',
USER_API_KEY_CREATE__FAILURE: 'USER_API_KEY_CREATE__FAILURE',
USER_API_KEY_DELETE: 'USER_API_KEY_DELETE',
USER_API_KEY_DELETE__SUCCESS: 'USER_API_KEY_DELETE__SUCCESS',
USER_API_KEY_DELETE__FAILURE: 'USER_API_KEY_DELETE__FAILURE',
USER_API_KEY_VALUE_CLEAR: 'USER_API_KEY_VALUE_CLEAR',
USER_DELETE: 'USER_DELETE',
USER_DELETE__SUCCESS: 'USER_DELETE__SUCCESS',
USER_DELETE__FAILURE: 'USER_DELETE__FAILURE',

View File

@@ -71,6 +71,9 @@ export default {
USER_USERNAME_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_USERNAME_UPDATE_ERROR_CLEAR`,
CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR`,
CURRENT_USER_AVATAR_UPDATE: `${PREFIX}/CURRENT_USER_AVATAR_UPDATE`,
USER_API_KEY_CREATE: `${PREFIX}/USER_API_KEY_CREATE`,
USER_API_KEY_DELETE: `${PREFIX}/USER_API_KEY_DELETE`,
USER_API_KEY_VALUE_CLEAR: `${PREFIX}/USER_API_KEY_VALUE_CLEAR`,
USER_DELETE: `${PREFIX}/USER_DELETE`,
USER_DELETE_HANDLE: `${PREFIX}/USER_DELETE_HANDLE`,
USER_TO_CARD_ADD: `${PREFIX}/USER_TO_CARD_ADD`,
@@ -183,7 +186,7 @@ export default {
CARDS_IN_CURRENT_LIST_FETCH: `${PREFIX}/CARDS_IN_CURRENT_LIST_FETCH`,
CARDS_UPDATE_HANDLE: `${PREFIX}/CARDS_UPDATE_HANDLE`,
CARD_CREATE: `${PREFIX}/CARD_CREATE`,
CARD_IN_FIRST_FINITE_LIST_CREATE: `${PREFIX}/CARD_IN_FIRST_FINITE_LIST_CREATE`,
CARD_IN_CURRENT_CONTEXT_CREATE: `${PREFIX}/CARD_IN_CURRENT_CONTEXT_CREATE`,
CARD_IN_CURRENT_LIST_CREATE: `${PREFIX}/CARD_IN_CURRENT_LIST_CREATE`,
CARD_CREATE_HANDLE: `${PREFIX}/CARD_CREATE_HANDLE`,
CARD_UPDATE: `${PREFIX}/CARD_UPDATE`,

View File

@@ -0,0 +1,8 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createContext } from 'react';
export default createContext([null, null]);

View File

@@ -4,5 +4,6 @@
*/
import ClosableContext from './ClosableContext';
import BoardShortcutsContext from './BoardShortcutsContext';
export { ClosableContext }; // eslint-disable-line import/prefer-default-export
export { ClosableContext, BoardShortcutsContext };

View File

@@ -28,8 +28,8 @@ const createCard = (listId, data, index, autoOpen = false) => ({
},
});
const createCardInFirstFiniteList = (data, index = 0, autoOpen = false) => ({
type: EntryActionTypes.CARD_IN_FIRST_FINITE_LIST_CREATE,
const createCardInCurrentContext = (data, index = 0, autoOpen = false) => ({
type: EntryActionTypes.CARD_IN_CURRENT_CONTEXT_CREATE,
payload: {
data,
index,
@@ -180,7 +180,7 @@ export default {
fetchCardsInCurrentList,
handleCardsUpdate,
createCard,
createCardInFirstFiniteList,
createCardInCurrentContext,
createCardInCurrentList,
handleCardCreate,
updateCard,

View File

@@ -141,6 +141,27 @@ const updateCurrentUserAvatar = (data) => ({
},
});
const createUserApiKey = (id) => ({
type: EntryActionTypes.USER_API_KEY_CREATE,
payload: {
id,
},
});
const deleteUserApiKey = (id) => ({
type: EntryActionTypes.USER_API_KEY_DELETE,
payload: {
id,
},
});
const clearUserApiKeyValue = (id) => ({
type: EntryActionTypes.USER_API_KEY_VALUE_CLEAR,
payload: {
id,
},
});
const deleteUser = (id) => ({
type: EntryActionTypes.USER_DELETE,
payload: {
@@ -245,6 +266,9 @@ export default {
clearUserUsernameUpdateError,
clearCurrentUserUsernameUpdateError,
updateCurrentUserAvatar,
createUserApiKey,
deleteUserApiKey,
clearUserApiKeyValue,
deleteUser,
handleUserDelete,
addUserToCard,

View File

@@ -13,13 +13,13 @@ import styles from './Popup.module.css';
export default (Step, { position, onOpen, onClose } = {}) => {
return useMemo(() => {
const Popup = React.forwardRef(({ children, ...stepProps }, ref) => {
const [isOpened, setIsOpened] = useState(false);
const [stepParams, setStepParams] = useState(null);
const wrapperRef = useRef(null);
const resizeObserverRef = useRef(null);
const open = useCallback(() => {
setIsOpened(true);
const open = useCallback((params = {}) => {
setStepParams(params);
if (onOpen) {
onOpen();
@@ -31,7 +31,7 @@ export default (Step, { position, onOpen, onClose } = {}) => {
}, [open]);
const handleClose = useCallback(() => {
setIsOpened(false);
setStepParams(null);
}, []);
const handleMouseDown = useCallback((event) => {
@@ -96,7 +96,7 @@ export default (Step, { position, onOpen, onClose } = {}) => {
ref={wrapperRef}
trigger={tigger}
on="click"
open={isOpened}
open={!!stepParams}
position={position || 'bottom left'}
popperModifiers={[
{
@@ -118,7 +118,7 @@ export default (Step, { position, onOpen, onClose } = {}) => {
<div ref={handleContentRef}>
<Button icon="close" onClick={handleClose} className={styles.closeButton} />
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<Step {...stepProps} onClose={handleClose} />
<Step {...stepProps} {...stepParams} onClose={handleClose} />
</div>
</SemanticUIPopup>
);

View File

@@ -40,6 +40,8 @@ export default {
'سيتم حفظ جميع التغييرات تلقائياً<br />بعد استعادة الإتصال.',
alphabetically: 'أبجدياً',
alwaysDisplayCardCreator: 'عرض منشئ البطاقة دائماً',
apiKeyCreated_title: 'تم إنشاء مفتاح API',
apiKey_title: 'مفتاح API',
archive: 'أرشيف',
archiveCard_title: 'أرشفة البطاقة',
archiveCards_title: 'أرشفة البطاقات',
@@ -49,6 +51,7 @@ export default {
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'هل أنت متأكد أنك تريد تعيين مدير المشروع هذا كمالك؟',
areYouSureYouWantToDeactivateThisUser: 'هل أنت متأكد أنك تريد إلغاء تفعيل هذا المستخدم؟',
areYouSureYouWantToDeleteThisApiKey: 'هل أنت متأكد أنك تريد حذف مفتاح API هذا؟',
areYouSureYouWantToDeleteThisAttachment: 'هل أنت متأكد أنك تريد حذف هذا المرفق؟',
areYouSureYouWantToDeleteThisBackgroundImage: 'هل أنت متأكد أنك تريد حذف صورة الخلفية هذه؟',
areYouSureYouWantToDeleteThisBoard: 'هل أنت متأكد أنك تريد حذف هذه اللوحة؟',
@@ -73,6 +76,8 @@ export default {
areYouSureYouWantToLeaveProject: 'هل أنت متأكد أنك تريد مغادرة المشروع؟',
areYouSureYouWantToMakeThisProjectPrivate: 'هل أنت متأكد أنك تريد جعل هذا المشروع خاصاً؟',
areYouSureYouWantToMakeThisProjectShared: 'هل أنت متأكد أنك تريد مشاركة هذا المشروع؟',
areYouSureYouWantToRegenerateThisApiKey:
'هل أنت متأكد أنك تريد إعادة إنشاء مفتاح API هذا؟ لن يعمل المفتاح السابق بعد الآن.',
areYouSureYouWantToRemoveThisManagerFromProject:
'هل أنت متأكد أنك تريد إزالة هذا المدير من المشروع؟',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -125,6 +130,7 @@ export default {
createTextFile_title: 'إنشاء ملف نصي',
creator: 'المنشئ',
currentPassword: 'كلمة المرور الحالية',
currentUser: 'المستخدم الحالي',
customFieldGroup_title: 'مجموعة الحقل المخصص',
customFieldGroups_title: 'مجموعات الحقول المخصصة',
customField_title: 'الحقل المخصص',
@@ -136,6 +142,7 @@ export default {
defaultFrom: 'افتراضي من',
defaultView_title: 'العرض الافتراضي',
deleteAllBoardsToBeAbleToDeleteThisProject: 'احذف جميع اللوحات لتتمكن من حذف هذا المشروع',
deleteApiKey_title: 'حذف مفتاح API',
deleteAttachment_title: 'حذف المرفق',
deleteBackgroundImage_title: 'حذف صورة الخلفية',
deleteBoard_title: 'حذف اللوحة',
@@ -191,6 +198,8 @@ export default {
forTeamBasedProjects: 'للمشاريع الجماعية.',
fromComputer_title: 'من الكمبيوتر',
fromTrello: 'من Trello',
fullKeyIsHiddenForSecurityReasons:
'المفتاح الكامل مخفي لأسباب أمنية. قم بإعادة إنشائه لإنشاء واحد جديد.',
general: 'عام',
gradients: 'التدرجات',
grid: 'الشبكة',
@@ -198,7 +207,9 @@ export default {
hideFromProjectListAndFavorites: 'إخفاء من قائمة المشاريع والمفضلة',
host: 'المضيف',
hours: 'ساعات',
identity: 'الهوية',
importBoard_title: 'استيراد اللوحة',
information: 'المعلومات',
invalidCurrentPassword: 'كلمة المرور الحالية غير صالحة',
kanban: 'كانبان',
labels: 'الملصقات',
@@ -227,6 +238,7 @@ export default {
newUsername: 'مستخدم جديد',
newVersionAvailable: 'إصدار جديد متاح',
newestFirst: 'الأحدث أولاً',
noApiKeyCreated: 'لم يتم إنشاء مفتاح API.',
noBoards: 'لا توجد لوحات',
noCardsFound: 'لم يتم العثور على بطاقات.',
noConnectionToServer: 'لا يوجد اتصال بالخادم',
@@ -254,10 +266,12 @@ export default {
projectNotFound_title: 'المشروع غير موجود',
projectOwner: 'مالك المشروع',
referenceDataAndKnowledgeStorage: 'تخزين البيانات المرجعية والمعرفة.',
regenerateApiKey_title: 'إعادة إنشاء مفتاح API',
rejectUnauthorizedTlsCertificates: 'رفض شهادات TLS غير المصرح بها',
removeManager_title: 'إزالة المدير',
removeMember_title: 'إزالة العضو',
role: 'الدور',
saveThisKeyItWillNotBeShownAgain: 'احفظ هذا المفتاح — لن يتم عرضه مرة أخرى!',
searchCards: 'البحث عن البطاقات...',
searchCustomFieldGroups: 'البحث عن مجموعات الحقول المخصصة...',
searchCustomFields: 'البحث عن الحقول المخصصة...',
@@ -369,6 +383,7 @@ export default {
archiveCards_title: 'أرشفة البطاقات',
assignAsOwner: 'تعيين كمالك',
cancel: 'إلغاء',
createApiKey: 'إنشاء مفتاح API',
createBoard: 'إنشاء لوحة',
createCustomFieldGroup: 'إنشاء مجموعة حقل مخصص',
createFile: 'إنشاء ملف',
@@ -378,6 +393,7 @@ export default {
deactivateUser: 'إلغاء تفعيل المستخدم',
deactivateUser_title: 'إلغاء تفعيل المستخدم',
delete: 'حذف',
deleteApiKey: 'حذف مفتاح API',
deleteAttachment: 'حذف المرفق',
deleteAvatar: 'حذف الصورة الرمزية',
deleteBackgroundImage: 'حذف صورة الخلفية',
@@ -436,6 +452,7 @@ export default {
move: 'نقل',
moveCard_title: 'نقل البطاقة',
moveList_title: 'نقل القائمة',
regenerateApiKey: 'إعادة إنشاء مفتاح API',
remove: 'حذف',
removeAssignee: 'إزالة المكلف',
removeColor: 'إزالة اللون',

View File

@@ -19,11 +19,14 @@ export default {
unknownError: 'خطأ غير معروف، يرجى المحاولة لاحقاً',
useSingleSignOn: 'استخدم تسجيل الدخول الموحد',
usernameAlreadyInUse: 'اسم المستخدم تم استخدامه بالفعل',
whoops_title: 'عفواً!',
},
action: {
cancelAndClose: 'إلغاء وإغلاق',
continue: 'متابعة',
goBack: 'العودة',
goHome: 'الذهاب للرئيسية',
logIn: 'تسجيل الدخول',
logInWithSso: 'تسجيل الدخول باستخدام SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'Всички промени ще бъдат автоматично запазени<br />след възстановяване на връзката.',
alphabetically: 'По азбучен ред',
alwaysDisplayCardCreator: 'Винаги показвай създателя на картата',
apiKeyCreated_title: 'API ключ създаден',
apiKey_title: 'API ключ',
archive: 'Архив',
archiveCard_title: 'Архивиране на карта',
archiveCards_title: 'Архивиране на карти',
@@ -51,6 +53,7 @@ export default {
'Сигурни ли сте, че искате да назначите този мениджър на проекта като собственик?',
areYouSureYouWantToDeactivateThisUser:
'Сигурни ли сте, че искате да деактивирате този потребител?',
areYouSureYouWantToDeleteThisApiKey: 'Сигурни ли сте, че искате да изтриете този API ключ?',
areYouSureYouWantToDeleteThisAttachment:
'Сигурни ли сте, че искате да изтриете този прикачен файл?',
areYouSureYouWantToDeleteThisBackgroundImage:
@@ -82,6 +85,8 @@ export default {
'Сигурни ли сте, че искате да направите този проект частен?',
areYouSureYouWantToMakeThisProjectShared:
'Сигурни ли сте, че искате да споделите този проект?',
areYouSureYouWantToRegenerateThisApiKey:
'Сигурни ли сте, че искате да регенерирате този API ключ? Предишният ключ няма да работи повече.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Сигурни ли сте, че искате да премахнете този мениджър от проекта?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -136,6 +141,7 @@ export default {
createTextFile_title: 'Създаване на текстов файл',
creator: 'Създател',
currentPassword: 'Текуща парола',
currentUser: 'Текущ потребител',
customFieldGroup_title: 'Група персонализирани полета',
customFieldGroups_title: 'Групи персонализирани полета',
customField_title: 'Персонализирано поле',
@@ -148,6 +154,7 @@ export default {
defaultView_title: 'Изглед по подразбиране',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Изтрийте всички табла, за да можете да изтриете този проект',
deleteApiKey_title: 'Изтриване на API ключ',
deleteAttachment_title: 'Изтриване на прикачен файл',
deleteBackgroundImage_title: 'Изтриване на фоново изображение',
deleteBoard_title: 'Изтриване на табло',
@@ -203,6 +210,8 @@ export default {
forTeamBasedProjects: 'За екипни проекти.',
fromComputer_title: 'От компютър',
fromTrello: 'От Trello',
fullKeyIsHiddenForSecurityReasons:
'Пълният ключ е скрит от съображения за сигурност. Регенерирайте го, за да създадете нов.',
general: 'Общ',
gradients: 'Градиенти',
grid: 'Мрежа',
@@ -210,7 +219,9 @@ export default {
hideFromProjectListAndFavorites: 'Скриване от списъка с проекти и любими',
host: 'Хост',
hours: 'Часове',
identity: 'Самоличност',
importBoard_title: 'Импортиране на табло',
information: 'Информация',
invalidCurrentPassword: 'Невалидна текуща парола',
kanban: 'Канбан',
labels: 'Етикети',
@@ -239,6 +250,7 @@ export default {
newUsername: 'Ново потребителско име',
newVersionAvailable: 'Налична е нова версия',
newestFirst: 'Първо най-новите',
noApiKeyCreated: 'Няма създаден API ключ.',
noBoards: 'Няма табла',
noCardsFound: 'Не са намерени карти.',
noConnectionToServer: 'Няма връзка със сървъра',
@@ -266,10 +278,12 @@ export default {
projectNotFound_title: 'Проектът не е намерен',
projectOwner: 'Собственик на проект',
referenceDataAndKnowledgeStorage: 'Съхранение на референтни данни и знания.',
regenerateApiKey_title: 'Регенериране на API ключ',
rejectUnauthorizedTlsCertificates: 'Отхвърляне на неоторизирани TLS сертификати',
removeManager_title: 'Премахване на мениджър',
removeMember_title: 'Премахване на член',
role: 'Роля',
saveThisKeyItWillNotBeShownAgain: 'Запазете този ключ — няма да бъде показан отново!',
searchCards: 'Търсене на карти...',
searchCustomFieldGroups: 'Търсене на групи персонализирани полета...',
searchCustomFields: 'Търсене на персонализирани полета...',
@@ -383,6 +397,7 @@ export default {
archiveCards_title: 'Архивиране на карти',
assignAsOwner: 'Назначаване като собственик',
cancel: 'Отказ',
createApiKey: 'Създаване на API ключ',
createBoard: 'Създаване на табло',
createCustomFieldGroup: 'Създаване на група персонализирани полета',
createFile: 'Създаване на файл',
@@ -392,6 +407,7 @@ export default {
deactivateUser: 'Деактивиране на потребител',
deactivateUser_title: 'Деактивиране на потребител',
delete: 'Изтриване',
deleteApiKey: 'Изтриване на API ключ',
deleteAttachment: 'Изтриване на прикачения файл',
deleteAvatar: 'Изтриване на аватар',
deleteBackgroundImage: 'Изтриване на фоново изображение',
@@ -450,6 +466,7 @@ export default {
move: 'Преместване',
moveCard_title: 'Преместване на карта',
moveList_title: 'Преместване на списък',
regenerateApiKey: 'Регенериране на API ключ',
remove: 'Премахване',
removeAssignee: 'Премахване на изпълнител',
removeColor: 'Премахване на цвят',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Неизвестна грешка, опитайте отново по-късно',
useSingleSignOn: 'Използване на single sign-on',
usernameAlreadyInUse: 'Потребителското име вече се използва',
whoops_title: 'Опа!',
},
action: {
cancelAndClose: 'Отказ и затваряне',
continue: 'Продължи',
goBack: 'Назад',
goHome: 'Към началото',
logIn: 'Вход',
logInWithSso: 'Вход чрез SSO',
},

View File

@@ -0,0 +1,499 @@
import dateFns from 'date-fns/locale/ca';
import timeAgo from 'javascript-time-ago/locale/ca';
import markdownEditor from './markdown-editor.json';
export default {
dateFns,
timeAgo,
markdownEditor,
format: {
date: 'd/M/yyyy',
time: 'p',
dateTime: '$t(format:date) $t(format:time)',
longDate: 'd MMM',
longDateTime: "d 'de' MMMM 'a les' p",
fullDate: 'd MMM y',
fullDateTime: "d 'de' MMMM 'de' y 'a les' p",
},
translation: {
common: {
aboutPlanka: 'Quant a PLANKA',
accessToken: "Token d'accés",
account: 'Compte',
actions: 'Accions',
activateUser_title: 'Activar usuari',
active: 'Actiu',
addAttachment_title: 'Afegir fitxer adjunt',
addCustomFieldGroup_title: 'Afegir grup de camps personalitzats',
addCustomField_title: 'Afegir camp personalitzat',
addManager_title: 'Afegir gestor',
addMember_title: 'Afegir membre',
addTaskList_title: 'Afegir llista de tasques',
addUser_title: 'Afegir usuari',
admin: 'Administrador',
administration: 'Administració',
all: 'Tot',
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'Tots els canvis es desaran automàticament<br />quan es restableixi la connexió.',
alphabetically: 'Alfabèticament',
alwaysDisplayCardCreator: 'Mostrar sempre el creador de la targeta',
apiKeyCreated_title: 'Clau API creada',
apiKey_title: 'Clau API',
archive: 'Arxivar',
archiveCard_title: 'Arxivar targeta',
archiveCards_title: 'Arxivar targetes',
areYouSureYouWantToActivateThisUser: 'Estàs segur que vols activar aquest usuari?',
areYouSureYouWantToArchiveCards: 'Estàs segur que vols arxivar les targetes?',
areYouSureYouWantToArchiveThisCard: 'Estàs segur que vols arxivar aquesta targeta?',
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'Estàs segur que vols assignar aquest gestor de projecte com a propietari?',
areYouSureYouWantToDeactivateThisUser: 'Estàs segur que vols desactivar aquest usuari?',
areYouSureYouWantToDeleteThisApiKey: 'Estàs segur que vols eliminar aquesta clau API?',
areYouSureYouWantToDeleteThisAttachment:
'Estàs segur que vols eliminar aquest fitxer adjunt?',
areYouSureYouWantToDeleteThisBackgroundImage:
'Estàs segur que vols eliminar aquesta imatge de fons?',
areYouSureYouWantToDeleteThisBoard: 'Estàs segur que vols eliminar aquest tauler?',
areYouSureYouWantToDeleteThisCard: 'Estàs segur que vols eliminar aquesta targeta?',
areYouSureYouWantToDeleteThisCardForever:
'Estàs segur que vols eliminar aquesta targeta per sempre?',
areYouSureYouWantToDeleteThisComment: 'Estàs segur que vols eliminar aquest comentari?',
areYouSureYouWantToDeleteThisCustomField:
'Estàs segur que vols eliminar aquest camp personalitzat?',
areYouSureYouWantToDeleteThisCustomFieldGroup:
'Estàs segur que vols eliminar aquest grup de camps personalitzats?',
areYouSureYouWantToDeleteThisLabel: 'Estàs segur que vols eliminar aquesta etiqueta?',
areYouSureYouWantToDeleteThisList:
'Estàs segur que vols eliminar aquesta llista? Totes les targetes es mouran a la paperera.',
areYouSureYouWantToDeleteThisNotificationService:
'Estàs segur que vols eliminar aquest servei de notificació?',
areYouSureYouWantToDeleteThisProject: 'Estàs segur que vols eliminar aquest projecte?',
areYouSureYouWantToDeleteThisTask: 'Estàs segur que vols eliminar aquesta tasca?',
areYouSureYouWantToDeleteThisTaskList:
'Estàs segur que vols eliminar aquesta llista de tasques?',
areYouSureYouWantToDeleteThisUser: 'Estàs segur que vols eliminar aquest usuari?',
areYouSureYouWantToDeleteThisWebhook: 'Estàs segur que vols eliminar aquest webhook?',
areYouSureYouWantToEmptyTrash: 'Estàs segur que vols buidar la paperera?',
areYouSureYouWantToLeaveBoard: 'Estàs segur que vols abandonar el tauler?',
areYouSureYouWantToLeaveProject: 'Estàs segur que vols abandonar el projecte?',
areYouSureYouWantToMakeThisProjectPrivate:
'Estàs segur que vols canviar aquest projecte a privat?',
areYouSureYouWantToMakeThisProjectShared:
'Estàs segur que vols canviar aquest projecte a compartit?',
areYouSureYouWantToRegenerateThisApiKey:
'Estàs segur que vols regenerar aquesta clau API? La clau anterior ja no funcionarà.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Estàs segur que vols eliminar aquest gestor del projecte?',
areYouSureYouWantToRemoveThisMemberFromBoard:
'Estàs segur que vols eliminar aquest membre del tauler?',
assignAsOwner_title: 'Assignar com a propietari',
atLeastOneListMustBePresent: "Hi ha d'haver com a mínim una llista",
attachment: 'Fitxer adjunt',
attachments: 'Fitxers adjunts',
authentication: 'Autenticació',
background: 'Fons',
baseCustomFields_title: 'Camps personalitzats base',
baseGroup: 'Grup base',
board: 'Tauler',
boardActions_title: 'Accions del tauler',
boardNotFound_title: 'Tauler no trobat',
boardSubscribed: 'Tauler subscrit',
boardUser: 'Usuari del tauler',
byCreationTime: 'Per data de creació',
byDefault: 'Per defecte',
byDueDate: 'Per data de venciment',
canBeInvitedToWorkInBoards: 'Pot ser convidat a treballar a taulers.',
canComment: 'Pot comentar',
canCreateOwnProjectsAndBeInvitedToWorkInOthers:
'Pot crear projectes propis i ser convidat a treballar a altres.',
canEditBoardLayoutAndAssignMembersToCards:
'Pot editar el disseny del tauler i assignar membres a les targetes.',
canManageSystemWideSettingsAndActAsProjectOwner:
'Pot gestionar la configuració general del sistema i actuar com a propietari del projecte.',
canOnlyViewBoard: 'Només pot veure el tauler.',
cardActions_title: 'Accions de la targeta',
cardNotFound_title: 'Targeta no trobada',
cardsOnThisListAreAvailableToAllBoardMembers:
"Les targetes d'aquesta llista estan disponibles per a tots els membres del tauler.",
cardsOnThisListAreCompleteAndReadyToBeArchived:
"Les targetes d'aquesta llista estan completes i llestes per ser arxivades.",
cardsOnThisListAreReadyToBeWorkedOn:
"Les targetes d'aquesta llista estan llestes per treballar-hi.",
clickHereOrRefreshPageToUpdate:
'<0>Fes clic aquí</0> o actualitza la pàgina per actualitzar.',
clientHostnameInEhlo: "Nom de l'amfitrió del client en EHLO",
closed: 'Tancat',
color: 'Color',
comments: 'Comentaris',
contentExceedsLimit: 'El contingut excedeix {{limit}}',
contentOfThisAttachmentIsTooBigToDisplay:
"El contingut d'aquest fitxer adjunt és massa gran per mostrar-se.",
copy_inline: 'còpia',
createBoard_title: 'Crear tauler',
createCustomFieldGroup_title: 'Crear grup de camps personalitzats',
createLabel_title: 'Crear etiqueta',
createNewOneOrSelectExistingOne: "Crea'n un de nou o selecciona'n<br />un d'existent.",
createProject_title: 'Crear projecte',
createTextFile_title: 'Crear fitxer de text',
creator: 'Creador',
currentPassword: 'Contrasenya actual',
currentUser: 'Usuari actual',
customFieldGroup_title: 'Grup de camps personalitzats',
customFieldGroups_title: 'Grups de camps personalitzats',
customField_title: 'Camp personalitzat',
customFields_title: 'Camps personalitzats',
dangerZone_title: 'Zona de perill',
date: 'Data',
deactivateUser_title: 'Desactivar usuari',
defaultCardType_title: 'Tipus de targeta per defecte',
defaultFrom: '"De" per defecte',
defaultView_title: 'Vista per defecte',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Elimina tots els taulers per poder eliminar aquest projecte',
deleteApiKey_title: 'Eliminar clau API',
deleteAttachment_title: 'Eliminar fitxer adjunt',
deleteBackgroundImage_title: 'Eliminar imatge de fons',
deleteBoard_title: 'Eliminar tauler',
deleteCardForever_title: 'Eliminar targeta per sempre',
deleteCard_title: 'Eliminar targeta',
deleteComment_title: 'Eliminar comentari',
deleteCustomFieldGroup_title: 'Eliminar grup de camps personalitzats',
deleteCustomField_title: 'Eliminar camp personalitzat',
deleteLabel_title: 'Eliminar etiqueta',
deleteList_title: 'Eliminar llista',
deleteNotificationService_title: 'Eliminar servei de notificació',
deleteProject_title: 'Eliminar projecte',
deleteTaskList_title: 'Eliminar llista de tasques',
deleteTask_title: 'Eliminar tasca',
deleteUser_title: 'Eliminar usuari',
deleteWebhook_title: 'Eliminar webhook',
deletedUser_title: 'Usuari eliminat',
description: 'Descripció',
display: 'Mostrar',
dropFileToUpload: 'Arrossega fitxer per pujar-lo',
dueDate_title: 'Data de venciment',
dynamicAndUnevenlySpacedLayout: 'Disseny dinàmic i amb espaiat irregular.',
editAttachment_title: 'Editar fitxer adjunt',
editAvatar_title: 'Editar avatar',
editColor_title: 'Editar color',
editCustomFieldGroup_title: 'Editar grup de camps personalitzats',
editCustomField_title: 'Editar camp personalitzat',
editDueDate_title: 'Editar data de venciment',
editEmail_title: 'Editar correu electrònic',
editInformation_title: 'Editar informació',
editLabel_title: 'Editar etiqueta',
editPassword_title: 'Editar contrasenya',
editPermissions_title: 'Editar permisos',
editRole_title: 'Editar rol',
editStopwatch_title: 'Editar cronòmetre',
editType_title: 'Editar tipus',
editUsername_title: "Editar nom d'usuari",
editor: 'Editor',
editors: 'Editors',
email: 'Correu electrònic',
emptyTrash_title: 'Buidar paperera',
enterCardTitle: 'Introdueix el títol de la targeta...',
enterDescription: 'Introdueix una descripció...',
enterFilename: 'Introdueix el nom del fitxer',
enterListTitle: 'Introdueix el títol de la llista...',
enterTaskDescription: 'Introdueix la descripció de la tasca...',
events: 'Esdeveniments',
excludedEvents: 'Esdeveniments exclosos',
expandTaskListsByDefault: 'Expandir llistes de tasques per defecte',
filterByLabels_title: 'Filtrar per etiquetes',
filterByMembers_title: 'Filtrar per membres',
forPersonalProjects: 'Per a projectes personals.',
forTeamBasedProjects: 'Per a projectes en equip.',
fromComputer_title: "Des de l'ordinador",
fromTrello: 'Des de Trello',
fullKeyIsHiddenForSecurityReasons:
'La clau completa està oculta per raons de seguretat. Regenera-la per crear una de nova.',
general: 'General',
gradients: 'Degradats',
grid: 'Graella',
hideCompletedTasks: 'Ocultar tasques completes',
hideFromProjectListAndFavorites: 'Ocultar de la llista de projectes i favorits',
host: 'Amfitrió',
hours: 'Hores',
identity: 'Identitat',
importBoard_title: 'Importar tauler',
information: 'Informació',
invalidCurrentPassword: 'Contrasenya actual incorrecta',
kanban: 'Kanban',
labels: 'Etiquetes',
language: 'Idioma',
leaveBoard_title: 'Abandonar tauler',
leaveProject_title: 'Abandonar projecte',
limitCardTypesToDefaultOne: 'Limitar tipus de targeta al predeterminat',
linkToCard: 'Enllaç a la targeta',
list: 'Llista',
listActions_title: 'Accions de la llista',
lists: 'Llistes',
makeProjectPrivate_title: 'Canviar el projecte a privat',
makeProjectShared_title: 'Canviar el projecte a compartit',
managers: 'Gestors',
memberActions_title: 'Accions del membre',
members: 'Membres',
minutes: 'Minuts',
moreActions: 'Més accions',
moreActions_title: 'Més accions',
moveCard_title: 'Moure targeta',
moveList_title: 'Moure llista',
myOwn_title: 'Propis',
name: 'Nom',
newEmail: 'Nou correu electrònic',
newPassword: 'Nova contrasenya',
newUsername: "Nou nom d'usuari",
newVersionAvailable: 'Nova versió disponible',
newestFirst: 'Més recents primer',
noApiKeyCreated: "No s'ha creat cap clau API.",
noBoards: 'No hi ha taulers',
noCardsFound: "No s'han trobat targetes.",
noConnectionToServer: 'Sense connexió al servidor',
noLists: 'No hi ha llistes',
noProjects: 'No hi ha projectes',
noUnreadNotifications: 'No hi ha notificacions sense llegir.',
notifications: 'Notificacions',
oldestFirst: 'Més antics primer',
onlyOneManagerShouldRemainToMakeThisProjectPrivate:
'Només ha de quedar un gestor per canviar aquest projecte a privat',
openBoard_title: 'Obrir tauler',
optional_inline: 'opcional',
organization: 'Organització',
others: 'Altres',
passwordIsSet: 'La contrasenya està establerta',
phone: 'Telèfon',
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
'PLANKA utilitza <1><0>Apprise</0></1> per enviar notificacions a més de 100 serveis populars.',
port: 'Port',
preferences: 'Preferències',
pressPasteShortcutToAddAttachmentFromClipboard:
'Consell: prem Ctrl-V (Cmd-V a Mac) per afegir un fitxer adjunt des del porta-retalls.',
private: 'Privat',
project: 'Projecte',
projectNotFound_title: "No s'ha trobat el projecte",
projectOwner: 'Propietari del projecte',
referenceDataAndKnowledgeStorage: 'Emmagatzematge de dades de referència i coneixement.',
regenerateApiKey_title: 'Regenerar clau API',
rejectUnauthorizedTlsCertificates: 'Rebutjar certificats TLS no autoritzats',
removeManager_title: 'Eliminar gestor',
removeMember_title: 'Eliminar membre',
role: 'Rol',
saveThisKeyItWillNotBeShownAgain: 'Desa aquesta clau, no es tornarà a mostrar!',
searchCards: 'Cercar targetes...',
searchCustomFieldGroups: 'Cercar grups de camps personalitzats...',
searchCustomFields: 'Cercar camps personalitzats...',
searchLabels: 'Cercar etiquetes...',
searchLists: 'Cercar llistes...',
searchMembers: 'Cercar membres...',
searchProjects: 'Cercar projectes...',
searchUsers: 'Cercar usuaris...',
seconds: 'Segons',
selectAssignee_title: 'Seleccionar assignat',
selectBoard: 'Seleccionar tauler',
selectList: 'Seleccionar llista',
selectListToRestoreThisCard: 'Selecciona una llista per restaurar aquesta targeta',
selectOrder_title: 'Seleccionar ordre',
selectPermissions_title: 'Seleccionar permisos',
selectProject: 'Seleccionar projecte',
selectRole_title: 'Seleccionar rol',
selectType_title: 'Seleccionar tipus',
sequentialDisplayOfCards: 'Visualització seqüencial de targetes.',
settings: 'Configuració',
shared: 'Compartit',
sharedWithMe_title: 'Compartit amb mi',
showOnFrontOfCard: 'Mostrar a la part frontal de la targeta',
smtp: 'SMTP',
sortList_title: 'Ordenar llista',
stopwatch: 'Cronòmetre',
story: 'Història',
subscribeToCardWhenCommenting: "Subscriure's a la targeta en comentar",
subscribeToMyOwnCardsByDefault: "Subscriure's a les meves pròpies targetes per defecte",
taskActions_title: 'Accions de la tasca',
taskAssignmentAndProjectCompletion: 'Assignació de tasques i finalització del projecte.',
taskListActions_title: 'Accions de la llista de tasques',
taskList_title: 'Llista de tasques',
team: 'Equip',
terms: 'Termes',
testLog_title: 'Registre de prova',
thereIsNoPreviewAvailableForThisAttachment:
'No hi ha vista prèvia disponible per a aquest fitxer adjunt.',
time: 'Hora',
title: 'Títol',
trash: 'Paperera',
trashHasBeenSuccessfullyEmptied: "La paperera s'ha buidat correctament.",
turnOffRecentCardHighlighting: 'Desactivar ressaltat de targetes recents',
typeNameToConfirm: 'Escriu el nom per confirmar.',
typeTitleToConfirm: 'Escriu el títol per confirmar.',
unsavedChanges: 'Canvis sense desar',
uploadFailedFileIsTooBig: 'Error en pujar: El fitxer és massa gran.',
uploadFailedNotEnoughStorageSpace: "Error en pujar: No hi ha prou espai d'emmagatzematge.",
uploadedImages: 'Imatges pujades',
url: 'URL',
useSecureConnection: 'Utilitzar connexió segura',
userActions_title: "Accions de l'usuari",
userAddedCardToList: '<0>{{user}}</0> ha afegit <2>{{card}}</2> a {{list}}',
userAddedThisCardToList: '<0>{{user}}</0> ha afegit aquesta targeta a {{list}}',
userAddedUserToCard: '<0>{{actorUser}}</0> ha afegit {{addedUser}} a <4>{{card}}</4>',
userAddedUserToThisCard: '<0>{{actorUser}}</0> ha afegit {{addedUser}} a aquesta targeta',
userAddedYouToCard: "<0>{{user}}</0> t'ha afegit a <2>{{card}}</2>",
userCompletedTaskOnCard: '<0>{{user}}</0> ha completat {{task}} a <4>{{card}}</4>',
userCompletedTaskOnThisCard: '<0>{{user}}</0> ha completat {{task}} a aquesta targeta',
userJoinedCard: "<0>{{user}}</0> s'ha unit a <2>{{card}}</2>",
userJoinedThisCard: "<0>{{user}}</0> s'ha unit a aquesta targeta",
userLeftCard: '<0>{{user}}</0> ha abandonat <2>{{card}}</2>',
userLeftNewCommentToCard:
'<0>{{user}}</0> ha deixat un nou comentari «{{comment}}» a <2>{{card}}</2>',
userLeftThisCard: '<0>{{user}}</0> ha abandonat aquesta targeta',
userMarkedTaskIncompleteOnCard:
'<0>{{user}}</0> ha marcat {{task}} com a incompleta a <4>{{card}}</4>',
userMarkedTaskIncompleteOnThisCard:
'<0>{{user}}</0> ha marcat {{task}} com a incompleta a aquesta targeta',
userMentionedYouInCommentOnCard:
"<0>{{user}}</0> t'ha esmentat en un comentari «{{comment}}» a <2>{{card}}</2>",
userMovedCardFromListToList:
'<0>{{user}}</0> ha mogut <2>{{card}}</2> de {{fromList}} a {{toList}}',
userMovedThisCardFromListToList:
'<0>{{user}}</0> ha mogut aquesta targeta de {{fromList}} a {{toList}}',
userRemovedUserFromCard:
'<0>{{actorUser}}</0> ha eliminat {{removedUser}} de <4>{{card}}</4>',
userRemovedUserFromThisCard:
"<0>{{actorUser}}</0> ha eliminat {{removedUser}} d'aquesta targeta",
username: "Nom d'usuari",
users: 'Usuaris',
viewer: 'Observador',
viewers: 'Observadors',
visualTaskManagementWithLists: 'Gestió visual de tasques amb llistes.',
webhooks: 'Webhooks',
withoutBaseGroup: 'Sense grup base',
writeComment: 'Escriu un comentari...',
},
action: {
activateUser: 'Activar usuari',
activateUser_title: 'Activar usuari',
addAnotherCard: 'Afegir una altra targeta',
addAnotherList: 'Afegir una altra llista',
addAnotherTask: 'Afegir una altra tasca',
addCard: 'Afegir targeta',
addCard_title: 'Afegir targeta',
addComment: 'Afegir comentari',
addCustomField: 'Afegir camp personalitzat',
addCustomFieldGroup: 'Afegir grup de camps personalitzats',
addList: 'Afegir llista',
addMember: 'Afegir membre',
addMoreDetailedDescription: 'Afegir una descripció més detallada',
addTask: 'Afegir tasca',
addTaskList: 'Afegir llista de tasques',
addToCard: 'Afegir a targeta',
addUser: 'Afegir usuari',
addWebhook: 'Afegir webhook',
archive: 'Arxivar',
archiveCard: 'Arxivar targeta',
archiveCard_title: 'Arxivar targeta',
archiveCards: 'Arxivar targetes',
archiveCards_title: 'Arxivar targetes',
assignAsOwner: 'Assignar com a propietari',
cancel: 'Cancel·lar',
createApiKey: 'Crear clau API',
createBoard: 'Crear tauler',
createCustomFieldGroup: 'Crear grup de camps personalitzats',
createFile: 'Crear fitxer',
createLabel: 'Crear etiqueta',
createNewLabel: 'Crear nova etiqueta',
createProject: 'Crear projecte',
deactivateUser: 'Desactivar usuari',
deactivateUser_title: 'Desactivar usuari',
delete: 'Eliminar',
deleteApiKey: 'Eliminar clau API',
deleteAttachment: 'Eliminar fitxer adjunt',
deleteAvatar: 'Eliminar avatar',
deleteBackgroundImage: 'Eliminar imatge de fons',
deleteBoard: 'Eliminar tauler',
deleteBoard_title: 'Eliminar tauler',
deleteCard: 'Eliminar targeta',
deleteCardForever: 'Eliminar targeta per sempre',
deleteCard_title: 'Eliminar targeta',
deleteComment: 'Eliminar comentari',
deleteCustomField: 'Eliminar camp personalitzat',
deleteCustomFieldGroup: 'Eliminar grup de camps personalitzats',
deleteForever_title: 'Eliminar per sempre',
deleteGroup: 'Eliminar grup',
deleteLabel: 'Eliminar etiqueta',
deleteList: 'Eliminar llista',
deleteList_title: 'Eliminar llista',
deleteNotificationService: 'Eliminar servei de notificació',
deleteProject: 'Eliminar projecte',
deleteProject_title: 'Eliminar projecte',
deleteTask: 'Eliminar tasca',
deleteTaskList: 'Eliminar llista de tasques',
deleteTask_title: 'Eliminar tasca',
deleteUser: 'Eliminar usuari',
deleteUser_title: 'Eliminar usuari',
deleteWebhook: 'Eliminar webhook',
dismissAll: 'Descartar tot',
download: 'Descarregar',
duplicateCard_title: 'Duplicar targeta',
edit: 'Editar',
editColor_title: 'Editar color',
editDescription_title: 'Editar descripció',
editDueDate_title: 'Editar data de venciment',
editEmail_title: 'Editar correu electrònic',
editGroup: 'Editar grup',
editInformation_title: 'Editar informació',
editPassword_title: 'Editar contrasenya',
editPermissions: 'Editar permisos',
editRole_title: 'Editar rol',
editStopwatch_title: 'Editar cronòmetre',
editTitle_title: 'Editar títol',
editType_title: 'Editar tipus',
editUsername_title: "Editar nom d'usuari",
emptyTrash: 'Buidar paperera',
emptyTrash_title: 'Buidar paperera',
import: 'Importar',
join: 'Unir-se',
leave: 'Abandonar',
leaveBoard: 'Abandonar tauler',
leaveProject: 'Abandonar projecte',
logOut_title: 'Tancar sessió',
makeCover_title: 'Fer portada',
makeProjectPrivate: 'Fer projecte privat',
makeProjectPrivate_title: 'Fer projecte privat',
makeProjectShared: 'Fer projecte compartit',
makeProjectShared_title: 'Fer projecte compartit',
move: 'Moure',
moveCard_title: 'Moure targeta',
moveList_title: 'Moure llista',
regenerateApiKey: 'Regenerar clau API',
remove: 'Eliminar',
removeAssignee: 'Eliminar assignat',
removeColor: 'Eliminar color',
removeCover_title: 'Eliminar portada',
removeFromBoard: 'Eliminar del tauler',
removeFromProject: 'Eliminar del projecte',
removeManager: 'Eliminar gestor',
removeMember: 'Eliminar membre',
restoreToList: 'Restaurar a {{list}}',
returnToBoard: 'Tornar al tauler',
save: 'Desar',
sendTestEmail: 'Enviar correu de prova',
showActive: 'Mostrar actius',
showAllAttachments: 'Mostrar tots els fitxers adjunts ({{hidden}} ocults)',
showCardsWithThisUser: 'Mostrar targetes amb aquest usuari',
showDeactivated: 'Mostrar desactivats',
showFewerAttachments: 'Mostrar menys fitxers adjunts',
showLess: 'Mostrar menys',
showMore: 'Mostrar més',
sortList_title: 'Ordenar llista',
start: 'Iniciar',
stop: 'Aturar',
subscribe: "Subscriure's",
unsubscribe: 'Cancel·lar subscripció',
uploadNewAvatar: 'Pujar nou avatar',
uploadNewImage: 'Pujar nova imatge',
},
},
};

View File

@@ -0,0 +1,8 @@
import login from './login';
export default {
language: 'ca-ES',
country: 'es',
name: 'Català',
embeddedLocale: login,
};

View File

@@ -0,0 +1,35 @@
export default {
translation: {
common: {
activeUsersLimitReached: "S'ha assolit el límit d'usuaris actius",
adminLoginRequiredToInitializeInstance:
"Es requereix inici de sessió d'administrador per inicialitzar la instància",
emailAlreadyInUse: 'Correu electrònic ja en ús',
emailOrUsername: "Correu electrònic o nom d'usuari",
iHaveReadAndAgreeToTheseTerms: 'He llegit i accepto aquests termes',
invalidCredentials: 'Credencials no vàlides',
invalidEmailOrUsername: "Correu electrònic o nom d'usuari no vàlid",
invalidPassword: 'Contrasenya no vàlida',
logIn_title: 'Iniciar sessió',
noInternetConnection: 'Sense connexió a internet',
or: 'O',
pageNotFound_title: 'Pàgina no trobada',
password: 'Contrasenya',
poweredByPlanka: 'Desenvolupat amb <1>PLANKA</1>',
serverConnectionFailed: 'Error de connexió amb el servidor',
unknownError: 'Error desconegut, torna-ho a provar més tard',
useSingleSignOn: 'Utilitzar inici de sessió únic',
usernameAlreadyInUse: "Nom d'usuari ja en ús",
whoops_title: 'Ups!',
},
action: {
cancelAndClose: 'Cancel·lar i tancar',
continue: 'Continuar',
goBack: 'Tornar',
goHome: "Anar a l'inici",
logIn: 'Iniciar sessió',
logInWithSso: 'Iniciar sessió amb SSO',
},
},
};

View File

@@ -0,0 +1,165 @@
{
"action-previews": {
"text": "Aquest és un text sense títol.\nTant el títol com el text\npoden ressaltar-se en negreta, cursiva, color,\nratllat i subratllat.",
"text-with-head": "Aquest és un text amb títol.\nTant el títol com el text\npoden ressaltar-se en negreta, cursiva, color,\nratllat i subratllat.",
"heading": "Títol"
},
"bundle": {
"error-title": "Error en l'editor de markdown",
"settings_wysiwyg": "Editor visual (wysiwyg)",
"settings_markup": "Markdown",
"markup_placeholder": "Introdueix markdown..."
},
"codeblock": {
"remove": "Eliminar",
"empty_option": "No s'han trobat coincidències"
},
"common": {
"delete": "Eliminar",
"edit": "Editar",
"toolbar_action_disabled": "Element de marcat incompatible"
},
"forms": {
"common_action_cancel": "Cancel·lar",
"common_action_submit": "Enviar",
"common_action_upload": "Seleccionar",
"common_tab_attach": "Afegir des del dispositiu",
"common_tab_link": "Afegir per enllaç",
"common_link": "Enllaç",
"common_sizes": "Mida, px",
"image_name": "Títol",
"image_link_href": "Enllaç de la imatge",
"image_link_href_help": "Adreça a la qual porta l'enllaç de la imatge.",
"image_alt": "Text alternatiu",
"image_alt_help": "El text alternatiu es mostra si la imatge no es pot carregar.",
"image_upload_help": "Imatge JPEG, GIF o PNG no més gran que 1 MB.",
"image_upload_failed": "Error en afegir imatge",
"image_size_width": "Amplada",
"image_size_height": "Alçada",
"link_url_help": "Adreça a la qual porta l'enllaç.",
"link_text": "Text de l'enllaç",
"link_text_help": "Text mostrat com a enllaç.",
"link_open_help": "Obrir l'enllaç en una nova pestanya"
},
"md-hints": {
"header_title": "Capçalera",
"header_hint": "# El teu text",
"italic_title": "Cursiva",
"italic_hint": "_El teu text_",
"bold_title": "Negreta",
"bold_hint": "**El teu text**",
"strikethrough_title": "Ratllat",
"strikethrough_hint": "~~El teu text~~",
"blockquote_title": "Cita",
"blockquote_hint": "> El teu text",
"code_title": "Codi",
"code_hint": "```El teu text```",
"link_title": "Enllaç",
"link_hint": "[El teu text](url)",
"image_title": "Imatge",
"image_hint": "![El teu text](url)",
"list_title": "Element de llista",
"list_hint": "- El teu text",
"numbered-list_title": "Llista numerada",
"numbered-list_hint": "1. El teu text",
"documentation": "Documentació",
"documentation_link": "https://diplodoc.com/docs/en/syntax/"
},
"menubar": {
"bold": "Negreta",
"code": "Codi",
"code_inline": "Codi en línia",
"codeblock": "Bloc de codi",
"colorify": "Color del text",
"colorify__color_blue": "Blau",
"colorify__color_default": "Per defecte",
"colorify__color_gray": "Gris",
"colorify__color_green": "Verd",
"colorify__color_orange": "Taronja",
"colorify__color_red": "Vermell",
"colorify__color_violet": "Violeta",
"colorify__color_yellow": "Groc",
"colorify__group_text": "Text",
"cut": "Tallar",
"emoji": "Emoji",
"emoji__hint": "Els emojis es poden afegir en WYSIWYG o manualment amb marcat",
"heading": "Capçalera",
"heading1": "Capçalera 1",
"heading2": "Capçalera 2",
"heading3": "Capçalera 3",
"heading4": "Capçalera 4",
"heading5": "Capçalera 5",
"heading6": "Capçalera 6",
"hrule": "Separador",
"image": "Imatge",
"italic": "Cursiva",
"link": "Enllaç",
"list": "Llista",
"list__action_lift": "Elevar element",
"list__action_sink": "Baixar element",
"list_action_disabled": "Contradiu la lògica de la llista",
"mark": "Marcat",
"mono": "Monospace",
"more_action": "Més accions",
"note": "Nota",
"olist": "Llista ordenada",
"quote": "Cita",
"redo": "Refer",
"strike": "Ratllat",
"table": "Taula",
"text": "Text",
"ulist": "Llista amb vinyetes",
"underline": "Subratllat",
"undo": "Desfer"
},
"placeholder": {
"doc_empty": "Escriu / per usar ordres de barra...",
"checkbox": "Introdueix descripció de la tasca...",
"deflist_term": "Terme",
"deflist_desc": "Descripció de la definició",
"heading": "Capçalera",
"cut_title": "Títol",
"cut_content": "Contingut a mostrar en fer clic",
"note_title": "Títol",
"note_content": "Contingut de la nota",
"table_cell": "Contingut de la cel·la",
"select_filter": "Cercar idiomes..."
},
"search": {
"label_case-sensitive": "Distinguir majúscules",
"label_whole-word": "Paraula completa",
"title": "Cercar en el codi"
},
"suggest": {
"empty-msg": "No trobat"
},
"widgets": {
"image": "Afegir imatge",
"link": "Afegir enllaç"
},
"yfm-note": {
"info": "Informació",
"tip": "Consell",
"warning": "Advertència",
"alert": "Alerta",
"remove": "Eliminar"
},
"yfm-table": {
"column.add.before": "Afegir columna abans",
"column.add.after": "Afegir columna després",
"column.remove": "Eliminar columna",
"row.add.before": "Afegir fila abans",
"row.add.after": "Afegir fila després",
"row.remove": "Eliminar fila",
"table.remove": "Eliminar taula",
"table.menu.cell.align.left": "Alinear contingut de la cel·la a l'esquerra",
"table.menu.cell.align.right": "Alinear contingut de la cel·la a la dreta",
"table.menu.cell.align.center": "Centrar contingut de la cel·la",
"table.menu.row.add": "Afegir fila després",
"table.menu.row.remove": "Eliminar fila",
"table.menu.column.add": "Afegir columna després",
"table.menu.column.remove": "Eliminar columna",
"table.menu.convert.yfm": "Convertir a taula YFM",
"table.menu.table.remove": "Eliminar taula"
}
}

View File

@@ -40,6 +40,8 @@ export default {
'Všechny změny budou automaticky uloženy<br />po obnovení spojení.',
alphabetically: 'Abecedně',
alwaysDisplayCardCreator: 'Vždy zobrazit tvůrce karty',
apiKeyCreated_title: 'API klíč vytvořen',
apiKey_title: 'API klíč',
archive: 'Archivovat',
archiveCard_title: 'Archivovat kartu',
archiveCards_title: 'Archiv karet',
@@ -49,6 +51,7 @@ export default {
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'Opravdu chcete tohoto správce přiřadit jako vlastníka?',
areYouSureYouWantToDeactivateThisUser: 'Opravdu chcete deaktivovat tohoto uživatele?',
areYouSureYouWantToDeleteThisApiKey: 'Opravdu chcete smazat tento API klíč?',
areYouSureYouWantToDeleteThisAttachment: 'Opravdu chcete smazat tuto přílohu?',
areYouSureYouWantToDeleteThisBackgroundImage:
'Opravdu chcete tento obrázek na pozadí odstranit?',
@@ -75,6 +78,8 @@ export default {
areYouSureYouWantToMakeThisProjectPrivate:
'Opravdu chcete tento projekt nastavit jako soukromý?',
areYouSureYouWantToMakeThisProjectShared: 'Opravdu chcete tento projekt sdílet?',
areYouSureYouWantToRegenerateThisApiKey:
'Opravdu chcete znovu vygenerovat tento API klíč? Předchozí klíč již nebude fungovat.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Opravdu chcete tohoto správce z projektu odebrat?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -127,6 +132,7 @@ export default {
createTextFile_title: 'Vytvořit textový soubor',
creator: 'Tvůrce',
currentPassword: 'Aktuální heslo',
currentUser: 'Aktuální uživatel',
customFieldGroup_title: 'Skupina vlastního pole',
customFieldGroups_title: 'Skupina vlastních polí',
customField_title: 'Vlastní pole',
@@ -139,6 +145,7 @@ export default {
defaultView_title: 'Výchozí zobrazení',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Pro smazání tohoto projektu je třeba nejprve smazat všechny nástěnky',
deleteApiKey_title: 'Smazat API klíč',
deleteAttachment_title: 'Smazat přílohu',
deleteBackgroundImage_title: 'Smazat obrázek pozadí',
deleteBoard_title: 'Smazat nástěnku',
@@ -194,6 +201,8 @@ export default {
forTeamBasedProjects: 'Pro týmové projekty.',
fromComputer_title: 'Z počítače',
fromTrello: 'Z Trella',
fullKeyIsHiddenForSecurityReasons:
'Celý klíč je z bezpečnostních důvodů skrytý. Vygenerujte ho znovu pro vytvoření nového.',
general: 'Obecné',
gradients: 'Přechody',
grid: 'Mřížka',
@@ -201,7 +210,9 @@ export default {
hideFromProjectListAndFavorites: 'Skrýt ze seznamu projektů a oblíbených položek',
host: 'Host',
hours: 'Hodiny',
identity: 'Identita',
importBoard_title: 'Importovat nástěnku',
information: 'Informace',
invalidCurrentPassword: 'Neplatné aktuální heslo',
kanban: 'Kanban',
labels: 'Štítky',
@@ -230,6 +241,7 @@ export default {
newUsername: 'Nové uživatelské jméno',
newVersionAvailable: 'Nová verze je k dispozici',
newestFirst: 'Nejnovější',
noApiKeyCreated: 'Nebyl vytvořen žádný API klíč.',
noBoards: 'Žádné nástěnky',
noCardsFound: 'Nebyly nalezeny žádné karty.',
noConnectionToServer: 'Není spojení k serveru',
@@ -257,10 +269,12 @@ export default {
projectNotFound_title: 'Projekt nenalezen',
projectOwner: 'Vlastník projektu',
referenceDataAndKnowledgeStorage: 'Uchovávání referenčních údajů a znalostí.',
regenerateApiKey_title: 'Znovu vygenerovat API klíč',
rejectUnauthorizedTlsCertificates: 'Odmítnout neautorizované TLS certifikáty',
removeManager_title: 'Odstranit správce',
removeMember_title: 'Odstranit člena',
role: 'Role',
saveThisKeyItWillNotBeShownAgain: 'Uložte si tento klíč — již nebude znovu zobrazen!',
searchCards: 'Hledat karty...',
searchCustomFieldGroups: 'Hledat skupiny vlastních polí...',
searchCustomFields: 'Hledat vlastní pole...',
@@ -373,6 +387,7 @@ export default {
archiveCards_title: 'Archiv karet',
assignAsOwner: 'Přiřadit jako vlastníka',
cancel: 'Zrušit',
createApiKey: 'Vytvořit API klíč',
createBoard: 'Vytvořit nástěnku',
createCustomFieldGroup: 'Vytvořit vlastní skupinu polí',
createFile: 'Vytvořit soubor',
@@ -382,6 +397,7 @@ export default {
deactivateUser: 'Deaktivace uživatele',
deactivateUser_title: 'Deaktivace uživatele',
delete: 'Smazat',
deleteApiKey: 'Smazat API klíč',
deleteAttachment: 'Smazat přílohu',
deleteAvatar: 'Smazat avatar',
deleteBackgroundImage: 'Smazat obrázek pozadí',
@@ -440,6 +456,7 @@ export default {
move: 'Přesunout',
moveCard_title: 'Přesunout kartu',
moveList_title: 'Přesunout seznam',
regenerateApiKey: 'Znovu vygenerovat API klíč',
remove: 'Odstranit',
removeAssignee: 'Odstranit přiřazení',
removeColor: 'Smazat barvu',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Neznámá chyba, zkuste to později',
useSingleSignOn: 'Použít jednorázové přihlášení',
usernameAlreadyInUse: 'Uživatelské jméno se již používá',
whoops_title: 'Jejda!',
},
action: {
cancelAndClose: 'Zrušit a zavřít',
continue: 'Pokračovat',
goBack: 'Zpět',
goHome: 'Domů',
logIn: 'Přihlásit se',
logInWithSso: 'Přihlásit se pomocí SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'Alle ændringer vil automatisk blive gemt<br />ved genoprettelse af forbindelsen.',
alphabetically: 'Alfabetisk',
alwaysDisplayCardCreator: 'Vis altid kortets skaber',
apiKeyCreated_title: 'API-nøgle oprettet',
apiKey_title: 'API-nøgle',
archive: 'Arkiv',
archiveCard_title: 'Arkiver kort',
archiveCards_title: 'Arkiver kort',
@@ -49,6 +51,7 @@ export default {
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'Er du sikker på, at du vil sætte denne projektleder som ejer?',
areYouSureYouWantToDeactivateThisUser: 'Er du sikker på, at du vil deaktivere denne bruger?',
areYouSureYouWantToDeleteThisApiKey: 'Er du sikker på, at du vil slette denne API-nøgle?',
areYouSureYouWantToDeleteThisAttachment:
'Er du sikker på at du vil slette denne vedhæftede fil?',
areYouSureYouWantToDeleteThisBackgroundImage:
@@ -78,6 +81,8 @@ export default {
areYouSureYouWantToMakeThisProjectPrivate:
'Er du sikker på at du vil gøre dette projekt privat?',
areYouSureYouWantToMakeThisProjectShared: 'Er du sikker på at du vil dele dette projekt?',
areYouSureYouWantToRegenerateThisApiKey:
'Er du sikker på, at du vil regenerere denne API-nøgle? Den forrige nøgle vil ikke længere virke.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Er du sikker på at du vil fjerne denne projektleder fra projektet?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -131,6 +136,7 @@ export default {
createTextFile_title: 'Opret tekstfil',
creator: 'Skaber',
currentPassword: 'Nuværende adgangskode',
currentUser: 'Nuværende bruger',
customFieldGroup_title: 'Brugerdefineret feltgruppe',
customFieldGroups_title: 'Brugerdefinerede feltgrupper',
customField_title: 'Brugerdefineret felt',
@@ -143,6 +149,7 @@ export default {
defaultView_title: 'Standard visning',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Slet alle tavler for at kunne slette dette projekt.',
deleteApiKey_title: 'Slet API-nøgle',
deleteAttachment_title: 'Slet vedhæftning',
deleteBackgroundImage_title: 'Slet baggrundsbillede',
deleteBoard_title: 'Slet tavle',
@@ -198,6 +205,8 @@ export default {
forTeamBasedProjects: 'For team-baserede projekter.',
fromComputer_title: 'Fra computer',
fromTrello: 'Fra Trello',
fullKeyIsHiddenForSecurityReasons:
'Den fulde nøgle er skjult af sikkerhedsmæssige årsager. Regenerer den for at oprette en ny.',
general: 'Generelt',
gradients: 'Gradienter',
grid: 'Gitter',
@@ -205,7 +214,9 @@ export default {
hideFromProjectListAndFavorites: 'Skjul fra projektliste og favoritter',
host: 'Vært',
hours: 'Timer',
identity: 'Identitet',
importBoard_title: 'Importer tavle',
information: 'Information',
invalidCurrentPassword: 'Nuværende adgangskode er ugyldig',
kanban: 'Kanban',
labels: 'Labels',
@@ -234,6 +245,7 @@ export default {
newUsername: 'Nyt brugernavn',
newVersionAvailable: 'Ny version tilgængelig',
newestFirst: 'Nyeste først',
noApiKeyCreated: 'Ingen API-nøgle oprettet.',
noBoards: 'Ingen tavler',
noCardsFound: 'Ingen kort fundet.',
noConnectionToServer: 'Ingen forbindelse til serveren',
@@ -261,10 +273,12 @@ export default {
projectNotFound_title: 'Projekt ikke fundet',
projectOwner: 'Projektejer',
referenceDataAndKnowledgeStorage: 'Reference data og vidensopbevaring.',
regenerateApiKey_title: 'Regenerer API-nøgle',
rejectUnauthorizedTlsCertificates: 'Afvis uautoriserede TLS-certifikater',
removeManager_title: 'Fjern projektleder',
removeMember_title: 'Fjern medlem',
role: 'Rolle',
saveThisKeyItWillNotBeShownAgain: 'Gem denne nøgle — den vises ikke igen!',
searchCards: 'Søg efter kort...',
searchCustomFieldGroups: 'Søg efter brugerdefinerede feltgrupper...',
searchCustomFields: 'Søg efter brugerdefinerede felter...',
@@ -378,6 +392,7 @@ export default {
archiveCards_title: 'Arkivér kort',
assignAsOwner: 'Sæt som ejer',
cancel: 'Annuller',
createApiKey: 'Opret API-nøgle',
createBoard: 'Opret tavle',
createCustomFieldGroup: 'Opret brugerdefineret feltgruppe',
createFile: 'Opret fil',
@@ -387,6 +402,7 @@ export default {
deactivateUser: 'Deaktivér bruger',
deactivateUser_title: 'Deaktivér bruger',
delete: 'Slet',
deleteApiKey: 'Slet API-nøgle',
deleteAttachment: 'Slet vedhæftning',
deleteAvatar: 'Slet profilbillede',
deleteBackgroundImage: 'Slet baggrundsbillede',
@@ -445,6 +461,7 @@ export default {
move: 'Flyt',
moveCard_title: 'Flyt kort',
moveList_title: 'Flyt liste',
regenerateApiKey: 'Regenerer API-nøgle',
remove: 'Fjern',
removeAssignee: 'Fjern ansvarlig',
removeColor: 'Fjern farve',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Ukendt fejl - prøv igen',
useSingleSignOn: 'Anvend single sign-on',
usernameAlreadyInUse: 'Brugernavn allerede i brug',
whoops_title: 'Ups!',
},
action: {
cancelAndClose: 'Annuller og luk',
continue: 'Fortsæt',
goBack: 'Gå tilbage',
goHome: 'Gå hjem',
logIn: 'Log på',
logInWithSso: 'Log på med SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'Alle Änderungen werden automatisch gespeichert, sobald die Verbindung wiederhergestellt wurde.',
alphabetically: 'Alphabetisch',
alwaysDisplayCardCreator: 'Kartenersteller immer anzeigen',
apiKeyCreated_title: 'API-Schlüssel erstellt',
apiKey_title: 'API-Schlüssel',
archive: 'Archiv',
archiveCard_title: 'Karte archivieren',
archiveCards_title: 'Karten archivieren',
@@ -53,6 +55,8 @@ export default {
'Sind Sie sicher, dass Sie diesen Projektleiter als Eigentümer festlegen möchten?',
areYouSureYouWantToDeactivateThisUser:
'Sind Sie sicher, dass Sie diesen Benutzer deaktivieren möchten?',
areYouSureYouWantToDeleteThisApiKey:
'Sind Sie sicher, dass Sie diesen API-Schlüssel löschen möchten?',
areYouSureYouWantToDeleteThisAttachment:
'Sind Sie sicher, dass Sie diesen Anhang löschen möchten?',
areYouSureYouWantToDeleteThisBackgroundImage:
@@ -90,6 +94,8 @@ export default {
'Sind Sie sicher, dass Sie dieses Projekt privat machen möchten?',
areYouSureYouWantToMakeThisProjectShared:
'Sind Sie sicher, dass Sie dieses Projekt freigeben möchten?',
areYouSureYouWantToRegenerateThisApiKey:
'Sind Sie sicher, dass Sie diesen API-Schlüssel neu generieren möchten? Der vorherige Schlüssel wird nicht mehr funktionieren.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Sind Sie sicher, dass Sie diesen Projektleiter aus dem Projekt entfernen möchten?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -145,6 +151,7 @@ export default {
createTextFile_title: 'Textdatei erstellen',
creator: 'Ersteller',
currentPassword: 'Derzeitiges Passwort',
currentUser: 'Aktueller Benutzer',
customFieldGroup_title: 'Feldgruppe',
customFieldGroups_title: 'Benutzerdefinierte Feldgruppen',
customField_title: 'Feldgruppe',
@@ -157,6 +164,7 @@ export default {
defaultView_title: 'Standardansicht',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Löschen Sie alle Arbeitsbereiche, um dieses Projekt löschen zu können',
deleteApiKey_title: 'API-Schlüssel löschen',
deleteAttachment_title: 'Anhang löschen',
deleteBackgroundImage_title: 'Hintergrundbild löschen',
deleteBoard_title: 'Arbeitsbereich löschen',
@@ -212,6 +220,8 @@ export default {
forTeamBasedProjects: 'Für teambasierte Projekte.',
fromComputer_title: 'Vom Computer',
fromTrello: 'Von Trello',
fullKeyIsHiddenForSecurityReasons:
'Der vollständige Schlüssel ist aus Sicherheitsgründen verborgen. Generieren Sie ihn neu, um einen neuen zu erstellen.',
general: 'Allgemein',
gradients: 'Verläufe',
grid: 'Raster',
@@ -219,7 +229,9 @@ export default {
hideFromProjectListAndFavorites: 'Aus Projektliste und Favoriten ausblenden',
host: 'Host',
hours: 'Stunden',
identity: 'Identität',
importBoard_title: 'Arbeitsbereich importieren',
information: 'Information',
invalidCurrentPassword: 'Das aktuelle Passwort ist falsch',
kanban: 'Kanban',
labels: 'Labels',
@@ -248,6 +260,7 @@ export default {
newUsername: 'Neuer Benutzername',
newVersionAvailable: 'Neue Version verfügbar',
newestFirst: 'Neueste zuerst',
noApiKeyCreated: 'Kein API-Schlüssel erstellt.',
noBoards: 'Keine Arbeitsbereiche',
noCardsFound: 'Keine Karten gefunden.',
noConnectionToServer: 'Keine Verbindung zum Server',
@@ -275,10 +288,13 @@ export default {
projectNotFound_title: 'Projekt nicht gefunden',
projectOwner: 'Projektleitung',
referenceDataAndKnowledgeStorage: 'Speichern von Wissen und Referenzen.',
regenerateApiKey_title: 'API-Schlüssel neu generieren',
rejectUnauthorizedTlsCertificates: 'Nicht autorisierte TLS-Zertifikate ablehnen',
removeManager_title: 'Projektleiter entfernen',
removeMember_title: 'Mitglied entfernen',
role: 'Rolle',
saveThisKeyItWillNotBeShownAgain:
'Speichern Sie diesen Schlüssel — er wird nicht erneut angezeigt!',
searchCards: 'Karte suchen...',
searchCustomFieldGroups: 'Benutzerdefinierte Feldgruppen suchen...',
searchCustomFields: 'In Feldgruppen suchen...',
@@ -393,6 +409,7 @@ export default {
archiveCards_title: 'Karten archivieren',
assignAsOwner: 'Als Eigentümer zuweisen',
cancel: 'Abbrechen',
createApiKey: 'API-Schlüssel erstellen',
createBoard: 'Arbeitsbereich erstellen',
createCustomFieldGroup: 'Feldgruppe erstellen',
createFile: 'Datei erstellen',
@@ -402,6 +419,7 @@ export default {
deactivateUser: 'Benutzer deaktivieren',
deactivateUser_title: 'Benutzer deaktivieren',
delete: 'Löschen',
deleteApiKey: 'API-Schlüssel löschen',
deleteAttachment: 'Anhang löschen',
deleteAvatar: 'Avatar löschen',
deleteBackgroundImage: 'Hintergrundbild löschen',
@@ -460,6 +478,7 @@ export default {
move: 'Verschieben',
moveCard_title: 'Karte bewegen',
moveList_title: 'Liste verschieben',
regenerateApiKey: 'API-Schlüssel neu generieren',
remove: 'Löschen',
removeAssignee: 'Zuständigen entfernen',
removeColor: 'Farbe löschen',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Unbekannter Fehler, bitte später erneut versuchen',
useSingleSignOn: 'Einmalige Anmeldung (SSO) verwenden',
usernameAlreadyInUse: 'Benutzername wird bereits verwendet',
whoops_title: 'Hoppla!',
},
action: {
cancelAndClose: 'Abbrechen und schließen',
continue: 'Fortfahren',
goBack: 'Zurück gehen',
goHome: 'Zur Startseite',
logIn: 'Einloggen',
logInWithSso: 'Einloggen mit SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'Όλες οι αλλαγές θα αποθηκευτούν αυτόματα<br />όταν αποκατασταθεί η σύνδεση.',
alphabetically: 'Αλφαβητικά',
alwaysDisplayCardCreator: 'Πάντα εμφάνιση δημιουργού κάρτας',
apiKeyCreated_title: 'Δημιουργήθηκε κλειδί API',
apiKey_title: 'Κλειδί API',
archive: 'Αρχειοθέτηση',
archiveCard_title: 'Αρχειοθέτηση κάρτας',
archiveCards_title: 'Αρχειοθέτηση καρτών',
@@ -52,6 +54,8 @@ export default {
'Είστε σίγουροι ότι θέλετε να ορίσετε αυτόν τον διαχειριστή έργου ως ιδιοκτήτη;',
areYouSureYouWantToDeactivateThisUser:
'Είστε σίγουροι ότι θέλετε να απενεργοποιήσετε αυτόν τον χρήστη;',
areYouSureYouWantToDeleteThisApiKey:
'Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το κλειδί API;',
areYouSureYouWantToDeleteThisAttachment:
'Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το συνημμένο;',
areYouSureYouWantToDeleteThisBackgroundImage:
@@ -89,6 +93,8 @@ export default {
'Είστε σίγουροι ότι θέλετε να κάνετε αυτό το έργο ιδιωτικό;',
areYouSureYouWantToMakeThisProjectShared:
'Είστε σίγουροι ότι θέλετε να κάνετε αυτό το έργο κοινόχρηστο;',
areYouSureYouWantToRegenerateThisApiKey:
'Είστε βέβαιοι ότι θέλετε να αναδημιουργήσετε αυτό το κλειδί API; Το προηγούμενο κλειδί δεν θα λειτουργεί πλέον.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Είστε σίγουροι ότι θέλετε να αφαιρέσετε αυτόν τον διαχειριστή από το έργο;',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -143,6 +149,7 @@ export default {
createTextFile_title: 'Δημιουργία αρχείου κειμένου',
creator: 'Δημιουργός',
currentPassword: 'Τρέχων κωδικός',
currentUser: 'Τρέχων χρήστης',
customFieldGroup_title: 'Ομάδα προσαρμοσμένων πεδίων',
customFieldGroups_title: 'Ομάδες προσαρμοσμένων πεδίων',
customField_title: 'Προσαρμοσμένο πεδίο',
@@ -155,6 +162,7 @@ export default {
defaultView_title: 'Προεπιλεγμένη προβολή',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Διαγράψτε όλους τους πίνακες για να μπορέσετε να διαγράψετε αυτό το έργο',
deleteApiKey_title: 'Διαγραφή κλειδιού API',
deleteAttachment_title: 'Διαγραφή συνημμένου',
deleteBackgroundImage_title: 'Διαγραφή εικόνας φόντου',
deleteBoard_title: 'Διαγραφή πίνακα',
@@ -210,6 +218,8 @@ export default {
forTeamBasedProjects: 'Για έργα βασισμένα σε ομάδες.',
fromComputer_title: 'Από υπολογιστή',
fromTrello: 'Από Trello',
fullKeyIsHiddenForSecurityReasons:
'Το πλήρες κλειδί είναι κρυφό για λόγους ασφαλείας. Αναδημιουργήστε το για να δημιουργήσετε ένα νέο.',
general: 'Γενικά',
gradients: 'Διαβαθμίσεις',
grid: 'Πλέγμα',
@@ -217,7 +227,9 @@ export default {
hideFromProjectListAndFavorites: 'Απόκρυψη από τη λίστα έργων και τα αγαπημένα',
host: 'Κεντρικός υπολογιστής',
hours: 'Ώρες',
identity: 'Ταυτότητα',
importBoard_title: 'Εισαγωγή πίνακα',
information: 'Πληροφορίες',
invalidCurrentPassword: 'Μη έγκυρος τρέχων κωδικός',
kanban: 'Kanban',
labels: 'Ετικέτες',
@@ -246,6 +258,7 @@ export default {
newUsername: 'Νέο όνομα χρήστη',
newVersionAvailable: 'Διαθέσιμη νέα έκδοση',
newestFirst: 'Νεότερα πρώτα',
noApiKeyCreated: 'Δεν έχει δημιουργηθεί κλειδί API.',
noBoards: 'Δεν υπάρχουν πίνακες',
noCardsFound: 'Δεν βρέθηκαν κάρτες.',
noConnectionToServer: 'Δεν υπάρχει σύνδεση με τον διακομιστή',
@@ -273,10 +286,12 @@ export default {
projectNotFound_title: 'Το έργο δεν βρέθηκε',
projectOwner: 'Ιδιοκτήτης έργου',
referenceDataAndKnowledgeStorage: 'Αποθήκευση δεδομένων και γνώσης αναφοράς.',
regenerateApiKey_title: 'Αναδημιουργία κλειδιού API',
rejectUnauthorizedTlsCertificates: 'Απόρριψη μη εξουσιοδοτημένων πιστοποιητικών TLS',
removeManager_title: 'Αφαίρεση διαχειριστή',
removeMember_title: 'Αφαίρεση μέλους',
role: 'Ρόλος',
saveThisKeyItWillNotBeShownAgain: 'Αποθηκεύστε αυτό το κλειδί — δεν θα εμφανιστεί ξανά!',
searchCards: 'Αναζήτηση καρτών...',
searchCustomFieldGroups: 'Αναζήτηση ομάδων προσαρμοσμένων πεδίων...',
searchCustomFields: 'Αναζήτηση προσαρμοσμένων πεδίων...',
@@ -397,6 +412,7 @@ export default {
archiveCards_title: 'Αρχειοθέτηση καρτών',
assignAsOwner: 'Ορισμός ως ιδιοκτήτης',
cancel: 'Ακύρωση',
createApiKey: 'Δημιουργία κλειδιού API',
createBoard: 'Δημιουργία πίνακα',
createCustomFieldGroup: 'Δημιουργία ομάδας προσαρμοσμένων πεδίων',
createFile: 'Δημιουργία αρχείου',
@@ -406,6 +422,7 @@ export default {
deactivateUser: 'Απενεργοποίηση χρήστη',
deactivateUser_title: 'Απενεργοποίηση χρήστη',
delete: 'Διαγραφή',
deleteApiKey: 'Διαγραφή κλειδιού API',
deleteAttachment: 'Διαγραφή συνημμένου',
deleteAvatar: 'Διαγραφή avatar',
deleteBackgroundImage: 'Διαγραφή εικόνας φόντου',
@@ -464,6 +481,7 @@ export default {
move: 'Μετακίνηση',
moveCard_title: 'Μετακίνηση κάρτας',
moveList_title: 'Μετακίνηση λίστας',
regenerateApiKey: 'Αναδημιουργία κλειδιού API',
remove: 'Αφαίρεση',
removeAssignee: 'Αφαίρεση υπευθύνου',
removeColor: 'Αφαίρεση χρώματος',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Άγνωστο σφάλμα, δοκιμάστε ξανά αργότερα',
useSingleSignOn: 'Χρήση Single Sign-On',
usernameAlreadyInUse: 'Το όνομα χρήστη χρησιμοποιείται ήδη',
whoops_title: 'Ωχ!',
},
action: {
cancelAndClose: 'Ακύρωση και κλείσιμο',
continue: 'Συνέχεια',
goBack: 'Επιστροφή',
goHome: 'Αρχική σελίδα',
logIn: 'Σύνδεση',
logInWithSso: 'Σύνδεση με SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'All changes will be automatically saved<br />after connection restored.',
alphabetically: 'Alphabetically',
alwaysDisplayCardCreator: 'Always display card creator',
apiKeyCreated_title: 'API Key Created',
apiKey_title: 'API Key',
archive: 'Archive',
archiveCard_title: 'Archive Card',
archiveCards_title: 'Archive Cards',
@@ -49,6 +51,7 @@ export default {
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'Are you sure you want to assign this project manager as owner?',
areYouSureYouWantToDeactivateThisUser: 'Are you sure you want to deactivate this user?',
areYouSureYouWantToDeleteThisApiKey: 'Are you sure you want to delete this API key?',
areYouSureYouWantToDeleteThisAttachment: 'Are you sure you want to delete this attachment?',
areYouSureYouWantToDeleteThisBackgroundImage:
'Are you sure you want to delete this background image?',
@@ -78,6 +81,8 @@ export default {
'Are you sure you want to make this project private?',
areYouSureYouWantToMakeThisProjectShared:
'Are you sure you want to make this project shared?',
areYouSureYouWantToRegenerateThisApiKey:
'Are you sure you want to regenerate this API key? The previous key will no longer work.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Are you sure you want to remove this manager from the project?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -130,6 +135,7 @@ export default {
createTextFile_title: 'Create Text File',
creator: 'Creator',
currentPassword: 'Current password',
currentUser: 'Current user',
customFieldGroup_title: 'Custom Field Group',
customFieldGroups_title: 'Custom Field Groups',
customField_title: 'Custom Field',
@@ -142,6 +148,7 @@ export default {
defaultView_title: 'Default View',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Delete all boards to be able to delete this project',
deleteApiKey_title: 'Delete API Key',
deleteAttachment_title: 'Delete Attachment',
deleteBackgroundImage_title: 'Delete Background Image',
deleteBoard_title: 'Delete Board',
@@ -197,6 +204,8 @@ export default {
forTeamBasedProjects: 'For team-based projects.',
fromComputer_title: 'From Computer',
fromTrello: 'From Trello',
fullKeyIsHiddenForSecurityReasons:
'The full key is hidden for security reasons. Regenerate it to create a new one.',
general: 'General',
gradients: 'Gradients',
grid: 'Grid',
@@ -204,7 +213,9 @@ export default {
hideFromProjectListAndFavorites: 'Hide from project list and favorites',
host: 'Host',
hours: 'Hours',
identity: 'Identity',
importBoard_title: 'Import Board',
information: 'Information',
invalidCurrentPassword: 'Invalid current password',
kanban: 'Kanban',
labels: 'Labels',
@@ -233,6 +244,7 @@ export default {
newUsername: 'New username',
newVersionAvailable: 'New version available',
newestFirst: 'Newest first',
noApiKeyCreated: 'No API key created.',
noBoards: 'No boards',
noCardsFound: 'No cards found.',
noConnectionToServer: 'No connection to server',
@@ -260,10 +272,12 @@ export default {
projectNotFound_title: 'Project Not Found',
projectOwner: 'Project owner',
referenceDataAndKnowledgeStorage: 'Reference data and knowledge storage.',
regenerateApiKey_title: 'Regenerate API Key',
rejectUnauthorizedTlsCertificates: 'Reject unauthorized TLS certificates',
removeManager_title: 'Remove Manager',
removeMember_title: 'Remove Member',
role: 'Role',
saveThisKeyItWillNotBeShownAgain: 'Save this key — it will not be shown again!',
searchCards: 'Search cards...',
searchCustomFieldGroups: 'Search custom field groups...',
searchCustomFields: 'Search custom fields...',
@@ -376,6 +390,7 @@ export default {
archiveCards_title: 'Archive Cards',
assignAsOwner: 'Assign as owner',
cancel: 'Cancel',
createApiKey: 'Create API key',
createBoard: 'Create board',
createCustomFieldGroup: 'Create custom field group',
createFile: 'Create file',
@@ -385,6 +400,7 @@ export default {
deactivateUser: 'Deactivate user',
deactivateUser_title: 'Deactivate User',
delete: 'Delete',
deleteApiKey: 'Delete API key',
deleteAttachment: 'Delete attachment',
deleteAvatar: 'Delete avatar',
deleteBackgroundImage: 'Delete background image',
@@ -443,6 +459,7 @@ export default {
move: 'Move',
moveCard_title: 'Move Card',
moveList_title: 'Move List',
regenerateApiKey: 'Regenerate API key',
remove: 'Remove',
removeAssignee: 'Remove assignee',
removeColor: 'Remove color',

View File

@@ -19,11 +19,14 @@ export default {
unknownError: 'Unknown error, try again later',
useSingleSignOn: 'Use single sign-on',
usernameAlreadyInUse: 'Username already in use',
whoops_title: 'Whoops!',
},
action: {
cancelAndClose: 'Cancel and close',
continue: 'Continue',
goBack: 'Go back',
goHome: 'Go home',
logIn: 'Log in',
logInWithSso: 'Log in with SSO',
},

View File

@@ -35,6 +35,8 @@ export default {
'All changes will be automatically saved<br />after connection restored.',
alphabetically: 'Alphabetically',
alwaysDisplayCardCreator: 'Always display card creator',
apiKeyCreated_title: 'API Key Created',
apiKey_title: 'API Key',
archive: 'Archive',
archiveCard_title: 'Archive Card',
archiveCards_title: 'Archive Cards',
@@ -44,6 +46,7 @@ export default {
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'Are you sure you want to assign this project manager as owner?',
areYouSureYouWantToDeactivateThisUser: 'Are you sure you want to deactivate this user?',
areYouSureYouWantToDeleteThisApiKey: 'Are you sure you want to delete this API key?',
areYouSureYouWantToDeleteThisAttachment: 'Are you sure you want to delete this attachment?',
areYouSureYouWantToDeleteThisBackgroundImage:
'Are you sure you want to delete this background image?',
@@ -73,6 +76,8 @@ export default {
'Are you sure you want to make this project private?',
areYouSureYouWantToMakeThisProjectShared:
'Are you sure you want to make this project shared?',
areYouSureYouWantToRegenerateThisApiKey:
'Are you sure you want to regenerate this API key? The previous key will no longer work.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Are you sure you want to remove this manager from the project?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -125,6 +130,7 @@ export default {
createTextFile_title: 'Create Text File',
creator: 'Creator',
currentPassword: 'Current password',
currentUser: 'Current user',
customFieldGroup_title: 'Custom Field Group',
customFieldGroups_title: 'Custom Field Groups',
customField_title: 'Custom Field',
@@ -137,6 +143,7 @@ export default {
defaultView_title: 'Default View',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Delete all boards to be able to delete this project',
deleteApiKey_title: 'Delete API Key',
deleteAttachment_title: 'Delete Attachment',
deleteBackgroundImage_title: 'Delete Background Image',
deleteBoard_title: 'Delete Board',
@@ -192,6 +199,8 @@ export default {
forTeamBasedProjects: 'For team-based projects.',
fromComputer_title: 'From Computer',
fromTrello: 'From Trello',
fullKeyIsHiddenForSecurityReasons:
'The full key is hidden for security reasons. Regenerate it to create a new one.',
general: 'General',
gradients: 'Gradients',
grid: 'Grid',
@@ -199,7 +208,9 @@ export default {
hideFromProjectListAndFavorites: 'Hide from project list and favorites',
host: 'Host',
hours: 'Hours',
identity: 'Identity',
importBoard_title: 'Import Board',
information: 'Information',
invalidCurrentPassword: 'Invalid current password',
kanban: 'Kanban',
labels: 'Labels',
@@ -228,6 +239,7 @@ export default {
newUsername: 'New username',
newVersionAvailable: 'New version available',
newestFirst: 'Newest first',
noApiKeyCreated: 'No API key created.',
noBoards: 'No boards',
noCardsFound: 'No cards found.',
noConnectionToServer: 'No connection to server',
@@ -255,10 +267,12 @@ export default {
projectNotFound_title: 'Project Not Found',
projectOwner: 'Project owner',
referenceDataAndKnowledgeStorage: 'Reference data and knowledge storage.',
regenerateApiKey_title: 'Regenerate API Key',
rejectUnauthorizedTlsCertificates: 'Reject unauthorized TLS certificates',
removeManager_title: 'Remove Manager',
removeMember_title: 'Remove Member',
role: 'Role',
saveThisKeyItWillNotBeShownAgain: 'Save this key — it will not be shown again!',
searchCards: 'Search cards...',
searchCustomFieldGroups: 'Search custom field groups...',
searchCustomFields: 'Search custom fields...',
@@ -371,6 +385,7 @@ export default {
archiveCards_title: 'Archive Cards',
assignAsOwner: 'Assign as owner',
cancel: 'Cancel',
createApiKey: 'Create API key',
createBoard: 'Create board',
createCustomFieldGroup: 'Create custom field group',
createFile: 'Create file',
@@ -380,6 +395,7 @@ export default {
deactivateUser: 'Deactivate user',
deactivateUser_title: 'Deactivate User',
delete: 'Delete',
deleteApiKey: 'Delete API key',
deleteAttachment: 'Delete attachment',
deleteAvatar: 'Delete avatar',
deleteBackgroundImage: 'Delete background image',
@@ -438,6 +454,7 @@ export default {
move: 'Move',
moveCard_title: 'Move Card',
moveList_title: 'Move List',
regenerateApiKey: 'Regenerate API key',
remove: 'Remove',
removeAssignee: 'Remove assignee',
removeColor: 'Remove color',

View File

@@ -19,11 +19,14 @@ export default {
unknownError: 'Unknown error, try again later',
useSingleSignOn: 'Use single sign-on',
usernameAlreadyInUse: 'Username already in use',
whoops_title: 'Whoops!',
},
action: {
cancelAndClose: 'Cancel and close',
continue: 'Continue',
goBack: 'Go back',
goHome: 'Go home',
logIn: 'Log in',
logInWithSso: 'Log in with SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'Todos los cambios se guardarán automáticamente<br />cuando se restablezca la conexión.',
alphabetically: 'Alfabéticamente',
alwaysDisplayCardCreator: 'Mostrar siempre el creador de la tarjeta',
apiKeyCreated_title: 'Clave API creada',
apiKey_title: 'Clave API',
archive: 'Archivar',
archiveCard_title: 'Archivar tarjeta',
archiveCards_title: 'Archivar tarjetas',
@@ -50,6 +52,7 @@ export default {
'¿Estás seguro de que quieres asignar este gestor de proyecto como propietario?',
areYouSureYouWantToDeactivateThisUser:
'¿Estás seguro de que quieres desactivar este usuario?',
areYouSureYouWantToDeleteThisApiKey: '¿Estás seguro de que quieres eliminar esta clave API?',
areYouSureYouWantToDeleteThisAttachment:
'¿Estás seguro de que quieres eliminar este archivo adjunto?',
areYouSureYouWantToDeleteThisBackgroundImage:
@@ -82,6 +85,8 @@ export default {
'¿Estás seguro de que quieres hacer este proyecto privado?',
areYouSureYouWantToMakeThisProjectShared:
'¿Estás seguro de que quieres hacer este proyecto compartido?',
areYouSureYouWantToRegenerateThisApiKey:
'¿Estás seguro de que quieres regenerar esta clave API? La clave anterior ya no funcionará.',
areYouSureYouWantToRemoveThisManagerFromProject:
'¿Estás seguro de que quieres eliminar este gestor del proyecto?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -136,6 +141,7 @@ export default {
createTextFile_title: 'Crear archivo de texto',
creator: 'Creador',
currentPassword: 'Contraseña actual',
currentUser: 'Usuario actual',
customFieldGroup_title: 'Grupo de campos personalizados',
customFieldGroups_title: 'Grupos de campos personalizados',
customField_title: 'Campo personalizado',
@@ -148,6 +154,7 @@ export default {
defaultView_title: 'Vista por defecto',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Elimina todos los tableros para poder eliminar este proyecto',
deleteApiKey_title: 'Eliminar clave API',
deleteAttachment_title: 'Eliminar archivo adjunto',
deleteBackgroundImage_title: 'Eliminar imagen de fondo',
deleteBoard_title: 'Eliminar tablero',
@@ -203,6 +210,8 @@ export default {
forTeamBasedProjects: 'Para proyectos en equipo.',
fromComputer_title: 'Desde el ordenador',
fromTrello: 'Desde Trello',
fullKeyIsHiddenForSecurityReasons:
'La clave completa está oculta por razones de seguridad. Regénérala para crear una nueva.',
general: 'General',
gradients: 'Degradados',
grid: 'Cuadrícula',
@@ -210,7 +219,9 @@ export default {
hideFromProjectListAndFavorites: 'Ocultar de la lista de proyectos y favoritos',
host: 'Host',
hours: 'Horas',
identity: 'Identidad',
importBoard_title: 'Importar tablero',
information: 'Información',
invalidCurrentPassword: 'Contraseña actual incorrecta',
kanban: 'Kanban',
labels: 'Etiquetas',
@@ -239,6 +250,7 @@ export default {
newUsername: 'Nuevo nombre de usuario',
newVersionAvailable: 'Nueva versión disponible',
newestFirst: 'Más recientes primero',
noApiKeyCreated: 'No se ha creado ninguna clave API.',
noBoards: 'Sin tableros',
noCardsFound: 'No se encontraron tarjetas.',
noConnectionToServer: 'Sin conexión al servidor',
@@ -266,10 +278,12 @@ export default {
projectNotFound_title: 'Proyecto no encontrado',
projectOwner: 'Propietario del proyecto',
referenceDataAndKnowledgeStorage: 'Almacenamiento de datos de referencia y conocimiento.',
regenerateApiKey_title: 'Regenerar clave API',
rejectUnauthorizedTlsCertificates: 'Rechazar certificados TLS no autorizados',
removeManager_title: 'Eliminar gestor',
removeMember_title: 'Eliminar miembro',
role: 'Rol',
saveThisKeyItWillNotBeShownAgain: '¡Guarda esta clave, no se mostrará de nuevo!',
searchCards: 'Buscar tarjetas...',
searchCustomFieldGroups: 'Buscar grupos de campos personalizados...',
searchCustomFields: 'Buscar campos personalizados...',
@@ -384,6 +398,7 @@ export default {
archiveCards_title: 'Archivar tarjetas',
assignAsOwner: 'Asignar como propietario',
cancel: 'Cancelar',
createApiKey: 'Crear clave API',
createBoard: 'Crear tablero',
createCustomFieldGroup: 'Crear grupo de campos personalizados',
createFile: 'Crear archivo',
@@ -393,6 +408,7 @@ export default {
deactivateUser: 'Desactivar usuario',
deactivateUser_title: 'Desactivar usuario',
delete: 'Eliminar',
deleteApiKey: 'Eliminar clave API',
deleteAttachment: 'Eliminar archivo adjunto',
deleteAvatar: 'Eliminar avatar',
deleteBackgroundImage: 'Eliminar imagen de fondo',
@@ -451,6 +467,7 @@ export default {
move: 'Mover',
moveCard_title: 'Mover tarjeta',
moveList_title: 'Mover lista',
regenerateApiKey: 'Regenerar clave API',
remove: 'Eliminar',
removeAssignee: 'Eliminar asignado',
removeColor: 'Eliminar color',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Error desconocido, inténtalo más tarde',
useSingleSignOn: 'Usar inicio de sesión único',
usernameAlreadyInUse: 'Nombre de usuario ya en uso',
whoops_title: '¡Ups!',
},
action: {
cancelAndClose: 'Cancelar y cerrar',
continue: 'Continuar',
goBack: 'Volver',
goHome: 'Ir al inicio',
logIn: 'Iniciar sesión',
logInWithSso: 'Iniciar sesión con SSO',
},

View File

@@ -40,6 +40,8 @@ export default {
'Kõik muudatused salvestatakse automaatselt<br />pärast ühenduse taastamist.',
alphabetically: 'Tähestiku järgi',
alwaysDisplayCardCreator: 'Näita alati kaardi loojat',
apiKeyCreated_title: 'API võti loodud',
apiKey_title: 'API võti',
archive: 'Arhiveeri',
archiveCard_title: 'Arhiveeri kaart',
archiveCards_title: 'Arhiveeri kaardid',
@@ -49,6 +51,7 @@ export default {
areYouSureYouWantToAssignThisProjectManagerAsOwner:
'Oled kindel, et soovid seda projektihaldurit omanikuks määrata?',
areYouSureYouWantToDeactivateThisUser: 'Oled kindel, et soovid seda kasutajat deaktiveerida?',
areYouSureYouWantToDeleteThisApiKey: 'Kas olete kindel, et soovite seda API võtit kustutada?',
areYouSureYouWantToDeleteThisAttachment: 'Oled kindel, et soovid seda manusi kustutada?',
areYouSureYouWantToDeleteThisBackgroundImage:
'Oled kindel, et soovid seda taustapilla kustutada?',
@@ -79,6 +82,8 @@ export default {
'Oled kindel, et soovid seda projekti privaatseks muuta?',
areYouSureYouWantToMakeThisProjectShared:
'Oled kindel, et soovid seda projekti jagatavaks määrata?',
areYouSureYouWantToRegenerateThisApiKey:
'Kas olete kindel, et soovite seda API võtit taastada? Eelmine võti ei tööta enam.',
areYouSureYouWantToRemoveThisManagerFromProject:
'Oled kindel, et soovid seda haldurit projektist eemaldada?',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -131,6 +136,7 @@ export default {
createTextFile_title: 'Loo tekstifail',
creator: 'Looja',
currentPassword: 'Praegune parool',
currentUser: 'Praegune kasutaja',
customFieldGroup_title: 'Kohandatud väljade grupp',
customFieldGroups_title: 'Kohandatud väljade grupid',
customField_title: 'Kohandatud väli',
@@ -143,6 +149,7 @@ export default {
defaultView_title: 'Vaikimisi vaade',
deleteAllBoardsToBeAbleToDeleteThisProject:
'Kustuta kõik tahvlid, et seda projekti kustutada',
deleteApiKey_title: 'Kustuta API võti',
deleteAttachment_title: 'Kustuta manus',
deleteBackgroundImage_title: 'Kustuta taustapilt',
deleteBoard_title: 'Kustuta tahvel',
@@ -198,6 +205,8 @@ export default {
forTeamBasedProjects: 'Töögrupi põhised projektid.',
fromComputer_title: 'Arvutist',
fromTrello: 'Trellost',
fullKeyIsHiddenForSecurityReasons:
'Täielik võti on turvakaalutlustel peidetud. Taasta see, et luua uus.',
general: 'Üldine',
gradients: 'Gradiendid',
grid: 'Grill',
@@ -205,7 +214,9 @@ export default {
hideFromProjectListAndFavorites: 'Peida projektiloendist ja lemmikutest',
host: 'Host',
hours: 'Tunnid',
identity: 'Identiteet',
importBoard_title: 'Impordi tahvel',
information: 'Informatsioon',
invalidCurrentPassword: 'Vale praegune parool',
kanban: 'Kanban',
labels: 'Sildid',
@@ -234,6 +245,7 @@ export default {
newUsername: 'Uus kasutajanimi',
newVersionAvailable: 'Uus versioon saadaval',
newestFirst: 'Kõige uuem',
noApiKeyCreated: 'API võtit pole loodud.',
noBoards: 'Tahvleid pole',
noCardsFound: 'Kaarte ei leitud.',
noConnectionToServer: 'Ühendust serveriga ei leitud',
@@ -261,10 +273,12 @@ export default {
projectNotFound_title: 'Projekt ei leitud',
projectOwner: 'Projekti omanik',
referenceDataAndKnowledgeStorage: 'Viideandmete ja teadmise salvestamiseks.',
regenerateApiKey_title: 'Taasta API võti',
rejectUnauthorizedTlsCertificates: 'Lükka tagasi volitamata TLS-sertifikaadid',
removeManager_title: 'Eemalda haldur',
removeMember_title: 'Eemalda liige',
role: 'Roll',
saveThisKeyItWillNotBeShownAgain: 'Salvesta see võti — seda ei näidata enam!',
searchCards: 'Kaartide otsimine...',
searchCustomFieldGroups: 'Kohandatud väljade gruppide otsimine...',
searchCustomFields: 'Kohandatud väljade otsimine...',
@@ -378,6 +392,7 @@ export default {
archiveCards_title: 'Arhiveeri kaardid',
assignAsOwner: 'Määra omanikuks',
cancel: 'Tühista',
createApiKey: 'Loo API võti',
createBoard: 'Loo tahvel',
createCustomFieldGroup: 'Loo kohandatud väljade grupp',
createFile: 'Loo fail',
@@ -387,6 +402,7 @@ export default {
deactivateUser: 'Deaktiveeri kasutaja',
deactivateUser_title: 'Deaktiveeri kasutaja',
delete: 'Kustuta',
deleteApiKey: 'Kustuta API võti',
deleteAttachment: 'Kustuta manus',
deleteAvatar: 'Kustuta avatar',
deleteBackgroundImage: 'Kustuta taustapilt',
@@ -445,6 +461,7 @@ export default {
move: 'Liiguta',
moveCard_title: 'Liiguta kaart',
moveList_title: 'Liiguta nimekiri',
regenerateApiKey: 'Taasta API võti',
remove: 'Eemalda',
removeAssignee: 'Eemalda vastutaja',
removeColor: 'Eemalda värv',

View File

@@ -20,11 +20,14 @@ export default {
unknownError: 'Tundmatu viga, proovi hiljem uuesti',
useSingleSignOn: 'Kasuta ühekordset sisselogimist',
usernameAlreadyInUse: 'Kasutajanimi on juba kasutusel',
whoops_title: 'Ups!',
},
action: {
cancelAndClose: 'Tühista ja sulge',
continue: 'Jätka',
goBack: 'Tagasi',
goHome: 'Koju',
logIn: 'Logi sisse',
logInWithSso: 'Logi sisse SSO-ga',
},

View File

@@ -40,6 +40,8 @@ export default {
'تمام تغییرات به صورت خودکار ذخیره می‌شوند<br />بعد از بازیابی ارتباط.',
alphabetically: 'بر اساس حروف الفبا',
alwaysDisplayCardCreator: 'همیشه سازنده کارت را نمایش بده',
apiKeyCreated_title: 'کلید API ایجاد شد',
apiKey_title: 'کلید API',
archive: 'آرشیو',
archiveCard_title: 'آرشیو کارت',
archiveCards_title: 'آرشیو کارت‌ها',
@@ -50,6 +52,7 @@ export default {
'آیا مطمئن هستید که می‌خواهید این مدیر پروژه را به عنوان مالک تعیین کنید؟',
areYouSureYouWantToDeactivateThisUser:
'آیا مطمئن هستید که می‌خواهید این کاربر را غیرفعال کنید؟',
areYouSureYouWantToDeleteThisApiKey: 'آیا مطمئن هستید که می‌خواهید این کلید API را حذف کنید؟',
areYouSureYouWantToDeleteThisAttachment:
'آیا مطمئن هستید که می‌خواهید این پیوست را حذف کنید؟',
areYouSureYouWantToDeleteThisBackgroundImage:
@@ -81,6 +84,8 @@ export default {
'آیا مطمئن هستید که می‌خواهید این پروژه را خصوصی کنید؟',
areYouSureYouWantToMakeThisProjectShared:
'آیا مطمئن هستید که می‌خواهید این پروژه را به اشتراک بگذارید؟',
areYouSureYouWantToRegenerateThisApiKey:
'آیا مطمئن هستید که می‌خواهید این کلید API را بازسازی کنید؟ کلید قبلی دیگر کار نخواهد کرد.',
areYouSureYouWantToRemoveThisManagerFromProject:
'آیا مطمئن هستید که می‌خواهید این مدیر را از پروژه حذف کنید؟',
areYouSureYouWantToRemoveThisMemberFromBoard:
@@ -134,6 +139,7 @@ export default {
createTextFile_title: 'ایجاد فایل متنی',
creator: 'سازنده',
currentPassword: 'رمز عبور فعلی',
currentUser: 'کاربر فعلی',
customFieldGroup_title: 'گروه فیلد سفارشی',
customFieldGroups_title: 'گروه‌های فیلد سفارشی',
customField_title: 'فیلد سفارشی',
@@ -146,6 +152,7 @@ export default {
defaultView_title: 'نمای پیش‌فرض',
deleteAllBoardsToBeAbleToDeleteThisProject:
'همه بردها را حذف کنید تا بتوانید این پروژه را حذف کنید',
deleteApiKey_title: 'حذف کلید API',
deleteAttachment_title: 'حذف پیوست',
deleteBackgroundImage_title: 'حذف تصویر پس‌زمینه',
deleteBoard_title: 'حذف برد',
@@ -201,6 +208,8 @@ export default {
forTeamBasedProjects: 'برای پروژه‌های تیمی.',
fromComputer_title: 'از کامپیوتر',
fromTrello: 'از Trello',
fullKeyIsHiddenForSecurityReasons:
'کلید کامل به دلایل امنیتی مخفی است. آن را بازسازی کنید تا یک کلید جدید ایجاد شود.',
general: 'عمومی',
gradients: 'گرادیان‌ها',
grid: 'شبکه',
@@ -208,7 +217,9 @@ export default {
hideFromProjectListAndFavorites: 'مخفی کردن از لیست پروژه‌ها و علاقه‌مندی‌ها',
host: 'میزبان',
hours: 'ساعت‌ها',
identity: 'هویت',
importBoard_title: 'وارد کردن برد',
information: 'اطلاعات',
invalidCurrentPassword: 'رمز عبور فعلی نامعتبر است',
kanban: 'کانبان',
labels: 'برچسب‌ها',
@@ -237,6 +248,7 @@ export default {
newUsername: 'نام کاربری جدید',
newVersionAvailable: 'نسخه جدید موجود است',
newestFirst: 'جدیدترین اول',
noApiKeyCreated: 'هیچ کلید API ایجاد نشده است.',
noBoards: 'بردی وجود ندارد',
noCardsFound: 'کارتی یافت نشد.',
noConnectionToServer: 'ارتباط با سرور قطع است',
@@ -264,10 +276,12 @@ export default {
projectNotFound_title: 'پروژه یافت نشد',
projectOwner: 'مالک پروژه',
referenceDataAndKnowledgeStorage: 'ذخیره‌سازی داده‌های مرجع و دانش.',
regenerateApiKey_title: 'بازسازی کلید API',
rejectUnauthorizedTlsCertificates: 'رد کردن گواهی‌نامه‌های TLS غیرمجاز',
removeManager_title: 'حذف مدیر',
removeMember_title: 'حذف عضو',
role: 'نقش',
saveThisKeyItWillNotBeShownAgain: 'این کلید را ذخیره کنید — دیگر نشان داده نخواهد شد!',
searchCards: 'جستجوی کارت‌ها...',
searchCustomFieldGroups: 'جستجوی گروه‌های فیلد سفارشی...',
searchCustomFields: 'جستجوی فیلدهای سفارشی...',
@@ -380,6 +394,7 @@ export default {
archiveCards_title: 'آرشیو کارت‌ها',
assignAsOwner: 'تعیین به عنوان مالک',
cancel: 'لغو',
createApiKey: 'ایجاد کلید API',
createBoard: 'ایجاد برد',
createCustomFieldGroup: 'ایجاد گروه فیلد سفارشی',
createFile: 'ایجاد فایل',
@@ -389,6 +404,7 @@ export default {
deactivateUser: 'غیرفعال کردن کاربر',
deactivateUser_title: 'غیرفعال کردن کاربر',
delete: 'حذف',
deleteApiKey: 'حذف کلید API',
deleteAttachment: 'حذف پیوست',
deleteAvatar: 'حذف آواتار',
deleteBackgroundImage: 'حذف تصویر پس‌زمینه',
@@ -447,6 +463,7 @@ export default {
move: 'انتقال',
moveCard_title: 'انتقال کارت',
moveList_title: 'انتقال لیست',
regenerateApiKey: 'بازسازی کلید API',
remove: 'حذف',
removeAssignee: 'حذف مسئول',
removeColor: 'حذف رنگ',

Some files were not shown because too many files have changed in this diff Show More