Compare commits
67
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01535ecf51 | ||
|
|
1217969e22 | ||
|
|
e410e21363 | ||
|
|
a0f584e814 | ||
|
|
135f5eccb5 | ||
|
|
10001db8cf | ||
|
|
7ee351fe30 | ||
|
|
73746b83d1 | ||
|
|
f9a7b38ce9 | ||
|
|
1f261fe7b6 | ||
|
|
80bee30463 | ||
|
|
a9957481b6 | ||
|
|
36b05a5478 | ||
|
|
af91512abf | ||
|
|
8fd0f682d9 | ||
|
|
9699fbe76a | ||
|
|
4176a62f1a | ||
|
|
c12f0844d7 | ||
|
|
e6644eb745 | ||
|
|
b2e1fba9a0 | ||
|
|
818ae4a6df | ||
|
|
0de0e809ac | ||
|
|
da727c0a0b | ||
|
|
4841a65c77 | ||
|
|
0ec4b619d9 | ||
|
|
12f05adde7 | ||
|
|
c594e8bd71 | ||
|
|
f84166406f | ||
|
|
82b2ba298d | ||
|
|
38b6805ba1 | ||
|
|
6a7a582045 | ||
|
|
c53a00b4ba | ||
|
|
52103c775b | ||
|
|
eeb1bd80cc | ||
|
|
e6b8538863 | ||
|
|
9dc38932fb | ||
|
|
e9b4a4adfc | ||
|
|
76c78c7252 | ||
|
|
04f52a63f2 | ||
|
|
6b16a104b1 | ||
|
|
aff853c602 | ||
|
|
ad2966c5d6 | ||
|
|
8d74cc1732 | ||
|
|
ea94fe8bf3 | ||
|
|
6052f8999f | ||
|
|
67d1b1c194 | ||
|
|
b3cb8405f5 | ||
|
|
7acaec6d04 | ||
|
|
72896a66b3 | ||
|
|
4be4783251 | ||
|
|
5c6c6a0bcf | ||
|
|
ba178d66fa | ||
|
|
270ce29f87 | ||
|
|
de1ed3c784 | ||
|
|
caf8790f28 | ||
|
|
81436b373d | ||
|
|
cc1e886a31 | ||
|
|
f0e9fe7904 | ||
|
|
5fcea5d651 | ||
|
|
a963d85a66 | ||
|
|
04b42decef | ||
|
|
c065566c15 | ||
|
|
3779bdb053 | ||
|
|
4124ab17d2 | ||
|
|
0e3bc92a61 | ||
|
|
b8d7e713b9 | ||
|
|
017eee8302 |
@@ -0,0 +1,53 @@
|
|||||||
|
name: "🐛 Bug Report"
|
||||||
|
description: Report a bug found while using Planka
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: ["Type: Bug", "Status: Triage"]
|
||||||
|
body:
|
||||||
|
- type: dropdown
|
||||||
|
id: issue-type
|
||||||
|
attributes:
|
||||||
|
label: Where is the problem occurring?
|
||||||
|
description: Select the part of the application where you encountered the issue.
|
||||||
|
options:
|
||||||
|
- "I encountered the problem while using the application (Frontend)"
|
||||||
|
- "I encountered the problem while interacting with the server (Backend)"
|
||||||
|
- "I'm not sure"
|
||||||
|
- type: dropdown
|
||||||
|
id: browsers
|
||||||
|
attributes:
|
||||||
|
label: What browsers are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Brave
|
||||||
|
- Chrome
|
||||||
|
- Firefox
|
||||||
|
- Microsoft Edge
|
||||||
|
- Safari
|
||||||
|
- Other
|
||||||
|
- type: textarea
|
||||||
|
id: current-behavior
|
||||||
|
attributes:
|
||||||
|
label: Current behaviour
|
||||||
|
description: A description of what is currently happening, including screenshots and other useful information (**DO NOT INCLUDE PRIVATE INFORMATION**).
|
||||||
|
placeholder: Currently...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: desired-behavior
|
||||||
|
attributes:
|
||||||
|
label: Desired behaviour
|
||||||
|
description: A clear description of what you think should happen.
|
||||||
|
placeholder: In this situation, I expected ...
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Clearly describe which steps or actions you have taken to arrive at the problem. If you have some experience with the code, please link to the specific pieces of code.
|
||||||
|
placeholder: I did X, then Y, before arriving at Z, when ERROR ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: other
|
||||||
|
attributes:
|
||||||
|
label: Other information
|
||||||
|
description: Any other details?
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
name: "✨ Feature Request"
|
||||||
|
description: Suggest a feature or enhancement to improve Planka.
|
||||||
|
labels: ["Type: Idea"]
|
||||||
|
body:
|
||||||
|
- type: dropdown
|
||||||
|
id: idea-type
|
||||||
|
attributes:
|
||||||
|
label: Is this a feature for the backend or frontend?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Backend
|
||||||
|
- Frontend
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: feature
|
||||||
|
attributes:
|
||||||
|
label: What would you like?
|
||||||
|
description: A clear description of the feature or enhancement wanted.
|
||||||
|
placeholder: I'd like to be able to...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reason
|
||||||
|
attributes:
|
||||||
|
label: Why is this needed?
|
||||||
|
description: A clear description of why this would be useful to have.
|
||||||
|
placeholder: I want this because...
|
||||||
|
- type: textarea
|
||||||
|
id: other
|
||||||
|
attributes:
|
||||||
|
label: Other information
|
||||||
|
placeholder: Any other details?
|
||||||
Executable → Regular
-2
@@ -1,4 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx lint-staged
|
npx lint-staged
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Planka
|
# Planka
|
||||||
#### Elegant open source project tracking.
|
#### Elegant open source project tracking.
|
||||||
|
|
||||||
  
