Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e3bc92a61 | ||
|
|
b8d7e713b9 | ||
|
|
017eee8302 | ||
|
|
1b28892cf5 | ||
|
|
013ea1b869 | ||
|
|
4975fa2eeb | ||
|
|
3e28ad87c7 | ||
|
|
2d6666d693 | ||
|
|
4cbd2f85c7 | ||
|
|
fae9de4e1c | ||
|
|
89c1ed71e1 | ||
|
|
1725f3cb8a | ||
|
|
f7be49e402 | ||
|
|
c8d732d936 | ||
|
|
5a32b6327c | ||
|
|
8488105810 | ||
|
|
57ebfa51d8 | ||
|
|
40170fdf90 | ||
|
|
1629f62d09 | ||
|
|
fff0552081 | ||
|
|
e792cb26b1 | ||
|
|
7ee2d76be4 | ||
|
|
eb56b2147b | ||
|
|
8747aa59de | ||
|
|
934dcdf39b | ||
|
|
2c84316fe0 | ||
|
|
03825c3bed | ||
|
|
200ea3bbc0 | ||
|
|
5b4b0cd635 | ||
|
|
a1323cd516 | ||
|
|
92f75789fa | ||
|
|
8a8c1fee0c | ||
|
|
16499052f7 | ||
|
|
cc32daa009 | ||
|
|
ea1b3b7f92 | ||
|
|
4e2863faa7 | ||
|
|
b46fb43e6f | ||
|
|
bcacfa8fe2 | ||
|
|
4df2becd6f | ||
|
|
2990ea593a | ||
|
|
b976673826 | ||
|
|
16e034efcb | ||
|
|
6712625c3f | ||
|
|
b5bbf6a6a4 | ||
|
|
3d3e7761f5 | ||
|
|
fa56b9c01f | ||
|
|
882213784f | ||
|
|
251bd793f3 | ||
|
|
088b0e760b | ||
|
|
cc1fa1cc3b | ||
|
|
bcd3ea86e8 | ||
|
|
0176650f67 | ||
|
|
f3963aded3 | ||
|
|
bbe720481a | ||
|
|
04f9030474 | ||
|
|
6d135be859 | ||
|
|
63cbe7063e | ||
|
|
7985bea2b8 | ||
|
|
733abccaa9 | ||
|
|
119cce82b4 | ||
|
|
988e70ec40 | ||
|
|
5e3fca3771 | ||
|
|
6d9ce56f4a | ||
|
|
3c8c04fd37 | ||
|
|
790a1d5cc9 | ||
|
|
4b352fd878 | ||
|
|
a196f2ef11 | ||
|
|
6c65d135f7 | ||
|
|
a1a1e9a86a | ||
|
|
0d39a7567f | ||
|
|
76a18d06d6 | ||
|
|
ce52fc5af7 | ||
|
|
0a2863a79c | ||
|
|
39e320fe4b | ||
|
|
2f3dfe775e | ||
|
|
22964cb375 | ||
|
|
fd89cf7f2b | ||
|
|
5acf84c20b | ||
|
|
6cd9da844f | ||
|
|
d7847fe89b | ||
|
|
d033520927 | ||
|
|
273d994750 | ||
|
|
86d21d6abd | ||
|
|
a9c200cc3b | ||
|
|
3fedc14062 | ||
|
|
c42e1fb5dd | ||
|
|
9961340118 | ||
|
|
f54bd22757 | ||
|
|
1043dacd67 |
@@ -40,5 +40,5 @@ jobs:
|
|||||||
build-args: ALPINE_VERSION=${{ env.ALPINE_VERSION }}
|
build-args: ALPINE_VERSION=${{ env.ALPINE_VERSION }}
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/plankanban/planka:base-latest
|
ghcr.io/${{ github.repository }}:base-latest
|
||||||
ghcr.io/plankanban/planka:base-${{ env.ALPINE_VERSION }}
|
ghcr.io/${{ github.repository }}:base-${{ env.ALPINE_VERSION }}
|
||||||
|
|||||||
@@ -9,99 +9,45 @@ on:
|
|||||||
- 'docker-*.sh'
|
- 'docker-*.sh'
|
||||||
- '*.md'
|
- '*.md'
|
||||||
branches: [master]
|
branches: [master]
|
||||||
|
workflow_dispatch:
|
||||||
env:
|
env:
|
||||||
REGISTRY_IMAGE: ghcr.io/plankanban/planka
|
REGISTRY_IMAGE: ghcr.io/${{ github.repository }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
runs-on: self-hosted
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: [self-hosted, x64]
|
|
||||||
platform: linux/amd64
|
|
||||||
- os: [self-hosted, arm64]
|
|
||||||
platform: linux/arm64
|
|
||||||
- os: [self-hosted, arm64]
|
|
||||||
platform: linux/arm/v7
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push by digest
|
|
||||||
id: build
|
- name: Generate docker image tags
|
||||||
uses: docker/build-push-action@v5
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY_IMAGE }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=dev
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
push: true
|
||||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
- name: Export digest
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
run: |
|
cache-from: type=gha
|
||||||
mkdir -p /tmp/digests
|
cache-to: type=gha,mode=max
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: digests
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge:
|
|
||||||
runs-on: [self-hosted]
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
steps:
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: digests
|
|
||||||
path: /tmp/digests
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
|
||||||
rerun-failed-jobs:
|
|
||||||
runs-on: [self-hosted]
|
|
||||||
needs: [ build, merge]
|
|
||||||
if: failure()
|
|
||||||
steps:
|
|
||||||
- name: Rerun failed jobs in the current workflow
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: gh run rerun ${{ github.run_id }} --failed
|
|
||||||
|
|||||||
@@ -31,12 +31,23 @@ jobs:
|
|||||||
result-encoding: string
|
result-encoding: string
|
||||||
script: return context.payload.release.tag_name.replace('v', '')
|
script: return context.payload.release.tag_name.replace('v', '')
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
name=ghcr.io/${{ github.repository }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=${{ steps.set-version.outputs.result }}
|
||||||
|
type=raw,value=latest
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
ghcr.io/plankanban/planka:latest
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
ghcr.io/plankanban/planka:${{ steps.set-version.outputs.result }}
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
Executable → Regular
-3
@@ -1,4 +1 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx lint-staged
|
npx lint-staged
|
||||||
|
|||||||
@@ -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.20
|
version: 0.1.33
|
||||||
|
|
||||||
# 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.16.1"
|
appVersion: "1.17.5"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- alias: postgresql
|
- alias: postgresql
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ If you want to fully uninstall this chart including the data, follow [these step
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
If you just want to spin up an instance using help, please see [these docs](https://docs.planka.cloud/docs/installation/kubernetes/helm_chart/). If you want to make changes to the chart locally, and deploy them, see the below section.
|
||||||
|
|
||||||
|
## Local Building and Using the Chart
|
||||||
|
|
||||||
The basic usage of the chart can be found below:
|
The basic usage of the chart can be found below:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Chris-Greaves/planka-helm-chart.git
|
git clone https://github.com/plankanban/planka.git
|
||||||
cd planka-helm-chart
|
cd planka/charts/planka
|
||||||
helm dependency build
|
helm dependency build
|
||||||
export SECRETKEY=$(openssl rand -hex 64)
|
export SECRETKEY=$(openssl rand -hex 64)
|
||||||
helm install planka . --set secretkey=$SECRETKEY \
|
helm install planka . --set secretkey=$SECRETKEY \
|
||||||
@@ -46,7 +50,7 @@ helm install planka . --set secretkey=$SECRETKEY \
|
|||||||
--set admin_email="demo@demo.demo" \
|
--set admin_email="demo@demo.demo" \
|
||||||
--set admin_password="demo" \
|
--set admin_password="demo" \
|
||||||
--set admin_name="Demo Demo" \
|
--set admin_name="Demo Demo" \
|
||||||
--set admin_username="demo"
|
--set admin_username="demo" \
|
||||||
--set ingress.enabled=true \
|
--set ingress.enabled=true \
|
||||||
--set ingress.hosts[0].host=planka.example.dev \
|
--set ingress.hosts[0].host=planka.example.dev \
|
||||||
|
|
||||||
@@ -55,7 +59,7 @@ helm install planka . --set secretkey=$SECRETKEY \
|
|||||||
--set admin_email="demo@demo.demo" \
|
--set admin_email="demo@demo.demo" \
|
||||||
--set admin_password="demo" \
|
--set admin_password="demo" \
|
||||||
--set admin_name="Demo Demo" \
|
--set admin_name="Demo Demo" \
|
||||||
--set admin_username="demo"
|
--set admin_username="demo" \
|
||||||
--set ingress.enabled=true \
|
--set ingress.enabled=true \
|
||||||
--set ingress.hosts[0].host=planka.example.dev \
|
--set ingress.hosts[0].host=planka.example.dev \
|
||||||
--set ingress.tls[0].secretName=planka-tls \
|
--set ingress.tls[0].secretName=planka-tls \
|
||||||
@@ -76,6 +80,7 @@ admin_name: "Demo Demo"
|
|||||||
admin_username: "demo"
|
admin_username: "demo"
|
||||||
# Admin user
|
# Admin user
|
||||||
|
|
||||||
|
# Ingress
|
||||||
ingress:
|
ingress:
|
||||||
enabled: true
|
enabled: true
|
||||||
hosts:
|
hosts:
|
||||||
|
|||||||
@@ -55,14 +55,32 @@ spec:
|
|||||||
- mountPath: /app/private/attachments
|
- mountPath: /app/private/attachments
|
||||||
subPath: attachments
|
subPath: attachments
|
||||||
name: planka
|
name: planka
|
||||||
|
{{- if .Values.securityContext.readOnlyRootFilesystem }}
|
||||||
|
- mountPath: /app/logs
|
||||||
|
subPath: app-logs
|
||||||
|
name: emptydir
|
||||||
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
env:
|
env:
|
||||||
|
{{- if not .Values.postgresql.enabled }}
|
||||||
|
{{- if .Values.existingDburlSecret }}
|
||||||
|
- name: DATABASE_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ .Values.existingDburlSecret }}
|
||||||
|
key: uri
|
||||||
|
{{- else }}
|
||||||
|
- name: DATABASE_URL
|
||||||
|
value: {{ required "If the included postgresql deployment is disabled you need to provide an existing secret in .Values.existingDburlSecret or define a Database URL in 'dburl'" .Values.dburl }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
- name: DATABASE_URL
|
- name: DATABASE_URL
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: planka-postgresql-svcbind-custom-user
|
name: planka-postgresql-svcbind-custom-user
|
||||||
key: uri
|
key: uri
|
||||||
|
{{- end }}
|
||||||
- name: BASE_URL
|
- name: BASE_URL
|
||||||
{{- if .Values.baseUrl }}
|
{{- if .Values.baseUrl }}
|
||||||
value: {{ .Values.baseUrl }}
|
value: {{ .Values.baseUrl }}
|
||||||
@@ -134,3 +152,7 @@ spec:
|
|||||||
{{- else }}
|
{{- else }}
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.securityContext.readOnlyRootFilesystem }}
|
||||||
|
- name: emptydir
|
||||||
|
emptyDir: {}
|
||||||
|
{{- end }}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{{- if .Values.autoscaling.enabled }}
|
{{- if .Values.autoscaling.enabled }}
|
||||||
apiVersion: autoscaling/v2beta1
|
apiVersion: autoscaling/v2
|
||||||
kind: HorizontalPodAutoscaler
|
kind: HorizontalPodAutoscaler
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "planka.fullname" . }}
|
name: {{ include "planka.fullname" . }}
|
||||||
@@ -17,12 +17,16 @@ spec:
|
|||||||
- type: Resource
|
- type: Resource
|
||||||
resource:
|
resource:
|
||||||
name: cpu
|
name: cpu
|
||||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||||
- type: Resource
|
- type: Resource
|
||||||
resource:
|
resource:
|
||||||
name: memory
|
name: memory
|
||||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -105,12 +105,20 @@ postgresql:
|
|||||||
serviceBindings:
|
serviceBindings:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
## Set this or existingDburlSecret if you disable the built-in postgresql deployment
|
||||||
|
dburl:
|
||||||
|
|
||||||
|
## @param existingDburlSecret Name of an existing secret containing a DBurl connection string
|
||||||
|
## NOTE: Must contain key `uri`
|
||||||
|
## NOTE: When it's set, the `dburl` parameter is ignored
|
||||||
|
##
|
||||||
|
existingDburlSecret: ""
|
||||||
|
|
||||||
## PVC-based data storage configuration
|
## PVC-based data storage configuration
|
||||||
persistence:
|
persistence:
|
||||||
enabled: false
|
enabled: false
|
||||||
# existingClaim: netbox-data
|
# existingClaim: netbox-data
|
||||||
# storageClass: "-"
|
# storageClass: "-"
|
||||||
|
|
||||||
accessMode: ReadWriteOnce
|
accessMode: ReadWriteOnce
|
||||||
size: 10Gi
|
size: 10Gi
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
REACT_APP_VERSION=1.16.1
|
REACT_APP_VERSION=1.17.5
|
||||||
|
|||||||
Generated
+9461
-3572
File diff suppressed because it is too large
Load Diff
+22
-20
@@ -60,62 +60,64 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.5.1",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"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.7.6",
|
"i18next": "^23.11.5",
|
||||||
"i18next-browser-languagedetector": "^7.2.0",
|
"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",
|
||||||
|
"linkify-react": "^4.1.3",
|
||||||
|
"linkifyjs": "^4.1.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nanoid": "^5.0.3",
|
"nanoid": "^5.0.7",
|
||||||
"node-sass": "^9.0.0",
|
"node-sass": "^9.0.0",
|
||||||
"photoswipe": "^5.4.2",
|
"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.21.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": "^14.1.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.19.0",
|
"react-router-dom": "^6.23.1",
|
||||||
"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",
|
||||||
"redux": "^4.2.1",
|
"redux": "^4.2.1",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-orm": "^0.16.2",
|
"redux-orm": "^0.16.2",
|
||||||
"redux-saga": "^1.2.3",
|
"redux-saga": "^1.3.0",
|
||||||
"remark-breaks": "^4.0.0",
|
"remark-breaks": "^4.0.0",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"reselect": "^4.1.8",
|
"reselect": "^4.1.8",
|
||||||
"sails.io.js": "^1.2.1",
|
"sails.io.js": "^1.2.1",
|
||||||
"semantic-ui-react": "^2.1.4",
|
"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.19",
|
"whatwg-fetch": "^3.6.20",
|
||||||
"zxcvbn": "^4.4.2"
|
"zxcvbn": "^4.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^6.1.4",
|
"@testing-library/jest-dom": "^6.4.5",
|
||||||
"@testing-library/react": "^14.1.0",
|
"@testing-library/react": "^15.0.7",
|
||||||
"@testing-library/user-event": "^14.5.1",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"babel-preset-airbnb": "^5.0.0",
|
"babel-preset-airbnb": "^5.0.0",
|
||||||
"chai": "^4.3.10",
|
"chai": "^4.4.1",
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-plugin-import": "^2.29.0",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.34.2",
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,14 @@ createCard.failure = (localId, error) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCardCreate = (card) => ({
|
const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({
|
||||||
type: ActionTypes.CARD_CREATE_HANDLE,
|
type: ActionTypes.CARD_CREATE_HANDLE,
|
||||||
payload: {
|
payload: {
|
||||||
card,
|
card,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
tasks,
|
||||||
|
attachments,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,6 +64,34 @@ const handleCardUpdate = (card) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const duplicateCard = (id, card, taskIds) => ({
|
||||||
|
type: ActionTypes.CARD_DUPLICATE,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
card,
|
||||||
|
taskIds,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({
|
||||||
|
type: ActionTypes.CARD_DUPLICATE__SUCCESS,
|
||||||
|
payload: {
|
||||||
|
localId,
|
||||||
|
card,
|
||||||
|
cardMemberships,
|
||||||
|
cardLabels,
|
||||||
|
tasks,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
duplicateCard.failure = (id, error) => ({
|
||||||
|
type: ActionTypes.CARD_DUPLICATE__FAILURE,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteCard = (id) => ({
|
const deleteCard = (id) => ({
|
||||||
type: ActionTypes.CARD_DELETE,
|
type: ActionTypes.CARD_DELETE,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -89,11 +121,21 @@ const handleCardDelete = (card) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const filterText = (boardId, text) => ({
|
||||||
|
type: ActionTypes.TEXT_FILTER_IN_CURRENT_BOARD,
|
||||||
|
payload: {
|
||||||
|
boardId,
|
||||||
|
text,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createCard,
|
createCard,
|
||||||
handleCardCreate,
|
handleCardCreate,
|
||||||
updateCard,
|
updateCard,
|
||||||
handleCardUpdate,
|
handleCardUpdate,
|
||||||
|
duplicateCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
handleCardDelete,
|
handleCardDelete,
|
||||||
|
filterText,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,9 +47,11 @@ initializeCore.fetchConfig = (config) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const logout = () => ({
|
const logout = (invalidateAccessToken) => ({
|
||||||
type: ActionTypes.LOGOUT,
|
type: ActionTypes.LOGOUT,
|
||||||
payload: {},
|
payload: {
|
||||||
|
invalidateAccessToken,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logout.invalidateAccessToken = () => ({
|
logout.invalidateAccessToken = () => ({
|
||||||
|
|||||||
@@ -60,6 +60,38 @@ const handleListUpdate = (list) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sortList = (id, data) => ({
|
||||||
|
type: ActionTypes.LIST_SORT,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sortList.success = (list, cards) => ({
|
||||||
|
type: ActionTypes.LIST_SORT__SUCCESS,
|
||||||
|
payload: {
|
||||||
|
list,
|
||||||
|
cards,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sortList.failure = (id, error) => ({
|
||||||
|
type: ActionTypes.LIST_SORT__FAILURE,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleListSort = (list, cards) => ({
|
||||||
|
type: ActionTypes.LIST_SORT_HANDLE,
|
||||||
|
payload: {
|
||||||
|
list,
|
||||||
|
cards,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteList = (id) => ({
|
const deleteList = (id) => ({
|
||||||
type: ActionTypes.LIST_DELETE,
|
type: ActionTypes.LIST_DELETE,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -94,6 +126,8 @@ export default {
|
|||||||
handleListCreate,
|
handleListCreate,
|
||||||
updateList,
|
updateList,
|
||||||
handleListUpdate,
|
handleListUpdate,
|
||||||
|
sortList,
|
||||||
|
handleListSort,
|
||||||
deleteList,
|
deleteList,
|
||||||
handleListDelete,
|
handleListDelete,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ const updateCard = (id, data, headers) =>
|
|||||||
item: transformCard(body.item),
|
item: transformCard(body.item),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const duplicateCard = (id, data, headers) =>
|
||||||
|
socket.post(`/cards/${id}/duplicate`, data, headers).then((body) => ({
|
||||||
|
...body,
|
||||||
|
item: transformCard(body.item),
|
||||||
|
}));
|
||||||
|
|
||||||
const deleteCard = (id, headers) =>
|
const deleteCard = (id, headers) =>
|
||||||
socket.delete(`/cards/${id}`, undefined, headers).then((body) => ({
|
socket.delete(`/cards/${id}`, undefined, headers).then((body) => ({
|
||||||
...body,
|
...body,
|
||||||
@@ -81,6 +87,7 @@ export default {
|
|||||||
getCard,
|
getCard,
|
||||||
updateCard,
|
updateCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
|
duplicateCard,
|
||||||
makeHandleCardCreate,
|
makeHandleCardCreate,
|
||||||
makeHandleCardUpdate,
|
makeHandleCardUpdate,
|
||||||
makeHandleCardDelete,
|
makeHandleCardDelete,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import socket from './socket';
|
import socket from './socket';
|
||||||
|
import { transformCard } from './cards';
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
|
|
||||||
@@ -7,10 +8,33 @@ const createList = (boardId, data, headers) =>
|
|||||||
|
|
||||||
const updateList = (id, data, headers) => socket.patch(`/lists/${id}`, data, headers);
|
const updateList = (id, data, headers) => socket.patch(`/lists/${id}`, data, headers);
|
||||||
|
|
||||||
|
const sortList = (id, data, headers) =>
|
||||||
|
socket.post(`/lists/${id}/sort`, data, headers).then((body) => ({
|
||||||
|
...body,
|
||||||
|
included: {
|
||||||
|
...body.included,
|
||||||
|
cards: body.included.cards.map(transformCard),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const deleteList = (id, headers) => socket.delete(`/lists/${id}`, undefined, headers);
|
const deleteList = (id, headers) => socket.delete(`/lists/${id}`, undefined, headers);
|
||||||
|
|
||||||
|
/* Event handlers */
|
||||||
|
|
||||||
|
const makeHandleListSort = (next) => (body) => {
|
||||||
|
next({
|
||||||
|
...body,
|
||||||
|
included: {
|
||||||
|
...body.included,
|
||||||
|
cards: body.included.cards.map(transformCard),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createList,
|
createList,
|
||||||
updateList,
|
updateList,
|
||||||
|
sortList,
|
||||||
deleteList,
|
deleteList,
|
||||||
|
makeHandleListSort,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
+2337
File diff suppressed because it is too large
Load Diff
+4
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,6 +13,7 @@ const BoardActions = React.memo(
|
|||||||
labels,
|
labels,
|
||||||
filterUsers,
|
filterUsers,
|
||||||
filterLabels,
|
filterLabels,
|
||||||
|
filterText,
|
||||||
allUsers,
|
allUsers,
|
||||||
canEdit,
|
canEdit,
|
||||||
canEditMemberships,
|
canEditMemberships,
|
||||||
@@ -27,6 +28,7 @@ const BoardActions = React.memo(
|
|||||||
onLabelUpdate,
|
onLabelUpdate,
|
||||||
onLabelMove,
|
onLabelMove,
|
||||||
onLabelDelete,
|
onLabelDelete,
|
||||||
|
onTextFilterUpdate,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
@@ -46,6 +48,7 @@ const BoardActions = React.memo(
|
|||||||
<Filters
|
<Filters
|
||||||
users={filterUsers}
|
users={filterUsers}
|
||||||
labels={filterLabels}
|
labels={filterLabels}
|
||||||
|
filterText={filterText}
|
||||||
allBoardMemberships={memberships}
|
allBoardMemberships={memberships}
|
||||||
allLabels={labels}
|
allLabels={labels}
|
||||||
canEdit={canEdit}
|
canEdit={canEdit}
|
||||||
@@ -57,6 +60,7 @@ const BoardActions = React.memo(
|
|||||||
onLabelUpdate={onLabelUpdate}
|
onLabelUpdate={onLabelUpdate}
|
||||||
onLabelMove={onLabelMove}
|
onLabelMove={onLabelMove}
|
||||||
onLabelDelete={onLabelDelete}
|
onLabelDelete={onLabelDelete}
|
||||||
|
onTextFilterUpdate={onTextFilterUpdate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,6 +75,7 @@ BoardActions.propTypes = {
|
|||||||
labels: PropTypes.array.isRequired,
|
labels: PropTypes.array.isRequired,
|
||||||
filterUsers: PropTypes.array.isRequired,
|
filterUsers: PropTypes.array.isRequired,
|
||||||
filterLabels: PropTypes.array.isRequired,
|
filterLabels: PropTypes.array.isRequired,
|
||||||
|
filterText: PropTypes.string.isRequired,
|
||||||
allUsers: PropTypes.array.isRequired,
|
allUsers: PropTypes.array.isRequired,
|
||||||
/* eslint-enable react/forbid-prop-types */
|
/* eslint-enable react/forbid-prop-types */
|
||||||
canEdit: PropTypes.bool.isRequired,
|
canEdit: PropTypes.bool.isRequired,
|
||||||
@@ -86,6 +91,7 @@ BoardActions.propTypes = {
|
|||||||
onLabelUpdate: PropTypes.func.isRequired,
|
onLabelUpdate: PropTypes.func.isRequired,
|
||||||
onLabelMove: PropTypes.func.isRequired,
|
onLabelMove: PropTypes.func.isRequired,
|
||||||
onLabelDelete: PropTypes.func.isRequired,
|
onLabelDelete: PropTypes.func.isRequired,
|
||||||
|
onTextFilterUpdate: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BoardActions;
|
export default BoardActions;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
:global(#app) {
|
:global(#app) {
|
||||||
.action {
|
.action {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Icon } from 'semantic-ui-react';
|
||||||
import { usePopup } from '../../lib/popup';
|
import { usePopup } from '../../lib/popup';
|
||||||
|
import { Input } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import User from '../User';
|
import User from '../User';
|
||||||
import Label from '../Label';
|
import Label from '../Label';
|
||||||
@@ -14,6 +17,7 @@ const Filters = React.memo(
|
|||||||
({
|
({
|
||||||
users,
|
users,
|
||||||
labels,
|
labels,
|
||||||
|
filterText,
|
||||||
allBoardMemberships,
|
allBoardMemberships,
|
||||||
allLabels,
|
allLabels,
|
||||||
canEdit,
|
canEdit,
|
||||||
@@ -25,8 +29,17 @@ const Filters = React.memo(
|
|||||||
onLabelUpdate,
|
onLabelUpdate,
|
||||||
onLabelMove,
|
onLabelMove,
|
||||||
onLabelDelete,
|
onLabelDelete,
|
||||||
|
onTextFilterUpdate,
|
||||||
}) => {
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
const [isSearchFocused, setIsSearchFocused] = useState(false);
|
||||||
|
|
||||||
|
const searchFieldRef = useRef(null);
|
||||||
|
|
||||||
|
const cancelSearch = useCallback(() => {
|
||||||
|
onTextFilterUpdate('');
|
||||||
|
searchFieldRef.current.blur();
|
||||||
|
}, [onTextFilterUpdate]);
|
||||||
|
|
||||||
const handleRemoveUserClick = useCallback(
|
const handleRemoveUserClick = useCallback(
|
||||||
(id) => {
|
(id) => {
|
||||||
@@ -42,9 +55,39 @@ const Filters = React.memo(
|
|||||||
[onLabelRemove],
|
[onLabelRemove],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleSearchChange = useCallback(
|
||||||
|
(_, { value }) => {
|
||||||
|
onTextFilterUpdate(value);
|
||||||
|
},
|
||||||
|
[onTextFilterUpdate],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearchFocus = useCallback(() => {
|
||||||
|
setIsSearchFocused(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearchKeyDown = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
cancelSearch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[cancelSearch],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearchBlur = useCallback(() => {
|
||||||
|
setIsSearchFocused(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCancelSearchClick = useCallback(() => {
|
||||||
|
cancelSearch();
|
||||||
|
}, [cancelSearch]);
|
||||||
|
|
||||||
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
|
const BoardMembershipsPopup = usePopup(BoardMembershipsStep);
|
||||||
const LabelsPopup = usePopup(LabelsStep);
|
const LabelsPopup = usePopup(LabelsStep);
|
||||||
|
|
||||||
|
const isSearchActive = filterText || isSearchFocused;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span className={styles.filter}>
|
<span className={styles.filter}>
|
||||||
@@ -100,6 +143,25 @@ const Filters = React.memo(
|
|||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
|
<span className={styles.filter}>
|
||||||
|
<Input
|
||||||
|
ref={searchFieldRef}
|
||||||
|
value={filterText}
|
||||||
|
placeholder={t('common.searchCards')}
|
||||||
|
icon={
|
||||||
|
isSearchActive ? (
|
||||||
|
<Icon link name="cancel" onClick={handleCancelSearchClick} />
|
||||||
|
) : (
|
||||||
|
'search'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className={classNames(styles.search, !isSearchActive && styles.searchInactive)}
|
||||||
|
onFocus={handleSearchFocus}
|
||||||
|
onKeyDown={handleSearchKeyDown}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
onBlur={handleSearchBlur}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -109,6 +171,7 @@ Filters.propTypes = {
|
|||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
users: PropTypes.array.isRequired,
|
users: PropTypes.array.isRequired,
|
||||||
labels: PropTypes.array.isRequired,
|
labels: PropTypes.array.isRequired,
|
||||||
|
filterText: PropTypes.string.isRequired,
|
||||||
allBoardMemberships: PropTypes.array.isRequired,
|
allBoardMemberships: PropTypes.array.isRequired,
|
||||||
allLabels: PropTypes.array.isRequired,
|
allLabels: PropTypes.array.isRequired,
|
||||||
/* eslint-enable react/forbid-prop-types */
|
/* eslint-enable react/forbid-prop-types */
|
||||||
@@ -121,6 +184,7 @@ Filters.propTypes = {
|
|||||||
onLabelUpdate: PropTypes.func.isRequired,
|
onLabelUpdate: PropTypes.func.isRequired,
|
||||||
onLabelMove: PropTypes.func.isRequired,
|
onLabelMove: PropTypes.func.isRequired,
|
||||||
onLabelDelete: PropTypes.func.isRequired,
|
onLabelDelete: PropTypes.func.isRequired,
|
||||||
|
onTextFilterUpdate: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Filters;
|
export default Filters;
|
||||||
|
|||||||
@@ -43,4 +43,32 @@
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 2px 12px;
|
padding: 2px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
height: 30px;
|
||||||
|
margin: 0 12px;
|
||||||
|
transition: width 0.2s ease;
|
||||||
|
width: 280px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInactive {
|
||||||
|
color: #fff;
|
||||||
|
height: 24px;
|
||||||
|
width: 220px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: rgba(0, 0, 0, 0.24);
|
||||||
|
border: none;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const ActionsStep = React.memo(
|
|||||||
onUpdate,
|
onUpdate,
|
||||||
onMove,
|
onMove,
|
||||||
onTransfer,
|
onTransfer,
|
||||||
|
onDuplicate,
|
||||||
onDelete,
|
onDelete,
|
||||||
onUserAdd,
|
onUserAdd,
|
||||||
onUserRemove,
|
onUserRemove,
|
||||||
@@ -76,6 +77,11 @@ const ActionsStep = React.memo(
|
|||||||
openStep(StepTypes.MOVE);
|
openStep(StepTypes.MOVE);
|
||||||
}, [openStep]);
|
}, [openStep]);
|
||||||
|
|
||||||
|
const handleDuplicateClick = useCallback(() => {
|
||||||
|
onDuplicate();
|
||||||
|
onClose();
|
||||||
|
}, [onDuplicate, onClose]);
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(() => {
|
const handleDeleteClick = useCallback(() => {
|
||||||
openStep(StepTypes.DELETE);
|
openStep(StepTypes.DELETE);
|
||||||
}, [openStep]);
|
}, [openStep]);
|
||||||
@@ -207,6 +213,11 @@ const ActionsStep = React.memo(
|
|||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item className={styles.menuItem} onClick={handleDuplicateClick}>
|
||||||
|
{t('action.duplicateCard', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Menu.Item>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
||||||
{t('action.deleteCard', {
|
{t('action.deleteCard', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
@@ -232,6 +243,7 @@ ActionsStep.propTypes = {
|
|||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
onMove: PropTypes.func.isRequired,
|
onMove: PropTypes.func.isRequired,
|
||||||
onTransfer: PropTypes.func.isRequired,
|
onTransfer: PropTypes.func.isRequired,
|
||||||
|
onDuplicate: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onUserAdd: PropTypes.func.isRequired,
|
onUserAdd: PropTypes.func.isRequired,
|
||||||
onUserRemove: PropTypes.func.isRequired,
|
onUserRemove: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const Card = React.memo(
|
|||||||
onUpdate,
|
onUpdate,
|
||||||
onMove,
|
onMove,
|
||||||
onTransfer,
|
onTransfer,
|
||||||
|
onDuplicate,
|
||||||
onDelete,
|
onDelete,
|
||||||
onUserAdd,
|
onUserAdd,
|
||||||
onUserRemove,
|
onUserRemove,
|
||||||
@@ -185,6 +186,7 @@ const Card = React.memo(
|
|||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
onMove={onMove}
|
onMove={onMove}
|
||||||
onTransfer={onTransfer}
|
onTransfer={onTransfer}
|
||||||
|
onDuplicate={onDuplicate}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
onUserAdd={onUserAdd}
|
onUserAdd={onUserAdd}
|
||||||
onUserRemove={onUserRemove}
|
onUserRemove={onUserRemove}
|
||||||
@@ -238,6 +240,7 @@ Card.propTypes = {
|
|||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
onMove: PropTypes.func.isRequired,
|
onMove: PropTypes.func.isRequired,
|
||||||
onTransfer: PropTypes.func.isRequired,
|
onTransfer: PropTypes.func.isRequired,
|
||||||
|
onDuplicate: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onUserAdd: PropTypes.func.isRequired,
|
onUserAdd: PropTypes.func.isRequired,
|
||||||
onUserRemove: PropTypes.func.isRequired,
|
onUserRemove: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import classNames from 'classnames';
|
|||||||
import { Progress } from 'semantic-ui-react';
|
import { Progress } from 'semantic-ui-react';
|
||||||
import { useToggle } from '../../lib/hooks';
|
import { useToggle } from '../../lib/hooks';
|
||||||
|
|
||||||
|
import Linkify from '../Linkify';
|
||||||
|
|
||||||
import styles from './Tasks.module.scss';
|
import styles from './Tasks.module.scss';
|
||||||
|
|
||||||
const Tasks = React.memo(({ items }) => {
|
const Tasks = React.memo(({ items }) => {
|
||||||
@@ -48,7 +50,7 @@ const Tasks = React.memo(({ items }) => {
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
className={classNames(styles.task, item.isCompleted && styles.taskCompleted)}
|
className={classNames(styles.task, item.isCompleted && styles.taskCompleted)}
|
||||||
>
|
>
|
||||||
{item.name}
|
<Linkify linkStopPropagation>{item.name}</Linkify>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -55,8 +55,10 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
|
overflow: hidden;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "–";
|
content: "–";
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ const CardModal = React.memo(
|
|||||||
onUpdate,
|
onUpdate,
|
||||||
onMove,
|
onMove,
|
||||||
onTransfer,
|
onTransfer,
|
||||||
|
onDuplicate,
|
||||||
onDelete,
|
onDelete,
|
||||||
onUserAdd,
|
onUserAdd,
|
||||||
onUserRemove,
|
onUserRemove,
|
||||||
@@ -140,6 +141,11 @@ const CardModal = React.memo(
|
|||||||
});
|
});
|
||||||
}, [isSubscribed, onUpdate]);
|
}, [isSubscribed, onUpdate]);
|
||||||
|
|
||||||
|
const handleDuplicateClick = useCallback(() => {
|
||||||
|
onDuplicate();
|
||||||
|
onClose();
|
||||||
|
}, [onDuplicate, onClose]);
|
||||||
|
|
||||||
const handleGalleryOpen = useCallback(() => {
|
const handleGalleryOpen = useCallback(() => {
|
||||||
isGalleryOpened.current = true;
|
isGalleryOpened.current = true;
|
||||||
}, []);
|
}, []);
|
||||||
@@ -496,6 +502,10 @@ const CardModal = React.memo(
|
|||||||
{t('action.move')}
|
{t('action.move')}
|
||||||
</Button>
|
</Button>
|
||||||
</CardMovePopup>
|
</CardMovePopup>
|
||||||
|
<Button fluid className={styles.actionButton} onClick={handleDuplicateClick}>
|
||||||
|
<Icon name="copy outline" className={styles.actionIcon} />
|
||||||
|
{t('action.duplicate')}
|
||||||
|
</Button>
|
||||||
<DeletePopup
|
<DeletePopup
|
||||||
title="common.deleteCard"
|
title="common.deleteCard"
|
||||||
content="common.areYouSureYouWantToDeleteThisCard"
|
content="common.areYouSureYouWantToDeleteThisCard"
|
||||||
@@ -555,6 +565,7 @@ CardModal.propTypes = {
|
|||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
onMove: PropTypes.func.isRequired,
|
onMove: PropTypes.func.isRequired,
|
||||||
onTransfer: PropTypes.func.isRequired,
|
onTransfer: PropTypes.func.isRequired,
|
||||||
|
onDuplicate: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onUserAdd: PropTypes.func.isRequired,
|
onUserAdd: PropTypes.func.isRequired,
|
||||||
onUserRemove: PropTypes.func.isRequired,
|
onUserRemove: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ const DescriptionEdit = React.forwardRef(({ children, defaultValue, onUpdate },
|
|||||||
|
|
||||||
const mdEditorOptions = useMemo(
|
const mdEditorOptions = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
autoDownloadFontAwesome: false,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
spellChecker: false,
|
spellChecker: false,
|
||||||
status: false,
|
status: false,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { usePopup } from '../../../lib/popup';
|
|||||||
|
|
||||||
import NameEdit from './NameEdit';
|
import NameEdit from './NameEdit';
|
||||||
import ActionsStep from './ActionsStep';
|
import ActionsStep from './ActionsStep';
|
||||||
|
import Linkify from '../../Linkify';
|
||||||
|
|
||||||
import styles from './Item.module.scss';
|
import styles from './Item.module.scss';
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ const Item = React.memo(
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
|
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
|
||||||
{name}
|
<Linkify linkStopPropagation>{name}</Linkify>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{isPersisted && canEdit && (
|
{isPersisted && canEdit && (
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { usePopup } from '../../lib/popup';
|
|||||||
|
|
||||||
import Paths from '../../constants/Paths';
|
import Paths from '../../constants/Paths';
|
||||||
import NotificationsStep from './NotificationsStep';
|
import NotificationsStep from './NotificationsStep';
|
||||||
|
import User from '../User';
|
||||||
import UserStep from '../UserStep';
|
import UserStep from '../UserStep';
|
||||||
|
|
||||||
import styles from './Header.module.scss';
|
import styles from './Header.module.scss';
|
||||||
@@ -91,7 +92,8 @@ const Header = React.memo(
|
|||||||
onLogout={onLogout}
|
onLogout={onLogout}
|
||||||
>
|
>
|
||||||
<Menu.Item className={classNames(styles.item, styles.itemHoverable)}>
|
<Menu.Item className={classNames(styles.item, styles.itemHoverable)}>
|
||||||
{user.name}
|
<span className={styles.userName}>{user.name}</span>
|
||||||
|
<User name={user.name} avatarUrl={user.avatarUrl} size="small" />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</UserPopup>
|
</UserPopup>
|
||||||
</Menu.Menu>
|
</Menu.Menu>
|
||||||
|
|||||||
@@ -86,6 +86,10 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userName {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
background: rgba(0, 0, 0, 0.24);
|
background: rgba(0, 0, 0, 0.24);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import LinkifyReact from 'linkify-react';
|
||||||
|
|
||||||
|
import history from '../history';
|
||||||
|
|
||||||
|
const Linkify = React.memo(({ children, linkStopPropagation, ...props }) => {
|
||||||
|
const handleLinkClick = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (linkStopPropagation) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.target.getAttribute('target')) {
|
||||||
|
event.preventDefault();
|
||||||
|
history.push(event.target.href);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[linkStopPropagation],
|
||||||
|
);
|
||||||
|
|
||||||
|
const linkRenderer = useCallback(
|
||||||
|
({ attributes: { href, ...linkProps }, content }) => {
|
||||||
|
let url;
|
||||||
|
try {
|
||||||
|
url = new URL(href, window.location);
|
||||||
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
const isSameSite = !!url && url.origin === window.location.origin;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
{...linkProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
|
href={href}
|
||||||
|
target={isSameSite ? undefined : '_blank'}
|
||||||
|
rel={isSameSite ? undefined : 'noreferrer'}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
>
|
||||||
|
{isSameSite ? url.pathname : content}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[handleLinkClick],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LinkifyReact
|
||||||
|
{...props} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
|
options={{
|
||||||
|
defaultProtocol: 'https',
|
||||||
|
render: linkRenderer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</LinkifyReact>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Linkify.propTypes = {
|
||||||
|
children: PropTypes.string.isRequired,
|
||||||
|
linkStopPropagation: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
Linkify.defaultProps = {
|
||||||
|
linkStopPropagation: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Linkify;
|
||||||
@@ -5,15 +5,17 @@ import { Menu } from 'semantic-ui-react';
|
|||||||
import { Popup } from '../../lib/custom-ui';
|
import { Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import { useSteps } from '../../hooks';
|
import { useSteps } from '../../hooks';
|
||||||
|
import ListSortStep from '../ListSortStep';
|
||||||
import DeleteStep from '../DeleteStep';
|
import DeleteStep from '../DeleteStep';
|
||||||
|
|
||||||
import styles from './ActionsStep.module.scss';
|
import styles from './ActionsStep.module.scss';
|
||||||
|
|
||||||
const StepTypes = {
|
const StepTypes = {
|
||||||
DELETE: 'DELETE',
|
DELETE: 'DELETE',
|
||||||
|
SORT: 'SORT',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) => {
|
const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onSort, onDelete, onClose }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [step, openStep, handleBack] = useSteps();
|
const [step, openStep, handleBack] = useSteps();
|
||||||
|
|
||||||
@@ -27,11 +29,30 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
|
|||||||
onClose();
|
onClose();
|
||||||
}, [onCardAdd, onClose]);
|
}, [onCardAdd, onClose]);
|
||||||
|
|
||||||
|
const handleSortClick = useCallback(() => {
|
||||||
|
openStep(StepTypes.SORT);
|
||||||
|
}, [openStep]);
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(() => {
|
const handleDeleteClick = useCallback(() => {
|
||||||
openStep(StepTypes.DELETE);
|
openStep(StepTypes.DELETE);
|
||||||
}, [openStep]);
|
}, [openStep]);
|
||||||
|
|
||||||
if (step && step.type === StepTypes.DELETE) {
|
const handleSortTypeSelect = useCallback(
|
||||||
|
(type) => {
|
||||||
|
onSort({
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
[onSort, onClose],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (step && step.type) {
|
||||||
|
switch (step.type) {
|
||||||
|
case StepTypes.SORT:
|
||||||
|
return <ListSortStep onTypeSelect={handleSortTypeSelect} onBack={handleBack} />;
|
||||||
|
case StepTypes.DELETE:
|
||||||
return (
|
return (
|
||||||
<DeleteStep
|
<DeleteStep
|
||||||
title="common.deleteList"
|
title="common.deleteList"
|
||||||
@@ -41,6 +62,8 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
|
|||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -62,6 +85,11 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
|
|||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item className={styles.menuItem} onClick={handleSortClick}>
|
||||||
|
{t('action.sortList', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Menu.Item>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
||||||
{t('action.deleteList', {
|
{t('action.deleteList', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
@@ -77,6 +105,7 @@ ActionsStep.propTypes = {
|
|||||||
onNameEdit: PropTypes.func.isRequired,
|
onNameEdit: PropTypes.func.isRequired,
|
||||||
onCardAdd: PropTypes.func.isRequired,
|
onCardAdd: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
|
onSort: PropTypes.func.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,18 @@ import { ReactComponent as PlusMathIcon } from '../../assets/images/plus-math-ic
|
|||||||
import styles from './List.module.scss';
|
import styles from './List.module.scss';
|
||||||
|
|
||||||
const List = React.memo(
|
const List = React.memo(
|
||||||
({ id, index, name, isPersisted, cardIds, canEdit, onUpdate, onDelete, onCardCreate }) => {
|
({
|
||||||
|
id,
|
||||||
|
index,
|
||||||
|
name,
|
||||||
|
isPersisted,
|
||||||
|
cardIds,
|
||||||
|
canEdit,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
onSort,
|
||||||
|
onCardCreate,
|
||||||
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
||||||
|
|
||||||
@@ -114,6 +125,7 @@ const List = React.memo(
|
|||||||
onNameEdit={handleNameEdit}
|
onNameEdit={handleNameEdit}
|
||||||
onCardAdd={handleCardAdd}
|
onCardAdd={handleCardAdd}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
|
onSort={onSort}
|
||||||
>
|
>
|
||||||
<Button className={classNames(styles.headerButton, styles.target)}>
|
<Button className={classNames(styles.headerButton, styles.target)}>
|
||||||
<Icon fitted name="pencil" size="small" />
|
<Icon fitted name="pencil" size="small" />
|
||||||
@@ -159,6 +171,7 @@ List.propTypes = {
|
|||||||
cardIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
cardIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
canEdit: PropTypes.bool.isRequired,
|
canEdit: PropTypes.bool.isRequired,
|
||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
|
onSort: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onCardCreate: PropTypes.func.isRequired,
|
onCardCreate: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Menu } from 'semantic-ui-react';
|
||||||
|
import { Popup } from '../../lib/custom-ui';
|
||||||
|
import { ListSortTypes } from '../../constants/Enums';
|
||||||
|
|
||||||
|
import styles from './ListSortStep.module.scss';
|
||||||
|
|
||||||
|
const ListSortStep = React.memo(({ onTypeSelect, onBack }) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Popup.Header onBack={onBack}>
|
||||||
|
{t('common.sortList', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Popup.Header>
|
||||||
|
<Popup.Content>
|
||||||
|
<Menu secondary vertical className={styles.menu}>
|
||||||
|
<Menu.Item
|
||||||
|
className={styles.menuItem}
|
||||||
|
onClick={() => onTypeSelect(ListSortTypes.NAME_ASC)}
|
||||||
|
>
|
||||||
|
{t('common.title')}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
className={styles.menuItem}
|
||||||
|
onClick={() => onTypeSelect(ListSortTypes.DUE_DATE_ASC)}
|
||||||
|
>
|
||||||
|
{t('common.dueDate')}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
className={styles.menuItem}
|
||||||
|
onClick={() => onTypeSelect(ListSortTypes.CREATED_AT_ASC)}
|
||||||
|
>
|
||||||
|
{t('common.oldestFirst')}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
className={styles.menuItem}
|
||||||
|
onClick={() => onTypeSelect(ListSortTypes.CREATED_AT_DESC)}
|
||||||
|
>
|
||||||
|
{t('common.newestFirst')}
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</Popup.Content>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ListSortStep.propTypes = {
|
||||||
|
onTypeSelect: PropTypes.func.isRequired,
|
||||||
|
onBack: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
ListSortStep.defaultProps = {
|
||||||
|
onBack: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListSortStep;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
:global(#app) {
|
||||||
|
.menu {
|
||||||
|
margin: -7px -12px -5px;
|
||||||
|
width: calc(100% + 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuItem {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import ListSortStep from './ListSortStep';
|
||||||
|
|
||||||
|
export default ListSortStep;
|
||||||
@@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button } from 'semantic-ui-react';
|
import { Button } from 'semantic-ui-react';
|
||||||
|
import { Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import { useSteps } from '../../hooks';
|
import { useSteps } from '../../hooks';
|
||||||
import User from '../User';
|
import User from '../User';
|
||||||
@@ -19,6 +20,7 @@ const ActionsStep = React.memo(
|
|||||||
({
|
({
|
||||||
membership,
|
membership,
|
||||||
permissionsSelectStep,
|
permissionsSelectStep,
|
||||||
|
title,
|
||||||
leaveButtonContent,
|
leaveButtonContent,
|
||||||
leaveConfirmationTitle,
|
leaveConfirmationTitle,
|
||||||
leaveConfirmationContent,
|
leaveConfirmationContent,
|
||||||
@@ -31,6 +33,7 @@ const ActionsStep = React.memo(
|
|||||||
canLeave,
|
canLeave,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
onBack,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
@@ -53,6 +56,11 @@ const ActionsStep = React.memo(
|
|||||||
[onUpdate],
|
[onUpdate],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleDeleteConfirm = useCallback(() => {
|
||||||
|
onDelete();
|
||||||
|
onClose();
|
||||||
|
}, [onDelete, onClose]);
|
||||||
|
|
||||||
if (step) {
|
if (step) {
|
||||||
switch (step.type) {
|
switch (step.type) {
|
||||||
case StepTypes.EDIT_PERMISSIONS: {
|
case StepTypes.EDIT_PERMISSIONS: {
|
||||||
@@ -81,7 +89,7 @@ const ActionsStep = React.memo(
|
|||||||
? leaveConfirmationButtonContent
|
? leaveConfirmationButtonContent
|
||||||
: deleteConfirmationButtonContent
|
: deleteConfirmationButtonContent
|
||||||
}
|
}
|
||||||
onConfirm={onDelete}
|
onConfirm={handleDeleteConfirm}
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -89,7 +97,7 @@ const ActionsStep = React.memo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const contentNode = (
|
||||||
<>
|
<>
|
||||||
<span className={styles.user}>
|
<span className={styles.user}>
|
||||||
<User name={membership.user.name} avatarUrl={membership.user.avatarUrl} size="large" />
|
<User name={membership.user.name} avatarUrl={membership.user.avatarUrl} size="large" />
|
||||||
@@ -125,12 +133,26 @@ const ActionsStep = React.memo(
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return onBack ? (
|
||||||
|
<>
|
||||||
|
<Popup.Header onBack={onBack}>
|
||||||
|
{t(title, {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Popup.Header>
|
||||||
|
<Popup.Content>{contentNode}</Popup.Content>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
contentNode
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
ActionsStep.propTypes = {
|
ActionsStep.propTypes = {
|
||||||
membership: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
membership: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
permissionsSelectStep: PropTypes.elementType,
|
permissionsSelectStep: PropTypes.elementType,
|
||||||
|
title: PropTypes.string,
|
||||||
leaveButtonContent: PropTypes.string,
|
leaveButtonContent: PropTypes.string,
|
||||||
leaveConfirmationTitle: PropTypes.string,
|
leaveConfirmationTitle: PropTypes.string,
|
||||||
leaveConfirmationContent: PropTypes.string,
|
leaveConfirmationContent: PropTypes.string,
|
||||||
@@ -143,11 +165,13 @@ ActionsStep.propTypes = {
|
|||||||
canLeave: PropTypes.bool.isRequired,
|
canLeave: PropTypes.bool.isRequired,
|
||||||
onUpdate: PropTypes.func,
|
onUpdate: PropTypes.func,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
|
onBack: PropTypes.func,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
ActionsStep.defaultProps = {
|
ActionsStep.defaultProps = {
|
||||||
permissionsSelectStep: undefined,
|
permissionsSelectStep: undefined,
|
||||||
|
title: 'common.memberActions',
|
||||||
leaveButtonContent: 'action.leaveBoard',
|
leaveButtonContent: 'action.leaveBoard',
|
||||||
leaveConfirmationTitle: 'common.leaveBoard',
|
leaveConfirmationTitle: 'common.leaveBoard',
|
||||||
leaveConfirmationContent: 'common.areYouSureYouWantToLeaveBoard',
|
leaveConfirmationContent: 'common.areYouSureYouWantToLeaveBoard',
|
||||||
@@ -157,6 +181,7 @@ ActionsStep.defaultProps = {
|
|||||||
deleteConfirmationContent: 'common.areYouSureYouWantToRemoveThisMemberFromBoard',
|
deleteConfirmationContent: 'common.areYouSureYouWantToRemoveThisMemberFromBoard',
|
||||||
deleteConfirmationButtonContent: 'action.removeMember',
|
deleteConfirmationButtonContent: 'action.removeMember',
|
||||||
onUpdate: undefined,
|
onUpdate: undefined,
|
||||||
|
onBack: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ActionsStep;
|
export default ActionsStep;
|
||||||
|
|||||||
@@ -5,16 +5,21 @@ import { usePopup } from '../../lib/popup';
|
|||||||
|
|
||||||
import AddStep from './AddStep';
|
import AddStep from './AddStep';
|
||||||
import ActionsStep from './ActionsStep';
|
import ActionsStep from './ActionsStep';
|
||||||
|
import MembershipsStep from './MembershipsStep';
|
||||||
import User from '../User';
|
import User from '../User';
|
||||||
|
|
||||||
import styles from './Memberships.module.scss';
|
import styles from './Memberships.module.scss';
|
||||||
|
|
||||||
|
const MAX_MEMBERS = 6;
|
||||||
|
|
||||||
const Memberships = React.memo(
|
const Memberships = React.memo(
|
||||||
({
|
({
|
||||||
items,
|
items,
|
||||||
allUsers,
|
allUsers,
|
||||||
permissionsSelectStep,
|
permissionsSelectStep,
|
||||||
|
title,
|
||||||
addTitle,
|
addTitle,
|
||||||
|
actionsTitle,
|
||||||
leaveButtonContent,
|
leaveButtonContent,
|
||||||
leaveConfirmationTitle,
|
leaveConfirmationTitle,
|
||||||
leaveConfirmationContent,
|
leaveConfirmationContent,
|
||||||
@@ -31,11 +36,14 @@ const Memberships = React.memo(
|
|||||||
}) => {
|
}) => {
|
||||||
const AddPopup = usePopup(AddStep);
|
const AddPopup = usePopup(AddStep);
|
||||||
const ActionsPopup = usePopup(ActionsStep);
|
const ActionsPopup = usePopup(ActionsStep);
|
||||||
|
const MembershipsPopup = usePopup(MembershipsStep);
|
||||||
|
|
||||||
|
const remainMembersCount = items.length - MAX_MEMBERS;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span className={styles.users}>
|
<span className={styles.users}>
|
||||||
{items.map((item) => (
|
{items.slice(0, MAX_MEMBERS).map((item) => (
|
||||||
<span key={item.id} className={styles.user}>
|
<span key={item.id} className={styles.user}>
|
||||||
<ActionsPopup
|
<ActionsPopup
|
||||||
membership={item}
|
membership={item}
|
||||||
@@ -63,6 +71,30 @@ const Memberships = React.memo(
|
|||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
|
{remainMembersCount > 0 && (
|
||||||
|
<MembershipsPopup
|
||||||
|
items={items}
|
||||||
|
permissionsSelectStep={permissionsSelectStep}
|
||||||
|
title={title}
|
||||||
|
actionsTitle={actionsTitle}
|
||||||
|
leaveButtonContent={leaveButtonContent}
|
||||||
|
leaveConfirmationTitle={leaveConfirmationTitle}
|
||||||
|
leaveConfirmationContent={leaveConfirmationContent}
|
||||||
|
leaveConfirmationButtonContent={leaveConfirmationButtonContent}
|
||||||
|
deleteButtonContent={deleteButtonContent}
|
||||||
|
deleteConfirmationTitle={deleteConfirmationTitle}
|
||||||
|
deleteConfirmationContent={deleteConfirmationContent}
|
||||||
|
deleteConfirmationButtonContent={deleteConfirmationButtonContent}
|
||||||
|
canEdit={canEdit}
|
||||||
|
canLeave={items.length > 1 || canLeaveIfLast}
|
||||||
|
onUpdate={onUpdate}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
|
<Button icon className={styles.addUser}>
|
||||||
|
+{remainMembersCount < 99 ? remainMembersCount : 99}
|
||||||
|
</Button>
|
||||||
|
</MembershipsPopup>
|
||||||
|
)}
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<AddPopup
|
<AddPopup
|
||||||
users={allUsers}
|
users={allUsers}
|
||||||
@@ -85,7 +117,9 @@ Memberships.propTypes = {
|
|||||||
allUsers: PropTypes.array.isRequired,
|
allUsers: PropTypes.array.isRequired,
|
||||||
/* eslint-enable react/forbid-prop-types */
|
/* eslint-enable react/forbid-prop-types */
|
||||||
permissionsSelectStep: PropTypes.elementType,
|
permissionsSelectStep: PropTypes.elementType,
|
||||||
|
title: PropTypes.string,
|
||||||
addTitle: PropTypes.string,
|
addTitle: PropTypes.string,
|
||||||
|
actionsTitle: PropTypes.string,
|
||||||
leaveButtonContent: PropTypes.string,
|
leaveButtonContent: PropTypes.string,
|
||||||
leaveConfirmationTitle: PropTypes.string,
|
leaveConfirmationTitle: PropTypes.string,
|
||||||
leaveConfirmationContent: PropTypes.string,
|
leaveConfirmationContent: PropTypes.string,
|
||||||
@@ -103,7 +137,9 @@ Memberships.propTypes = {
|
|||||||
|
|
||||||
Memberships.defaultProps = {
|
Memberships.defaultProps = {
|
||||||
permissionsSelectStep: undefined,
|
permissionsSelectStep: undefined,
|
||||||
|
title: undefined,
|
||||||
addTitle: undefined,
|
addTitle: undefined,
|
||||||
|
actionsTitle: undefined,
|
||||||
leaveButtonContent: undefined,
|
leaveButtonContent: undefined,
|
||||||
leaveConfirmationTitle: undefined,
|
leaveConfirmationTitle: undefined,
|
||||||
leaveConfirmationContent: undefined,
|
leaveConfirmationContent: undefined,
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { useSteps } from '../../hooks';
|
||||||
|
import ActionsStep from './ActionsStep';
|
||||||
|
import BoardMembershipsStep from '../BoardMembershipsStep';
|
||||||
|
|
||||||
|
const StepTypes = {
|
||||||
|
EDIT: 'EDIT',
|
||||||
|
};
|
||||||
|
|
||||||
|
const MembershipsStep = React.memo(
|
||||||
|
({
|
||||||
|
items,
|
||||||
|
permissionsSelectStep,
|
||||||
|
title,
|
||||||
|
actionsTitle,
|
||||||
|
leaveButtonContent,
|
||||||
|
leaveConfirmationTitle,
|
||||||
|
leaveConfirmationContent,
|
||||||
|
leaveConfirmationButtonContent,
|
||||||
|
deleteButtonContent,
|
||||||
|
deleteConfirmationTitle,
|
||||||
|
deleteConfirmationContent,
|
||||||
|
deleteConfirmationButtonContent,
|
||||||
|
canEdit,
|
||||||
|
canLeave,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
onClose,
|
||||||
|
}) => {
|
||||||
|
const [step, openStep, handleBack] = useSteps();
|
||||||
|
|
||||||
|
const handleUserSelect = useCallback(
|
||||||
|
(userId) => {
|
||||||
|
openStep(StepTypes.EDIT, {
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[openStep],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (step && step.type === StepTypes.EDIT) {
|
||||||
|
const currentItem = items.find((item) => item.userId === step.params.userId);
|
||||||
|
|
||||||
|
if (currentItem) {
|
||||||
|
return (
|
||||||
|
<ActionsStep
|
||||||
|
membership={currentItem}
|
||||||
|
permissionsSelectStep={permissionsSelectStep}
|
||||||
|
title={actionsTitle}
|
||||||
|
leaveButtonContent={leaveButtonContent}
|
||||||
|
leaveConfirmationTitle={leaveConfirmationTitle}
|
||||||
|
leaveConfirmationContent={leaveConfirmationContent}
|
||||||
|
leaveConfirmationButtonContent={leaveConfirmationButtonContent}
|
||||||
|
deleteButtonContent={deleteButtonContent}
|
||||||
|
deleteConfirmationTitle={deleteConfirmationTitle}
|
||||||
|
deleteConfirmationContent={deleteConfirmationContent}
|
||||||
|
deleteConfirmationButtonContent={deleteConfirmationButtonContent}
|
||||||
|
canEdit={canEdit}
|
||||||
|
canLeave={canLeave}
|
||||||
|
onUpdate={(data) => onUpdate(currentItem.id, data)}
|
||||||
|
onDelete={() => onDelete(currentItem.id)}
|
||||||
|
onBack={handleBack}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
openStep(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
// FIXME: hack
|
||||||
|
<BoardMembershipsStep
|
||||||
|
items={items}
|
||||||
|
currentUserIds={[]}
|
||||||
|
title={title}
|
||||||
|
onUserSelect={handleUserSelect}
|
||||||
|
onUserDeselect={() => {}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
MembershipsStep.propTypes = {
|
||||||
|
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
permissionsSelectStep: PropTypes.elementType,
|
||||||
|
title: PropTypes.string,
|
||||||
|
actionsTitle: PropTypes.string,
|
||||||
|
leaveButtonContent: PropTypes.string,
|
||||||
|
leaveConfirmationTitle: PropTypes.string,
|
||||||
|
leaveConfirmationContent: PropTypes.string,
|
||||||
|
leaveConfirmationButtonContent: PropTypes.string,
|
||||||
|
deleteButtonContent: PropTypes.string,
|
||||||
|
deleteConfirmationTitle: PropTypes.string,
|
||||||
|
deleteConfirmationContent: PropTypes.string,
|
||||||
|
deleteConfirmationButtonContent: PropTypes.string,
|
||||||
|
canEdit: PropTypes.bool.isRequired,
|
||||||
|
canLeave: PropTypes.bool.isRequired,
|
||||||
|
onUpdate: PropTypes.func,
|
||||||
|
onDelete: PropTypes.func.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
MembershipsStep.defaultProps = {
|
||||||
|
permissionsSelectStep: undefined,
|
||||||
|
title: undefined,
|
||||||
|
actionsTitle: undefined,
|
||||||
|
leaveButtonContent: undefined,
|
||||||
|
leaveConfirmationTitle: undefined,
|
||||||
|
leaveConfirmationContent: undefined,
|
||||||
|
leaveConfirmationButtonContent: undefined,
|
||||||
|
deleteButtonContent: undefined,
|
||||||
|
deleteConfirmationTitle: undefined,
|
||||||
|
deleteConfirmationContent: undefined,
|
||||||
|
deleteConfirmationButtonContent: undefined,
|
||||||
|
onUpdate: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MembershipsStep;
|
||||||
@@ -12,7 +12,9 @@ const ManagersPane = React.memo(({ items, allUsers, onCreate, onDelete }) => {
|
|||||||
<Memberships
|
<Memberships
|
||||||
items={items}
|
items={items}
|
||||||
allUsers={allUsers}
|
allUsers={allUsers}
|
||||||
|
title="common.managers"
|
||||||
addTitle="common.addManager"
|
addTitle="common.addManager"
|
||||||
|
actionsTitle="common.managerActions"
|
||||||
leaveButtonContent="action.leaveProject"
|
leaveButtonContent="action.leaveProject"
|
||||||
leaveConfirmationTitle="common.leaveProject"
|
leaveConfirmationTitle="common.leaveProject"
|
||||||
leaveConfirmationContent="common.areYouSureYouWantToLeaveProject"
|
leaveConfirmationContent="common.areYouSureYouWantToLeaveProject"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'react-datepicker/dist/react-datepicker.css';
|
|||||||
import 'photoswipe/dist/photoswipe.css';
|
import 'photoswipe/dist/photoswipe.css';
|
||||||
import 'easymde/dist/easymde.min.css';
|
import 'easymde/dist/easymde.min.css';
|
||||||
import '../lib/custom-ui/styles.css';
|
import '../lib/custom-ui/styles.css';
|
||||||
|
import '../assets/css/font-awesome.css';
|
||||||
import '../styles.module.scss';
|
import '../styles.module.scss';
|
||||||
|
|
||||||
function Root({ store, history }) {
|
function Root({ store, history }) {
|
||||||
|
|||||||
@@ -173,6 +173,10 @@ export default {
|
|||||||
LIST_UPDATE__SUCCESS: 'LIST_UPDATE__SUCCESS',
|
LIST_UPDATE__SUCCESS: 'LIST_UPDATE__SUCCESS',
|
||||||
LIST_UPDATE__FAILURE: 'LIST_UPDATE__FAILURE',
|
LIST_UPDATE__FAILURE: 'LIST_UPDATE__FAILURE',
|
||||||
LIST_UPDATE_HANDLE: 'LIST_UPDATE_HANDLE',
|
LIST_UPDATE_HANDLE: 'LIST_UPDATE_HANDLE',
|
||||||
|
LIST_SORT: 'LIST_SORT',
|
||||||
|
LIST_SORT__SUCCESS: 'LIST_SORT__SUCCESS',
|
||||||
|
LIST_SORT__FAILURE: 'LIST_SORT__FAILURE',
|
||||||
|
LIST_SORT_HANDLE: 'LIST_SORT_HANDLE',
|
||||||
LIST_DELETE: 'LIST_DELETE',
|
LIST_DELETE: 'LIST_DELETE',
|
||||||
LIST_DELETE__SUCCESS: 'LIST_DELETE__SUCCESS',
|
LIST_DELETE__SUCCESS: 'LIST_DELETE__SUCCESS',
|
||||||
LIST_DELETE__FAILURE: 'LIST_DELETE__FAILURE',
|
LIST_DELETE__FAILURE: 'LIST_DELETE__FAILURE',
|
||||||
@@ -194,10 +198,14 @@ export default {
|
|||||||
CARD_TRANSFER: 'CARD_TRANSFER',
|
CARD_TRANSFER: 'CARD_TRANSFER',
|
||||||
CARD_TRANSFER__SUCCESS: 'CARD_TRANSFER__SUCCESS',
|
CARD_TRANSFER__SUCCESS: 'CARD_TRANSFER__SUCCESS',
|
||||||
CARD_TRANSFER__FAILURE: 'CARD_TRANSFER__FAILURE',
|
CARD_TRANSFER__FAILURE: 'CARD_TRANSFER__FAILURE',
|
||||||
|
CARD_DUPLICATE: 'CARD_DUPLICATE',
|
||||||
|
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
|
||||||
|
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
|
||||||
CARD_DELETE: 'CARD_DELETE',
|
CARD_DELETE: 'CARD_DELETE',
|
||||||
CARD_DELETE__SUCCESS: 'CARD_DELETE__SUCCESS',
|
CARD_DELETE__SUCCESS: 'CARD_DELETE__SUCCESS',
|
||||||
CARD_DELETE__FAILURE: 'CARD_DELETE__FAILURE',
|
CARD_DELETE__FAILURE: 'CARD_DELETE__FAILURE',
|
||||||
CARD_DELETE_HANDLE: 'CARD_DELETE_HANDLE',
|
CARD_DELETE_HANDLE: 'CARD_DELETE_HANDLE',
|
||||||
|
TEXT_FILTER_IN_CURRENT_BOARD: 'TEXT_FILTER_IN_CURRENT_BOARD',
|
||||||
|
|
||||||
/* Tasks */
|
/* Tasks */
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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');
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ export default {
|
|||||||
VERSION,
|
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,
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ export default {
|
|||||||
LIST_MOVE: `${PREFIX}/LIST_MOVE`,
|
LIST_MOVE: `${PREFIX}/LIST_MOVE`,
|
||||||
LIST_DELETE: `${PREFIX}/LIST_DELETE`,
|
LIST_DELETE: `${PREFIX}/LIST_DELETE`,
|
||||||
LIST_DELETE_HANDLE: `${PREFIX}/LIST_DELETE_HANDLE`,
|
LIST_DELETE_HANDLE: `${PREFIX}/LIST_DELETE_HANDLE`,
|
||||||
|
LIST_SORT: `${PREFIX}/LIST_SORT`,
|
||||||
|
LIST_SORT_HANDLE: `${PREFIX}/LIST_SORT_HANDLE`,
|
||||||
|
|
||||||
/* Cards */
|
/* Cards */
|
||||||
|
|
||||||
@@ -132,9 +134,12 @@ export default {
|
|||||||
CURRENT_CARD_MOVE: `${PREFIX}/CURRENT_CARD_MOVE`,
|
CURRENT_CARD_MOVE: `${PREFIX}/CURRENT_CARD_MOVE`,
|
||||||
CARD_TRANSFER: `${PREFIX}/CARD_TRANSFER`,
|
CARD_TRANSFER: `${PREFIX}/CARD_TRANSFER`,
|
||||||
CURRENT_CARD_TRANSFER: `${PREFIX}/CURRENT_CARD_TRANSFER`,
|
CURRENT_CARD_TRANSFER: `${PREFIX}/CURRENT_CARD_TRANSFER`,
|
||||||
|
CARD_DUPLICATE: `${PREFIX}/CARD_DUPLICATE`,
|
||||||
|
CURRENT_CARD_DUPLICATE: `${PREFIX}/CURRENT_CARD_DUPLICATE`,
|
||||||
CARD_DELETE: `${PREFIX}/CARD_DELETE`,
|
CARD_DELETE: `${PREFIX}/CARD_DELETE`,
|
||||||
CURRENT_CARD_DELETE: `${PREFIX}/CURRENT_CARD_DELETE`,
|
CURRENT_CARD_DELETE: `${PREFIX}/CURRENT_CARD_DELETE`,
|
||||||
CARD_DELETE_HANDLE: `${PREFIX}/CARD_DELETE_HANDLE`,
|
CARD_DELETE_HANDLE: `${PREFIX}/CARD_DELETE_HANDLE`,
|
||||||
|
TEXT_FILTER_IN_CURRENT_BOARD: `${PREFIX}/FILTER_TEXT_HANDLE`,
|
||||||
|
|
||||||
/* Tasks */
|
/* Tasks */
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ export const BoardMembershipRoles = {
|
|||||||
VIEWER: 'viewer',
|
VIEWER: 'viewer',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ListSortTypes = {
|
||||||
|
NAME_ASC: 'name_asc',
|
||||||
|
DUE_DATE_ASC: 'dueDate_asc',
|
||||||
|
CREATED_AT_ASC: 'createdAt_asc',
|
||||||
|
CREATED_AT_DESC: 'createdAt_desc',
|
||||||
|
};
|
||||||
|
|
||||||
export const ActivityTypes = {
|
export const ActivityTypes = {
|
||||||
CREATE_CARD: 'createCard',
|
CREATE_CARD: 'createCard',
|
||||||
MOVE_CARD: 'moveCard',
|
MOVE_CARD: 'moveCard',
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const mapStateToProps = (state) => {
|
|||||||
const labels = selectors.selectLabelsForCurrentBoard(state);
|
const labels = selectors.selectLabelsForCurrentBoard(state);
|
||||||
const filterUsers = selectors.selectFilterUsersForCurrentBoard(state);
|
const filterUsers = selectors.selectFilterUsersForCurrentBoard(state);
|
||||||
const filterLabels = selectors.selectFilterLabelsForCurrentBoard(state);
|
const filterLabels = selectors.selectFilterLabelsForCurrentBoard(state);
|
||||||
|
const filterText = selectors.selectFilterTextForCurrentBoard(state);
|
||||||
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
|
||||||
|
|
||||||
const isCurrentUserEditor =
|
const isCurrentUserEditor =
|
||||||
@@ -23,6 +24,7 @@ const mapStateToProps = (state) => {
|
|||||||
labels,
|
labels,
|
||||||
filterUsers,
|
filterUsers,
|
||||||
filterLabels,
|
filterLabels,
|
||||||
|
filterText,
|
||||||
allUsers,
|
allUsers,
|
||||||
canEdit: isCurrentUserEditor,
|
canEdit: isCurrentUserEditor,
|
||||||
canEditMemberships: isCurrentUserManager,
|
canEditMemberships: isCurrentUserManager,
|
||||||
@@ -43,6 +45,7 @@ const mapDispatchToProps = (dispatch) =>
|
|||||||
onLabelUpdate: entryActions.updateLabel,
|
onLabelUpdate: entryActions.updateLabel,
|
||||||
onLabelMove: entryActions.moveLabel,
|
onLabelMove: entryActions.moveLabel,
|
||||||
onLabelDelete: entryActions.deleteLabel,
|
onLabelDelete: entryActions.deleteLabel,
|
||||||
|
onTextFilterUpdate: entryActions.filterText,
|
||||||
},
|
},
|
||||||
dispatch,
|
dispatch,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ const mapDispatchToProps = (dispatch, { id }) =>
|
|||||||
onUpdate: (data) => entryActions.updateCard(id, data),
|
onUpdate: (data) => entryActions.updateCard(id, data),
|
||||||
onMove: (listId, index) => entryActions.moveCard(id, listId, index),
|
onMove: (listId, index) => entryActions.moveCard(id, listId, index),
|
||||||
onTransfer: (boardId, listId) => entryActions.transferCard(id, boardId, listId),
|
onTransfer: (boardId, listId) => entryActions.transferCard(id, boardId, listId),
|
||||||
|
onDuplicate: () => entryActions.duplicateCard(id),
|
||||||
onDelete: () => entryActions.deleteCard(id),
|
onDelete: () => entryActions.deleteCard(id),
|
||||||
onUserAdd: (userId) => entryActions.addUserToCard(userId, id),
|
onUserAdd: (userId) => entryActions.addUserToCard(userId, id),
|
||||||
onUserRemove: (userId) => entryActions.removeUserFromCard(userId, id),
|
onUserRemove: (userId) => entryActions.removeUserFromCard(userId, id),
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ const mapDispatchToProps = (dispatch) =>
|
|||||||
onUpdate: entryActions.updateCurrentCard,
|
onUpdate: entryActions.updateCurrentCard,
|
||||||
onMove: entryActions.moveCurrentCard,
|
onMove: entryActions.moveCurrentCard,
|
||||||
onTransfer: entryActions.transferCurrentCard,
|
onTransfer: entryActions.transferCurrentCard,
|
||||||
|
onDuplicate: entryActions.duplicateCurrentCard,
|
||||||
onDelete: entryActions.deleteCurrentCard,
|
onDelete: entryActions.deleteCurrentCard,
|
||||||
onUserAdd: entryActions.addUserToCurrentCard,
|
onUserAdd: entryActions.addUserToCurrentCard,
|
||||||
onUserRemove: entryActions.removeUserFromCurrentCard,
|
onUserRemove: entryActions.removeUserFromCurrentCard,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const mapDispatchToProps = (dispatch, { id }) =>
|
|||||||
bindActionCreators(
|
bindActionCreators(
|
||||||
{
|
{
|
||||||
onUpdate: (data) => entryActions.updateList(id, data),
|
onUpdate: (data) => entryActions.updateList(id, data),
|
||||||
|
onSort: (data) => entryActions.sortList(id, data),
|
||||||
onDelete: () => entryActions.deleteList(id),
|
onDelete: () => entryActions.deleteList(id),
|
||||||
onCardCreate: (data, autoOpen) => entryActions.createCard(id, data, autoOpen),
|
onCardCreate: (data, autoOpen) => entryActions.createCard(id, data, autoOpen),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const mapStateToProps = (state) => {
|
|||||||
isSubmittingUsingOidc,
|
isSubmittingUsingOidc,
|
||||||
error,
|
error,
|
||||||
withOidc: !!oidcConfig,
|
withOidc: !!oidcConfig,
|
||||||
isOidcEnforced: oidcConfig && oidcConfig.isEnforced,
|
isOidcEnforced: !!oidcConfig && oidcConfig.isEnforced,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const handleCardUpdate = (card) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const moveCard = (id, listId, index = 0) => ({
|
const moveCard = (id, listId, index) => ({
|
||||||
type: EntryActionTypes.CARD_MOVE,
|
type: EntryActionTypes.CARD_MOVE,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
@@ -47,7 +47,7 @@ const moveCard = (id, listId, index = 0) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const moveCurrentCard = (listId, index = 0) => ({
|
const moveCurrentCard = (listId, index) => ({
|
||||||
type: EntryActionTypes.CURRENT_CARD_MOVE,
|
type: EntryActionTypes.CURRENT_CARD_MOVE,
|
||||||
payload: {
|
payload: {
|
||||||
listId,
|
listId,
|
||||||
@@ -55,7 +55,7 @@ const moveCurrentCard = (listId, index = 0) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const transferCard = (id, boardId, listId, index = 0) => ({
|
const transferCard = (id, boardId, listId, index) => ({
|
||||||
type: EntryActionTypes.CARD_TRANSFER,
|
type: EntryActionTypes.CARD_TRANSFER,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
@@ -65,7 +65,7 @@ const transferCard = (id, boardId, listId, index = 0) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const transferCurrentCard = (boardId, listId, index = 0) => ({
|
const transferCurrentCard = (boardId, listId, index) => ({
|
||||||
type: EntryActionTypes.CURRENT_CARD_TRANSFER,
|
type: EntryActionTypes.CURRENT_CARD_TRANSFER,
|
||||||
payload: {
|
payload: {
|
||||||
boardId,
|
boardId,
|
||||||
@@ -74,6 +74,18 @@ const transferCurrentCard = (boardId, listId, index = 0) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const duplicateCard = (id) => ({
|
||||||
|
type: EntryActionTypes.CARD_DUPLICATE,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const duplicateCurrentCard = () => ({
|
||||||
|
type: EntryActionTypes.CURRENT_CARD_DUPLICATE,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteCard = (id) => ({
|
const deleteCard = (id) => ({
|
||||||
type: EntryActionTypes.CARD_DELETE,
|
type: EntryActionTypes.CARD_DELETE,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -93,6 +105,13 @@ const handleCardDelete = (card) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const filterText = (text) => ({
|
||||||
|
type: EntryActionTypes.TEXT_FILTER_IN_CURRENT_BOARD,
|
||||||
|
payload: {
|
||||||
|
text,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createCard,
|
createCard,
|
||||||
handleCardCreate,
|
handleCardCreate,
|
||||||
@@ -103,7 +122,10 @@ export default {
|
|||||||
moveCurrentCard,
|
moveCurrentCard,
|
||||||
transferCard,
|
transferCard,
|
||||||
transferCurrentCard,
|
transferCurrentCard,
|
||||||
|
duplicateCard,
|
||||||
|
duplicateCurrentCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
deleteCurrentCard,
|
deleteCurrentCard,
|
||||||
handleCardDelete,
|
handleCardDelete,
|
||||||
|
filterText,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import EntryActionTypes from '../constants/EntryActionTypes';
|
import EntryActionTypes from '../constants/EntryActionTypes';
|
||||||
|
|
||||||
const logout = () => ({
|
const logout = (invalidateAccessToken) => ({
|
||||||
type: EntryActionTypes.LOGOUT,
|
type: EntryActionTypes.LOGOUT,
|
||||||
payload: {},
|
payload: {
|
||||||
|
invalidateAccessToken,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -37,6 +37,24 @@ const moveList = (id, index) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sortList = (id, data) => {
|
||||||
|
return {
|
||||||
|
type: EntryActionTypes.LIST_SORT,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleListSort = (list, cards) => ({
|
||||||
|
type: EntryActionTypes.LIST_SORT_HANDLE,
|
||||||
|
payload: {
|
||||||
|
list,
|
||||||
|
cards,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteList = (id) => ({
|
const deleteList = (id) => ({
|
||||||
type: EntryActionTypes.LIST_DELETE,
|
type: EntryActionTypes.LIST_DELETE,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -57,6 +75,8 @@ export default {
|
|||||||
updateList,
|
updateList,
|
||||||
handleListUpdate,
|
handleListUpdate,
|
||||||
moveList,
|
moveList,
|
||||||
|
sortList,
|
||||||
|
handleListSort,
|
||||||
deleteList,
|
deleteList,
|
||||||
handleListDelete,
|
handleListDelete,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ export default class Input extends SemanticUIInput {
|
|||||||
static Mask = InputMask;
|
static Mask = InputMask;
|
||||||
|
|
||||||
focus = (options) => this.inputRef.current.focus(options);
|
focus = (options) => this.inputRef.current.focus(options);
|
||||||
|
|
||||||
|
blur = () => this.inputRef.current.blur();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
import dateFns from 'date-fns/locale/bg';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dateFns,
|
||||||
|
|
||||||
|
format: {
|
||||||
|
date: 'd.MM.yyyy',
|
||||||
|
time: 'p',
|
||||||
|
dateTime: '$t(format:date) $t(format:time)',
|
||||||
|
longDate: 'd. MMM',
|
||||||
|
longDateTime: "d. MMMM 'в' p",
|
||||||
|
fullDate: 'd. MMM. y',
|
||||||
|
fullDateTime: "d. MMMM. y 'в' 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: 'Aвтоматично откриване',
|
||||||
|
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: 'Напуснете проекта',
|
||||||
|
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: 'Добавяне на потребител',
|
||||||
|
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}} скрити)',
|
||||||
|
showDetails: 'Показване на подробности',
|
||||||
|
showFewerAttachments: 'Показване на по-малко прикачени файлове',
|
||||||
|
sortList_title: 'Списък за сортиране',
|
||||||
|
start: 'Старт',
|
||||||
|
stop: 'Стоп',
|
||||||
|
subscribe: 'Абонирайте се',
|
||||||
|
unsubscribe: 'Отписване',
|
||||||
|
uploadNewAvatar: 'Качване на нов аватар',
|
||||||
|
uploadNewImage: 'Качване на ново изображение',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import login from './login';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
language: 'bg',
|
||||||
|
country: 'bg',
|
||||||
|
name: 'Български',
|
||||||
|
embeddedLocale: login,
|
||||||
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export default {
|
||||||
|
translation: {
|
||||||
|
common: {
|
||||||
|
emailOrUsername: 'Имейл или потребителско име',
|
||||||
|
invalidEmailOrUsername: 'Невалиден имейл или потребителско име',
|
||||||
|
invalidPassword: 'Невалидна парола',
|
||||||
|
logInToPlanka: 'Влезте в Planka',
|
||||||
|
noInternetConnection: 'Няма интернет връзка',
|
||||||
|
pageNotFound_title: 'Страницата не е намерена',
|
||||||
|
password: 'Парола',
|
||||||
|
projectManagement: 'Управление на проекти',
|
||||||
|
serverConnectionFailed: 'Неуспешна връзка със сървъра',
|
||||||
|
unknownError: 'Неизвестна грешка, опитайте отново по-късно',
|
||||||
|
useSingleSignOn: 'Използване на single sign-on',
|
||||||
|
},
|
||||||
|
|
||||||
|
action: {
|
||||||
|
logIn: 'Вход',
|
||||||
|
logInWithSSO: 'Вход чрез SSO',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -39,9 +39,9 @@ export default {
|
|||||||
areYouSureYouWantToLeaveBoard: 'Opravdu chcete opustit tuto tabuli?',
|
areYouSureYouWantToLeaveBoard: 'Opravdu chcete opustit tuto tabuli?',
|
||||||
areYouSureYouWantToLeaveProject: 'Opravdu chcete opustit projekt?',
|
areYouSureYouWantToLeaveProject: 'Opravdu chcete opustit projekt?',
|
||||||
areYouSureYouWantToRemoveThisManagerFromProject:
|
areYouSureYouWantToRemoveThisManagerFromProject:
|
||||||
'Are you sure you want to remove this manager from the project?',
|
'Jste si jisti, že chcete tohoto správce z projektu odebrat?',
|
||||||
areYouSureYouWantToRemoveThisMemberFromBoard:
|
areYouSureYouWantToRemoveThisMemberFromBoard:
|
||||||
'Opravdu chcete odstranit tohoto člena z tabule?',
|
'Jste si jisti, že chcete tohoto člena odebrat z tabule?',
|
||||||
attachment: 'Příloha',
|
attachment: 'Příloha',
|
||||||
attachments: 'Přílohy',
|
attachments: 'Přílohy',
|
||||||
authentication: 'Ověření',
|
authentication: 'Ověření',
|
||||||
@@ -55,6 +55,7 @@ export default {
|
|||||||
cardNotFound_title: 'Karta nenalezena',
|
cardNotFound_title: 'Karta nenalezena',
|
||||||
cardOrActionAreDeleted: 'Karta nebo akce je smazána.',
|
cardOrActionAreDeleted: 'Karta nebo akce je smazána.',
|
||||||
color: 'Barva',
|
color: 'Barva',
|
||||||
|
copy_inline: 'copy',
|
||||||
createBoard_title: 'Vytvořit tabuli',
|
createBoard_title: 'Vytvořit tabuli',
|
||||||
createLabel_title: 'Vytvořit štítek',
|
createLabel_title: 'Vytvořit štítek',
|
||||||
createNewOneOrSelectExistingOne: 'Vytvořit nový nebo vyberte<br />již existující.',
|
createNewOneOrSelectExistingOne: 'Vytvořit nový nebo vyberte<br />již existující.',
|
||||||
@@ -63,7 +64,8 @@ export default {
|
|||||||
currentPassword: 'Aktuální heslo',
|
currentPassword: 'Aktuální heslo',
|
||||||
dangerZone_title: 'Nebezpečná zóna',
|
dangerZone_title: 'Nebezpečná zóna',
|
||||||
date: 'Datum',
|
date: 'Datum',
|
||||||
dueDate_title: 'Termín do',
|
dueDate: 'Termín',
|
||||||
|
dueDate_title: 'Termín',
|
||||||
deleteAttachment_title: 'Smazat přílohu',
|
deleteAttachment_title: 'Smazat přílohu',
|
||||||
deleteBoard_title: 'Smazat tabuli',
|
deleteBoard_title: 'Smazat tabuli',
|
||||||
deleteCard_title: 'Smazat kartu',
|
deleteCard_title: 'Smazat kartu',
|
||||||
@@ -80,7 +82,7 @@ export default {
|
|||||||
editAttachment_title: 'Upravit přílohu',
|
editAttachment_title: 'Upravit přílohu',
|
||||||
editAvatar_title: 'Upravit avatar',
|
editAvatar_title: 'Upravit avatar',
|
||||||
editBoard_title: 'Upravit tabuli',
|
editBoard_title: 'Upravit tabuli',
|
||||||
editDueDate_title: 'Upravit Termín do',
|
editDueDate_title: 'Upravit termín',
|
||||||
editEmail_title: 'Upravit e-mail',
|
editEmail_title: 'Upravit e-mail',
|
||||||
editInformation_title: 'Upravit informace',
|
editInformation_title: 'Upravit informace',
|
||||||
editLabel_title: 'Upravit štítek',
|
editLabel_title: 'Upravit štítek',
|
||||||
@@ -115,6 +117,7 @@ export default {
|
|||||||
minutes: 'Minuty',
|
minutes: 'Minuty',
|
||||||
moveCard_title: 'Přesunout kartu',
|
moveCard_title: 'Přesunout kartu',
|
||||||
name: 'Jméno',
|
name: 'Jméno',
|
||||||
|
newestFirst: 'Nejnovější',
|
||||||
newEmail: 'Nový e-mail',
|
newEmail: 'Nový e-mail',
|
||||||
newPassword: 'Nové heslo',
|
newPassword: 'Nové heslo',
|
||||||
newUsername: 'Nové uživatelské jméno',
|
newUsername: 'Nové uživatelské jméno',
|
||||||
@@ -124,13 +127,14 @@ export default {
|
|||||||
noProjects: 'Žádné projekty',
|
noProjects: 'Žádné projekty',
|
||||||
notifications: 'Oznámení',
|
notifications: 'Oznámení',
|
||||||
noUnreadNotifications: 'Žádné nepřečtené oznámení',
|
noUnreadNotifications: 'Žádné nepřečtené oznámení',
|
||||||
|
oldestFirst: 'Nejstarší',
|
||||||
openBoard_title: 'Otevřít tabuli',
|
openBoard_title: 'Otevřít tabuli',
|
||||||
optional_inline: 'volitelné',
|
optional_inline: 'volitelné',
|
||||||
organization: 'Společnost',
|
organization: 'Společnost',
|
||||||
phone: 'Telefon',
|
phone: 'Telefon',
|
||||||
preferences: 'Volby',
|
preferences: 'Volby',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: dejte Ctrl-V (Cmd-V na Mac) pro vložení přílohy ze schránky.',
|
'Tip: stisknutím Ctrl-V (Cmd-V na Mac) přidáte přílohu ze schránky.',
|
||||||
project: 'Projekt',
|
project: 'Projekt',
|
||||||
projectNotFound_title: 'Projekt nenalezen',
|
projectNotFound_title: 'Projekt nenalezen',
|
||||||
removeManager_title: 'Odstranit vedoucího',
|
removeManager_title: 'Odstranit vedoucího',
|
||||||
@@ -138,12 +142,14 @@ export default {
|
|||||||
searchLabels: 'Hledat štítky...',
|
searchLabels: 'Hledat štítky...',
|
||||||
searchMembers: 'Hledat členy...',
|
searchMembers: 'Hledat členy...',
|
||||||
searchUsers: 'Hledat uživatele...',
|
searchUsers: 'Hledat uživatele...',
|
||||||
|
searchCards: 'Hledat karty...',
|
||||||
seconds: 'Vteřin',
|
seconds: 'Vteřin',
|
||||||
selectBoard: 'Vybrat tabuli',
|
selectBoard: 'Vybrat tabuli',
|
||||||
selectList: 'Vybrat seznam',
|
selectList: 'Vybrat seznam',
|
||||||
selectPermissions_title: 'Vybrat oprávnění',
|
selectPermissions_title: 'Vybrat oprávnění',
|
||||||
selectProject: 'Vybrat projekt',
|
selectProject: 'Vybrat projekt',
|
||||||
settings: 'Nastavení',
|
settings: 'Nastavení',
|
||||||
|
sortList_title: 'Řadit podle',
|
||||||
stopwatch: 'Časovač',
|
stopwatch: 'Časovač',
|
||||||
subscribeToMyOwnCardsByDefault: 'Ve výchozím nastavení odebírat vlastní karty',
|
subscribeToMyOwnCardsByDefault: 'Ve výchozím nastavení odebírat vlastní karty',
|
||||||
taskActions_title: 'Akce na úkolu',
|
taskActions_title: 'Akce na úkolu',
|
||||||
@@ -199,8 +205,10 @@ export default {
|
|||||||
deleteTask: 'Smazat úkol',
|
deleteTask: 'Smazat úkol',
|
||||||
deleteTask_title: 'Smazat úkol',
|
deleteTask_title: 'Smazat úkol',
|
||||||
deleteUser: 'Smazat uživatele',
|
deleteUser: 'Smazat uživatele',
|
||||||
|
duplicate: 'Duplikovat',
|
||||||
|
duplicateCard_title: 'Duplikovat kartu',
|
||||||
edit: 'Upravit',
|
edit: 'Upravit',
|
||||||
editDueDate_title: 'Upravit termín do',
|
editDueDate_title: 'Upravit termín',
|
||||||
editDescription_title: 'Upravit popis',
|
editDescription_title: 'Upravit popis',
|
||||||
editEmail_title: 'Upravit e-mail',
|
editEmail_title: 'Upravit e-mail',
|
||||||
editInformation_title: 'Upravit informace',
|
editInformation_title: 'Upravit informace',
|
||||||
@@ -226,13 +234,14 @@ export default {
|
|||||||
removeMember: 'Odstranit člena',
|
removeMember: 'Odstranit člena',
|
||||||
save: 'Uložit',
|
save: 'Uložit',
|
||||||
showAllAttachments: 'Zozbrazit všechny přílohy ({{hidden}} skryté)',
|
showAllAttachments: 'Zozbrazit všechny přílohy ({{hidden}} skryté)',
|
||||||
showFewerAttachments: 'Zobrazit méně příloh',
|
|
||||||
showDetails: 'Zobrazit detaily',
|
showDetails: 'Zobrazit detaily',
|
||||||
|
showFewerAttachments: 'Zobrazit méně příloh',
|
||||||
|
sortList_title: 'Řadit podle',
|
||||||
start: 'Start',
|
start: 'Start',
|
||||||
stop: 'Stop',
|
stop: 'Stop',
|
||||||
subscribe: 'Odebírat',
|
subscribe: 'Odebírat',
|
||||||
unsubscribe: 'Neodebírat',
|
unsubscribe: 'Neodebírat',
|
||||||
uploadNewAvatar: 'Nahrát nového avatara',
|
uploadNewAvatar: 'Nahrát nový avatar',
|
||||||
uploadNewImage: 'Nahrát nový obrázek',
|
uploadNewImage: 'Nahrát nový obrázek',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export default {
|
export default {
|
||||||
translation: {
|
translation: {
|
||||||
common: {
|
common: {
|
||||||
emailOrUsername: 'Email nebo uživatelské jméno',
|
emailOrUsername: 'E-mail nebo uživatelské jméno',
|
||||||
invalidEmailOrUsername: 'Nesprávný email nebo uživatelské jméno',
|
invalidEmailOrUsername: 'Nesprávný e-mail nebo uživatelské jméno',
|
||||||
invalidPassword: 'Nesprávné heslo',
|
invalidPassword: 'Nesprávné heslo',
|
||||||
logInToPlanka: 'Přihlásit se do Planka',
|
logInToPlanka: 'Přihlásit se do Planka',
|
||||||
noInternetConnection: 'Bez připojení k internetu',
|
noInternetConnection: 'Bez připojení k internetu',
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export default {
|
|||||||
cardNotFound_title: 'Card Not Found',
|
cardNotFound_title: 'Card Not Found',
|
||||||
cardOrActionAreDeleted: 'Card or action are deleted.',
|
cardOrActionAreDeleted: 'Card or action are deleted.',
|
||||||
color: 'Color',
|
color: 'Color',
|
||||||
|
copy_inline: 'copy',
|
||||||
createBoard_title: 'Create Board',
|
createBoard_title: 'Create Board',
|
||||||
createLabel_title: 'Create Label',
|
createLabel_title: 'Create Label',
|
||||||
createNewOneOrSelectExistingOne: 'Create a new one or select<br />an existing one.',
|
createNewOneOrSelectExistingOne: 'Create a new one or select<br />an existing one.',
|
||||||
@@ -59,6 +60,7 @@ export default {
|
|||||||
currentPassword: 'Current password',
|
currentPassword: 'Current password',
|
||||||
dangerZone_title: 'Danger Zone',
|
dangerZone_title: 'Danger Zone',
|
||||||
date: 'Date',
|
date: 'Date',
|
||||||
|
dueDate: 'Due date',
|
||||||
dueDate_title: 'Due Date',
|
dueDate_title: 'Due Date',
|
||||||
deleteAttachment_title: 'Delete Attachment',
|
deleteAttachment_title: 'Delete Attachment',
|
||||||
deleteBoard_title: 'Delete Board',
|
deleteBoard_title: 'Delete Board',
|
||||||
@@ -107,10 +109,13 @@ export default {
|
|||||||
list: 'List',
|
list: 'List',
|
||||||
listActions_title: 'List Actions',
|
listActions_title: 'List Actions',
|
||||||
managers: 'Managers',
|
managers: 'Managers',
|
||||||
|
managerActions_title: 'Manager Actions',
|
||||||
members: 'Members',
|
members: 'Members',
|
||||||
|
memberActions_title: 'Member Actions',
|
||||||
minutes: 'Minutes',
|
minutes: 'Minutes',
|
||||||
moveCard_title: 'Move Card',
|
moveCard_title: 'Move Card',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
|
newestFirst: 'Newest first',
|
||||||
newEmail: 'New e-mail',
|
newEmail: 'New e-mail',
|
||||||
newPassword: 'New password',
|
newPassword: 'New password',
|
||||||
newUsername: 'New username',
|
newUsername: 'New username',
|
||||||
@@ -120,6 +125,7 @@ export default {
|
|||||||
noProjects: 'No projects',
|
noProjects: 'No projects',
|
||||||
notifications: 'Notifications',
|
notifications: 'Notifications',
|
||||||
noUnreadNotifications: 'No unread notifications.',
|
noUnreadNotifications: 'No unread notifications.',
|
||||||
|
oldestFirst: 'Oldest first',
|
||||||
openBoard_title: 'Open Board',
|
openBoard_title: 'Open Board',
|
||||||
optional_inline: 'optional',
|
optional_inline: 'optional',
|
||||||
organization: 'Organization',
|
organization: 'Organization',
|
||||||
@@ -134,12 +140,14 @@ export default {
|
|||||||
searchLabels: 'Search labels...',
|
searchLabels: 'Search labels...',
|
||||||
searchMembers: 'Search members...',
|
searchMembers: 'Search members...',
|
||||||
searchUsers: 'Search users...',
|
searchUsers: 'Search users...',
|
||||||
|
searchCards: 'Search cards...',
|
||||||
seconds: 'Seconds',
|
seconds: 'Seconds',
|
||||||
selectBoard: 'Select board',
|
selectBoard: 'Select board',
|
||||||
selectList: 'Select list',
|
selectList: 'Select list',
|
||||||
selectPermissions_title: 'Select Permissions',
|
selectPermissions_title: 'Select Permissions',
|
||||||
selectProject: 'Select project',
|
selectProject: 'Select project',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
|
sortList_title: 'Sort List',
|
||||||
stopwatch: 'Stopwatch',
|
stopwatch: 'Stopwatch',
|
||||||
subscribeToMyOwnCardsByDefault: 'Subscribe to my own cards by default',
|
subscribeToMyOwnCardsByDefault: 'Subscribe to my own cards by default',
|
||||||
taskActions_title: 'Task Actions',
|
taskActions_title: 'Task Actions',
|
||||||
@@ -196,6 +204,8 @@ export default {
|
|||||||
deleteTask: 'Delete task',
|
deleteTask: 'Delete task',
|
||||||
deleteTask_title: 'Delete Task',
|
deleteTask_title: 'Delete Task',
|
||||||
deleteUser: 'Delete user',
|
deleteUser: 'Delete user',
|
||||||
|
duplicate: 'Duplicate',
|
||||||
|
duplicateCard_title: 'Duplicate Card',
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
editDueDate_title: 'Edit Due Date',
|
editDueDate_title: 'Edit Due Date',
|
||||||
editDescription_title: 'Edit Description',
|
editDescription_title: 'Edit Description',
|
||||||
@@ -225,6 +235,7 @@ export default {
|
|||||||
showAllAttachments: 'Show all attachments ({{hidden}} hidden)',
|
showAllAttachments: 'Show all attachments ({{hidden}} hidden)',
|
||||||
showDetails: 'Show details',
|
showDetails: 'Show details',
|
||||||
showFewerAttachments: 'Show fewer attachments',
|
showFewerAttachments: 'Show fewer attachments',
|
||||||
|
sortList_title: 'Sort List',
|
||||||
start: 'Start',
|
start: 'Start',
|
||||||
stop: 'Stop',
|
stop: 'Stop',
|
||||||
subscribe: 'Subscribe',
|
subscribe: 'Subscribe',
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ export default {
|
|||||||
project: 'Projet',
|
project: 'Projet',
|
||||||
projectNotFound_title: 'Projet introuvable',
|
projectNotFound_title: 'Projet introuvable',
|
||||||
removeMember_title: 'Supprimer le membre',
|
removeMember_title: 'Supprimer le membre',
|
||||||
|
searchLabels: 'Chercher une étiquette...',
|
||||||
|
searchMembers: 'Chercher un membre...',
|
||||||
|
searchUsers: 'Chercher un utilisateur...',
|
||||||
|
searchCards: 'Chercher une carte...',
|
||||||
seconds: 'Secondes',
|
seconds: 'Secondes',
|
||||||
selectBoard: 'Sélectionner une carte',
|
selectBoard: 'Sélectionner une carte',
|
||||||
selectList: 'Sélectionner une liste',
|
selectList: 'Sélectionner une liste',
|
||||||
@@ -169,6 +173,7 @@ export default {
|
|||||||
deleteTask: 'Supprimer la tâche',
|
deleteTask: 'Supprimer la tâche',
|
||||||
deleteTask_title: 'Supprimer la tâche',
|
deleteTask_title: 'Supprimer la tâche',
|
||||||
deleteUser: "Supprimer l'utilisateur",
|
deleteUser: "Supprimer l'utilisateur",
|
||||||
|
duplicate: 'Dupliquer',
|
||||||
edit: 'Modifier',
|
edit: 'Modifier',
|
||||||
editDueDate_title: "Modifier la date d'échéance",
|
editDueDate_title: "Modifier la date d'échéance",
|
||||||
editDescription_title: 'Éditer la description',
|
editDescription_title: 'Éditer la description',
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
import dateFns from 'date-fns/locale/hu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dateFns,
|
||||||
|
|
||||||
|
format: {
|
||||||
|
date: 'yyyy.M.d.',
|
||||||
|
time: 'p',
|
||||||
|
dateTime: '$t(format:date) $t(format:time)',
|
||||||
|
longDate: 'MMM. d.',
|
||||||
|
longDateTime: 'MMMM. d. p',
|
||||||
|
fullDate: 'y. MMM d.',
|
||||||
|
fullDateTime: 'y. MMMM d. p',
|
||||||
|
},
|
||||||
|
|
||||||
|
translation: {
|
||||||
|
common: {
|
||||||
|
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
||||||
|
'Az összes változás automatikusan mentésre kerül a kapcsolat helyreállása után.',
|
||||||
|
areYouSureYouWantToDeleteThisAttachment: 'Biztosan törölni szeretné ezt a mellékletet?',
|
||||||
|
areYouSureYouWantToDeleteThisBoard: 'Biztosan törölni szeretné ezt a táblát?',
|
||||||
|
areYouSureYouWantToDeleteThisCard: 'Biztosan törölni szeretné ezt a kártyát?',
|
||||||
|
areYouSureYouWantToDeleteThisComment: 'Biztosan törölni szeretné ezt a megjegyzést?',
|
||||||
|
areYouSureYouWantToDeleteThisLabel: 'Biztosan törölni szeretné ezt a címkét?',
|
||||||
|
areYouSureYouWantToDeleteThisList: 'Biztosan törölni szeretné ezt a listát?',
|
||||||
|
areYouSureYouWantToDeleteThisProject: 'Biztosan törölni szeretné ezt a projektet?',
|
||||||
|
areYouSureYouWantToDeleteThisTask: 'Biztosan törölni szeretné ezt a feladatot?',
|
||||||
|
areYouSureYouWantToDeleteThisUser: 'Biztosan törölni szeretné ezt a felhasználót?',
|
||||||
|
areYouSureYouWantToLeaveBoard: 'Biztosan el akarja hagyni a táblát?',
|
||||||
|
areYouSureYouWantToLeaveProject: 'Biztosan el akarja hagyni a projektet?',
|
||||||
|
areYouSureYouWantToRemoveThisManagerFromProject:
|
||||||
|
'Biztosan eltávolítja ezt a vezetőt a projektből?',
|
||||||
|
areYouSureYouWantToRemoveThisMemberFromBoard: 'Biztosan eltávolítja ezt a tagot a tábláról?',
|
||||||
|
attachment: 'Melléklet',
|
||||||
|
attachments: 'Mellékletek',
|
||||||
|
authentication: 'Hitelesítés',
|
||||||
|
background: 'Háttér',
|
||||||
|
board: 'Tábla',
|
||||||
|
boardNotFound_title: 'Tábla nem található',
|
||||||
|
canComment: 'Hozzászólhat',
|
||||||
|
canEditContentOfBoard: 'Szerkesztheti a tábla tartalmát.',
|
||||||
|
canOnlyViewBoard: 'Csak megtekintheti a táblát.',
|
||||||
|
cardActions_title: 'Kártya műveletek',
|
||||||
|
cardNotFound_title: 'Kártya nem található',
|
||||||
|
cardOrActionAreDeleted: 'A kártya vagy művelet törölve lett.',
|
||||||
|
color: 'Szín',
|
||||||
|
createBoard_title: 'Tábla létrehozása',
|
||||||
|
createLabel_title: 'Címke létrehozása',
|
||||||
|
createNewOneOrSelectExistingOne: 'Hozzon létre egy újat, vagy válasszon ki egy meglévőt.',
|
||||||
|
createProject_title: 'Projekt létrehozása',
|
||||||
|
createTextFile_title: 'Szövegfájl létrehozása',
|
||||||
|
currentPassword: 'Jelenlegi jelszó',
|
||||||
|
dangerZone_title: 'Veszélyzóna',
|
||||||
|
date: 'Dátum',
|
||||||
|
dueDate_title: 'Esedékesség dátuma',
|
||||||
|
deleteAttachment_title: 'Melléklet törlése',
|
||||||
|
deleteBoard_title: 'Tábla törlése',
|
||||||
|
deleteCard_title: 'Kártya törlése',
|
||||||
|
deleteComment_title: 'Megjegyzés törlése',
|
||||||
|
deleteLabel_title: 'Címke törlése',
|
||||||
|
deleteList_title: 'Lista törlése',
|
||||||
|
deleteProject_title: 'Projekt törlése',
|
||||||
|
deleteTask_title: 'Feladat törlése',
|
||||||
|
deleteUser_title: 'Felhasználó törlése',
|
||||||
|
description: 'Leírás',
|
||||||
|
detectAutomatically: 'Automatikus érzékelés',
|
||||||
|
dropFileToUpload: 'Dobja ide a fájlt a feltöltéshez',
|
||||||
|
editor: 'Szerkesztő',
|
||||||
|
editAttachment_title: 'Melléklet szerkesztése',
|
||||||
|
editAvatar_title: 'Avatar szerkesztése',
|
||||||
|
editBoard_title: 'Tábla szerkesztése',
|
||||||
|
editDueDate_title: 'Esedékesség dátumának szerkesztése',
|
||||||
|
editEmail_title: 'E-mail szerkesztése',
|
||||||
|
editInformation_title: 'Információ szerkesztése',
|
||||||
|
editLabel_title: 'Címke szerkesztése',
|
||||||
|
editPassword_title: 'Jelszó szerkesztése',
|
||||||
|
editPermissions_title: 'Jogosultságok szerkesztése',
|
||||||
|
editStopwatch_title: 'Stopper szerkesztése',
|
||||||
|
editUsername_title: 'Felhasználónév szerkesztése',
|
||||||
|
email: 'E-mail',
|
||||||
|
emailAlreadyInUse: 'Az e-mail cím már használatban van',
|
||||||
|
enterCardTitle: 'Adja meg a kártya címét... [Ctrl+Enter] az automatikus megnyitáshoz.',
|
||||||
|
enterDescription: 'Adja meg a leírást...',
|
||||||
|
enterFilename: 'Adja meg a fájl nevét',
|
||||||
|
enterListTitle: 'Adja meg a lista címét...',
|
||||||
|
enterProjectTitle: 'Adja meg a projekt címét',
|
||||||
|
enterTaskDescription: 'Adja meg a feladat leírását...',
|
||||||
|
filterByLabels_title: 'Szűrés címkék alapján',
|
||||||
|
filterByMembers_title: 'Szűrés tagok alapján',
|
||||||
|
fromComputer_title: 'Számítógépről',
|
||||||
|
fromTrello: 'Trello-ról',
|
||||||
|
general: 'Általános',
|
||||||
|
hours: 'Órák',
|
||||||
|
importBoard_title: 'Tábla importálása',
|
||||||
|
invalidCurrentPassword: 'Érvénytelen jelenlegi jelszó',
|
||||||
|
labels: 'Címkék',
|
||||||
|
language: 'Nyelv',
|
||||||
|
leaveBoard_title: 'Tábla elhagyása',
|
||||||
|
leaveProject_title: 'Projekt elhagyása',
|
||||||
|
list: 'Lista',
|
||||||
|
listActions_title: 'Lista műveletek',
|
||||||
|
managers: 'Vezetők',
|
||||||
|
members: 'Tagok',
|
||||||
|
minutes: 'Percek',
|
||||||
|
moveCard_title: 'Kártya áthelyezése',
|
||||||
|
name: 'Név',
|
||||||
|
newEmail: 'Új e-mail',
|
||||||
|
newPassword: 'Új jelszó',
|
||||||
|
newUsername: 'Új felhasználónév',
|
||||||
|
noConnectionToServer: 'Nincs kapcsolat a szerverrel',
|
||||||
|
noBoards: 'Nincsenek táblák',
|
||||||
|
noLists: 'Nincsenek listák',
|
||||||
|
noProjects: 'Nincsenek projektek',
|
||||||
|
notifications: 'Értesítések',
|
||||||
|
noUnreadNotifications: 'Nincsenek olvasatlan értesítések.',
|
||||||
|
openBoard_title: 'Tábla megnyitása',
|
||||||
|
optional_inline: 'opcionális',
|
||||||
|
organization: 'Szervezet',
|
||||||
|
phone: 'Telefon',
|
||||||
|
preferences: 'Beállítások',
|
||||||
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
|
'Tipp: nyomja meg a Ctrl-V (Cmd-V a Mac-en) billentyűkombinációt a vágólapról történő melléklet hozzáadásához.',
|
||||||
|
project: 'Projekt',
|
||||||
|
projectNotFound_title: 'Projekt nem található',
|
||||||
|
removeManager_title: 'Vezető eltávolítása',
|
||||||
|
removeMember_title: 'Tag eltávolítása',
|
||||||
|
searchLabels: 'Címkék keresése...',
|
||||||
|
searchMembers: 'Tagok keresése...',
|
||||||
|
searchUsers: 'Felhasználók keresése...',
|
||||||
|
seconds: 'Másodpercek',
|
||||||
|
selectBoard: 'Válassza ki a táblát',
|
||||||
|
selectList: 'Válassza ki a listát',
|
||||||
|
selectPermissions_title: 'Jogosultságok kiválasztása',
|
||||||
|
selectProject: 'Válassza ki a projektet',
|
||||||
|
settings: 'Beállítások',
|
||||||
|
stopwatch: 'Stopper',
|
||||||
|
subscribeToMyOwnCardsByDefault: 'Alapértelmezés szerint feliratkozás a saját kártyáimra',
|
||||||
|
taskActions_title: 'Feladat műveletek',
|
||||||
|
tasks: 'Feladatok',
|
||||||
|
thereIsNoPreviewAvailableForThisAttachment: 'Nincs elérhető előnézet ehhez a melléklethez.',
|
||||||
|
time: 'Idő',
|
||||||
|
title: 'Cím',
|
||||||
|
userActions_title: 'Felhasználói műveletek',
|
||||||
|
userAddedThisCardToList:
|
||||||
|
'<0>{{user}}</0><1> hozzáadta ezt a kártyát a következőhöz: {{list}}</1>',
|
||||||
|
userLeftNewCommentToCard:
|
||||||
|
'{{user}} új kommentet hagyott itt: «{{comment}}» a következő kártyán: <2>{{card}}</2>',
|
||||||
|
userMovedCardFromListToList:
|
||||||
|
'{{user}} áthelyezte ezt a kártyát innen: {{fromList}} ide: {{toList}}',
|
||||||
|
userMovedThisCardFromListToList:
|
||||||
|
'<0>{{user}}</0><1> áthelyezte ezt a kártyát innen: {{fromList}} ide: {{toList}}</1>',
|
||||||
|
username: 'Felhasználónév',
|
||||||
|
usernameAlreadyInUse: 'A felhasználónév már használatban van',
|
||||||
|
users: 'Felhasználók',
|
||||||
|
version: 'Verzió',
|
||||||
|
viewer: 'Néző',
|
||||||
|
writeComment: 'Írjon egy megjegyzést...',
|
||||||
|
},
|
||||||
|
|
||||||
|
action: {
|
||||||
|
addAnotherCard: 'Új kártya hozzáadása',
|
||||||
|
addAnotherList: 'Új lista hozzáadása',
|
||||||
|
addAnotherTask: 'Új feladat hozzáadása',
|
||||||
|
addCard: 'Kártya hozzáadása',
|
||||||
|
addCard_title: 'Kártya hozzáadása',
|
||||||
|
addComment: 'Megjegyzés hozzáadása',
|
||||||
|
addList: 'Lista hozzáadása',
|
||||||
|
addMember: 'Tag hozzáadása',
|
||||||
|
addMoreDetailedDescription: 'További részletes leírás hozzáadása',
|
||||||
|
addTask: 'Feladat hozzáadása',
|
||||||
|
addToCard: 'Kártyához hozzáadása',
|
||||||
|
addUser: 'Felhasználó hozzáadása',
|
||||||
|
createBoard: 'Tábla létrehozása',
|
||||||
|
createFile: 'Fájl létrehozása',
|
||||||
|
createLabel: 'Címke létrehozása',
|
||||||
|
createNewLabel: 'Új címke létrehozása',
|
||||||
|
createProject: 'Projekt létrehozása',
|
||||||
|
delete: 'Törlés',
|
||||||
|
deleteAttachment: 'Melléklet törlése',
|
||||||
|
deleteAvatar: 'Avatar törlése',
|
||||||
|
deleteBoard: 'Tábla törlése',
|
||||||
|
deleteCard: 'Kártya törlése',
|
||||||
|
deleteCard_title: 'Kártya törlése',
|
||||||
|
deleteComment: 'Megjegyzés törlése',
|
||||||
|
deleteImage: 'Kép törlése',
|
||||||
|
deleteLabel: 'Címke törlése',
|
||||||
|
deleteList: 'Lista törlése',
|
||||||
|
deleteList_title: 'Lista törlése',
|
||||||
|
deleteProject: 'Projekt törlése',
|
||||||
|
deleteProject_title: 'Projekt törlése',
|
||||||
|
deleteTask: 'Feladat törlése',
|
||||||
|
deleteTask_title: 'Feladat törlése',
|
||||||
|
deleteUser: 'Felhasználó törlése',
|
||||||
|
edit: 'Szerkesztés',
|
||||||
|
editDueDate_title: 'Esedékesség dátumának szerkesztése',
|
||||||
|
editDescription_title: 'Leírás szerkesztése',
|
||||||
|
editEmail_title: 'E-mail szerkesztése',
|
||||||
|
editInformation_title: 'Információ szerkesztése',
|
||||||
|
editPassword_title: 'Jelszó szerkesztése',
|
||||||
|
editPermissions: 'Jogosultságok szerkesztése',
|
||||||
|
editStopwatch_title: 'Stopper szerkesztése',
|
||||||
|
editTitle_title: 'Cím szerkesztése',
|
||||||
|
editUsername_title: 'Felhasználónév szerkesztése',
|
||||||
|
hideDetails: 'Részletek elrejtése',
|
||||||
|
import: 'Importálás',
|
||||||
|
leaveBoard: 'Tábla elhagyása',
|
||||||
|
leaveProject: 'Projekt elhagyása',
|
||||||
|
logOut_title: 'Kijelentkezés',
|
||||||
|
makeCover_title: 'Borító készítése',
|
||||||
|
move: 'Áthelyezés',
|
||||||
|
moveCard_title: 'Kártya áthelyezése',
|
||||||
|
remove: 'Eltávolítás',
|
||||||
|
removeBackground: 'Háttér eltávolítása',
|
||||||
|
removeCover_title: 'Borító eltávolítása',
|
||||||
|
removeFromBoard: 'Eltávolítás a tábláról',
|
||||||
|
removeFromProject: 'Eltávolítás a projektről',
|
||||||
|
removeManager: 'Vezető eltávolítása',
|
||||||
|
removeMember: 'Tag eltávolítása',
|
||||||
|
save: 'Mentés',
|
||||||
|
showAllAttachments: 'Összes melléklet megjelenítése ({{hidden}} rejtve)',
|
||||||
|
showDetails: 'Részletek megjelenítése',
|
||||||
|
showFewerAttachments: 'Kevesebb melléklet megjelenítése',
|
||||||
|
start: 'Indítás',
|
||||||
|
stop: 'Megállítás',
|
||||||
|
subscribe: 'Feliratkozás',
|
||||||
|
unsubscribe: 'Leiratkozás',
|
||||||
|
uploadNewAvatar: 'Új avatar feltöltése',
|
||||||
|
uploadNewImage: 'Új kép feltöltése',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import login from './login';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
language: 'hu',
|
||||||
|
country: 'hu',
|
||||||
|
name: 'Magyar',
|
||||||
|
embeddedLocale: login,
|
||||||
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export default {
|
||||||
|
translation: {
|
||||||
|
common: {
|
||||||
|
emailOrUsername: 'E-mail vagy felhasználó',
|
||||||
|
invalidEmailOrUsername: 'Érvénytelen e-mail vagy felhasználó',
|
||||||
|
invalidPassword: 'Érvénytelen jelszó',
|
||||||
|
logInToPlanka: 'Plankába belépés',
|
||||||
|
noInternetConnection: 'Nincs internet kapcsolat',
|
||||||
|
pageNotFound_title: 'Az oldal nem található',
|
||||||
|
password: 'Jelszó',
|
||||||
|
projectManagement: 'Projektmenedzsment',
|
||||||
|
serverConnectionFailed: 'A szerverkapcsolat sikertelen',
|
||||||
|
unknownError: 'Ismeretlen hiba, próbáld meg később újra',
|
||||||
|
useSingleSignOn: 'Egyszeri bejelentkezés használata',
|
||||||
|
},
|
||||||
|
|
||||||
|
action: {
|
||||||
|
logIn: 'Belépés',
|
||||||
|
logInWithSSO: 'Belépés SSO-val',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import bg from './bg';
|
||||||
import cs from './cs';
|
import cs from './cs';
|
||||||
import da from './da';
|
import da from './da';
|
||||||
import de from './de';
|
import de from './de';
|
||||||
import en from './en';
|
import en from './en';
|
||||||
import es from './es';
|
import es from './es';
|
||||||
import fr from './fr';
|
import fr from './fr';
|
||||||
|
import hu from './hu';
|
||||||
import id from './id';
|
import id from './id';
|
||||||
import it from './it';
|
import it from './it';
|
||||||
import ja from './ja';
|
import ja from './ja';
|
||||||
@@ -21,12 +23,14 @@ import uz from './uz';
|
|||||||
import zh from './zh';
|
import zh from './zh';
|
||||||
|
|
||||||
const locales = [
|
const locales = [
|
||||||
|
bg,
|
||||||
cs,
|
cs,
|
||||||
da,
|
da,
|
||||||
de,
|
de,
|
||||||
en,
|
en,
|
||||||
es,
|
es,
|
||||||
fr,
|
fr,
|
||||||
|
hu,
|
||||||
id,
|
id,
|
||||||
it,
|
it,
|
||||||
ja,
|
ja,
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ export default {
|
|||||||
date: 'd/M/yyyy',
|
date: 'd/M/yyyy',
|
||||||
time: 'p',
|
time: 'p',
|
||||||
dateTime: '$t(format:date) $t(format:time)',
|
dateTime: '$t(format:date) $t(format:time)',
|
||||||
longDate: 'MMM d',
|
longDate: 'd MMM',
|
||||||
longDateTime: "MMMM d 'at' p",
|
longDateTime: "d MMMM 'alle' p",
|
||||||
fullDate: 'MMM d, y',
|
fullDate: 'd MMM, y',
|
||||||
fullDateTime: "MMMM d, y 'at' p",
|
fullDateTime: "d MMMM, y 'alle' p",
|
||||||
},
|
},
|
||||||
|
|
||||||
translation: {
|
translation: {
|
||||||
@@ -21,7 +21,7 @@ export default {
|
|||||||
administrator: 'Amministratore',
|
administrator: 'Amministratore',
|
||||||
all: 'tutto',
|
all: 'tutto',
|
||||||
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
||||||
'All changes will be automatically saved<br />after connection restored.',
|
'Tutte le modifiche verranno salvate<br />al ripristino della connessione.',
|
||||||
areYouSureYouWantToDeleteThisAttachment: 'Sei sicuro di voler eliminare questo allegato?',
|
areYouSureYouWantToDeleteThisAttachment: 'Sei sicuro di voler eliminare questo allegato?',
|
||||||
areYouSureYouWantToDeleteThisBoard: 'Sei sicuro di voler eliminare questa bacheca?',
|
areYouSureYouWantToDeleteThisBoard: 'Sei sicuro di voler eliminare questa bacheca?',
|
||||||
areYouSureYouWantToDeleteThisCard: 'Sei sicuro di voler eliminare questa card?',
|
areYouSureYouWantToDeleteThisCard: 'Sei sicuro di voler eliminare questa card?',
|
||||||
@@ -50,6 +50,7 @@ export default {
|
|||||||
cardNotFound_title: 'Card non trovata',
|
cardNotFound_title: 'Card non trovata',
|
||||||
cardOrActionAreDeleted: "La card o l'azione vengono eliminate.",
|
cardOrActionAreDeleted: "La card o l'azione vengono eliminate.",
|
||||||
color: 'Colore',
|
color: 'Colore',
|
||||||
|
copy_inline: 'copia',
|
||||||
createBoard_title: 'Crea Bacheca',
|
createBoard_title: 'Crea Bacheca',
|
||||||
createLabel_title: 'Crea Etichetta',
|
createLabel_title: 'Crea Etichetta',
|
||||||
createNewOneOrSelectExistingOne: 'Crea nuovo o seleziona<br />esistente.',
|
createNewOneOrSelectExistingOne: 'Crea nuovo o seleziona<br />esistente.',
|
||||||
@@ -58,6 +59,7 @@ export default {
|
|||||||
currentPassword: 'Password corrente',
|
currentPassword: 'Password corrente',
|
||||||
dangerZone_title: 'Zona pericolosa',
|
dangerZone_title: 'Zona pericolosa',
|
||||||
date: 'Data',
|
date: 'Data',
|
||||||
|
dueDate: 'Data di scadenza',
|
||||||
dueDate_title: 'Data di scadenza',
|
dueDate_title: 'Data di scadenza',
|
||||||
deleteAttachment_title: 'Elimina Allegato',
|
deleteAttachment_title: 'Elimina Allegato',
|
||||||
deleteBoard_title: 'Elimina Bacheca',
|
deleteBoard_title: 'Elimina Bacheca',
|
||||||
@@ -94,8 +96,10 @@ export default {
|
|||||||
filterByLabels_title: 'Filtra per Etichetta',
|
filterByLabels_title: 'Filtra per Etichetta',
|
||||||
filterByMembers_title: 'Filtra per Membro',
|
filterByMembers_title: 'Filtra per Membro',
|
||||||
fromComputer_title: 'Dal Computer',
|
fromComputer_title: 'Dal Computer',
|
||||||
|
fromTrello: 'Da Trello',
|
||||||
general: 'Generale',
|
general: 'Generale',
|
||||||
hours: 'Ore',
|
hours: 'Ore',
|
||||||
|
importBoard_title: 'Importa Board',
|
||||||
invalidCurrentPassword: 'Password corrente non valida',
|
invalidCurrentPassword: 'Password corrente non valida',
|
||||||
labels: 'Etichette',
|
labels: 'Etichette',
|
||||||
language: 'Lingua',
|
language: 'Lingua',
|
||||||
@@ -108,6 +112,7 @@ export default {
|
|||||||
minutes: 'Minuti',
|
minutes: 'Minuti',
|
||||||
moveCard_title: 'Sposta card',
|
moveCard_title: 'Sposta card',
|
||||||
name: 'Nome',
|
name: 'Nome',
|
||||||
|
newestFirst: 'Dal più recente',
|
||||||
newEmail: 'Nuova e-mail',
|
newEmail: 'Nuova e-mail',
|
||||||
newPassword: 'Nuova password',
|
newPassword: 'Nuova password',
|
||||||
newUsername: 'Nuovo username',
|
newUsername: 'Nuovo username',
|
||||||
@@ -117,13 +122,14 @@ export default {
|
|||||||
noProjects: 'No progetti',
|
noProjects: 'No progetti',
|
||||||
notifications: 'Notifiche',
|
notifications: 'Notifiche',
|
||||||
noUnreadNotifications: 'Nessuna notifica non letta.',
|
noUnreadNotifications: 'Nessuna notifica non letta.',
|
||||||
|
oldestFirst: 'Dal meno recente',
|
||||||
openBoard_title: 'Bacheca aperta',
|
openBoard_title: 'Bacheca aperta',
|
||||||
optional_inline: 'opzionale',
|
optional_inline: 'opzionale',
|
||||||
organization: 'Organizazzione',
|
organization: 'Organizazzione',
|
||||||
phone: 'Telefono',
|
phone: 'Telefono',
|
||||||
preferences: 'Preferenze',
|
preferences: 'Preferenze',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: prmi Ctrl-V (Cmd-V on Mac) per aggiungere un allegato dalla clipboard.',
|
'Consiglio: premi Ctrl-V (Cmd-V on Mac) per aggiungere un allegato dalla clipboard.',
|
||||||
project: 'Progetto',
|
project: 'Progetto',
|
||||||
projectNotFound_title: 'Progetto non trovato',
|
projectNotFound_title: 'Progetto non trovato',
|
||||||
removeManager_title: 'Rimuovi Manager',
|
removeManager_title: 'Rimuovi Manager',
|
||||||
@@ -131,12 +137,14 @@ export default {
|
|||||||
searchLabels: 'Cerca etichette...',
|
searchLabels: 'Cerca etichette...',
|
||||||
searchMembers: 'Cerca membri...',
|
searchMembers: 'Cerca membri...',
|
||||||
searchUsers: 'Cerca utenti...',
|
searchUsers: 'Cerca utenti...',
|
||||||
|
searchCards: 'Cerca cards...',
|
||||||
seconds: 'Secondi',
|
seconds: 'Secondi',
|
||||||
selectBoard: 'Seleziona bacheca',
|
selectBoard: 'Seleziona bacheca',
|
||||||
selectList: 'Seleziona lista',
|
selectList: 'Seleziona lista',
|
||||||
selectPermissions_title: 'Seleziona Permessi',
|
selectPermissions_title: 'Seleziona Permessi',
|
||||||
selectProject: 'Seleziona progetto',
|
selectProject: 'Seleziona progetto',
|
||||||
settings: 'Impostazioni',
|
settings: 'Impostazioni',
|
||||||
|
sortList_title: 'Ordina',
|
||||||
stopwatch: 'Timer',
|
stopwatch: 'Timer',
|
||||||
subscribeToMyOwnCardsByDefault: 'Abbonati alle mie card per impostazione predefinita',
|
subscribeToMyOwnCardsByDefault: 'Abbonati alle mie card per impostazione predefinita',
|
||||||
taskActions_title: 'Task Azioni',
|
taskActions_title: 'Task Azioni',
|
||||||
@@ -193,6 +201,8 @@ export default {
|
|||||||
deleteTask: 'Elimina task',
|
deleteTask: 'Elimina task',
|
||||||
deleteTask_title: 'Elimina Task',
|
deleteTask_title: 'Elimina Task',
|
||||||
deleteUser: 'Elimina utente',
|
deleteUser: 'Elimina utente',
|
||||||
|
duplicate: 'Duplica',
|
||||||
|
duplicateCard_title: 'Duplica Card',
|
||||||
edit: 'Modifica',
|
edit: 'Modifica',
|
||||||
editDueDate_title: 'Modifica data di scadenza',
|
editDueDate_title: 'Modifica data di scadenza',
|
||||||
editDescription_title: 'Modifica Descrizione',
|
editDescription_title: 'Modifica Descrizione',
|
||||||
@@ -204,9 +214,10 @@ export default {
|
|||||||
editTitle_title: 'Modifica Titolo',
|
editTitle_title: 'Modifica Titolo',
|
||||||
editUsername_title: 'Modifica Username',
|
editUsername_title: 'Modifica Username',
|
||||||
hideDetails: 'Nascondi dettagli',
|
hideDetails: 'Nascondi dettagli',
|
||||||
|
import: 'Importa',
|
||||||
leaveBoard: 'Lascia bacheca',
|
leaveBoard: 'Lascia bacheca',
|
||||||
leaveProject: 'Lascia progetto',
|
leaveProject: 'Lascia progetto',
|
||||||
logOut_title: 'Log Out',
|
logOut_title: 'Disconnettiti',
|
||||||
makeCover_title: 'Crea Cover',
|
makeCover_title: 'Crea Cover',
|
||||||
move: 'Muovi',
|
move: 'Muovi',
|
||||||
moveCard_title: 'Muovi Card',
|
moveCard_title: 'Muovi Card',
|
||||||
@@ -221,6 +232,7 @@ export default {
|
|||||||
showAllAttachments: 'Mostra tutti gli allegati ({{hidden}} hidden)',
|
showAllAttachments: 'Mostra tutti gli allegati ({{hidden}} hidden)',
|
||||||
showDetails: 'Mostra dettagli',
|
showDetails: 'Mostra dettagli',
|
||||||
showFewerAttachments: 'Mostra meno allegati',
|
showFewerAttachments: 'Mostra meno allegati',
|
||||||
|
sortList_title: 'Ordina',
|
||||||
start: 'Inizio',
|
start: 'Inizio',
|
||||||
stop: 'Ferma',
|
stop: 'Ferma',
|
||||||
subscribe: 'Iscriviti',
|
subscribe: 'Iscriviti',
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ export default {
|
|||||||
searchLabels: 'Поиск меток...',
|
searchLabels: 'Поиск меток...',
|
||||||
searchMembers: 'Поиск участников...',
|
searchMembers: 'Поиск участников...',
|
||||||
searchUsers: 'Поиск пользователей...',
|
searchUsers: 'Поиск пользователей...',
|
||||||
|
searchCards: 'Поиск карточек...',
|
||||||
seconds: 'Секунды',
|
seconds: 'Секунды',
|
||||||
selectBoard: 'Выберите доску',
|
selectBoard: 'Выберите доску',
|
||||||
selectList: 'Выберите список',
|
selectList: 'Выберите список',
|
||||||
@@ -199,6 +200,8 @@ export default {
|
|||||||
deleteProject: 'Удалить проект',
|
deleteProject: 'Удалить проект',
|
||||||
deleteTask: 'Удалить задачу',
|
deleteTask: 'Удалить задачу',
|
||||||
deleteUser: 'Удалить пользователя',
|
deleteUser: 'Удалить пользователя',
|
||||||
|
duplicate: 'Дублировать',
|
||||||
|
duplicateCard_title: 'Дублировать карточку',
|
||||||
edit: 'Изменить',
|
edit: 'Изменить',
|
||||||
editBackground: 'Изменить фон',
|
editBackground: 'Изменить фон',
|
||||||
editDueDate: 'Изменить срок',
|
editDueDate: 'Изменить срок',
|
||||||
@@ -230,6 +233,7 @@ export default {
|
|||||||
showAllAttachments: 'Показать все вложения ({{hidden}} скрыто)',
|
showAllAttachments: 'Показать все вложения ({{hidden}} скрыто)',
|
||||||
showDetails: 'Показать подробности',
|
showDetails: 'Показать подробности',
|
||||||
showFewerAttachments: 'Показать меньше вложений',
|
showFewerAttachments: 'Показать меньше вложений',
|
||||||
|
sortList_title: 'Сортировать список',
|
||||||
start: 'Начать',
|
start: 'Начать',
|
||||||
stop: 'Остановить',
|
stop: 'Остановить',
|
||||||
subscribe: 'Подписаться',
|
subscribe: 'Подписаться',
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { attr, fk, many } from 'redux-orm';
|
|||||||
import BaseModel from './BaseModel';
|
import BaseModel from './BaseModel';
|
||||||
import ActionTypes from '../constants/ActionTypes';
|
import ActionTypes from '../constants/ActionTypes';
|
||||||
|
|
||||||
|
import User from './User';
|
||||||
|
import Label from './Label';
|
||||||
|
|
||||||
export default class extends BaseModel {
|
export default class extends BaseModel {
|
||||||
static modelName = 'Board';
|
static modelName = 'Board';
|
||||||
|
|
||||||
@@ -25,6 +28,9 @@ export default class extends BaseModel {
|
|||||||
}),
|
}),
|
||||||
filterUsers: many('User', 'filterBoards'),
|
filterUsers: many('User', 'filterBoards'),
|
||||||
filterLabels: many('Label', 'filterBoards'),
|
filterLabels: many('Label', 'filterBoards'),
|
||||||
|
filterText: attr({
|
||||||
|
getDefault: () => '',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static reducer({ type, payload }, Board) {
|
static reducer({ type, payload }, Board) {
|
||||||
@@ -167,6 +173,47 @@ export default class extends BaseModel {
|
|||||||
Board.withId(payload.boardId).filterLabels.remove(payload.id);
|
Board.withId(payload.boardId).filterLabels.remove(payload.id);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.TEXT_FILTER_IN_CURRENT_BOARD: {
|
||||||
|
const board = Board.withId(payload.boardId);
|
||||||
|
let filterText = payload.text;
|
||||||
|
const posSpace = filterText.indexOf(' ');
|
||||||
|
|
||||||
|
// Shortcut to user filters
|
||||||
|
const posAT = filterText.indexOf('@');
|
||||||
|
if (posAT >= 0 && posSpace > 0 && posAT < posSpace) {
|
||||||
|
const userId = User.findUsersFromText(
|
||||||
|
filterText.substring(posAT + 1, posSpace),
|
||||||
|
board.memberships.toModelArray().map((membership) => membership.user),
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
userId &&
|
||||||
|
board.filterUsers.toModelArray().filter((user) => user.id === userId).length === 0
|
||||||
|
) {
|
||||||
|
board.filterUsers.add(userId);
|
||||||
|
filterText = filterText.substring(0, posAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcut to label filters
|
||||||
|
const posSharp = filterText.indexOf('#');
|
||||||
|
if (posSharp >= 0 && posSpace > 0 && posSharp < posSpace) {
|
||||||
|
const labelId = Label.findLabelsFromText(
|
||||||
|
filterText.substring(posSharp + 1, posSpace),
|
||||||
|
board.labels.toModelArray(),
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
labelId &&
|
||||||
|
board.filterLabels.toModelArray().filter((label) => label.id === labelId).length === 0
|
||||||
|
) {
|
||||||
|
board.filterLabels.add(labelId);
|
||||||
|
filterText = filterText.substring(0, posSharp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
board.update({ filterText });
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import pick from 'lodash/pick';
|
||||||
import { attr, fk, many, oneToOne } from 'redux-orm';
|
import { attr, fk, many, oneToOne } from 'redux-orm';
|
||||||
|
|
||||||
import BaseModel from './BaseModel';
|
import BaseModel from './BaseModel';
|
||||||
@@ -13,6 +14,11 @@ export default class extends BaseModel {
|
|||||||
position: attr(),
|
position: attr(),
|
||||||
name: attr(),
|
name: attr(),
|
||||||
description: attr(),
|
description: attr(),
|
||||||
|
creatorUserId: oneToOne({
|
||||||
|
to: 'User',
|
||||||
|
as: 'creatorUser',
|
||||||
|
relatedName: 'ownCards',
|
||||||
|
}),
|
||||||
dueDate: attr(),
|
dueDate: attr(),
|
||||||
stopwatch: attr(),
|
stopwatch: attr(),
|
||||||
isSubscribed: attr({
|
isSubscribed: attr({
|
||||||
@@ -163,9 +169,16 @@ export default class extends BaseModel {
|
|||||||
Card.withId(payload.cardLabel.cardId).labels.remove(payload.cardLabel.labelId);
|
Card.withId(payload.cardLabel.cardId).labels.remove(payload.cardLabel.labelId);
|
||||||
} catch {} // eslint-disable-line no-empty
|
} catch {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ActionTypes.LIST_SORT__SUCCESS:
|
||||||
|
case ActionTypes.LIST_SORT_HANDLE:
|
||||||
|
case ActionTypes.NOTIFICATION_CREATE_HANDLE:
|
||||||
|
payload.cards.forEach((card) => {
|
||||||
|
Card.upsert(card);
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ActionTypes.CARD_CREATE:
|
case ActionTypes.CARD_CREATE:
|
||||||
case ActionTypes.CARD_CREATE_HANDLE:
|
|
||||||
case ActionTypes.CARD_UPDATE__SUCCESS:
|
case ActionTypes.CARD_UPDATE__SUCCESS:
|
||||||
case ActionTypes.CARD_UPDATE_HANDLE:
|
case ActionTypes.CARD_UPDATE_HANDLE:
|
||||||
Card.upsert(payload.card);
|
Card.upsert(payload.card);
|
||||||
@@ -176,10 +189,63 @@ export default class extends BaseModel {
|
|||||||
Card.upsert(payload.card);
|
Card.upsert(payload.card);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.CARD_CREATE_HANDLE: {
|
||||||
|
const cardModel = Card.upsert(payload.card);
|
||||||
|
|
||||||
|
payload.cardMemberships.forEach(({ userId }) => {
|
||||||
|
cardModel.users.add(userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
payload.cardLabels.forEach(({ labelId }) => {
|
||||||
|
cardModel.labels.add(labelId);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ActionTypes.CARD_UPDATE:
|
case ActionTypes.CARD_UPDATE:
|
||||||
Card.withId(payload.id).update(payload.data);
|
Card.withId(payload.id).update(payload.data);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.CARD_DUPLICATE: {
|
||||||
|
const cardModel = Card.withId(payload.id);
|
||||||
|
|
||||||
|
const nextCardModel = Card.upsert({
|
||||||
|
...pick(cardModel.ref, [
|
||||||
|
'boardId',
|
||||||
|
'listId',
|
||||||
|
'position',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'dueDate',
|
||||||
|
'stopwatch',
|
||||||
|
]),
|
||||||
|
...payload.card,
|
||||||
|
});
|
||||||
|
|
||||||
|
cardModel.users.toRefArray().forEach(({ id }) => {
|
||||||
|
nextCardModel.users.add(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
cardModel.labels.toRefArray().forEach(({ id }) => {
|
||||||
|
nextCardModel.labels.add(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ActionTypes.CARD_DUPLICATE__SUCCESS: {
|
||||||
|
Card.withId(payload.localId).deleteWithRelated();
|
||||||
|
const cardModel = Card.upsert(payload.card);
|
||||||
|
|
||||||
|
payload.cardMemberships.forEach(({ userId }) => {
|
||||||
|
cardModel.users.add(userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
payload.cardLabels.forEach(({ labelId }) => {
|
||||||
|
cardModel.labels.add(labelId);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ActionTypes.CARD_DELETE:
|
case ActionTypes.CARD_DELETE:
|
||||||
Card.withId(payload.id).deleteWithRelated();
|
Card.withId(payload.id).deleteWithRelated();
|
||||||
|
|
||||||
@@ -229,12 +295,6 @@ export default class extends BaseModel {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ActionTypes.NOTIFICATION_CREATE_HANDLE:
|
|
||||||
payload.cards.forEach((card) => {
|
|
||||||
Card.upsert(card);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,4 +80,16 @@ export default class extends BaseModel {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static findLabelsFromText(filterText, labels) {
|
||||||
|
const selectLabel = filterText.toLocaleLowerCase();
|
||||||
|
const matchingLabels = labels.filter((label) =>
|
||||||
|
label.name ? label.name.toLocaleLowerCase().startsWith(selectLabel) : false,
|
||||||
|
);
|
||||||
|
if (matchingLabels.length === 1) {
|
||||||
|
// Appens the user to the filter
|
||||||
|
return matchingLabels[0].id;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { attr, fk } from 'redux-orm';
|
import { attr, fk } from 'redux-orm';
|
||||||
|
|
||||||
import BaseModel from './BaseModel';
|
import BaseModel from './BaseModel';
|
||||||
|
import User from './User';
|
||||||
import ActionTypes from '../constants/ActionTypes';
|
import ActionTypes from '../constants/ActionTypes';
|
||||||
|
|
||||||
export default class extends BaseModel {
|
export default class extends BaseModel {
|
||||||
@@ -50,6 +51,8 @@ export default class extends BaseModel {
|
|||||||
case ActionTypes.LIST_CREATE_HANDLE:
|
case ActionTypes.LIST_CREATE_HANDLE:
|
||||||
case ActionTypes.LIST_UPDATE__SUCCESS:
|
case ActionTypes.LIST_UPDATE__SUCCESS:
|
||||||
case ActionTypes.LIST_UPDATE_HANDLE:
|
case ActionTypes.LIST_UPDATE_HANDLE:
|
||||||
|
case ActionTypes.LIST_SORT__SUCCESS:
|
||||||
|
case ActionTypes.LIST_SORT_HANDLE:
|
||||||
List.upsert(payload.list);
|
List.upsert(payload.list);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -87,6 +90,41 @@ export default class extends BaseModel {
|
|||||||
getFilteredOrderedCardsModelArray() {
|
getFilteredOrderedCardsModelArray() {
|
||||||
let cardModels = this.getOrderedCardsQuerySet().toModelArray();
|
let cardModels = this.getOrderedCardsQuerySet().toModelArray();
|
||||||
|
|
||||||
|
const { filterText } = this.board;
|
||||||
|
|
||||||
|
if (filterText !== '') {
|
||||||
|
let re = null;
|
||||||
|
const posSpace = filterText.indexOf(' ');
|
||||||
|
|
||||||
|
if (filterText.startsWith('/')) {
|
||||||
|
re = new RegExp(filterText.substring(1), 'i');
|
||||||
|
}
|
||||||
|
let doRegularSearch = true;
|
||||||
|
if (re) {
|
||||||
|
cardModels = cardModels.filter(
|
||||||
|
(cardModel) => re.test(cardModel.name) || re.test(cardModel?.description),
|
||||||
|
);
|
||||||
|
doRegularSearch = false;
|
||||||
|
} else if (filterText.startsWith('!') && posSpace > 0) {
|
||||||
|
const creatorUserId = User.findUsersFromText(
|
||||||
|
filterText.substring(1, posSpace),
|
||||||
|
this.board.memberships.toModelArray().map((membership) => membership.user),
|
||||||
|
);
|
||||||
|
if (creatorUserId != null) {
|
||||||
|
doRegularSearch = false;
|
||||||
|
cardModels = cardModels.filter((cardModel) => cardModel.creatorUser.id === creatorUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doRegularSearch) {
|
||||||
|
const lowerCasedFilter = filterText.toLocaleLowerCase();
|
||||||
|
cardModels = cardModels.filter(
|
||||||
|
(cardModel) =>
|
||||||
|
cardModel.name.toLocaleLowerCase().indexOf(lowerCasedFilter) >= 0 ||
|
||||||
|
cardModel.description?.toLocaleLowerCase().indexOf(lowerCasedFilter) >= 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const filterUserIds = this.board.filterUsers.toRefArray().map((user) => user.id);
|
const filterUserIds = this.board.filterUsers.toRefArray().map((user) => user.id);
|
||||||
const filterLabelIds = this.board.filterLabels.toRefArray().map((label) => label.id);
|
const filterLabelIds = this.board.filterLabels.toRefArray().map((label) => label.id);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { attr, fk } from 'redux-orm';
|
import { attr, fk } from 'redux-orm';
|
||||||
|
|
||||||
|
import { createLocalId } from '../utils/local-id';
|
||||||
import BaseModel from './BaseModel';
|
import BaseModel from './BaseModel';
|
||||||
import ActionTypes from '../constants/ActionTypes';
|
import ActionTypes from '../constants/ActionTypes';
|
||||||
|
|
||||||
@@ -44,10 +45,24 @@ export default class extends BaseModel {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case ActionTypes.BOARD_FETCH__SUCCESS:
|
case ActionTypes.BOARD_FETCH__SUCCESS:
|
||||||
|
case ActionTypes.CARD_CREATE_HANDLE:
|
||||||
|
case ActionTypes.CARD_DUPLICATE__SUCCESS:
|
||||||
payload.tasks.forEach((task) => {
|
payload.tasks.forEach((task) => {
|
||||||
Task.upsert(task);
|
Task.upsert(task);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ActionTypes.CARD_DUPLICATE:
|
||||||
|
payload.taskIds.forEach((taskId, index) => {
|
||||||
|
const taskModel = Task.withId(taskId);
|
||||||
|
|
||||||
|
Task.upsert({
|
||||||
|
...taskModel.ref,
|
||||||
|
id: `${createLocalId()}-${index}`, // TODO: hack?
|
||||||
|
cardId: payload.card.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ActionTypes.TASK_CREATE:
|
case ActionTypes.TASK_CREATE:
|
||||||
case ActionTypes.TASK_CREATE_HANDLE:
|
case ActionTypes.TASK_CREATE_HANDLE:
|
||||||
|
|||||||
@@ -359,4 +359,18 @@ export default class extends BaseModel {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static findUsersFromText(filterText, users) {
|
||||||
|
const selectUser = filterText.toLocaleLowerCase();
|
||||||
|
const matchingUsers = users.filter(
|
||||||
|
(user) =>
|
||||||
|
user.name.toLocaleLowerCase().startsWith(selectUser) ||
|
||||||
|
user.username.toLocaleLowerCase().startsWith(selectUser),
|
||||||
|
);
|
||||||
|
if (matchingUsers.length === 1) {
|
||||||
|
// Appens the user to the filter
|
||||||
|
return matchingUsers[0].id;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { call, fork, join, put, select, take } from 'redux-saga/effects';
|
import { call, fork, join, put, select, take } from 'redux-saga/effects';
|
||||||
|
|
||||||
import selectors from '../../selectors';
|
import selectors from '../../selectors';
|
||||||
import actions from '../../actions';
|
import entryActions from '../../entry-actions';
|
||||||
import { removeAccessToken } from '../../utils/access-token-storage';
|
|
||||||
import ErrorCodes from '../../constants/ErrorCodes';
|
import ErrorCodes from '../../constants/ErrorCodes';
|
||||||
|
|
||||||
let lastRequestTask;
|
let lastRequestTask;
|
||||||
@@ -22,8 +21,7 @@ function* queueRequest(method, ...args) {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === ErrorCodes.UNAUTHORIZED) {
|
if (error.code === ErrorCodes.UNAUTHORIZED) {
|
||||||
yield call(removeAccessToken);
|
yield put(entryActions.logout(false));
|
||||||
yield put(actions.logout()); // TODO: next url
|
|
||||||
yield take();
|
yield take();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import request from '../request';
|
|||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import actions from '../../../actions';
|
import actions from '../../../actions';
|
||||||
import api from '../../../api';
|
import api from '../../../api';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
import { createLocalId } from '../../../utils/local-id';
|
import { createLocalId } from '../../../utils/local-id';
|
||||||
|
|
||||||
export function* createCard(listId, data, autoOpen) {
|
export function* createCard(listId, data, autoOpen) {
|
||||||
@@ -41,8 +42,23 @@ export function* createCard(listId, data, autoOpen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* handleCardCreate(card) {
|
export function* handleCardCreate({ id }) {
|
||||||
yield put(actions.handleCardCreate(card));
|
let card;
|
||||||
|
let cardMemberships;
|
||||||
|
let cardLabels;
|
||||||
|
let tasks;
|
||||||
|
let attachments;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({
|
||||||
|
item: card,
|
||||||
|
included: { cardMemberships, cardLabels, tasks, attachments },
|
||||||
|
} = yield call(request, api.getCard, id));
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(actions.handleCardCreate(card, cardMemberships, cardLabels, tasks, attachments));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* updateCard(id, data) {
|
export function* updateCard(id, data) {
|
||||||
@@ -70,7 +86,7 @@ export function* handleCardUpdate(card) {
|
|||||||
yield put(actions.handleCardUpdate(card));
|
yield put(actions.handleCardUpdate(card));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* moveCard(id, listId, index) {
|
export function* moveCard(id, listId, index = 0) {
|
||||||
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
||||||
|
|
||||||
yield call(updateCard, id, {
|
yield call(updateCard, id, {
|
||||||
@@ -85,7 +101,7 @@ export function* moveCurrentCard(listId, index) {
|
|||||||
yield call(moveCard, cardId, listId, index);
|
yield call(moveCard, cardId, listId, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* transferCard(id, boardId, listId, index) {
|
export function* transferCard(id, boardId, listId, index = 0) {
|
||||||
const { cardId: currentCardId, boardId: currentBoardId } = yield select(selectors.selectPath);
|
const { cardId: currentCardId, boardId: currentBoardId } = yield select(selectors.selectPath);
|
||||||
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
||||||
|
|
||||||
@@ -106,6 +122,55 @@ export function* transferCurrentCard(boardId, listId, index) {
|
|||||||
yield call(transferCard, cardId, boardId, listId, index);
|
yield call(transferCard, cardId, boardId, listId, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* duplicateCard(id) {
|
||||||
|
const { listId, name } = yield select(selectors.selectCardById, id);
|
||||||
|
const index = yield select(selectors.selectCardIndexById, id);
|
||||||
|
|
||||||
|
const nextData = {
|
||||||
|
position: yield select(selectors.selectNextCardPosition, listId, index + 1),
|
||||||
|
name: `${name} (${i18n.t('common.copy', {
|
||||||
|
context: 'inline',
|
||||||
|
})})`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const localId = yield call(createLocalId);
|
||||||
|
const taskIds = yield select(selectors.selectTaskIdsByCardId, id);
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
actions.duplicateCard(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
...nextData,
|
||||||
|
id: localId,
|
||||||
|
},
|
||||||
|
taskIds,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let card;
|
||||||
|
let cardMemberships;
|
||||||
|
let cardLabels;
|
||||||
|
let tasks;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({
|
||||||
|
item: card,
|
||||||
|
included: { cardMemberships, cardLabels, tasks },
|
||||||
|
} = yield call(request, api.duplicateCard, id, nextData));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(actions.duplicateCard.failure(localId, error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(actions.duplicateCard.success(localId, card, cardMemberships, cardLabels, tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* duplicateCurrentCard() {
|
||||||
|
const { cardId } = yield select(selectors.selectPath);
|
||||||
|
|
||||||
|
yield call(duplicateCard, cardId);
|
||||||
|
}
|
||||||
|
|
||||||
export function* deleteCard(id) {
|
export function* deleteCard(id) {
|
||||||
const { cardId, boardId } = yield select(selectors.selectPath);
|
const { cardId, boardId } = yield select(selectors.selectPath);
|
||||||
|
|
||||||
@@ -142,17 +207,26 @@ export function* handleCardDelete(card) {
|
|||||||
yield put(actions.handleCardDelete(card));
|
yield put(actions.handleCardDelete(card));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* handleTextFilter(text) {
|
||||||
|
const { boardId } = yield select(selectors.selectPath);
|
||||||
|
|
||||||
|
yield put(actions.filterText(boardId, text));
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createCard,
|
createCard,
|
||||||
handleCardCreate,
|
handleCardCreate,
|
||||||
updateCard,
|
updateCard,
|
||||||
updateCurrentCard,
|
updateCurrentCard,
|
||||||
|
handleCardUpdate,
|
||||||
moveCard,
|
moveCard,
|
||||||
moveCurrentCard,
|
moveCurrentCard,
|
||||||
transferCard,
|
transferCard,
|
||||||
transferCurrentCard,
|
transferCurrentCard,
|
||||||
handleCardUpdate,
|
duplicateCard,
|
||||||
|
duplicateCurrentCard,
|
||||||
deleteCard,
|
deleteCard,
|
||||||
deleteCurrentCard,
|
deleteCurrentCard,
|
||||||
handleCardDelete,
|
handleCardDelete,
|
||||||
|
handleTextFilter,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { call, put, select, take } from 'redux-saga/effects';
|
import { call, put, select } from 'redux-saga/effects';
|
||||||
|
|
||||||
import request from '../request';
|
import request from '../request';
|
||||||
import requests from '../requests';
|
import requests from '../requests';
|
||||||
@@ -84,8 +84,7 @@ export function* logout(invalidateAccessToken = true) {
|
|||||||
} catch (error) {} // eslint-disable-line no-empty
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
|
|
||||||
yield put(actions.logout());
|
yield put(actions.logout()); // TODO: next url
|
||||||
yield take();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -70,6 +70,30 @@ export function* moveList(id, index) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: sort locally
|
||||||
|
export function* sortList(id, data) {
|
||||||
|
yield put(actions.sortList(id, data));
|
||||||
|
|
||||||
|
let list;
|
||||||
|
let cards;
|
||||||
|
|
||||||
|
try {
|
||||||
|
({
|
||||||
|
item: list,
|
||||||
|
included: { cards },
|
||||||
|
} = yield call(request, api.sortList, id, data));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(actions.sortList.failure(id, error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(actions.sortList.success(list, cards));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* handleListSort(list, cards) {
|
||||||
|
yield put(actions.handleListSort(list, cards));
|
||||||
|
}
|
||||||
|
|
||||||
export function* deleteList(id) {
|
export function* deleteList(id) {
|
||||||
yield put(actions.deleteList(id));
|
yield put(actions.deleteList(id));
|
||||||
|
|
||||||
@@ -95,6 +119,8 @@ export default {
|
|||||||
updateList,
|
updateList,
|
||||||
handleListUpdate,
|
handleListUpdate,
|
||||||
moveList,
|
moveList,
|
||||||
|
sortList,
|
||||||
|
handleListSort,
|
||||||
deleteList,
|
deleteList,
|
||||||
handleListDelete,
|
handleListDelete,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { call, put, select, take } from 'redux-saga/effects';
|
import { call, put, select, take } from 'redux-saga/effects';
|
||||||
import { push } from '../../../lib/redux-router';
|
import { push } from '../../../lib/redux-router';
|
||||||
|
|
||||||
|
import { logout } from './core';
|
||||||
import request from '../request';
|
import request from '../request';
|
||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import actions from '../../../actions';
|
import actions from '../../../actions';
|
||||||
import api from '../../../api';
|
import api from '../../../api';
|
||||||
|
import { getAccessToken } from '../../../utils/access-token-storage';
|
||||||
import ActionTypes from '../../../constants/ActionTypes';
|
import ActionTypes from '../../../constants/ActionTypes';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
|
|
||||||
@@ -25,6 +27,13 @@ export function* goToCard(cardId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function* handleLocationChange() {
|
export function* handleLocationChange() {
|
||||||
|
const accessToken = yield call(getAccessToken);
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
yield call(logout, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const pathsMatch = yield select(selectors.selectPathsMatch);
|
const pathsMatch = yield select(selectors.selectPathsMatch);
|
||||||
|
|
||||||
if (!pathsMatch) {
|
if (!pathsMatch) {
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ export function* handleUserDelete(user) {
|
|||||||
|
|
||||||
if (user.id === currentUserId) {
|
if (user.id === currentUserId) {
|
||||||
yield call(logout, false);
|
yield call(logout, false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield put(actions.handleUserDelete(user));
|
yield put(actions.handleUserDelete(user));
|
||||||
|
|||||||
@@ -32,10 +32,15 @@ export default function* cardsWatchers() {
|
|||||||
takeEvery(EntryActionTypes.CURRENT_CARD_TRANSFER, ({ payload: { boardId, listId, index } }) =>
|
takeEvery(EntryActionTypes.CURRENT_CARD_TRANSFER, ({ payload: { boardId, listId, index } }) =>
|
||||||
services.transferCurrentCard(boardId, listId, index),
|
services.transferCurrentCard(boardId, listId, index),
|
||||||
),
|
),
|
||||||
|
takeEvery(EntryActionTypes.CARD_DUPLICATE, ({ payload: { id } }) => services.duplicateCard(id)),
|
||||||
|
takeEvery(EntryActionTypes.CURRENT_CARD_DUPLICATE, () => services.duplicateCurrentCard()),
|
||||||
takeEvery(EntryActionTypes.CARD_DELETE, ({ payload: { id } }) => services.deleteCard(id)),
|
takeEvery(EntryActionTypes.CARD_DELETE, ({ payload: { id } }) => services.deleteCard(id)),
|
||||||
takeEvery(EntryActionTypes.CURRENT_CARD_DELETE, () => services.deleteCurrentCard()),
|
takeEvery(EntryActionTypes.CURRENT_CARD_DELETE, () => services.deleteCurrentCard()),
|
||||||
takeEvery(EntryActionTypes.CARD_DELETE_HANDLE, ({ payload: { card } }) =>
|
takeEvery(EntryActionTypes.CARD_DELETE_HANDLE, ({ payload: { card } }) =>
|
||||||
services.handleCardDelete(card),
|
services.handleCardDelete(card),
|
||||||
),
|
),
|
||||||
|
takeEvery(EntryActionTypes.TEXT_FILTER_IN_CURRENT_BOARD, ({ payload: { text } }) =>
|
||||||
|
services.handleTextFilter(text),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,9 @@ import services from '../services';
|
|||||||
import EntryActionTypes from '../../../constants/EntryActionTypes';
|
import EntryActionTypes from '../../../constants/EntryActionTypes';
|
||||||
|
|
||||||
export default function* coreWatchers() {
|
export default function* coreWatchers() {
|
||||||
yield all([takeEvery(EntryActionTypes.LOGOUT, () => services.logout())]);
|
yield all([
|
||||||
|
takeEvery(EntryActionTypes.LOGOUT, ({ payload: { invalidateAccessToken } }) =>
|
||||||
|
services.logout(invalidateAccessToken),
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ export default function* listsWatchers() {
|
|||||||
takeEvery(EntryActionTypes.LIST_MOVE, ({ payload: { id, index } }) =>
|
takeEvery(EntryActionTypes.LIST_MOVE, ({ payload: { id, index } }) =>
|
||||||
services.moveList(id, index),
|
services.moveList(id, index),
|
||||||
),
|
),
|
||||||
|
takeEvery(EntryActionTypes.LIST_SORT, ({ payload: { id, data } }) =>
|
||||||
|
services.sortList(id, data),
|
||||||
|
),
|
||||||
|
takeEvery(EntryActionTypes.LIST_SORT_HANDLE, ({ payload: { list, cards } }) =>
|
||||||
|
services.handleListSort(list, cards),
|
||||||
|
),
|
||||||
takeEvery(EntryActionTypes.LIST_DELETE, ({ payload: { id } }) => services.deleteList(id)),
|
takeEvery(EntryActionTypes.LIST_DELETE, ({ payload: { id } }) => services.deleteList(id)),
|
||||||
takeEvery(EntryActionTypes.LIST_DELETE_HANDLE, ({ payload: { list } }) =>
|
takeEvery(EntryActionTypes.LIST_DELETE_HANDLE, ({ payload: { list } }) =>
|
||||||
services.handleListDelete(list),
|
services.handleListDelete(list),
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ const createSocketEventsChannel = () =>
|
|||||||
emit(entryActions.handleSocketReconnect());
|
emit(entryActions.handleSocketReconnect());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
emit(entryActions.logout(false));
|
||||||
|
};
|
||||||
|
|
||||||
const handleUserCreate = api.makeHandleUserCreate(({ item }) => {
|
const handleUserCreate = api.makeHandleUserCreate(({ item }) => {
|
||||||
emit(entryActions.handleUserCreate(item));
|
emit(entryActions.handleUserCreate(item));
|
||||||
});
|
});
|
||||||
@@ -80,6 +84,10 @@ const createSocketEventsChannel = () =>
|
|||||||
emit(entryActions.handleListUpdate(item));
|
emit(entryActions.handleListUpdate(item));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleListSort = api.makeHandleListSort(({ item, included: { cards } }) => {
|
||||||
|
emit(entryActions.handleListSort(item, cards));
|
||||||
|
});
|
||||||
|
|
||||||
const handleListDelete = ({ item }) => {
|
const handleListDelete = ({ item }) => {
|
||||||
emit(entryActions.handleListDelete(item));
|
emit(entryActions.handleListDelete(item));
|
||||||
};
|
};
|
||||||
@@ -171,6 +179,8 @@ const createSocketEventsChannel = () =>
|
|||||||
socket.on('disconnect', handleDisconnect);
|
socket.on('disconnect', handleDisconnect);
|
||||||
socket.on('reconnect', handleReconnect);
|
socket.on('reconnect', handleReconnect);
|
||||||
|
|
||||||
|
socket.on('logout', handleLogout);
|
||||||
|
|
||||||
socket.on('userCreate', handleUserCreate);
|
socket.on('userCreate', handleUserCreate);
|
||||||
socket.on('userUpdate', handleUserUpdate);
|
socket.on('userUpdate', handleUserUpdate);
|
||||||
socket.on('userDelete', handleUserDelete);
|
socket.on('userDelete', handleUserDelete);
|
||||||
@@ -192,6 +202,7 @@ const createSocketEventsChannel = () =>
|
|||||||
|
|
||||||
socket.on('listCreate', handleListCreate);
|
socket.on('listCreate', handleListCreate);
|
||||||
socket.on('listUpdate', handleListUpdate);
|
socket.on('listUpdate', handleListUpdate);
|
||||||
|
socket.on('listSort', handleListSort);
|
||||||
socket.on('listDelete', handleListDelete);
|
socket.on('listDelete', handleListDelete);
|
||||||
|
|
||||||
socket.on('labelCreate', handleLabelCreate);
|
socket.on('labelCreate', handleLabelCreate);
|
||||||
@@ -227,6 +238,8 @@ const createSocketEventsChannel = () =>
|
|||||||
socket.off('disconnect', handleDisconnect);
|
socket.off('disconnect', handleDisconnect);
|
||||||
socket.off('reconnect', handleReconnect);
|
socket.off('reconnect', handleReconnect);
|
||||||
|
|
||||||
|
socket.off('logout', handleLogout);
|
||||||
|
|
||||||
socket.off('userCreate', handleUserCreate);
|
socket.off('userCreate', handleUserCreate);
|
||||||
socket.off('userUpdate', handleUserUpdate);
|
socket.off('userUpdate', handleUserUpdate);
|
||||||
socket.off('userDelete', handleUserDelete);
|
socket.off('userDelete', handleUserDelete);
|
||||||
@@ -248,6 +261,7 @@ const createSocketEventsChannel = () =>
|
|||||||
|
|
||||||
socket.off('listCreate', handleListCreate);
|
socket.off('listCreate', handleListCreate);
|
||||||
socket.off('listUpdate', handleListUpdate);
|
socket.off('listUpdate', handleListUpdate);
|
||||||
|
socket.off('listSort', handleListSort);
|
||||||
socket.off('listDelete', handleListDelete);
|
socket.off('listDelete', handleListDelete);
|
||||||
|
|
||||||
socket.off('labelCreate', handleLabelCreate);
|
socket.off('labelCreate', handleLabelCreate);
|
||||||
|
|||||||
@@ -175,6 +175,24 @@ export const selectFilterLabelsForCurrentBoard = createSelector(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectFilterTextForCurrentBoard = createSelector(
|
||||||
|
orm,
|
||||||
|
(state) => selectPath(state).boardId,
|
||||||
|
({ Board }, id) => {
|
||||||
|
if (!id) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boardModel = Board.withId(id);
|
||||||
|
|
||||||
|
if (!boardModel) {
|
||||||
|
return boardModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boardModel.filterText;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const selectIsBoardWithIdExists = createSelector(
|
export const selectIsBoardWithIdExists = createSelector(
|
||||||
orm,
|
orm,
|
||||||
(_, id) => id,
|
(_, id) => id,
|
||||||
@@ -191,5 +209,6 @@ export default {
|
|||||||
selectListIdsForCurrentBoard,
|
selectListIdsForCurrentBoard,
|
||||||
selectFilterUsersForCurrentBoard,
|
selectFilterUsersForCurrentBoard,
|
||||||
selectFilterLabelsForCurrentBoard,
|
selectFilterLabelsForCurrentBoard,
|
||||||
|
selectFilterTextForCurrentBoard,
|
||||||
selectIsBoardWithIdExists,
|
selectIsBoardWithIdExists,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,6 +26,24 @@ export const makeSelectCardById = () =>
|
|||||||
|
|
||||||
export const selectCardById = makeSelectCardById();
|
export const selectCardById = makeSelectCardById();
|
||||||
|
|
||||||
|
export const makeSelectCardIndexById = () =>
|
||||||
|
createSelector(
|
||||||
|
orm,
|
||||||
|
(_, id) => id,
|
||||||
|
({ Card }, id) => {
|
||||||
|
const cardModel = Card.withId(id);
|
||||||
|
|
||||||
|
if (!cardModel) {
|
||||||
|
return cardModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardModels = cardModel.list.getFilteredOrderedCardsModelArray();
|
||||||
|
return cardModels.findIndex((cardModelItem) => cardModelItem.id === cardModel.id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectCardIndexById = makeSelectCardIndexById();
|
||||||
|
|
||||||
export const makeSelectUsersByCardId = () =>
|
export const makeSelectUsersByCardId = () =>
|
||||||
createSelector(
|
createSelector(
|
||||||
orm,
|
orm,
|
||||||
@@ -60,6 +78,26 @@ export const makeSelectLabelsByCardId = () =>
|
|||||||
|
|
||||||
export const selectLabelsByCardId = makeSelectLabelsByCardId();
|
export const selectLabelsByCardId = makeSelectLabelsByCardId();
|
||||||
|
|
||||||
|
export const makeSelectTaskIdsByCardId = () =>
|
||||||
|
createSelector(
|
||||||
|
orm,
|
||||||
|
(_, id) => id,
|
||||||
|
({ Card }, id) => {
|
||||||
|
const cardModel = Card.withId(id);
|
||||||
|
|
||||||
|
if (!cardModel) {
|
||||||
|
return cardModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardModel
|
||||||
|
.getOrderedTasksQuerySet()
|
||||||
|
.toRefArray()
|
||||||
|
.map((task) => task.id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectTaskIdsByCardId = makeSelectTaskIdsByCardId();
|
||||||
|
|
||||||
export const makeSelectTasksByCardId = () =>
|
export const makeSelectTasksByCardId = () =>
|
||||||
createSelector(
|
createSelector(
|
||||||
orm,
|
orm,
|
||||||
@@ -286,10 +324,14 @@ export const selectNotificationIdsForCurrentCard = createSelector(
|
|||||||
export default {
|
export default {
|
||||||
makeSelectCardById,
|
makeSelectCardById,
|
||||||
selectCardById,
|
selectCardById,
|
||||||
|
makeSelectCardIndexById,
|
||||||
|
selectCardIndexById,
|
||||||
makeSelectUsersByCardId,
|
makeSelectUsersByCardId,
|
||||||
selectUsersByCardId,
|
selectUsersByCardId,
|
||||||
makeSelectLabelsByCardId,
|
makeSelectLabelsByCardId,
|
||||||
selectLabelsByCardId,
|
selectLabelsByCardId,
|
||||||
|
makeSelectTaskIdsByCardId,
|
||||||
|
selectTaskIdsByCardId,
|
||||||
makeSelectTasksByCardId,
|
makeSelectTasksByCardId,
|
||||||
selectTasksByCardId,
|
selectTasksByCardId,
|
||||||
makeSelectLastActivityIdByCardId,
|
makeSelectLastActivityIdByCardId,
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
FROM node:18-alpine as server-dependencies
|
||||||
|
|
||||||
|
RUN apk -U upgrade \
|
||||||
|
&& apk add build-base python3 \
|
||||||
|
--no-cache
|
||||||
|
|
||||||
|
WORKDIR /app/client
|
||||||
|
COPY package.json package-lock.json /app/client/
|
||||||
|
RUN npm install npm@latest --global \
|
||||||
|
&& npm install pnpm --global \
|
||||||
|
&& pnpm import \
|
||||||
|
&& pnpm install
|
||||||
|
|
||||||
|
|
||||||
|
WORKDIR /app/
|
||||||
|
COPY ../../package.json ../../package-lock.json /app/
|
||||||
|
RUN pnpm import \
|
||||||
|
&& pnpm install
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FROM node:18-alpine as server-dependencies
|
||||||
|
|
||||||
|
RUN apk -U upgrade \
|
||||||
|
&& apk add build-base python3 \
|
||||||
|
--no-cache
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
|
||||||
|
RUN npm install npm@latest --global \
|
||||||
|
&& npm install pnpm --global \
|
||||||
|
&& pnpm import \
|
||||||
|
&& pnpm install
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
user nginx;
|
||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://server:1337;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /socket.io {
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_pass http://server:1337/socket.io;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://client:3000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+81
-35
@@ -1,20 +1,18 @@
|
|||||||
version: '3'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
planka:
|
|
||||||
image: ghcr.io/plankanban/planka:master
|
|
||||||
restart: on-failure
|
|
||||||
volumes:
|
|
||||||
- user-avatars:/app/public/user-avatars
|
|
||||||
- project-background-images:/app/public/project-background-images
|
|
||||||
- attachments:/app/private/attachments
|
|
||||||
ports:
|
|
||||||
- 3000:1337
|
|
||||||
environment:
|
|
||||||
- BASE_URL=http://localhost:3000
|
|
||||||
- DATABASE_URL=postgresql://postgres@postgres/planka
|
|
||||||
- SECRET_KEY=notsecretkey
|
|
||||||
|
|
||||||
|
server:
|
||||||
|
build:
|
||||||
|
context: ./server
|
||||||
|
dockerfile: ../config/development/Dockerfile.server
|
||||||
|
volumes:
|
||||||
|
- ./server:/app
|
||||||
|
- /app/node_modules
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- DATABASE_URL=postgresql://user:password@postgres:5432/planka_db
|
||||||
|
- SECRET_KEY=notsecretkey
|
||||||
# - TRUST_PROXY=0
|
# - TRUST_PROXY=0
|
||||||
# - TOKEN_EXPIRES_IN=365 # In days
|
# - TOKEN_EXPIRES_IN=365 # In days
|
||||||
|
|
||||||
@@ -25,20 +23,6 @@ services:
|
|||||||
|
|
||||||
# Configure knex to accept SSL certificates
|
# Configure knex to accept SSL certificates
|
||||||
# - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
|
# - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
|
||||||
|
|
||||||
# - DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
|
||||||
# - DEFAULT_ADMIN_PASSWORD=demo
|
|
||||||
# - DEFAULT_ADMIN_NAME=Demo Demo
|
|
||||||
# - DEFAULT_ADMIN_USERNAME=demo
|
|
||||||
|
|
||||||
# Email Notifications (https://nodemailer.com/smtp/)
|
|
||||||
# - SMTP_HOST=
|
|
||||||
# - SMTP_PORT=587
|
|
||||||
# - SMTP_SECURE=true
|
|
||||||
# - SMTP_USER=
|
|
||||||
# - SMTP_PASSWORD=
|
|
||||||
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
|
|
||||||
|
|
||||||
# - OIDC_ISSUER=
|
# - OIDC_ISSUER=
|
||||||
# - OIDC_CLIENT_ID=
|
# - OIDC_CLIENT_ID=
|
||||||
# - OIDC_CLIENT_SECRET=
|
# - OIDC_CLIENT_SECRET=
|
||||||
@@ -51,26 +35,88 @@ services:
|
|||||||
# - OIDC_IGNORE_USERNAME=true
|
# - OIDC_IGNORE_USERNAME=true
|
||||||
# - OIDC_IGNORE_ROLES=true
|
# - OIDC_IGNORE_ROLES=true
|
||||||
# - OIDC_ENFORCED=true
|
# - OIDC_ENFORCED=true
|
||||||
|
|
||||||
|
# Email Notifications (https://nodemailer.com/smtp/)
|
||||||
|
# - SMTP_HOST=
|
||||||
|
# - SMTP_PORT=587
|
||||||
|
# - SMTP_NAME=
|
||||||
|
# - SMTP_SECURE=true
|
||||||
|
# - SMTP_USER=
|
||||||
|
# - SMTP_PASSWORD=
|
||||||
|
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
|
||||||
|
|
||||||
|
# - SLACK_BOT_TOKEN=
|
||||||
|
# - SLACK_CHANNEL_ID=
|
||||||
|
|
||||||
|
working_dir: /app
|
||||||
|
command: ["sh", "-c", "npm run start"]
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
client:
|
||||||
|
build:
|
||||||
|
context: ./client
|
||||||
|
dockerfile: ../config/development/Dockerfile.client
|
||||||
|
volumes:
|
||||||
|
- ./client:/app/client
|
||||||
|
- /app/client/node_modules
|
||||||
|
- /app/node_modules
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- CHOKIDAR_USEPOLLING=true
|
||||||
|
- BASE_URL=http://localhost:3000
|
||||||
|
- REACT_APP_SERVER_BASE_URL=http://localhost:3000
|
||||||
|
working_dir: /app/client
|
||||||
|
command: npm start
|
||||||
|
|
||||||
|
init-db:
|
||||||
|
build:
|
||||||
|
context: ./server
|
||||||
|
dockerfile: ../config/development/Dockerfile.server
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgresql://user:password@postgres:5432/planka_db
|
||||||
|
# - DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||||
|
# - DEFAULT_ADMIN_PASSWORD=demo
|
||||||
|
# - DEFAULT_ADMIN_NAME=Demo Demo
|
||||||
|
# - DEFAULT_ADMIN_USERNAME=demo
|
||||||
|
|
||||||
|
working_dir: /app
|
||||||
|
command: ["sh", "-c", "npm run db:init"]
|
||||||
|
volumes:
|
||||||
|
- ./server:/app
|
||||||
|
- /app/node_modules
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:14-alpine
|
image: postgres:latest
|
||||||
restart: on-failure
|
|
||||||
volumes:
|
volumes:
|
||||||
- db-data:/var/lib/postgresql/data
|
- db-data:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_DB=planka
|
POSTGRES_DB: planka_db
|
||||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
POSTGRES_USER: user
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U postgres -d planka"]
|
test: ["CMD-SHELL", "pg_isready -U postgres -d planka"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
image: nginx:alpine
|
||||||
|
ports:
|
||||||
|
- "3000:80"
|
||||||
|
volumes:
|
||||||
|
- ./config/development/nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
depends_on:
|
||||||
|
- server
|
||||||
|
- client
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
user-avatars:
|
|
||||||
project-background-images:
|
|
||||||
attachments:
|
|
||||||
db-data:
|
db-data:
|
||||||
|
|||||||
+12
-8
@@ -31,14 +31,6 @@ services:
|
|||||||
# - DEFAULT_ADMIN_NAME=Demo Demo
|
# - DEFAULT_ADMIN_NAME=Demo Demo
|
||||||
# - DEFAULT_ADMIN_USERNAME=demo
|
# - DEFAULT_ADMIN_USERNAME=demo
|
||||||
|
|
||||||
# Email Notifications (https://nodemailer.com/smtp/)
|
|
||||||
# - SMTP_HOST=
|
|
||||||
# - SMTP_PORT=587
|
|
||||||
# - SMTP_SECURE=true
|
|
||||||
# - SMTP_USER=
|
|
||||||
# - SMTP_PASSWORD=
|
|
||||||
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
|
|
||||||
|
|
||||||
# - OIDC_ISSUER=
|
# - OIDC_ISSUER=
|
||||||
# - OIDC_CLIENT_ID=
|
# - OIDC_CLIENT_ID=
|
||||||
# - OIDC_CLIENT_SECRET=
|
# - OIDC_CLIENT_SECRET=
|
||||||
@@ -51,6 +43,18 @@ services:
|
|||||||
# - OIDC_IGNORE_USERNAME=true
|
# - OIDC_IGNORE_USERNAME=true
|
||||||
# - OIDC_IGNORE_ROLES=true
|
# - OIDC_IGNORE_ROLES=true
|
||||||
# - OIDC_ENFORCED=true
|
# - OIDC_ENFORCED=true
|
||||||
|
|
||||||
|
# Email Notifications (https://nodemailer.com/smtp/)
|
||||||
|
# - SMTP_HOST=
|
||||||
|
# - SMTP_PORT=587
|
||||||
|
# - SMTP_NAME=
|
||||||
|
# - SMTP_SECURE=true
|
||||||
|
# - SMTP_USER=
|
||||||
|
# - SMTP_PASSWORD=
|
||||||
|
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
|
||||||
|
|
||||||
|
# - SLACK_BOT_TOKEN=
|
||||||
|
# - SLACK_CHANNEL_ID=
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
Generated
+262
-2267
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user