|
  
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.1.32
|
version: 0.2.8
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "1.17.4"
|
appVersion: "1.22.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- alias: postgresql
|
- alias: postgresql
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ spec:
|
|||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "planka.selectorLabels" . | nindent 6 }}
|
{{- include "planka.selectorLabels" . | nindent 6 }}
|
||||||
|
{{- if .Values.persistence.enabled }}
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
{{- end }}
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
{{- with .Values.podAnnotations }}
|
{{- with .Values.podAnnotations }}
|
||||||
@@ -90,17 +94,37 @@ spec:
|
|||||||
value: http://localhost:3000
|
value: http://localhost:3000
|
||||||
{{- end }}
|
{{- end }}
|
||||||
- name: SECRET_KEY
|
- name: SECRET_KEY
|
||||||
|
{{- if .Values.existingSecretkeySecret }}
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.existingSecretkeySecret }}
|
||||||
|
key: key
|
||||||
|
{{- else }}
|
||||||
value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }}
|
value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }}
|
||||||
|
{{- end }}
|
||||||
- name: TRUST_PROXY
|
- name: TRUST_PROXY
|
||||||
value: "0"
|
value: "0"
|
||||||
- name: DEFAULT_ADMIN_EMAIL
|
- name: DEFAULT_ADMIN_EMAIL
|
||||||
value: {{ .Values.admin_email }}
|
value: {{ .Values.admin_email }}
|
||||||
- name: DEFAULT_ADMIN_PASSWORD
|
|
||||||
value: {{ .Values.admin_password }}
|
|
||||||
- name: DEFAULT_ADMIN_NAME
|
- name: DEFAULT_ADMIN_NAME
|
||||||
value: {{ .Values.admin_name }}
|
value: {{ .Values.admin_name }}
|
||||||
|
{{- if .Values.existingAdminCredsSecret }}
|
||||||
|
- name: DEFAULT_ADMIN_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.existingAdminCredsSecret }}
|
||||||
|
key: username
|
||||||
|
- name: DEFAULT_ADMIN_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.existingAdminCredsSecret }}
|
||||||
|
key: password
|
||||||
|
{{- else }}
|
||||||
- name: DEFAULT_ADMIN_USERNAME
|
- name: DEFAULT_ADMIN_USERNAME
|
||||||
value: {{ .Values.admin_username }}
|
value: {{ .Values.admin_username }}
|
||||||
|
- name: DEFAULT_ADMIN_PASSWORD
|
||||||
|
value: {{ .Values.admin_password }}
|
||||||
|
{{- end }}
|
||||||
{{ range $k, $v := .Values.env }}
|
{{ range $k, $v := .Values.env }}
|
||||||
- name: {{ $k | quote }}
|
- name: {{ $k | quote }}
|
||||||
value: {{ $v | quote }}
|
value: {{ $v | quote }}
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ fullnameOverride: ""
|
|||||||
# Generate a secret using openssl rand -base64 45
|
# Generate a secret using openssl rand -base64 45
|
||||||
secretkey: ""
|
secretkey: ""
|
||||||
|
|
||||||
|
## @param existingSecretkeySecret Name of an existing secret containing the session key string
|
||||||
|
## NOTE: Must contain key `key`
|
||||||
|
## NOTE: When it's set, the secretkey parameter is ignored
|
||||||
|
existingSecretkeySecret: ""
|
||||||
|
|
||||||
|
## @param existingAdminCredsSecret Name of an existing secret containing the admin username and password
|
||||||
|
## NOTE: Must contain keys `username` and `password`
|
||||||
|
## NOTE: When it's set, the `admin_username` and `admin_password` parameters are ignored
|
||||||
|
existingAdminCredsSecret: ""
|
||||||
|
|
||||||
# Base url for Planka. Will override `ingress.hosts[0].host`
|
# Base url for Planka. Will override `ingress.hosts[0].host`
|
||||||
# Defaults to `http://localhost:3000` if ingress is disabled.
|
# Defaults to `http://localhost:3000` if ingress is disabled.
|
||||||
baseUrl: ""
|
baseUrl: ""
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
REACT_APP_VERSION=1.17.4
|
|
||||||
Generated
+2216
-7883
File diff suppressed because it is too large
Load Diff
+17
-16
@@ -65,8 +65,8 @@
|
|||||||
"dequal": "^2.0.3",
|
"dequal": "^2.0.3",
|
||||||
"easymde": "^2.18.0",
|
"easymde": "^2.18.0",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"i18next": "^23.11.2",
|
"i18next": "^23.15.1",
|
||||||
"i18next-browser-languagedetector": "^7.2.1",
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
"initials": "^3.1.2",
|
"initials": "^3.1.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
@@ -75,20 +75,20 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"node-sass": "^9.0.0",
|
"node-sass": "^9.0.0",
|
||||||
"photoswipe": "^5.4.3",
|
"photoswipe": "^5.4.4",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "18.2.0",
|
||||||
"react-app-rewired": "^2.2.1",
|
"react-app-rewired": "^2.2.1",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-datepicker": "^4.25.0",
|
"react-datepicker": "^4.25.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-i18next": "^13.5.0",
|
"react-i18next": "^15.0.2",
|
||||||
"react-input-mask": "^2.0.4",
|
"react-input-mask": "^2.0.4",
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"react-photoswipe-gallery": "^2.2.7",
|
"react-photoswipe-gallery": "^2.2.7",
|
||||||
"react-redux": "^8.1.3",
|
"react-redux": "^8.1.3",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.26.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"react-simplemde-editor": "^5.2.0",
|
"react-simplemde-editor": "^5.2.0",
|
||||||
"react-textarea-autosize": "^8.5.3",
|
"react-textarea-autosize": "^8.5.3",
|
||||||
@@ -102,22 +102,23 @@
|
|||||||
"sails.io.js": "^1.2.1",
|
"sails.io.js": "^1.2.1",
|
||||||
"semantic-ui-react": "^2.1.5",
|
"semantic-ui-react": "^2.1.5",
|
||||||
"socket.io-client": "^2.5.0",
|
"socket.io-client": "^2.5.0",
|
||||||
"validator": "^13.11.0",
|
"validator": "^13.12.0",
|
||||||
"whatwg-fetch": "^3.6.20",
|
"whatwg-fetch": "^3.6.20",
|
||||||
"zxcvbn": "^4.4.2"
|
"zxcvbn": "^4.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^6.4.2",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@testing-library/react": "^14.3.1",
|
"@testing-library/jest-dom": "^6.5.0",
|
||||||
|
"@testing-library/react": "^15.0.7",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"babel-preset-airbnb": "^5.0.0",
|
"babel-preset-airbnb": "^5.0.0",
|
||||||
"chai": "^4.4.1",
|
"chai": "^4.5.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
"eslint-plugin-jsx-a11y": "^6.10.0",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.36.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"react-test-renderer": "^18.2.0"
|
"react-test-renderer": "18.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
|
|||||||
@@ -57,10 +57,15 @@ updateCard.failure = (id, error) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCardUpdate = (card) => ({
|
const handleCardUpdate = (card, isFetched, cardMemberships, cardLabels, tasks, attachments) => ({
|
||||||
type: ActionTypes.CARD_UPDATE_HANDLE,
|
type: ActionTypes.CARD_UPDATE_HANDLE,
|
||||||
payload: {
|
payload: {
|
||||||
card,
|
card,
|
||||||
|
isFetched,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import http from './http';
|
import http from './http';
|
||||||
import socket from './socket';
|
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
|
|
||||||
const createAccessToken = (data, headers) => http.post('/access-tokens', data, headers);
|
const createAccessToken = (data, headers) =>
|
||||||
|
http.post('/access-tokens?withHttpOnlyToken=true', data, headers);
|
||||||
|
|
||||||
const exchangeForAccessTokenUsingOidc = (data, headers) =>
|
const exchangeForAccessTokenUsingOidc = (data, headers) =>
|
||||||
http.post('/access-tokens/exchange-using-oidc', data, headers);
|
http.post('/access-tokens/exchange-using-oidc?withHttpOnlyToken=true', data, headers);
|
||||||
|
|
||||||
const deleteCurrentAccessToken = (headers) =>
|
const deleteCurrentAccessToken = (headers) => http.delete('/access-tokens/me', undefined, headers);
|
||||||
socket.delete('/access-tokens/me', undefined, headers);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createAccessToken,
|
createAccessToken,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Config from '../constants/Config';
|
|||||||
const http = {};
|
const http = {};
|
||||||
|
|
||||||
// TODO: add all methods
|
// TODO: add all methods
|
||||||
['GET', 'POST'].forEach((method) => {
|
['GET', 'POST', 'DELETE'].forEach((method) => {
|
||||||
http[method.toLowerCase()] = (url, data, headers) => {
|
http[method.toLowerCase()] = (url, data, headers) => {
|
||||||
const formData =
|
const formData =
|
||||||
data &&
|
data &&
|
||||||
@@ -19,6 +19,7 @@ const http = {};
|
|||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
body: formData,
|
body: formData,
|
||||||
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
.then((response) =>
|
.then((response) =>
|
||||||
response.json().then((body) => ({
|
response.json().then((body) => ({
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ io.sails.environment = process.env.NODE_ENV;
|
|||||||
|
|
||||||
const { socket } = io;
|
const { socket } = io;
|
||||||
|
|
||||||
socket.path = `${Config.BASE_PATH}/socket.io`;
|
socket.path = `${Config.SERVER_BASE_PATH}/socket.io`;
|
||||||
socket.connect = socket._connect; // eslint-disable-line no-underscore-dangle
|
socket.connect = socket._connect; // eslint-disable-line no-underscore-dangle
|
||||||
|
|
||||||
['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].forEach((method) => {
|
['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].forEach((method) => {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 559 KiB After Width: | Height: | Size: 184 KiB |
@@ -1,6 +1,8 @@
|
|||||||
:global(#app) {
|
:global(#app) {
|
||||||
.wrapper {
|
.wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
max-height: 100vh;
|
||||||
|
max-width: 100vw;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
|||||||
@@ -48,16 +48,6 @@
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panelItem {
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
margin: 0 20px;
|
margin: 0 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin-right: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: flex-start;
|
||||||
margin: 20px 20px;
|
margin: 20px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,10 @@
|
|||||||
transition: width 0.2s ease;
|
transition: width 0.2s ease;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const Card = React.memo(
|
|||||||
index,
|
index,
|
||||||
name,
|
name,
|
||||||
dueDate,
|
dueDate,
|
||||||
|
isDueDateCompleted,
|
||||||
stopwatch,
|
stopwatch,
|
||||||
coverUrl,
|
coverUrl,
|
||||||
boardId,
|
boardId,
|
||||||
@@ -120,7 +121,7 @@ const Card = React.memo(
|
|||||||
)}
|
)}
|
||||||
{dueDate && (
|
{dueDate && (
|
||||||
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
|
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
|
||||||
<DueDate value={dueDate} size="tiny" />
|
<DueDate value={dueDate} isCompleted={isDueDateCompleted} size="tiny" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{stopwatch && (
|
{stopwatch && (
|
||||||
@@ -221,6 +222,7 @@ Card.propTypes = {
|
|||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
dueDate: PropTypes.instanceOf(Date),
|
dueDate: PropTypes.instanceOf(Date),
|
||||||
|
isDueDateCompleted: PropTypes.bool,
|
||||||
stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
coverUrl: PropTypes.string,
|
coverUrl: PropTypes.string,
|
||||||
boardId: PropTypes.string.isRequired,
|
boardId: PropTypes.string.isRequired,
|
||||||
@@ -255,6 +257,7 @@ Card.propTypes = {
|
|||||||
|
|
||||||
Card.defaultProps = {
|
Card.defaultProps = {
|
||||||
dueDate: undefined,
|
dueDate: undefined,
|
||||||
|
isDueDateCompleted: undefined,
|
||||||
stopwatch: undefined,
|
stopwatch: undefined,
|
||||||
coverUrl: undefined,
|
coverUrl: undefined,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,14 @@
|
|||||||
background: #ebeef0;
|
background: #ebeef0;
|
||||||
color: #516b7a;
|
color: #516b7a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
background: #ebeef0;
|
||||||
|
color: #516b7a;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachment {
|
.attachment {
|
||||||
@@ -55,6 +63,12 @@
|
|||||||
box-shadow: 0 1px 0 #ccc;
|
box-shadow: 0 1px 0 #ccc;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
.target {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #f5f6f7;
|
background: #f5f6f7;
|
||||||
border-bottom-color: rgba(9, 30, 66, 0.25);
|
border-bottom-color: rgba(9, 30, 66, 0.25);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import TextareaAutosize from 'react-textarea-autosize';
|
|||||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||||
|
|
||||||
import { useClosableForm, useField } from '../../hooks';
|
import { useClosableForm, useField } from '../../hooks';
|
||||||
|
import { focusEnd } from '../../utils/element-helpers';
|
||||||
|
|
||||||
import styles from './NameEdit.module.scss';
|
import styles from './NameEdit.module.scss';
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpened) {
|
if (isOpened) {
|
||||||
field.current.ref.current.focus();
|
focusEnd(field.current.ref.current);
|
||||||
}
|
}
|
||||||
}, [isOpened]);
|
}, [isOpened]);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import TextareaAutosize from 'react-textarea-autosize';
|
|||||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||||
|
|
||||||
import { useForm } from '../../../hooks';
|
import { useForm } from '../../../hooks';
|
||||||
|
import { focusEnd } from '../../../utils/element-helpers';
|
||||||
|
|
||||||
import styles from './CommentEdit.module.scss';
|
import styles from './CommentEdit.module.scss';
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ const CommentEdit = React.forwardRef(({ children, defaultData, onUpdate }, ref)
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpened) {
|
if (isOpened) {
|
||||||
textField.current.ref.current.focus();
|
focusEnd(textField.current.ref.current);
|
||||||
}
|
}
|
||||||
}, [isOpened]);
|
}, [isOpened]);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.dropzone {
|
.dropzone {
|
||||||
background: white;
|
background: white;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: bold;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
.contentError {
|
.contentError {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: bold;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 470px;
|
width: 470px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
color: #5e6c84;
|
color: #5e6c84;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 700;
|
font-weight: bold;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 80px;
|
line-height: 80px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
.name {
|
.name {
|
||||||
color: #17394d;
|
color: #17394d;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 700;
|
font-weight: bold;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useCallback, useRef } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Grid, Icon, Modal } from 'semantic-ui-react';
|
import { Button, Checkbox, Grid, Icon, Modal } from 'semantic-ui-react';
|
||||||
import { usePopup } from '../../lib/popup';
|
import { usePopup } from '../../lib/popup';
|
||||||
import { Markdown } from '../../lib/custom-ui';
|
import { Markdown } from '../../lib/custom-ui';
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ const CardModal = React.memo(
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
dueDate,
|
dueDate,
|
||||||
|
isDueDateCompleted,
|
||||||
stopwatch,
|
stopwatch,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
isActivitiesFetching,
|
isActivitiesFetching,
|
||||||
@@ -81,6 +82,7 @@ const CardModal = React.memo(
|
|||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
const [isLinkCopied, setIsLinkCopied] = useState(false);
|
||||||
|
|
||||||
const isGalleryOpened = useRef(false);
|
const isGalleryOpened = useRef(false);
|
||||||
|
|
||||||
@@ -117,6 +119,12 @@ const CardModal = React.memo(
|
|||||||
[onUpdate],
|
[onUpdate],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleDueDateCompletionChange = useCallback(() => {
|
||||||
|
onUpdate({
|
||||||
|
isDueDateCompleted: !isDueDateCompleted,
|
||||||
|
});
|
||||||
|
}, [isDueDateCompleted, onUpdate]);
|
||||||
|
|
||||||
const handleStopwatchUpdate = useCallback(
|
const handleStopwatchUpdate = useCallback(
|
||||||
(newStopwatch) => {
|
(newStopwatch) => {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
@@ -146,6 +154,14 @@ const CardModal = React.memo(
|
|||||||
onClose();
|
onClose();
|
||||||
}, [onDuplicate, onClose]);
|
}, [onDuplicate, onClose]);
|
||||||
|
|
||||||
|
const handleCopyLinkClick = useCallback(() => {
|
||||||
|
navigator.clipboard.writeText(window.location.href);
|
||||||
|
setIsLinkCopied(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLinkCopied(false);
|
||||||
|
}, 5000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleGalleryOpen = useCallback(() => {
|
const handleGalleryOpen = useCallback(() => {
|
||||||
isGalleryOpened.current = true;
|
isGalleryOpened.current = true;
|
||||||
}, []);
|
}, []);
|
||||||
@@ -291,13 +307,24 @@ const CardModal = React.memo(
|
|||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<span className={styles.attachment}>
|
<span className={classNames(styles.attachment, styles.attachmentDueDate)}>
|
||||||
{canEdit ? (
|
{canEdit ? (
|
||||||
<DueDateEditPopup defaultValue={dueDate} onUpdate={handleDueDateUpdate}>
|
<>
|
||||||
<DueDate value={dueDate} />
|
<Checkbox
|
||||||
</DueDateEditPopup>
|
checked={isDueDateCompleted}
|
||||||
|
disabled={!canEdit}
|
||||||
|
onChange={handleDueDateCompletionChange}
|
||||||
|
/>
|
||||||
|
<DueDateEditPopup defaultValue={dueDate} onUpdate={handleDueDateUpdate}>
|
||||||
|
<DueDate
|
||||||
|
withStatusIcon
|
||||||
|
value={dueDate}
|
||||||
|
isCompleted={isDueDateCompleted}
|
||||||
|
/>
|
||||||
|
</DueDateEditPopup>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<DueDate value={dueDate} />
|
<DueDate withStatusIcon value={dueDate} isCompleted={isDueDateCompleted} />
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -506,6 +533,19 @@ const CardModal = React.memo(
|
|||||||
<Icon name="copy outline" className={styles.actionIcon} />
|
<Icon name="copy outline" className={styles.actionIcon} />
|
||||||
{t('action.duplicate')}
|
{t('action.duplicate')}
|
||||||
</Button>
|
</Button>
|
||||||
|
{window.isSecureContext && (
|
||||||
|
<Button fluid className={styles.actionButton} onClick={handleCopyLinkClick}>
|
||||||
|
<Icon
|
||||||
|
name={isLinkCopied ? 'linkify' : 'unlink'}
|
||||||
|
className={styles.actionIcon}
|
||||||
|
/>
|
||||||
|
{isLinkCopied
|
||||||
|
? t('common.linkIsCopied')
|
||||||
|
: t('action.copyLink', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<DeletePopup
|
<DeletePopup
|
||||||
title="common.deleteCard"
|
title="common.deleteCard"
|
||||||
content="common.areYouSureYouWantToDeleteThisCard"
|
content="common.areYouSureYouWantToDeleteThisCard"
|
||||||
@@ -540,6 +580,7 @@ CardModal.propTypes = {
|
|||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
description: PropTypes.string,
|
description: PropTypes.string,
|
||||||
dueDate: PropTypes.instanceOf(Date),
|
dueDate: PropTypes.instanceOf(Date),
|
||||||
|
isDueDateCompleted: PropTypes.bool,
|
||||||
stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
isSubscribed: PropTypes.bool.isRequired,
|
isSubscribed: PropTypes.bool.isRequired,
|
||||||
isActivitiesFetching: PropTypes.bool.isRequired,
|
isActivitiesFetching: PropTypes.bool.isRequired,
|
||||||
@@ -594,6 +635,7 @@ CardModal.propTypes = {
|
|||||||
CardModal.defaultProps = {
|
CardModal.defaultProps = {
|
||||||
description: undefined,
|
description: undefined,
|
||||||
dueDate: undefined,
|
dueDate: undefined,
|
||||||
|
isDueDateCompleted: false,
|
||||||
stopwatch: undefined,
|
stopwatch: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,18 +20,26 @@
|
|||||||
|
|
||||||
.actionIcon {
|
.actionIcon {
|
||||||
color: #17394d;
|
color: #17394d;
|
||||||
display: inline;
|
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 425px) {
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionsTitle {
|
.actionsTitle {
|
||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: normal;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -50,6 +58,12 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.attachmentDueDate {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.attachments {
|
.attachments {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 8px 8px 0;
|
margin: 0 8px 8px 0;
|
||||||
@@ -63,6 +77,11 @@
|
|||||||
|
|
||||||
.contentPadding {
|
.contentPadding {
|
||||||
padding: 8px 8px 0 16px;
|
padding: 8px 8px 0 16px;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
padding-right: 16px;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursorPointer {
|
.cursorPointer {
|
||||||
@@ -139,7 +158,7 @@
|
|||||||
.headerTitle {
|
.headerTitle {
|
||||||
color: #17394d;
|
color: #17394d;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: bold;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +174,11 @@
|
|||||||
|
|
||||||
.modalPadding {
|
.modalPadding {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.moduleHeader {
|
.moduleHeader {
|
||||||
@@ -185,6 +209,15 @@
|
|||||||
|
|
||||||
.sidebarPadding {
|
.sidebarPadding {
|
||||||
padding: 8px 16px 0 8px;
|
padding: 8px 16px 0 8px;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
align-items: flex-start;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
gap: 10px;
|
||||||
|
padding-left: 16px;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: #17394d;
|
color: #17394d;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: bold;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
margin: -5px;
|
margin: -5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import TextareaAutosize from 'react-textarea-autosize';
|
|||||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||||
|
|
||||||
import { useField } from '../../../hooks';
|
import { useField } from '../../../hooks';
|
||||||
|
import { focusEnd } from '../../../utils/element-helpers';
|
||||||
|
|
||||||
import styles from './NameEdit.module.scss';
|
import styles from './NameEdit.module.scss';
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpened) {
|
if (isOpened) {
|
||||||
field.current.ref.current.focus();
|
focusEnd(field.current.ref.current);
|
||||||
}
|
}
|
||||||
}, [isOpened]);
|
}, [isOpened]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
box-shadow: #b04632 0 1px 0;
|
box-shadow: #b04632 0 1px 0;
|
||||||
|
max-width: calc(100% - 40px);
|
||||||
padding: 12px 18px;
|
padding: 12px 18px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import upperFirst from 'lodash/upperFirst';
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import React from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Icon } from 'semantic-ui-react';
|
||||||
|
import { useForceUpdate } from '../../lib/hooks';
|
||||||
|
|
||||||
import getDateFormat from '../../utils/get-date-format';
|
import getDateFormat from '../../utils/get-date-format';
|
||||||
|
|
||||||
@@ -14,6 +16,12 @@ const SIZES = {
|
|||||||
MEDIUM: 'medium',
|
MEDIUM: 'medium',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STATUSES = {
|
||||||
|
DUE_SOON: 'dueSoon',
|
||||||
|
OVERDUE: 'overdue',
|
||||||
|
COMPLETED: 'completed',
|
||||||
|
};
|
||||||
|
|
||||||
const LONG_DATE_FORMAT_BY_SIZE = {
|
const LONG_DATE_FORMAT_BY_SIZE = {
|
||||||
tiny: 'longDate',
|
tiny: 'longDate',
|
||||||
small: 'longDate',
|
small: 'longDate',
|
||||||
@@ -26,8 +34,47 @@ const FULL_DATE_FORMAT_BY_SIZE = {
|
|||||||
medium: 'fullDateTime',
|
medium: 'fullDateTime',
|
||||||
};
|
};
|
||||||
|
|
||||||
const DueDate = React.memo(({ value, size, isDisabled, onClick }) => {
|
const STATUS_ICON_PROPS_BY_STATUS = {
|
||||||
|
[STATUSES.DUE_SOON]: {
|
||||||
|
name: 'hourglass half',
|
||||||
|
color: 'orange',
|
||||||
|
},
|
||||||
|
[STATUSES.OVERDUE]: {
|
||||||
|
name: 'hourglass end',
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
[STATUSES.COMPLETED]: {
|
||||||
|
name: 'checkmark',
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatus = (dateTime, isCompleted) => {
|
||||||
|
if (isCompleted) {
|
||||||
|
return STATUSES.COMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secondsLeft = Math.floor((dateTime.getTime() - new Date().getTime()) / 1000);
|
||||||
|
|
||||||
|
if (secondsLeft <= 0) {
|
||||||
|
return STATUSES.OVERDUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondsLeft <= 24 * 60 * 60) {
|
||||||
|
return STATUSES.DUE_SOON;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DueDate = React.memo(({ value, size, isCompleted, isDisabled, withStatusIcon, onClick }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
|
const statusRef = useRef(null);
|
||||||
|
statusRef.current = getStatus(value, isCompleted);
|
||||||
|
|
||||||
|
const intervalRef = useRef(null);
|
||||||
|
|
||||||
const dateFormat = getDateFormat(
|
const dateFormat = getDateFormat(
|
||||||
value,
|
value,
|
||||||
@@ -35,11 +82,34 @@ const DueDate = React.memo(({ value, size, isDisabled, onClick }) => {
|
|||||||
FULL_DATE_FORMAT_BY_SIZE[size],
|
FULL_DATE_FORMAT_BY_SIZE[size],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ([null, STATUSES.DUE_SOON].includes(statusRef.current)) {
|
||||||
|
intervalRef.current = setInterval(() => {
|
||||||
|
const status = getStatus(value, isCompleted);
|
||||||
|
|
||||||
|
if (status !== statusRef.current) {
|
||||||
|
forceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === STATUSES.OVERDUE) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [value, isCompleted, forceUpdate]);
|
||||||
|
|
||||||
const contentNode = (
|
const contentNode = (
|
||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.wrapper,
|
styles.wrapper,
|
||||||
styles[`wrapper${upperFirst(size)}`],
|
styles[`wrapper${upperFirst(size)}`],
|
||||||
|
!withStatusIcon && statusRef.current && styles[`wrapper${upperFirst(statusRef.current)}`],
|
||||||
onClick && styles.wrapperHoverable,
|
onClick && styles.wrapperHoverable,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -47,6 +117,10 @@ const DueDate = React.memo(({ value, size, isDisabled, onClick }) => {
|
|||||||
value,
|
value,
|
||||||
postProcess: 'formatDate',
|
postProcess: 'formatDate',
|
||||||
})}
|
})}
|
||||||
|
{withStatusIcon && statusRef.current && (
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
<Icon {...STATUS_ICON_PROPS_BY_STATUS[statusRef.current]} className={styles.statusIcon} />
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,14 +136,19 @@ const DueDate = React.memo(({ value, size, isDisabled, onClick }) => {
|
|||||||
DueDate.propTypes = {
|
DueDate.propTypes = {
|
||||||
value: PropTypes.instanceOf(Date).isRequired,
|
value: PropTypes.instanceOf(Date).isRequired,
|
||||||
size: PropTypes.oneOf(Object.values(SIZES)),
|
size: PropTypes.oneOf(Object.values(SIZES)),
|
||||||
|
isCompleted: PropTypes.bool.isRequired,
|
||||||
isDisabled: PropTypes.bool,
|
isDisabled: PropTypes.bool,
|
||||||
|
withStatusIcon: PropTypes.bool,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
|
onCompletionToggle: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
DueDate.defaultProps = {
|
DueDate.defaultProps = {
|
||||||
size: SIZES.MEDIUM,
|
size: SIZES.MEDIUM,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
|
withStatusIcon: false,
|
||||||
onClick: undefined,
|
onClick: undefined,
|
||||||
|
onCompletionToggle: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DueDate;
|
export default DueDate;
|
||||||
|
|||||||
@@ -8,16 +8,17 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.statusIcon {
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
background: #dce0e4;
|
background: #dce0e4;
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: #6a808b;
|
color: #6a808b;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
outline: none;
|
|
||||||
text-align: left;
|
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapperHoverable:hover {
|
.wrapperHoverable:hover {
|
||||||
@@ -43,4 +44,21 @@
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Statuses */
|
||||||
|
|
||||||
|
.wrapperDueSoon {
|
||||||
|
background: #f2711c;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapperOverdue {
|
||||||
|
background: #db2828;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapperCompleted {
|
||||||
|
background: #21ba45;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
:global(#app) {
|
:global(#app) {
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
max-width: 100vw;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|||||||
@@ -87,7 +87,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.userName {
|
.userName {
|
||||||
|
display: none;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
|
@media only screen and (min-width: 797px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const SIZES = {
|
|||||||
|
|
||||||
const Label = React.memo(({ name, color, size, isDisabled, onClick }) => {
|
const Label = React.memo(({ name, color, size, isDisabled, onClick }) => {
|
||||||
const contentNode = (
|
const contentNode = (
|
||||||
<div
|
<span
|
||||||
title={name}
|
title={name}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.wrapper,
|
styles.wrapper,
|
||||||
@@ -28,7 +28,7 @@ const Label = React.memo(({ name, color, size, isDisabled, onClick }) => {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{name || '\u00A0'}
|
{name || '\u00A0'}
|
||||||
</div>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
return onClick ? (
|
return onClick ? (
|
||||||
|
|||||||
@@ -11,17 +11,17 @@
|
|||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-sizing: border-box;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 400;
|
display: inline-block;
|
||||||
outline: none;
|
font-weight: normal;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.2);
|
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
vertical-align: top;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapperNameless{
|
.wrapperNameless {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,12 @@
|
|||||||
&:hover .target {
|
&:hover .target {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
.target {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerEditable {
|
.headerEditable {
|
||||||
@@ -103,6 +109,15 @@
|
|||||||
background: rgba(9, 30, 66, 0.13);
|
background: rgba(9, 30, 66, 0.13);
|
||||||
color: #516b7a;
|
color: #516b7a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
background: rgba(9, 30, 66, 0.13);
|
||||||
|
color: #516b7a;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerName {
|
.headerName {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import TextareaAutosize from 'react-textarea-autosize';
|
|||||||
import { TextArea } from 'semantic-ui-react';
|
import { TextArea } from 'semantic-ui-react';
|
||||||
|
|
||||||
import { useField } from '../../hooks';
|
import { useField } from '../../hooks';
|
||||||
|
import { focusEnd } from '../../utils/element-helpers';
|
||||||
|
|
||||||
import styles from './NameEdit.module.scss';
|
import styles from './NameEdit.module.scss';
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpened) {
|
if (isOpened) {
|
||||||
field.current.ref.current.select();
|
focusEnd(field.current.ref.current);
|
||||||
}
|
}
|
||||||
}, [isOpened]);
|
}, [isOpened]);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ const createMessage = (error) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (error.message) {
|
switch (error.message) {
|
||||||
|
case 'Invalid credentials':
|
||||||
|
return {
|
||||||
|
type: 'error',
|
||||||
|
content: 'common.invalidCredentials',
|
||||||
|
};
|
||||||
case 'Invalid email or username':
|
case 'Invalid email or username':
|
||||||
return {
|
return {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@@ -116,6 +121,7 @@ const Login = React.memo(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wasSubmitting && !isSubmitting && error) {
|
if (wasSubmitting && !isSubmitting && error) {
|
||||||
switch (error.message) {
|
switch (error.message) {
|
||||||
|
case 'Invalid credentials':
|
||||||
case 'Invalid email or username':
|
case 'Invalid email or username':
|
||||||
emailOrUsernameField.current.select();
|
emailOrUsernameField.current.select();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 797px) {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, 0.32);
|
background: rgba(0, 0, 0, 0.32);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,10 @@
|
|||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
background: #dce0e4;
|
background: #dce0e4;
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: #6a808b;
|
color: #6a808b;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
outline: none;
|
|
||||||
text-align: left;
|
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
border: none;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
outline: none;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
@@ -32,7 +30,6 @@
|
|||||||
|
|
||||||
.wrapperTiny {
|
.wrapperTiny {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 400;
|
|
||||||
height: 24px;
|
height: 24px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
@@ -41,7 +38,6 @@
|
|||||||
|
|
||||||
.wrapperSmall {
|
.wrapperSmall {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
|
||||||
height: 28px;
|
height: 28px;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
width: 28px;
|
width: 28px;
|
||||||
@@ -49,7 +45,6 @@
|
|||||||
|
|
||||||
.wrapperMedium {
|
.wrapperMedium {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
@@ -57,7 +52,6 @@
|
|||||||
|
|
||||||
.wrapperLarge {
|
.wrapperLarge {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
|
||||||
height: 36px;
|
height: 36px;
|
||||||
padding: 12px 0 10px;
|
padding: 12px 0 10px;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
@@ -65,7 +59,6 @@
|
|||||||
|
|
||||||
.wrapperMassive {
|
.wrapperMassive {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
font-weight: 500;
|
|
||||||
height: 100px;
|
height: 100px;
|
||||||
padding: 32px 0 10px;
|
padding: 32px 0 10px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Image, Tab } from 'semantic-ui-react';
|
import { Image, Tab } from 'semantic-ui-react';
|
||||||
|
|
||||||
import Config from '../../constants/Config';
|
import version from '../../version';
|
||||||
|
|
||||||
import logo from '../../assets/images/logo.png';
|
import logo from '../../assets/images/logo.png';
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ const AboutPane = React.memo(() => {
|
|||||||
<Tab.Pane attached={false} className={styles.wrapper}>
|
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||||
<Image centered src={logo} size="large" />
|
<Image centered src={logo} size="large" />
|
||||||
<div className={styles.version}>
|
<div className={styles.version}>
|
||||||
{t('common.version')} {Config.VERSION}
|
{t('common.version')} {version}
|
||||||
</div>
|
</div>
|
||||||
</Tab.Pane>
|
</Tab.Pane>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -195,9 +195,6 @@ export default {
|
|||||||
CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS',
|
CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS',
|
||||||
CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE',
|
CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE',
|
||||||
CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE',
|
CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE',
|
||||||
CARD_TRANSFER: 'CARD_TRANSFER',
|
|
||||||
CARD_TRANSFER__SUCCESS: 'CARD_TRANSFER__SUCCESS',
|
|
||||||
CARD_TRANSFER__FAILURE: 'CARD_TRANSFER__FAILURE',
|
|
||||||
CARD_DUPLICATE: 'CARD_DUPLICATE',
|
CARD_DUPLICATE: 'CARD_DUPLICATE',
|
||||||
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
|
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
|
||||||
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
|
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
const VERSION = process.env.REACT_APP_VERSION;
|
|
||||||
|
|
||||||
const { BASE_URL } = window;
|
const { BASE_URL } = window;
|
||||||
|
|
||||||
const BASE_PATH = BASE_URL.replace(/^.*\/\/[^/]*(.*)[^?#]*.*$/, '$1');
|
const BASE_PATH = BASE_URL.replace(/^.*\/\/[^/]*(.*)[^?#]*.*$/, '$1');
|
||||||
|
|
||||||
const SERVER_BASE_URL =
|
const SERVER_BASE_URL =
|
||||||
process.env.REACT_APP_SERVER_BASE_URL ||
|
process.env.REACT_APP_SERVER_BASE_URL ||
|
||||||
(process.env.NODE_ENV === 'production' ? BASE_URL : 'http://localhost:1337');
|
(process.env.NODE_ENV === 'production' ? BASE_URL : 'http://localhost:1337');
|
||||||
|
|
||||||
|
const SERVER_BASE_PATH = SERVER_BASE_URL.replace(/^.*\/\/[^/]*(.*)[^?#]*.*$/, '$1');
|
||||||
const SERVER_HOST_NAME = SERVER_BASE_URL.replace(/^(.*\/\/[^/?#]*).*$/, '$1');
|
const SERVER_HOST_NAME = SERVER_BASE_URL.replace(/^(.*\/\/[^/?#]*).*$/, '$1');
|
||||||
|
|
||||||
const ACCESS_TOKEN_KEY = 'accessToken';
|
const ACCESS_TOKEN_KEY = 'accessToken';
|
||||||
@@ -17,9 +17,9 @@ const POSITION_GAP = 65535;
|
|||||||
const ACTIVITIES_LIMIT = 50;
|
const ACTIVITIES_LIMIT = 50;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
VERSION,
|
|
||||||
BASE_PATH,
|
BASE_PATH,
|
||||||
SERVER_BASE_URL,
|
SERVER_BASE_URL,
|
||||||
|
SERVER_BASE_PATH,
|
||||||
SERVER_HOST_NAME,
|
SERVER_HOST_NAME,
|
||||||
ACCESS_TOKEN_KEY,
|
ACCESS_TOKEN_KEY,
|
||||||
ACCESS_TOKEN_VERSION_KEY,
|
ACCESS_TOKEN_VERSION_KEY,
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ const makeMapStateToProps = () => {
|
|||||||
const allLabels = selectors.selectLabelsForCurrentBoard(state);
|
const allLabels = selectors.selectLabelsForCurrentBoard(state);
|
||||||
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
|
||||||
const { name, dueDate, stopwatch, coverUrl, boardId, listId, isPersisted } = selectCardById(
|
const { name, dueDate, isDueDateCompleted, stopwatch, coverUrl, boardId, listId, isPersisted } =
|
||||||
state,
|
selectCardById(state, id);
|
||||||
id,
|
|
||||||
);
|
|
||||||
|
|
||||||
const users = selectUsersByCardId(state, id);
|
const users = selectUsersByCardId(state, id);
|
||||||
const labels = selectLabelsByCardId(state, id);
|
const labels = selectLabelsByCardId(state, id);
|
||||||
@@ -38,6 +36,7 @@ const makeMapStateToProps = () => {
|
|||||||
index,
|
index,
|
||||||
name,
|
name,
|
||||||
dueDate,
|
dueDate,
|
||||||
|
isDueDateCompleted,
|
||||||
stopwatch,
|
stopwatch,
|
||||||
coverUrl,
|
coverUrl,
|
||||||
boardId,
|
boardId,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const mapStateToProps = (state) => {
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
dueDate,
|
dueDate,
|
||||||
|
isDueDateCompleted,
|
||||||
stopwatch,
|
stopwatch,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
isActivitiesFetching,
|
isActivitiesFetching,
|
||||||
@@ -49,6 +50,7 @@ const mapStateToProps = (state) => {
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
dueDate,
|
dueDate,
|
||||||
|
isDueDateCompleted,
|
||||||
stopwatch,
|
stopwatch,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
isActivitiesFetching,
|
isActivitiesFetching,
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import entryActions from '../entry-actions';
|
|||||||
import Projects from '../components/Projects';
|
import Projects from '../components/Projects';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
|
const { allowAllToCreateProjects } = selectors.selectConfig(state);
|
||||||
const { isAdmin } = selectors.selectCurrentUser(state);
|
const { isAdmin } = selectors.selectCurrentUser(state);
|
||||||
const projects = selectors.selectProjectsForCurrentUser(state);
|
const projects = selectors.selectProjectsForCurrentUser(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: projects,
|
items: projects,
|
||||||
canAdd: isAdmin,
|
canAdd: allowAllToCreateProjects || isAdmin,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -58,9 +58,9 @@ i18n
|
|||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
resources: embeddedLocales,
|
resources: embeddedLocales,
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en-US',
|
||||||
supportedLngs: languages,
|
supportedLngs: languages,
|
||||||
load: 'languageOnly',
|
load: 'currentOnly',
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
format(value, format, language) {
|
format(value, format, language) {
|
||||||
@@ -80,7 +80,7 @@ i18n
|
|||||||
});
|
});
|
||||||
|
|
||||||
i18n.loadCoreLocale = async (language = i18n.resolvedLanguage) => {
|
i18n.loadCoreLocale = async (language = i18n.resolvedLanguage) => {
|
||||||
if (language === 'en') {
|
if (language === i18n.options.fallbackLng[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
.markdown-body b,
|
.markdown-body b,
|
||||||
.markdown-body strong {
|
.markdown-body strong {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body dfn {
|
.markdown-body dfn {
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
|
|
||||||
.markdown-body h1 {
|
.markdown-body h1 {
|
||||||
margin: .67em 0;
|
margin: .67em 0;
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
padding-bottom: .3em;
|
padding-bottom: .3em;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
border-bottom: 1px solid hsla(210, 18%, 87%, 1);
|
border-bottom: 1px solid hsla(210, 18%, 87%, 1);
|
||||||
@@ -253,34 +253,34 @@
|
|||||||
.markdown-body h6 {
|
.markdown-body h6 {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body h2 {
|
.markdown-body h2 {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
padding-bottom: .3em;
|
padding-bottom: .3em;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
border-bottom: 1px solid hsla(210, 18%, 87%, 1);
|
border-bottom: 1px solid hsla(210, 18%, 87%, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body h3 {
|
.markdown-body h3 {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body h4 {
|
.markdown-body h4 {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body h5 {
|
.markdown-body h5 {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
font-size: .875em;
|
font-size: .875em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body h6 {
|
.markdown-body h6 {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
color: #57606a;
|
color: #57606a;
|
||||||
}
|
}
|
||||||
@@ -481,7 +481,7 @@
|
|||||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-style: normal !important;
|
font-style: normal !important;
|
||||||
font-weight: 400;
|
font-weight: normal; // 400
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
vertical-align: -0.075em;
|
vertical-align: -0.075em;
|
||||||
}
|
}
|
||||||
@@ -650,7 +650,7 @@
|
|||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body dl dd {
|
.markdown-body dl dd {
|
||||||
@@ -659,7 +659,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body table th {
|
.markdown-body table th {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body table th,
|
.markdown-body table th,
|
||||||
@@ -868,7 +868,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body .csv-data th {
|
.markdown-body .csv-data th {
|
||||||
font-weight: 600;
|
font-weight: bold; // 600
|
||||||
background: #f6f8fa;
|
background: #f6f8fa;
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
@@ -912,7 +912,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body .task-list-item label {
|
.markdown-body .task-list-item label {
|
||||||
font-weight: 400;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body .task-list-item.enabled label {
|
.markdown-body .task-list-item.enabled label {
|
||||||
|
|||||||
Vendored
+2568
-2729
File diff suppressed because one or more lines are too long
@@ -0,0 +1,252 @@
|
|||||||
|
import dateFns from 'date-fns/locale/ar';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dateFns,
|
||||||
|
|
||||||
|
format: {
|
||||||
|
date: 'M/d/yyyy',
|
||||||
|
time: 'p',
|
||||||
|
dateTime: '$t(format:date) $t(format:time)',
|
||||||
|
longDate: 'MMM d',
|
||||||
|
longDateTime: "MMMM d 'at' p",
|
||||||
|
fullDate: 'MMM d, y',
|
||||||
|
fullDateTime: "MMMM d, y 'at' p",
|
||||||
|
},
|
||||||
|
|
||||||
|
translation: {
|
||||||
|
common: {
|
||||||
|
aboutPlanka: 'حول Planka',
|
||||||
|
account: 'الحساب',
|
||||||
|
actions: 'إجراءات',
|
||||||
|
addAttachment_title: 'إضافة مرفق',
|
||||||
|
addComment: 'إضافة تعليق',
|
||||||
|
addManager_title: 'إضافة مدير',
|
||||||
|
addMember_title: 'إضافة عضو',
|
||||||
|
addUser_title: 'إضافة مستخدم',
|
||||||
|
administrator: 'المدير',
|
||||||
|
all: 'الكل',
|
||||||
|
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
||||||
|
'سيتم حفظ جميع التغييرات تلقائياً<br />بعد استعادة الإتصال.',
|
||||||
|
areYouSureYouWantToDeleteThisAttachment: 'هل أنت متأكد أنك تريد حذف هذا المرفق؟',
|
||||||
|
areYouSureYouWantToDeleteThisBoard: 'هل أنت متأكد أنك تريد حذف هذه اللوحة؟',
|
||||||
|
areYouSureYouWantToDeleteThisCard: 'هل أنت متأكد أنك تريد حذف هذه البطاقة؟',
|
||||||
|
areYouSureYouWantToDeleteThisComment: 'هل أنت متأكد أنك تريد حذف هذا التعليق؟',
|
||||||
|
areYouSureYouWantToDeleteThisLabel: 'هل أنت متأكد أنك تريد حذف هذا الملصق؟',
|
||||||
|
areYouSureYouWantToDeleteThisList: 'هل أنت متأكد أنك تريد حذف هذه القائمة؟',
|
||||||
|
areYouSureYouWantToDeleteThisProject: 'هل أنت متأكد أنك تريد حذف هذا المشروع؟',
|
||||||
|
areYouSureYouWantToDeleteThisTask: 'هل أنت متأكد أنك تريد حذف هذه المهمة؟',
|
||||||
|
areYouSureYouWantToDeleteThisUser: 'هل أنت متأكد أنك تريد حذف هذا المستخدم؟',
|
||||||
|
areYouSureYouWantToLeaveBoard: 'هل أنت متأكد أنك تريد مغادرة اللوحة؟',
|
||||||
|
areYouSureYouWantToLeaveProject: 'هل أنت متأكد أنك تريد مغادرة المشروع؟',
|
||||||
|
areYouSureYouWantToRemoveThisManagerFromProject:
|
||||||
|
'هل أنت متأكد أنك تريد إزالة هذا المدير من المشروع؟',
|
||||||
|
areYouSureYouWantToRemoveThisMemberFromBoard:
|
||||||
|
'هل أنت متأكد أنك تريد إزالة هذا العضو من اللوحة؟',
|
||||||
|
attachment: 'مرفق',
|
||||||
|
attachments: 'مرفقات',
|
||||||
|
authentication: 'المصادقة',
|
||||||
|
background: 'الخلفية',
|
||||||
|
board: 'لوحة',
|
||||||
|
boardNotFound_title: 'لم يتم العثور على اللوحة',
|
||||||
|
canComment: 'يمكن التعليق',
|
||||||
|
canEditContentOfBoard: 'يمكن تعديل محتوى اللوحة.',
|
||||||
|
canOnlyViewBoard: 'يمكن فقط عرض اللوحة.',
|
||||||
|
cardActions_title: 'إجراءات البطاقة',
|
||||||
|
cardNotFound_title: 'لم يتم العثور على البطاقة',
|
||||||
|
cardOrActionAreDeleted: 'تم حذف البطاقة أو الإجراء.',
|
||||||
|
color: 'اللون',
|
||||||
|
copy_inline: 'نسخ',
|
||||||
|
createBoard_title: 'إنشاء لوحة',
|
||||||
|
createLabel_title: 'إنشاء ملصق',
|
||||||
|
createNewOneOrSelectExistingOne: 'أنشئ واحدة جديدة أو اختر<br />واحدة موجودة.',
|
||||||
|
createProject_title: 'إنشاء مشروع',
|
||||||
|
createTextFile_title: 'إنشاء ملف نصي',
|
||||||
|
currentPassword: 'كلمة المرور الحالية',
|
||||||
|
dangerZone_title: 'منطقة الخطر',
|
||||||
|
date: 'تاريخ',
|
||||||
|
dueDate: 'تاريخ الاستحقاق',
|
||||||
|
dueDate_title: 'تاريخ الاستحقاق',
|
||||||
|
deleteAttachment_title: 'حذف المرفق',
|
||||||
|
deleteBoard_title: 'حذف اللوحة',
|
||||||
|
deleteCard_title: 'حذف البطاقة',
|
||||||
|
deleteComment_title: 'حذف التعليق',
|
||||||
|
deleteLabel_title: 'حذف الملصق',
|
||||||
|
deleteList_title: 'حذف القائمة',
|
||||||
|
deleteProject_title: 'حذف المشروع',
|
||||||
|
deleteTask_title: 'حذف المهمة',
|
||||||
|
deleteUser_title: 'حذف المستخدم',
|
||||||
|
description: 'الوصف',
|
||||||
|
detectAutomatically: 'الكشف تلقائياً',
|
||||||
|
dropFileToUpload: 'أفلت الملف لرفعه',
|
||||||
|
editor: 'محرر',
|
||||||
|
editAttachment_title: 'تعديل المرفق',
|
||||||
|
editAvatar_title: 'تحرير الصورة الرمزية',
|
||||||
|
editBoard_title: 'تعديل اللوحة',
|
||||||
|
editDueDate_title: 'تعديل تاريخ الاستحقاق',
|
||||||
|
editEmail_title: 'تعديل البريد الإلكتروني',
|
||||||
|
editInformation_title: 'تعديل المعلومات',
|
||||||
|
editLabel_title: 'تعديل الملصق',
|
||||||
|
editPassword_title: 'تعديل كلمة المرور',
|
||||||
|
editPermissions_title: 'تعديل الأذونات',
|
||||||
|
editStopwatch_title: 'تعديل المؤقت',
|
||||||
|
editUsername_title: 'تعديل اسم المستخدم',
|
||||||
|
email: 'البريد الإلكتروني',
|
||||||
|
emailAlreadyInUse: 'البريد الإلكتروني مستخدم بالفعل',
|
||||||
|
enterCardTitle: 'أدخل عنوان البطاقة... [Ctrl+Enter] لفتحها تلقائيًا.',
|
||||||
|
enterDescription: 'أدخل الوصف...',
|
||||||
|
enterFilename: 'أدخل اسم الملف',
|
||||||
|
enterListTitle: 'أدخل عنوان القائمة...',
|
||||||
|
enterProjectTitle: 'أدخل عنوان المشروع',
|
||||||
|
enterTaskDescription: 'أدخل وصف المهمة...',
|
||||||
|
filterByLabels_title: 'تصفية حسب الملصقات',
|
||||||
|
filterByMembers_title: 'تصفية حسب الأعضاء',
|
||||||
|
fromComputer_title: 'من الكمبيوتر',
|
||||||
|
fromTrello: 'من Trello',
|
||||||
|
general: 'عام',
|
||||||
|
hours: 'ساعات',
|
||||||
|
importBoard_title: 'استيراد اللوحة',
|
||||||
|
invalidCurrentPassword: 'كلمة المرور الحالية غير صالحة',
|
||||||
|
labels: 'الملصقات',
|
||||||
|
language: 'اللغة',
|
||||||
|
leaveBoard_title: 'غادر اللوحة',
|
||||||
|
leaveProject_title: 'غادر المشروع',
|
||||||
|
linkIsCopied: 'تم نسخ الرابط',
|
||||||
|
list: 'القائمة',
|
||||||
|
listActions_title: 'قائمة الإجراءات',
|
||||||
|
managers: 'المديرون',
|
||||||
|
managerActions_title: 'إجراءات المدير',
|
||||||
|
members: 'الأعضاء',
|
||||||
|
memberActions_title: 'إجراءات العضو',
|
||||||
|
minutes: 'الدقائق',
|
||||||
|
moveCard_title: 'نقل البطاقة',
|
||||||
|
name: 'الاسم',
|
||||||
|
newestFirst: 'الأحدث أولاً',
|
||||||
|
newEmail: 'بريد إلكتروني جديد',
|
||||||
|
newPassword: 'كلمة سر جديدة',
|
||||||
|
newUsername: 'مستخدم جديد',
|
||||||
|
noConnectionToServer: 'لا يوجد اتصال بالخادم',
|
||||||
|
noBoards: 'لا توجد لوحات',
|
||||||
|
noLists: 'لاتوجد قوائم',
|
||||||
|
noProjects: 'لاتوجد مشاريع',
|
||||||
|
notifications: 'الإشعارات',
|
||||||
|
noUnreadNotifications: 'لاتوجد إشعارات غير مقروءة',
|
||||||
|
oldestFirst: 'الأقدم أولاً',
|
||||||
|
openBoard_title: 'فتح اللوحة',
|
||||||
|
optional_inline: 'اختياري',
|
||||||
|
organization: 'المنظمة',
|
||||||
|
phone: 'الهاتف',
|
||||||
|
preferences: 'التفضيلات',
|
||||||
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
|
'نصيحة: اضغط على Ctrl-V (Cmd-V على Mac) لإضافة مرفق من الحافظة.',
|
||||||
|
project: 'مشروع',
|
||||||
|
projectNotFound_title: 'المشروع غير موجود',
|
||||||
|
removeManager_title: 'إزالة المدير',
|
||||||
|
removeMember_title: 'إزالة العضو',
|
||||||
|
searchLabels: 'البحث عن التصنيفات...',
|
||||||
|
searchMembers: 'البحث عن الأعضاء...',
|
||||||
|
searchUsers: 'البحث عن المستخدمين...',
|
||||||
|
searchCards: 'البحث عن البطاقات...',
|
||||||
|
seconds: 'ثواني',
|
||||||
|
selectBoard: 'اختر لوحة',
|
||||||
|
selectList: 'اختر قائمة',
|
||||||
|
selectPermissions_title: 'حدد الأذونات',
|
||||||
|
selectProject: 'حدد المشروع',
|
||||||
|
settings: 'الإعدادات',
|
||||||
|
sortList_title: 'فرز القائمة',
|
||||||
|
stopwatch: 'المؤقت',
|
||||||
|
subscribeToMyOwnCardsByDefault: 'الاشتراك في بطاقاتي الخاصة إفتراضياً',
|
||||||
|
taskActions_title: 'إجراءات المهمة',
|
||||||
|
tasks: 'المهام',
|
||||||
|
thereIsNoPreviewAvailableForThisAttachment: 'لا يوجد معاينة متاحة لهذا المرفق.',
|
||||||
|
time: 'الوقت',
|
||||||
|
title: 'العنوان',
|
||||||
|
userActions_title: 'إجراءات المستخدم',
|
||||||
|
userAddedThisCardToList: '<0>{{user}}</0><1> تمت إضافة هذه البطاقة إلى {{list}}</1>',
|
||||||
|
userLeftNewCommentToCard: '{{user}} ترك تعليق جديد «{{comment}}» إلى <2>{{card}}</2>',
|
||||||
|
userMovedCardFromListToList: '{{user}} انتقل <2>{{card}}</2> من {{fromList}} إلى {{toList}}',
|
||||||
|
userMovedThisCardFromListToList:
|
||||||
|
'<0>{{user}}</0><1> نُقلت هذه البطاقة من {{fromList}} إلى {{toList}}</1>',
|
||||||
|
username: 'اسم المستخدم',
|
||||||
|
usernameAlreadyInUse: 'اسم المستخدم تم استخدامه بالفعل',
|
||||||
|
users: 'المستخدمين',
|
||||||
|
version: 'الإصدار',
|
||||||
|
viewer: 'مشاهد',
|
||||||
|
writeComment: 'اكتب تعليقاً...',
|
||||||
|
},
|
||||||
|
|
||||||
|
action: {
|
||||||
|
addAnotherCard: 'إضافة بطاقة أخرى',
|
||||||
|
addAnotherList: 'إضافة قائمة أخرى',
|
||||||
|
addAnotherTask: 'إضافة مهمة أخرى',
|
||||||
|
addCard: 'إضافة بطاقة',
|
||||||
|
addCard_title: 'إضافة بطاقة',
|
||||||
|
addComment: 'إضافة تعليق',
|
||||||
|
addList: 'إضافة قائمة',
|
||||||
|
addMember: 'إضافة عضو',
|
||||||
|
addMoreDetailedDescription: 'إضافة وصف أكثر تفصيلاً',
|
||||||
|
addTask: 'إضافة مهمة',
|
||||||
|
addToCard: 'إضافة إلى البطاقة',
|
||||||
|
addUser: 'إضافة مستخدم',
|
||||||
|
copyLink_title: 'نسخ الرابط',
|
||||||
|
createBoard: 'إنشاء لوحة',
|
||||||
|
createFile: 'إنشاء ملف',
|
||||||
|
createLabel: 'إنشاء ملصق',
|
||||||
|
createNewLabel: 'إنشاء ملصق جديد',
|
||||||
|
createProject: 'إنشاء مشروع',
|
||||||
|
delete: 'حذف',
|
||||||
|
deleteAttachment: 'حذف المرفق',
|
||||||
|
deleteAvatar: 'حذف الصورة الرمزية',
|
||||||
|
deleteBoard: 'حذف اللوحة',
|
||||||
|
deleteCard: 'حذف البطاقة',
|
||||||
|
deleteCard_title: 'حذف البطاقة',
|
||||||
|
deleteComment: 'حذف التعليق',
|
||||||
|
deleteImage: 'حذف الصورة',
|
||||||
|
deleteLabel: 'حذف الملصق',
|
||||||
|
deleteList: 'حذف القائمة',
|
||||||
|
deleteList_title: 'حذف القائمة',
|
||||||
|
deleteProject: 'حذف المشروع',
|
||||||
|
deleteProject_title: 'حذف المشروع',
|
||||||
|
deleteTask: 'حذف المهمة',
|
||||||
|
deleteTask_title: 'حذف المهمة',
|
||||||
|
deleteUser: 'حذف المستخدم',
|
||||||
|
duplicate: 'تكرار',
|
||||||
|
duplicateCard_title: 'تكرار البطاقة',
|
||||||
|
edit: 'تعديل',
|
||||||
|
editDueDate_title: 'تعديل تاريخ الاستحقاق',
|
||||||
|
editDescription_title: 'تعديل الوصف',
|
||||||
|
editEmail_title: 'تعديل البريد الإلكتروني',
|
||||||
|
editInformation_title: 'تعديل المعلومات',
|
||||||
|
editPassword_title: 'تعديل كلمة المرور',
|
||||||
|
editPermissions: 'تعديل الأذونات',
|
||||||
|
editStopwatch_title: 'تعديل المؤقت',
|
||||||
|
editTitle_title: 'تعديل العنوان',
|
||||||
|
editUsername_title: 'تعديل اسم المستخدم',
|
||||||
|
hideDetails: 'إخفاء التفاصيل',
|
||||||
|
import: 'استيراد',
|
||||||
|
leaveBoard: 'غادر اللوحة',
|
||||||
|
leaveProject: 'غادر المشروع',
|
||||||
|
logOut_title: 'تسجيل الخروج',
|
||||||
|
makeCover_title: 'إصنع غلافاً',
|
||||||
|
move: 'نقل',
|
||||||
|
moveCard_title: 'نقل البطاقة',
|
||||||
|
remove: 'حذف',
|
||||||
|
removeBackground: 'إزالة الخلفية',
|
||||||
|
removeCover_title: 'إزالة الغلاف',
|
||||||
|
removeFromBoard: 'إزالة اللوحة',
|
||||||
|
removeFromProject: 'إزالة المشروع',
|
||||||
|
removeManager: 'إزالة المدير',
|
||||||
|
removeMember: 'إزالة العضو',
|
||||||
|
save: 'حفظ',
|
||||||
|
showAllAttachments: 'إظهار جميع المرفقات ({{hidden}} hidden)',
|
||||||
|
showDetails: 'إظهار التفاصيل',
|
||||||
|
showFewerAttachments: 'عرض مرفقات أقل',
|
||||||
|
sortList_title: 'فرز القائمة',
|
||||||
|
start: 'ابدأ',
|
||||||
|
stop: 'توقف',
|
||||||
|
subscribe: 'اشترك',
|
||||||
|
unsubscribe: 'إلغاء الاشتراك',
|
||||||
|
uploadNewAvatar: 'رفع صورة رمزية جديدة',
|
||||||
|
uploadNewImage: 'رفع صورة جديدة',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import login from './login';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
language: 'ar-YE',
|
||||||
|
country: 'ye',
|
||||||
|
name: 'العربية',
|
||||||
|
embeddedLocale: login,
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export default {
|
||||||
|
translation: {
|
||||||
|
common: {
|
||||||
|
emailOrUsername: 'البريد الإلكتروني أو اسم المستخدم',
|
||||||
|
invalidEmailOrUsername: 'البريد الإلكتروني أو اسم المستخدم غير صالح',
|
||||||
|
invalidCredentials: 'بيانات الاعتماد غير صالحة',
|
||||||
|
invalidPassword: 'كلمة المرور غير صالحة',
|
||||||
|
logInToPlanka: 'تسجيل الدخول إلى Planka',
|
||||||
|
noInternetConnection: 'لا يوجد اتصال بالإنترنت',
|
||||||
|
pageNotFound_title: 'الصفحة غير موجودة',
|
||||||
|
password: 'كلمة المرور',
|
||||||
|
projectManagement: 'إدارة المشروع',
|
||||||
|
serverConnectionFailed: 'فشل الاتصال بالخادم',
|
||||||
|
unknownError: 'خطأ غير معروف، يرجى المحاولة لاحقاً',
|
||||||
|
useSingleSignOn: 'استخدم تسجيل الدخول الموحد',
|
||||||
|
},
|
||||||
|
|
||||||
|
action: {
|
||||||
|
logIn: 'تسجيل الدخول',
|
||||||
|
logInWithSSO: 'تسجيل الدخول باستخدام SSO',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import login from './login';
|
import login from './login';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
language: 'bg',
|
language: 'bg-BG',
|
||||||
country: 'bg',
|
country: 'bg',
|
||||||
name: 'Български',
|
name: 'Български',
|
||||||
embeddedLocale: login,
|
embeddedLocale: login,
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import login from './login';
|
import login from './login';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
language: 'cs',
|
language: 'cs-CZ',
|
||||||
country: 'cz',
|
country: 'cz',
|
||||||
name: 'Čeština',
|
name: 'Čeština',
|
||||||
embeddedLocale: login,
|
embeddedLocale: login,
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import login from './login';
|
import login from './login';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
language: 'da',
|
language: 'da-DK',
|
||||||
country: 'dk',
|
country: 'dk',
|
||||||
name: 'Dansk',
|
name: 'Dansk',
|
||||||
embeddedLocale: login,
|
embeddedLocale: login,
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user