Push changes

pull/687/head
Jyrezo 2 years ago
parent 41161d13e9
commit 86a68e2d96

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

@ -1,31 +1,31 @@
apiVersion: v2 apiVersion: v2
name: planka name: planka
description: A Helm chart to deploy Planka and it's dependencies. description: A Helm chart to deploy Planka and it's dependencies.
# A chart can be either an 'application' or a 'library' chart. # A chart can be either an 'application' or a 'library' chart.
# #
# Application charts are a collection of templates that can be packaged into versioned archives # Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed. # to be deployed.
# #
# Library charts provide useful utilities or functions for the chart developer. They're included as # Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering # a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed. # pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application 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.23 version: 0.1.23
# 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.2" appVersion: "1.16.2"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
condition: postgresql.enabled condition: postgresql.enabled
name: postgresql name: postgresql
repository: &bitnami-repo https://charts.bitnami.com/bitnami repository: &bitnami-repo https://charts.bitnami.com/bitnami
version: 12.5.1 version: 12.5.1

@ -1,141 +1,141 @@
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: {{ include "planka.fullname" . }} name: {{ include "planka.fullname" . }}
labels: labels:
{{- include "planka.labels" . | nindent 4 }} {{- include "planka.labels" . | nindent 4 }}
spec: spec:
{{- if not .Values.autoscaling.enabled }} {{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }} replicas: {{ .Values.replicaCount }}
{{- end }} {{- end }}
selector: selector:
matchLabels: matchLabels:
{{- include "planka.selectorLabels" . | nindent 6 }} {{- include "planka.selectorLabels" . | nindent 6 }}
template: template:
metadata: metadata:
{{- with .Values.podAnnotations }} {{- with .Values.podAnnotations }}
annotations: annotations:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
labels: labels:
{{- include "planka.selectorLabels" . | nindent 8 }} {{- include "planka.selectorLabels" . | nindent 8 }}
spec: spec:
{{- with .Values.imagePullSecrets }} {{- with .Values.imagePullSecrets }}
imagePullSecrets: imagePullSecrets:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
serviceAccountName: {{ include "planka.serviceAccountName" . }} serviceAccountName: {{ include "planka.serviceAccountName" . }}
securityContext: securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }} {{- toYaml .Values.podSecurityContext | nindent 8 }}
containers: containers:
- name: {{ .Chart.Name }} - name: {{ .Chart.Name }}
securityContext: securityContext:
{{- toYaml .Values.securityContext | nindent 12 }} {{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
- name: http - name: http
containerPort: {{ .Values.service.containerPort | default 1337 }} containerPort: {{ .Values.service.containerPort | default 1337 }}
protocol: TCP protocol: TCP
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /
port: http port: http
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /
port: http port: http
volumeMounts: volumeMounts:
- mountPath: /app/public/user-avatars - mountPath: /app/public/user-avatars
subPath: user-avatars subPath: user-avatars
name: planka name: planka
- mountPath: /app/public/project-background-images - mountPath: /app/public/project-background-images
subPath: project-background-images subPath: project-background-images
name: planka name: planka
- mountPath: /app/private/attachments - mountPath: /app/private/attachments
subPath: attachments subPath: attachments
name: planka name: planka
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
env: env:
{{- if not .Values.postgresql.enabled }} {{- if not .Values.postgresql.enabled }}
- name: DATABASE_URL - name: DATABASE_URL
value: {{ required "If the included postgresql deployment is disabled you need to define a Database URL in 'dburl'" .Values.dburl }} value: {{ required "If the included postgresql deployment is disabled you need to define a Database URL in 'dburl'" .Values.dburl }}
{{- else }} {{- 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 }} {{- end }}
- name: BASE_URL - name: BASE_URL
{{- if .Values.baseUrl }} {{- if .Values.baseUrl }}
value: {{ .Values.baseUrl }} value: {{ .Values.baseUrl }}
{{- else if .Values.ingress.enabled }} {{- else if .Values.ingress.enabled }}
value: {{ printf "https://%s" (first .Values.ingress.hosts).host }} value: {{ printf "https://%s" (first .Values.ingress.hosts).host }}
{{- else }} {{- else }}
value: http://localhost:3000 value: http://localhost:3000
{{- end }} {{- end }}
- name: SECRET_KEY - name: SECRET_KEY
value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }} value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }}
- name: TRUST_PROXY - name: TRUST_PROXY
value: "0" value: "0"
- name: DEFAULT_ADMIN_EMAIL - name: DEFAULT_ADMIN_EMAIL
value: {{ .Values.admin_email }} value: {{ .Values.admin_email }}
- name: DEFAULT_ADMIN_PASSWORD - name: DEFAULT_ADMIN_PASSWORD
value: {{ .Values.admin_password }} value: {{ .Values.admin_password }}
- name: DEFAULT_ADMIN_NAME - name: DEFAULT_ADMIN_NAME
value: {{ .Values.admin_name }} value: {{ .Values.admin_name }}
- name: DEFAULT_ADMIN_USERNAME - name: DEFAULT_ADMIN_USERNAME
value: {{ .Values.admin_username }} value: {{ .Values.admin_username }}
{{ range $k, $v := .Values.env }} {{ range $k, $v := .Values.env }}
- name: {{ $k | quote }} - name: {{ $k | quote }}
value: {{ $v | quote }} value: {{ $v | quote }}
{{- end }} {{- end }}
{{- if .Values.oidc.enabled }} {{- if .Values.oidc.enabled }}
{{- $secretName := default (printf "%s-oidc" (include "planka.fullname" .)) .Values.oidc.existingSecret }} {{- $secretName := default (printf "%s-oidc" (include "planka.fullname" .)) .Values.oidc.existingSecret }}
- name: OIDC_CLIENT_ID - name: OIDC_CLIENT_ID
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: clientId key: clientId
name: {{ $secretName }} name: {{ $secretName }}
- name: OIDC_CLIENT_SECRET - name: OIDC_CLIENT_SECRET
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: clientSecret key: clientSecret
name: {{ $secretName }} name: {{ $secretName }}
- name: OIDC_ISSUER - name: OIDC_ISSUER
value: {{ required "issuerUrl is required when configuring OIDC" .Values.oidc.issuerUrl | quote }} value: {{ required "issuerUrl is required when configuring OIDC" .Values.oidc.issuerUrl | quote }}
- name: OIDC_SCOPES - name: OIDC_SCOPES
value: {{ join " " .Values.oidc.scopes | default "openid profile email" | quote }} value: {{ join " " .Values.oidc.scopes | default "openid profile email" | quote }}
{{- if .Values.oidc.admin.roles }} {{- if .Values.oidc.admin.roles }}
- name: OIDC_ADMIN_ROLES - name: OIDC_ADMIN_ROLES
value: {{ join "," .Values.oidc.admin.roles | quote }} value: {{ join "," .Values.oidc.admin.roles | quote }}
{{- end }} {{- end }}
- name: OIDC_ROLES_ATTRIBUTE - name: OIDC_ROLES_ATTRIBUTE
value: {{ .Values.oidc.admin.rolesAttribute | default "groups" | quote }} value: {{ .Values.oidc.admin.rolesAttribute | default "groups" | quote }}
{{- if .Values.oidc.admin.ignoreRoles }} {{- if .Values.oidc.admin.ignoreRoles }}
- name: OIDC_IGNORE_ROLES - name: OIDC_IGNORE_ROLES
value: {{ .Values.oidc.admin.ignoreRoles | quote }} value: {{ .Values.oidc.admin.ignoreRoles | quote }}
{{- end }} {{- end }}
{{- end }} {{- end }}
{{- with .Values.nodeSelector }} {{- with .Values.nodeSelector }}
nodeSelector: nodeSelector:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- with .Values.affinity }} {{- with .Values.affinity }}
affinity: affinity:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- with .Values.tolerations }} {{- with .Values.tolerations }}
tolerations: tolerations:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
volumes: volumes:
- name: planka - name: planka
{{- if .Values.persistence.enabled }} {{- if .Values.persistence.enabled }}
persistentVolumeClaim: persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim | default (include "planka.fullname" .) }} claimName: {{ .Values.persistence.existingClaim | default (include "planka.fullname" .) }}
{{- else }} {{- else }}
emptyDir: {} emptyDir: {}
{{- end }} {{- end }}

@ -1,182 +1,182 @@
# Default values for planka. # Default values for planka.
# This is a YAML-formatted file. # This is a YAML-formatted file.
# Declare variables to be passed into your templates. # Declare variables to be passed into your templates.
replicaCount: 1 replicaCount: 1
image: image:
repository: ghcr.io/plankanban/planka repository: ghcr.io/plankanban/planka
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion. # Overrides the image tag whose default is the chart appVersion.
tag: "" tag: ""
imagePullSecrets: [] imagePullSecrets: []
nameOverride: "" nameOverride: ""
fullnameOverride: "" fullnameOverride: ""
# Generate a secret using openssl rand -base64 45 # Generate a secret using openssl rand -base64 45
secretkey: "" secretkey: ""
# Base url for Planka. Will override `ingress.hosts[0].host` # Base url for Planka. Will override `ingress.hosts[0].host`
# Defaults to `http://localhost:3000` if ingress is disabled. # Defaults to `http://localhost:3000` if ingress is disabled.
baseUrl: "" baseUrl: ""
serviceAccount: serviceAccount:
# Specifies whether a service account should be created # Specifies whether a service account should be created
create: true create: true
# Annotations to add to the service account # Annotations to add to the service account
annotations: {} annotations: {}
# The name of the service account to use. # The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template # If not set and create is true, a name is generated using the fullname template
name: "" name: ""
podAnnotations: {} podAnnotations: {}
podSecurityContext: {} podSecurityContext: {}
# fsGroup: 2000 # fsGroup: 2000
securityContext: {} securityContext: {}
# capabilities: # capabilities:
# drop: # drop:
# - ALL # - ALL
# readOnlyRootFilesystem: true # readOnlyRootFilesystem: true
# runAsNonRoot: true # runAsNonRoot: true
# runAsUser: 1000 # runAsUser: 1000
service: service:
type: ClusterIP type: ClusterIP
port: 1337 port: 1337
## @param service.containerPort Planka HTTP container port ## @param service.containerPort Planka HTTP container port
## If empty will default to 1337 ## If empty will default to 1337
## ##
containerPort: 1337 containerPort: 1337
ingress: ingress:
enabled: false enabled: false
className: "" className: ""
annotations: {} annotations: {}
# kubernetes.io/ingress.class: nginx # kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true" # kubernetes.io/tls-acme: "true"
hosts: hosts:
# Used to set planka BASE_URL if no `baseurl` is provided. # Used to set planka BASE_URL if no `baseurl` is provided.
- host: planka.local - host: planka.local
paths: paths:
- path: / - path: /
pathType: ImplementationSpecific pathType: ImplementationSpecific
tls: [] tls: []
# - secretName: planka-tls # - secretName: planka-tls
# hosts: # hosts:
# - planka.local # - planka.local
resources: {} resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious # We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little # choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following # resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'. # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits: # limits:
# cpu: 100m # cpu: 100m
# memory: 128Mi # memory: 128Mi
# requests: # requests:
# cpu: 100m # cpu: 100m
# memory: 128Mi # memory: 128Mi
autoscaling: autoscaling:
enabled: false enabled: false
minReplicas: 1 minReplicas: 1
maxReplicas: 100 maxReplicas: 100
targetCPUUtilizationPercentage: 80 targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80
nodeSelector: {} nodeSelector: {}
tolerations: [] tolerations: []
affinity: {} affinity: {}
postgresql: postgresql:
enabled: true enabled: true
auth: auth:
database: planka database: planka
username: planka username: planka
password: "" password: ""
postgresPassword: "" postgresPassword: ""
replicationPassword: "" replicationPassword: ""
# existingSecret: planka-postgresql # existingSecret: planka-postgresql
serviceBindings: serviceBindings:
enabled: true enabled: true
## Set this if you disable the built-in postgresql deployment ## Set this if you disable the built-in postgresql deployment
dburl: dburl:
## 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
## OpenID Identity Management configuration ## OpenID Identity Management configuration
## ##
## Example: ## Example:
## --------------- ## ---------------
## oidc: ## oidc:
## enabled: true ## enabled: true
## clientId: sxxaAIAxVXlCxTmc1YLHBbQr8NL8MqLI2DUbt42d ## clientId: sxxaAIAxVXlCxTmc1YLHBbQr8NL8MqLI2DUbt42d
## clientSecret: om4RTMRVHRszU7bqxB7RZNkHIzA8e4sGYWxeCwIMYQXPwEBWe4SY5a0wwCe9ltB3zrq5f0dnFnp34cEHD7QSMHsKvV9AiV5Z7eqDraMnv0I8IFivmuV5wovAECAYreSI ## clientSecret: om4RTMRVHRszU7bqxB7RZNkHIzA8e4sGYWxeCwIMYQXPwEBWe4SY5a0wwCe9ltB3zrq5f0dnFnp34cEHD7QSMHsKvV9AiV5Z7eqDraMnv0I8IFivmuV5wovAECAYreSI
## issuerUrl: https://auth.local/application/o/planka/ ## issuerUrl: https://auth.local/application/o/planka/
## admin: ## admin:
## roles: ## roles:
## - planka-admin ## - planka-admin
## ##
## --------------- ## ---------------
## NOTE: A minimal configuration requires setting `clientId`, `clientSecret` and `issuerUrl`. (plus `admin.roles` for administrators) ## NOTE: A minimal configuration requires setting `clientId`, `clientSecret` and `issuerUrl`. (plus `admin.roles` for administrators)
## ref: https://docs.planka.cloud/docs/Configuration/OIDC ## ref: https://docs.planka.cloud/docs/Configuration/OIDC
## ##
oidc: oidc:
## @param oidc.enabled Enable single sign-on (SSO) with OpenID Connect (OIDC) ## @param oidc.enabled Enable single sign-on (SSO) with OpenID Connect (OIDC)
## ##
enabled: false enabled: false
## OIDC credentials ## OIDC credentials
## @param oidc.clientId A string unique to the provider that identifies your app. ## @param oidc.clientId A string unique to the provider that identifies your app.
## @param oidc.clientSecret A secret string that the provider uses to confirm ownership of a client ID. ## @param oidc.clientSecret A secret string that the provider uses to confirm ownership of a client ID.
## ##
## NOTE: Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret` ## NOTE: Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`
## ##
clientId: "" clientId: ""
clientSecret: "" clientSecret: ""
## @param oidc.existingSecret Name of an existing secret containing OIDC credentials ## @param oidc.existingSecret Name of an existing secret containing OIDC credentials
## NOTE: Must contain key `clientId` and `clientSecret` ## NOTE: Must contain key `clientId` and `clientSecret`
## NOTE: When it's set, the `clientId` and `clientSecret` parameters are ignored ## NOTE: When it's set, the `clientId` and `clientSecret` parameters are ignored
## ##
existingSecret: "" existingSecret: ""
## @param oidc.issuerUrl The OpenID connect metadata document endpoint ## @param oidc.issuerUrl The OpenID connect metadata document endpoint
## ##
issuerUrl: "" issuerUrl: ""
## @param oidc.scopes A list of scopes required for OIDC client. ## @param oidc.scopes A list of scopes required for OIDC client.
## If empty will default to `openid`, `profile` and `email` ## If empty will default to `openid`, `profile` and `email`
## NOTE: Planka needs the email and name claims ## NOTE: Planka needs the email and name claims
## ##
scopes: [] scopes: []
## Admin permissions configuration ## Admin permissions configuration
admin: admin:
## @param oidc.admin.ignoreRoles If set to true, the admin roles will be ignored. ## @param oidc.admin.ignoreRoles If set to true, the admin roles will be ignored.
## It is useful if you want to use OIDC for authentication but not for authorization. ## It is useful if you want to use OIDC for authentication but not for authorization.
## If empty will default to `false` ## If empty will default to `false`
## ##
ignoreRoles: false ignoreRoles: false
## @param oidc.admin.rolesAttribute The name of a custom group claim that you have configured in your OIDC provider ## @param oidc.admin.rolesAttribute The name of a custom group claim that you have configured in your OIDC provider
## If empty will default to `groups` ## If empty will default to `groups`
## ##
rolesAttribute: groups rolesAttribute: groups
## @param oidc.admin.roles The names of the admin groups ## @param oidc.admin.roles The names of the admin groups
## ##
roles: [] roles: []
# - planka-admin # - planka-admin

@ -1 +1 @@
REACT_APP_VERSION=1.16.2 REACT_APP_VERSION=1.16.2

@ -1,132 +1,132 @@
import ActionTypes from '../constants/ActionTypes'; import ActionTypes from '../constants/ActionTypes';
const createCard = (card) => ({ const createCard = (card) => ({
type: ActionTypes.CARD_CREATE, type: ActionTypes.CARD_CREATE,
payload: { payload: {
card, card,
}, },
}); });
createCard.success = (localId, card) => ({ createCard.success = (localId, card) => ({
type: ActionTypes.CARD_CREATE__SUCCESS, type: ActionTypes.CARD_CREATE__SUCCESS,
payload: { payload: {
localId, localId,
card, card,
}, },
}); });
createCard.failure = (localId, error) => ({ createCard.failure = (localId, error) => ({
type: ActionTypes.CARD_CREATE__FAILURE, type: ActionTypes.CARD_CREATE__FAILURE,
payload: { payload: {
localId, localId,
error, error,
}, },
}); });
const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({ const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({
type: ActionTypes.CARD_CREATE_HANDLE, type: ActionTypes.CARD_CREATE_HANDLE,
payload: { payload: {
card, card,
cardMemberships, cardMemberships,
cardLabels, cardLabels,
tasks, tasks,
attachments, attachments,
}, },
}); });
const updateCard = (id, data) => ({ const updateCard = (id, data) => ({
type: ActionTypes.CARD_UPDATE, type: ActionTypes.CARD_UPDATE,
payload: { payload: {
id, id,
data, data,
}, },
}); });
updateCard.success = (card) => ({ updateCard.success = (card) => ({
type: ActionTypes.CARD_UPDATE__SUCCESS, type: ActionTypes.CARD_UPDATE__SUCCESS,
payload: { payload: {
card, card,
}, },
}); });
updateCard.failure = (id, error) => ({ updateCard.failure = (id, error) => ({
type: ActionTypes.CARD_UPDATE__FAILURE, type: ActionTypes.CARD_UPDATE__FAILURE,
payload: { payload: {
id, id,
error, error,
}, },
}); });
const handleCardUpdate = (card) => ({ const handleCardUpdate = (card) => ({
type: ActionTypes.CARD_UPDATE_HANDLE, type: ActionTypes.CARD_UPDATE_HANDLE,
payload: { payload: {
card, card,
}, },
}); });
const duplicateCard = (id, card, taskIds) => ({ const duplicateCard = (id, card, taskIds) => ({
type: ActionTypes.CARD_DUPLICATE, type: ActionTypes.CARD_DUPLICATE,
payload: { payload: {
id, id,
card, card,
taskIds, taskIds,
}, },
}); });
duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({ duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({
type: ActionTypes.CARD_DUPLICATE__SUCCESS, type: ActionTypes.CARD_DUPLICATE__SUCCESS,
payload: { payload: {
localId, localId,
card, card,
cardMemberships, cardMemberships,
cardLabels, cardLabels,
tasks, tasks,
}, },
}); });
duplicateCard.failure = (id, error) => ({ duplicateCard.failure = (id, error) => ({
type: ActionTypes.CARD_DUPLICATE__FAILURE, type: ActionTypes.CARD_DUPLICATE__FAILURE,
payload: { payload: {
id, id,
error, error,
}, },
}); });
const deleteCard = (id) => ({ const deleteCard = (id) => ({
type: ActionTypes.CARD_DELETE, type: ActionTypes.CARD_DELETE,
payload: { payload: {
id, id,
}, },
}); });
deleteCard.success = (card) => ({ deleteCard.success = (card) => ({
type: ActionTypes.CARD_DELETE__SUCCESS, type: ActionTypes.CARD_DELETE__SUCCESS,
payload: { payload: {
card, card,
}, },
}); });
deleteCard.failure = (id, error) => ({ deleteCard.failure = (id, error) => ({
type: ActionTypes.CARD_DELETE__FAILURE, type: ActionTypes.CARD_DELETE__FAILURE,
payload: { payload: {
id, id,
error, error,
}, },
}); });
const handleCardDelete = (card) => ({ const handleCardDelete = (card) => ({
type: ActionTypes.CARD_DELETE_HANDLE, type: ActionTypes.CARD_DELETE_HANDLE,
payload: { payload: {
card, card,
}, },
}); });
export default { export default {
createCard, createCard,
handleCardCreate, handleCardCreate,
updateCard, updateCard,
handleCardUpdate, handleCardUpdate,
duplicateCard, duplicateCard,
deleteCard, deleteCard,
handleCardDelete, handleCardDelete,
}; };

@ -1,94 +1,94 @@
import socket from './socket'; import socket from './socket';
import { transformAttachment } from './attachments'; import { transformAttachment } from './attachments';
/* Transformers */ /* Transformers */
export const transformCard = (card) => ({ export const transformCard = (card) => ({
...card, ...card,
...(card.dueDate && { ...(card.dueDate && {
dueDate: new Date(card.dueDate), dueDate: new Date(card.dueDate),
}), }),
...(card.stopwatch && { ...(card.stopwatch && {
stopwatch: { stopwatch: {
...card.stopwatch, ...card.stopwatch,
...(card.stopwatch.startedAt && { ...(card.stopwatch.startedAt && {
startedAt: new Date(card.stopwatch.startedAt), startedAt: new Date(card.stopwatch.startedAt),
}), }),
}, },
}), }),
}); });
export const transformCardData = (data) => ({ export const transformCardData = (data) => ({
...data, ...data,
...(data.dueDate && { ...(data.dueDate && {
dueDate: data.dueDate.toISOString(), dueDate: data.dueDate.toISOString(),
}), }),
...(data.stopwatch && { ...(data.stopwatch && {
stopwatch: { stopwatch: {
...data.stopwatch, ...data.stopwatch,
...(data.stopwatch.startedAt && { ...(data.stopwatch.startedAt && {
startedAt: data.stopwatch.startedAt.toISOString(), startedAt: data.stopwatch.startedAt.toISOString(),
}), }),
}, },
}), }),
}); });
/* Actions */ /* Actions */
const createCard = (listId, data, headers) => const createCard = (listId, data, headers) =>
socket.post(`/lists/${listId}/cards`, transformCardData(data), headers).then((body) => ({ socket.post(`/lists/${listId}/cards`, transformCardData(data), headers).then((body) => ({
...body, ...body,
item: transformCard(body.item), item: transformCard(body.item),
})); }));
const getCard = (id, headers) => const getCard = (id, headers) =>
socket.get(`/cards/${id}`, undefined, headers).then((body) => ({ socket.get(`/cards/${id}`, undefined, headers).then((body) => ({
...body, ...body,
item: transformCard(body.item), item: transformCard(body.item),
included: { included: {
...body.included, ...body.included,
attachments: body.included.attachments.map(transformAttachment), attachments: body.included.attachments.map(transformAttachment),
}, },
})); }));
const updateCard = (id, data, headers) => const updateCard = (id, data, headers) =>
socket.patch(`/cards/${id}`, transformCardData(data), headers).then((body) => ({ socket.patch(`/cards/${id}`, transformCardData(data), headers).then((body) => ({
...body, ...body,
item: transformCard(body.item), item: transformCard(body.item),
})); }));
const duplicateCard = (id, data, headers) => const duplicateCard = (id, data, headers) =>
socket.post(`/cards/${id}/duplicate`, data, headers).then((body) => ({ socket.post(`/cards/${id}/duplicate`, data, headers).then((body) => ({
...body, ...body,
item: transformCard(body.item), 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,
item: transformCard(body.item), item: transformCard(body.item),
})); }));
/* Event handlers */ /* Event handlers */
const makeHandleCardCreate = (next) => (body) => { const makeHandleCardCreate = (next) => (body) => {
next({ next({
...body, ...body,
item: transformCard(body.item), item: transformCard(body.item),
}); });
}; };
const makeHandleCardUpdate = makeHandleCardCreate; const makeHandleCardUpdate = makeHandleCardCreate;
const makeHandleCardDelete = makeHandleCardCreate; const makeHandleCardDelete = makeHandleCardCreate;
export default { export default {
createCard, createCard,
getCard, getCard,
updateCard, updateCard,
deleteCard, deleteCard,
duplicateCard, duplicateCard,
makeHandleCardCreate, makeHandleCardCreate,
makeHandleCardUpdate, makeHandleCardUpdate,
makeHandleCardDelete, makeHandleCardDelete,
}; };

@ -1,260 +1,260 @@
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import React, { useCallback } from 'react'; 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 { Menu } from 'semantic-ui-react'; 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 BoardMembershipsStep from '../BoardMembershipsStep'; import BoardMembershipsStep from '../BoardMembershipsStep';
import LabelsStep from '../LabelsStep'; import LabelsStep from '../LabelsStep';
import DueDateEditStep from '../DueDateEditStep'; import DueDateEditStep from '../DueDateEditStep';
import StopwatchEditStep from '../StopwatchEditStep'; import StopwatchEditStep from '../StopwatchEditStep';
import CardMoveStep from '../CardMoveStep'; import CardMoveStep from '../CardMoveStep';
import DeleteStep from '../DeleteStep'; import DeleteStep from '../DeleteStep';
import styles from './ActionsStep.module.scss'; import styles from './ActionsStep.module.scss';
const StepTypes = { const StepTypes = {
USERS: 'USERS', USERS: 'USERS',
LABELS: 'LABELS', LABELS: 'LABELS',
EDIT_DUE_DATE: 'EDIT_DUE_DATE', EDIT_DUE_DATE: 'EDIT_DUE_DATE',
EDIT_STOPWATCH: 'EDIT_STOPWATCH', EDIT_STOPWATCH: 'EDIT_STOPWATCH',
MOVE: 'MOVE', MOVE: 'MOVE',
DELETE: 'DELETE', DELETE: 'DELETE',
}; };
const ActionsStep = React.memo( const ActionsStep = React.memo(
({ ({
card, card,
projectsToLists, projectsToLists,
boardMemberships, boardMemberships,
currentUserIds, currentUserIds,
labels, labels,
currentLabelIds, currentLabelIds,
onNameEdit, onNameEdit,
onUpdate, onUpdate,
onMove, onMove,
onTransfer, onTransfer,
onDuplicate, onDuplicate,
onDelete, onDelete,
onUserAdd, onUserAdd,
onUserRemove, onUserRemove,
onBoardFetch, onBoardFetch,
onLabelAdd, onLabelAdd,
onLabelRemove, onLabelRemove,
onLabelCreate, onLabelCreate,
onLabelUpdate, onLabelUpdate,
onLabelMove, onLabelMove,
onLabelDelete, onLabelDelete,
onClose, onClose,
}) => { }) => {
const [t] = useTranslation(); const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps(); const [step, openStep, handleBack] = useSteps();
const handleEditNameClick = useCallback(() => { const handleEditNameClick = useCallback(() => {
onNameEdit(); onNameEdit();
onClose(); onClose();
}, [onNameEdit, onClose]); }, [onNameEdit, onClose]);
const handleUsersClick = useCallback(() => { const handleUsersClick = useCallback(() => {
openStep(StepTypes.USERS); openStep(StepTypes.USERS);
}, [openStep]); }, [openStep]);
const handleLabelsClick = useCallback(() => { const handleLabelsClick = useCallback(() => {
openStep(StepTypes.LABELS); openStep(StepTypes.LABELS);
}, [openStep]); }, [openStep]);
const handleEditDueDateClick = useCallback(() => { const handleEditDueDateClick = useCallback(() => {
openStep(StepTypes.EDIT_DUE_DATE); openStep(StepTypes.EDIT_DUE_DATE);
}, [openStep]); }, [openStep]);
const handleEditStopwatchClick = useCallback(() => { const handleEditStopwatchClick = useCallback(() => {
openStep(StepTypes.EDIT_STOPWATCH); openStep(StepTypes.EDIT_STOPWATCH);
}, [openStep]); }, [openStep]);
const handleMoveClick = useCallback(() => { const handleMoveClick = useCallback(() => {
openStep(StepTypes.MOVE); openStep(StepTypes.MOVE);
}, [openStep]); }, [openStep]);
const handleDuplicateClick = useCallback(() => { const handleDuplicateClick = useCallback(() => {
onDuplicate(); onDuplicate();
onClose(); onClose();
}, [onDuplicate, onClose]); }, [onDuplicate, onClose]);
const handleDeleteClick = useCallback(() => { const handleDeleteClick = useCallback(() => {
openStep(StepTypes.DELETE); openStep(StepTypes.DELETE);
}, [openStep]); }, [openStep]);
const handleDueDateUpdate = useCallback( const handleDueDateUpdate = useCallback(
(dueDate) => { (dueDate) => {
onUpdate({ onUpdate({
dueDate, dueDate,
}); });
}, },
[onUpdate], [onUpdate],
); );
const handleStopwatchUpdate = useCallback( const handleStopwatchUpdate = useCallback(
(stopwatch) => { (stopwatch) => {
onUpdate({ onUpdate({
stopwatch, stopwatch,
}); });
}, },
[onUpdate], [onUpdate],
); );
if (step) { if (step) {
switch (step.type) { switch (step.type) {
case StepTypes.USERS: case StepTypes.USERS:
return ( return (
<BoardMembershipsStep <BoardMembershipsStep
items={boardMemberships} items={boardMemberships}
currentUserIds={currentUserIds} currentUserIds={currentUserIds}
onUserSelect={onUserAdd} onUserSelect={onUserAdd}
onUserDeselect={onUserRemove} onUserDeselect={onUserRemove}
onBack={handleBack} onBack={handleBack}
/> />
); );
case StepTypes.LABELS: case StepTypes.LABELS:
return ( return (
<LabelsStep <LabelsStep
items={labels} items={labels}
currentIds={currentLabelIds} currentIds={currentLabelIds}
onSelect={onLabelAdd} onSelect={onLabelAdd}
onDeselect={onLabelRemove} onDeselect={onLabelRemove}
onCreate={onLabelCreate} onCreate={onLabelCreate}
onUpdate={onLabelUpdate} onUpdate={onLabelUpdate}
onMove={onLabelMove} onMove={onLabelMove}
onDelete={onLabelDelete} onDelete={onLabelDelete}
onBack={handleBack} onBack={handleBack}
/> />
); );
case StepTypes.EDIT_DUE_DATE: case StepTypes.EDIT_DUE_DATE:
return ( return (
<DueDateEditStep <DueDateEditStep
defaultValue={card.dueDate} defaultValue={card.dueDate}
onUpdate={handleDueDateUpdate} onUpdate={handleDueDateUpdate}
onBack={handleBack} onBack={handleBack}
onClose={onClose} onClose={onClose}
/> />
); );
case StepTypes.EDIT_STOPWATCH: case StepTypes.EDIT_STOPWATCH:
return ( return (
<StopwatchEditStep <StopwatchEditStep
defaultValue={card.stopwatch} defaultValue={card.stopwatch}
onUpdate={handleStopwatchUpdate} onUpdate={handleStopwatchUpdate}
onBack={handleBack} onBack={handleBack}
onClose={onClose} onClose={onClose}
/> />
); );
case StepTypes.MOVE: case StepTypes.MOVE:
return ( return (
<CardMoveStep <CardMoveStep
projectsToLists={projectsToLists} projectsToLists={projectsToLists}
defaultPath={pick(card, ['projectId', 'boardId', 'listId'])} defaultPath={pick(card, ['projectId', 'boardId', 'listId'])}
onMove={onMove} onMove={onMove}
onTransfer={onTransfer} onTransfer={onTransfer}
onBoardFetch={onBoardFetch} onBoardFetch={onBoardFetch}
onBack={handleBack} onBack={handleBack}
onClose={onClose} onClose={onClose}
/> />
); );
case StepTypes.DELETE: case StepTypes.DELETE:
return ( return (
<DeleteStep <DeleteStep
title="common.deleteCard" title="common.deleteCard"
content="common.areYouSureYouWantToDeleteThisCard" content="common.areYouSureYouWantToDeleteThisCard"
buttonContent="action.deleteCard" buttonContent="action.deleteCard"
onConfirm={onDelete} onConfirm={onDelete}
onBack={handleBack} onBack={handleBack}
/> />
); );
default: default:
} }
} }
return ( return (
<> <>
<Popup.Header> <Popup.Header>
{t('common.cardActions', { {t('common.cardActions', {
context: 'title', context: 'title',
})} })}
</Popup.Header> </Popup.Header>
<Popup.Content> <Popup.Content>
<Menu secondary vertical className={styles.menu}> <Menu secondary vertical className={styles.menu}>
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}> <Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
{t('action.editTitle', { {t('action.editTitle', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleUsersClick}> <Menu.Item className={styles.menuItem} onClick={handleUsersClick}>
{t('common.members', { {t('common.members', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleLabelsClick}> <Menu.Item className={styles.menuItem} onClick={handleLabelsClick}>
{t('common.labels', { {t('common.labels', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleEditDueDateClick}> <Menu.Item className={styles.menuItem} onClick={handleEditDueDateClick}>
{t('action.editDueDate', { {t('action.editDueDate', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleEditStopwatchClick}> <Menu.Item className={styles.menuItem} onClick={handleEditStopwatchClick}>
{t('action.editStopwatch', { {t('action.editStopwatch', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleMoveClick}> <Menu.Item className={styles.menuItem} onClick={handleMoveClick}>
{t('action.moveCard', { {t('action.moveCard', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleDuplicateClick}> <Menu.Item className={styles.menuItem} onClick={handleDuplicateClick}>
{t('action.duplicateCard', { {t('action.duplicateCard', {
context: 'title', context: 'title',
})} })}
</Menu.Item> </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',
})} })}
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Popup.Content> </Popup.Content>
</> </>
); );
}, },
); );
ActionsStep.propTypes = { ActionsStep.propTypes = {
/* eslint-disable react/forbid-prop-types */ /* eslint-disable react/forbid-prop-types */
card: PropTypes.object.isRequired, card: PropTypes.object.isRequired,
projectsToLists: PropTypes.array.isRequired, projectsToLists: PropTypes.array.isRequired,
boardMemberships: PropTypes.array.isRequired, boardMemberships: PropTypes.array.isRequired,
currentUserIds: PropTypes.array.isRequired, currentUserIds: PropTypes.array.isRequired,
labels: PropTypes.array.isRequired, labels: PropTypes.array.isRequired,
currentLabelIds: PropTypes.array.isRequired, currentLabelIds: PropTypes.array.isRequired,
/* eslint-enable react/forbid-prop-types */ /* eslint-enable react/forbid-prop-types */
onNameEdit: PropTypes.func.isRequired, onNameEdit: PropTypes.func.isRequired,
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, 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,
onBoardFetch: PropTypes.func.isRequired, onBoardFetch: PropTypes.func.isRequired,
onLabelAdd: PropTypes.func.isRequired, onLabelAdd: PropTypes.func.isRequired,
onLabelRemove: PropTypes.func.isRequired, onLabelRemove: PropTypes.func.isRequired,
onLabelCreate: PropTypes.func.isRequired, onLabelCreate: PropTypes.func.isRequired,
onLabelUpdate: PropTypes.func.isRequired, onLabelUpdate: PropTypes.func.isRequired,
onLabelMove: PropTypes.func.isRequired, onLabelMove: PropTypes.func.isRequired,
onLabelDelete: PropTypes.func.isRequired, onLabelDelete: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
export default ActionsStep; export default ActionsStep;

@ -1,262 +1,262 @@
import React, { useCallback, useRef } from 'react'; import React, { useCallback, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { Button, Icon } from 'semantic-ui-react'; import { Button, Icon } from 'semantic-ui-react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { usePopup } from '../../lib/popup'; import { usePopup } from '../../lib/popup';
import { startStopwatch, stopStopwatch } from '../../utils/stopwatch'; import { startStopwatch, stopStopwatch } from '../../utils/stopwatch';
import Paths from '../../constants/Paths'; import Paths from '../../constants/Paths';
import Tasks from './Tasks'; import Tasks from './Tasks';
import NameEdit from './NameEdit'; import NameEdit from './NameEdit';
import ActionsStep from './ActionsStep'; import ActionsStep from './ActionsStep';
import User from '../User'; import User from '../User';
import Label from '../Label'; import Label from '../Label';
import DueDate from '../DueDate'; import DueDate from '../DueDate';
import Stopwatch from '../Stopwatch'; import Stopwatch from '../Stopwatch';
import styles from './Card.module.scss'; import styles from './Card.module.scss';
const Card = React.memo( const Card = React.memo(
({ ({
id, id,
index, index,
name, name,
dueDate, dueDate,
stopwatch, stopwatch,
coverUrl, coverUrl,
boardId, boardId,
listId, listId,
projectId, projectId,
isPersisted, isPersisted,
notificationsTotal, notificationsTotal,
users, users,
labels, labels,
tasks, tasks,
allProjectsToLists, allProjectsToLists,
allBoardMemberships, allBoardMemberships,
allLabels, allLabels,
canEdit, canEdit,
onUpdate, onUpdate,
onMove, onMove,
onTransfer, onTransfer,
onDuplicate, onDuplicate,
onDelete, onDelete,
onUserAdd, onUserAdd,
onUserRemove, onUserRemove,
onBoardFetch, onBoardFetch,
onLabelAdd, onLabelAdd,
onLabelRemove, onLabelRemove,
onLabelCreate, onLabelCreate,
onLabelUpdate, onLabelUpdate,
onLabelMove, onLabelMove,
onLabelDelete, onLabelDelete,
}) => { }) => {
const nameEdit = useRef(null); const nameEdit = useRef(null);
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
if (document.activeElement) { if (document.activeElement) {
document.activeElement.blur(); document.activeElement.blur();
} }
}, []); }, []);
const handleToggleStopwatchClick = useCallback( const handleToggleStopwatchClick = useCallback(
(event) => { (event) => {
event.preventDefault(); event.preventDefault();
onUpdate({ onUpdate({
stopwatch: stopwatch.startedAt ? stopStopwatch(stopwatch) : startStopwatch(stopwatch), stopwatch: stopwatch.startedAt ? stopStopwatch(stopwatch) : startStopwatch(stopwatch),
}); });
}, },
[stopwatch, onUpdate], [stopwatch, onUpdate],
); );
const handleNameUpdate = useCallback( const handleNameUpdate = useCallback(
(newName) => { (newName) => {
onUpdate({ onUpdate({
name: newName, name: newName,
}); });
}, },
[onUpdate], [onUpdate],
); );
const handleNameEdit = useCallback(() => { const handleNameEdit = useCallback(() => {
nameEdit.current.open(); nameEdit.current.open();
}, []); }, []);
const ActionsPopup = usePopup(ActionsStep); const ActionsPopup = usePopup(ActionsStep);
const contentNode = ( const contentNode = (
<> <>
{coverUrl && <img src={coverUrl} alt="" className={styles.cover} />} {coverUrl && <img src={coverUrl} alt="" className={styles.cover} />}
<div className={styles.details}> <div className={styles.details}>
{labels.length > 0 && ( {labels.length > 0 && (
<span className={styles.labels}> <span className={styles.labels}>
{labels.map((label) => ( {labels.map((label) => (
<span <span
key={label.id} key={label.id}
className={classNames(styles.attachment, styles.attachmentLeft)} className={classNames(styles.attachment, styles.attachmentLeft)}
> >
<Label name={label.name} color={label.color} size="tiny" /> <Label name={label.name} color={label.color} size="tiny" />
</span> </span>
))} ))}
</span> </span>
)} )}
<div className={styles.name}>{name}</div> <div className={styles.name}>{name}</div>
{tasks.length > 0 && <Tasks items={tasks} />} {tasks.length > 0 && <Tasks items={tasks} />}
{(dueDate || stopwatch || notificationsTotal > 0) && ( {(dueDate || stopwatch || notificationsTotal > 0) && (
<span className={styles.attachments}> <span className={styles.attachments}>
{notificationsTotal > 0 && ( {notificationsTotal > 0 && (
<span <span
className={classNames( className={classNames(
styles.attachment, styles.attachment,
styles.attachmentLeft, styles.attachmentLeft,
styles.notification, styles.notification,
)} )}
> >
{notificationsTotal} {notificationsTotal}
</span> </span>
)} )}
{dueDate && ( {dueDate && (
<span className={classNames(styles.attachment, styles.attachmentLeft)}> <span className={classNames(styles.attachment, styles.attachmentLeft)}>
<DueDate value={dueDate} size="tiny" /> <DueDate value={dueDate} size="tiny" />
</span> </span>
)} )}
{stopwatch && ( {stopwatch && (
<span className={classNames(styles.attachment, styles.attachmentLeft)}> <span className={classNames(styles.attachment, styles.attachmentLeft)}>
<Stopwatch <Stopwatch
as="span" as="span"
startedAt={stopwatch.startedAt} startedAt={stopwatch.startedAt}
total={stopwatch.total} total={stopwatch.total}
size="tiny" size="tiny"
onClick={canEdit ? handleToggleStopwatchClick : undefined} onClick={canEdit ? handleToggleStopwatchClick : undefined}
/> />
</span> </span>
)} )}
</span> </span>
)} )}
{users.length > 0 && ( {users.length > 0 && (
<span className={classNames(styles.attachments, styles.attachmentsRight)}> <span className={classNames(styles.attachments, styles.attachmentsRight)}>
{users.map((user) => ( {users.map((user) => (
<span <span
key={user.id} key={user.id}
className={classNames(styles.attachment, styles.attachmentRight)} className={classNames(styles.attachment, styles.attachmentRight)}
> >
<User name={user.name} avatarUrl={user.avatarUrl} size="small" /> <User name={user.name} avatarUrl={user.avatarUrl} size="small" />
</span> </span>
))} ))}
</span> </span>
)} )}
</div> </div>
</> </>
); );
return ( return (
<Draggable draggableId={`card:${id}`} index={index} isDragDisabled={!isPersisted || !canEdit}> <Draggable draggableId={`card:${id}`} index={index} isDragDisabled={!isPersisted || !canEdit}>
{({ innerRef, draggableProps, dragHandleProps }) => ( {({ innerRef, draggableProps, dragHandleProps }) => (
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
<div {...draggableProps} {...dragHandleProps} ref={innerRef} className={styles.wrapper}> <div {...draggableProps} {...dragHandleProps} ref={innerRef} className={styles.wrapper}>
<NameEdit ref={nameEdit} defaultValue={name} onUpdate={handleNameUpdate}> <NameEdit ref={nameEdit} defaultValue={name} onUpdate={handleNameUpdate}>
<div className={styles.card}> <div className={styles.card}>
{isPersisted ? ( {isPersisted ? (
<> <>
<Link <Link
to={Paths.CARDS.replace(':id', id)} to={Paths.CARDS.replace(':id', id)}
className={styles.content} className={styles.content}
onClick={handleClick} onClick={handleClick}
> >
{contentNode} {contentNode}
</Link> </Link>
{canEdit && ( {canEdit && (
<ActionsPopup <ActionsPopup
card={{ card={{
dueDate, dueDate,
stopwatch, stopwatch,
boardId, boardId,
listId, listId,
projectId, projectId,
}} }}
projectsToLists={allProjectsToLists} projectsToLists={allProjectsToLists}
boardMemberships={allBoardMemberships} boardMemberships={allBoardMemberships}
currentUserIds={users.map((user) => user.id)} currentUserIds={users.map((user) => user.id)}
labels={allLabels} labels={allLabels}
currentLabelIds={labels.map((label) => label.id)} currentLabelIds={labels.map((label) => label.id)}
onNameEdit={handleNameEdit} onNameEdit={handleNameEdit}
onUpdate={onUpdate} onUpdate={onUpdate}
onMove={onMove} onMove={onMove}
onTransfer={onTransfer} onTransfer={onTransfer}
onDuplicate={onDuplicate} onDuplicate={onDuplicate}
onDelete={onDelete} onDelete={onDelete}
onUserAdd={onUserAdd} onUserAdd={onUserAdd}
onUserRemove={onUserRemove} onUserRemove={onUserRemove}
onBoardFetch={onBoardFetch} onBoardFetch={onBoardFetch}
onLabelAdd={onLabelAdd} onLabelAdd={onLabelAdd}
onLabelRemove={onLabelRemove} onLabelRemove={onLabelRemove}
onLabelCreate={onLabelCreate} onLabelCreate={onLabelCreate}
onLabelUpdate={onLabelUpdate} onLabelUpdate={onLabelUpdate}
onLabelMove={onLabelMove} onLabelMove={onLabelMove}
onLabelDelete={onLabelDelete} onLabelDelete={onLabelDelete}
> >
<Button className={classNames(styles.actionsButton, styles.target)}> <Button className={classNames(styles.actionsButton, styles.target)}>
<Icon fitted name="pencil" size="small" /> <Icon fitted name="pencil" size="small" />
</Button> </Button>
</ActionsPopup> </ActionsPopup>
)} )}
</> </>
) : ( ) : (
<span className={styles.content}>{contentNode}</span> <span className={styles.content}>{contentNode}</span>
)} )}
</div> </div>
</NameEdit> </NameEdit>
</div> </div>
)} )}
</Draggable> </Draggable>
); );
}, },
); );
Card.propTypes = { Card.propTypes = {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
index: PropTypes.number.isRequired, index: PropTypes.number.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
dueDate: PropTypes.instanceOf(Date), dueDate: PropTypes.instanceOf(Date),
stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types stopwatch: PropTypes.object, // eslint-disable-line react/forbid-prop-types
coverUrl: PropTypes.string, coverUrl: PropTypes.string,
boardId: PropTypes.string.isRequired, boardId: PropTypes.string.isRequired,
listId: PropTypes.string.isRequired, listId: PropTypes.string.isRequired,
projectId: PropTypes.string.isRequired, projectId: PropTypes.string.isRequired,
isPersisted: PropTypes.bool.isRequired, isPersisted: PropTypes.bool.isRequired,
notificationsTotal: PropTypes.number.isRequired, notificationsTotal: PropTypes.number.isRequired,
/* 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,
tasks: PropTypes.array.isRequired, tasks: PropTypes.array.isRequired,
allProjectsToLists: PropTypes.array.isRequired, allProjectsToLists: PropTypes.array.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 */
canEdit: PropTypes.bool.isRequired, canEdit: PropTypes.bool.isRequired,
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, 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,
onBoardFetch: PropTypes.func.isRequired, onBoardFetch: PropTypes.func.isRequired,
onLabelAdd: PropTypes.func.isRequired, onLabelAdd: PropTypes.func.isRequired,
onLabelRemove: PropTypes.func.isRequired, onLabelRemove: PropTypes.func.isRequired,
onLabelCreate: PropTypes.func.isRequired, onLabelCreate: PropTypes.func.isRequired,
onLabelUpdate: PropTypes.func.isRequired, onLabelUpdate: PropTypes.func.isRequired,
onLabelMove: PropTypes.func.isRequired, onLabelMove: PropTypes.func.isRequired,
onLabelDelete: PropTypes.func.isRequired, onLabelDelete: PropTypes.func.isRequired,
}; };
Card.defaultProps = { Card.defaultProps = {
dueDate: undefined, dueDate: undefined,
stopwatch: undefined, stopwatch: undefined,
coverUrl: undefined, coverUrl: undefined,
}; };
export default Card; export default Card;

File diff suppressed because it is too large Load Diff

@ -1,266 +1,266 @@
export default { export default {
/* Router */ /* Router */
LOCATION_CHANGE_HANDLE: 'LOCATION_CHANGE_HANDLE', LOCATION_CHANGE_HANDLE: 'LOCATION_CHANGE_HANDLE',
LOCATION_CHANGE_HANDLE__BOARD_FETCH: 'LOCATION_CHANGE_HANDLE__BOARD_FETCH', LOCATION_CHANGE_HANDLE__BOARD_FETCH: 'LOCATION_CHANGE_HANDLE__BOARD_FETCH',
/* Socket */ /* Socket */
SOCKET_DISCONNECT_HANDLE: 'SOCKET_DISCONNECT_HANDLE', SOCKET_DISCONNECT_HANDLE: 'SOCKET_DISCONNECT_HANDLE',
SOCKET_RECONNECT_HANDLE: 'SOCKET_RECONNECT_HANDLE', SOCKET_RECONNECT_HANDLE: 'SOCKET_RECONNECT_HANDLE',
SOCKET_RECONNECT_HANDLE__CORE_FETCH: 'SOCKET_RECONNECT_HANDLE__CORE_FETCH', SOCKET_RECONNECT_HANDLE__CORE_FETCH: 'SOCKET_RECONNECT_HANDLE__CORE_FETCH',
/* Login */ /* Login */
LOGIN_INITIALIZE: 'LOGIN_INITIALIZE', LOGIN_INITIALIZE: 'LOGIN_INITIALIZE',
AUTHENTICATE: 'AUTHENTICATE', AUTHENTICATE: 'AUTHENTICATE',
AUTHENTICATE__SUCCESS: 'AUTHENTICATE__SUCCESS', AUTHENTICATE__SUCCESS: 'AUTHENTICATE__SUCCESS',
AUTHENTICATE__FAILURE: 'AUTHENTICATE__FAILURE', AUTHENTICATE__FAILURE: 'AUTHENTICATE__FAILURE',
USING_OIDC_AUTHENTICATE: 'USING_OIDC_AUTHENTICATE', USING_OIDC_AUTHENTICATE: 'USING_OIDC_AUTHENTICATE',
USING_OIDC_AUTHENTICATE__SUCCESS: 'USING_OIDC_AUTHENTICATE__SUCCESS', USING_OIDC_AUTHENTICATE__SUCCESS: 'USING_OIDC_AUTHENTICATE__SUCCESS',
USING_OIDC_AUTHENTICATE__FAILURE: 'USING_OIDC_AUTHENTICATE__FAILURE', USING_OIDC_AUTHENTICATE__FAILURE: 'USING_OIDC_AUTHENTICATE__FAILURE',
AUTHENTICATE_ERROR_CLEAR: 'AUTHENTICATE_ERROR_CLEAR', AUTHENTICATE_ERROR_CLEAR: 'AUTHENTICATE_ERROR_CLEAR',
/* Core */ /* Core */
CORE_INITIALIZE: 'CORE_INITIALIZE', CORE_INITIALIZE: 'CORE_INITIALIZE',
CORE_INITIALIZE__CONFIG_FETCH: 'CORE_INITIALIZE__CONFIG_FETCH', CORE_INITIALIZE__CONFIG_FETCH: 'CORE_INITIALIZE__CONFIG_FETCH',
LOGOUT: 'LOGOUT', LOGOUT: 'LOGOUT',
LOGOUT__ACCESS_TOKEN_INVALIDATE: 'LOGOUT__ACCESS_TOKEN_INVALIDATE', LOGOUT__ACCESS_TOKEN_INVALIDATE: 'LOGOUT__ACCESS_TOKEN_INVALIDATE',
/* Modals */ /* Modals */
MODAL_OPEN: 'MODAL_OPEN', MODAL_OPEN: 'MODAL_OPEN',
MODAL_CLOSE: 'MODAL_CLOSE', MODAL_CLOSE: 'MODAL_CLOSE',
/* Users */ /* Users */
USER_CREATE: 'USER_CREATE', USER_CREATE: 'USER_CREATE',
USER_CREATE__SUCCESS: 'USER_CREATE__SUCCESS', USER_CREATE__SUCCESS: 'USER_CREATE__SUCCESS',
USER_CREATE__FAILURE: 'USER_CREATE__FAILURE', USER_CREATE__FAILURE: 'USER_CREATE__FAILURE',
USER_CREATE_HANDLE: 'USER_CREATE_HANDLE', USER_CREATE_HANDLE: 'USER_CREATE_HANDLE',
USER_CREATE_ERROR_CLEAR: 'USER_CREATE_ERROR_CLEAR', USER_CREATE_ERROR_CLEAR: 'USER_CREATE_ERROR_CLEAR',
USER_UPDATE: 'USER_UPDATE', USER_UPDATE: 'USER_UPDATE',
USER_UPDATE__SUCCESS: 'USER_UPDATE__SUCCESS', USER_UPDATE__SUCCESS: 'USER_UPDATE__SUCCESS',
USER_UPDATE__FAILURE: 'USER_UPDATE__FAILURE', USER_UPDATE__FAILURE: 'USER_UPDATE__FAILURE',
USER_UPDATE_HANDLE: 'USER_UPDATE_HANDLE', USER_UPDATE_HANDLE: 'USER_UPDATE_HANDLE',
USER_EMAIL_UPDATE: 'USER_EMAIL_UPDATE', USER_EMAIL_UPDATE: 'USER_EMAIL_UPDATE',
USER_EMAIL_UPDATE__SUCCESS: 'USER_EMAIL_UPDATE__SUCCESS', USER_EMAIL_UPDATE__SUCCESS: 'USER_EMAIL_UPDATE__SUCCESS',
USER_EMAIL_UPDATE__FAILURE: 'USER_EMAIL_UPDATE__FAILURE', USER_EMAIL_UPDATE__FAILURE: 'USER_EMAIL_UPDATE__FAILURE',
USER_EMAIL_UPDATE_ERROR_CLEAR: 'USER_EMAIL_UPDATE_ERROR_CLEAR', USER_EMAIL_UPDATE_ERROR_CLEAR: 'USER_EMAIL_UPDATE_ERROR_CLEAR',
USER_PASSWORD_UPDATE: 'USER_PASSWORD_UPDATE', USER_PASSWORD_UPDATE: 'USER_PASSWORD_UPDATE',
USER_PASSWORD_UPDATE__SUCCESS: 'USER_PASSWORD_UPDATE__SUCCESS', USER_PASSWORD_UPDATE__SUCCESS: 'USER_PASSWORD_UPDATE__SUCCESS',
USER_PASSWORD_UPDATE__FAILURE: 'USER_PASSWORD_UPDATE__FAILURE', USER_PASSWORD_UPDATE__FAILURE: 'USER_PASSWORD_UPDATE__FAILURE',
USER_PASSWORD_UPDATE_ERROR_CLEAR: 'USER_PASSWORD_UPDATE_ERROR_CLEAR', USER_PASSWORD_UPDATE_ERROR_CLEAR: 'USER_PASSWORD_UPDATE_ERROR_CLEAR',
USER_USERNAME_UPDATE: 'USER_USERNAME_UPDATE', USER_USERNAME_UPDATE: 'USER_USERNAME_UPDATE',
USER_USERNAME_UPDATE__SUCCESS: 'USER_USERNAME_UPDATE__SUCCESS', USER_USERNAME_UPDATE__SUCCESS: 'USER_USERNAME_UPDATE__SUCCESS',
USER_USERNAME_UPDATE__FAILURE: 'USER_USERNAME_UPDATE__FAILURE', USER_USERNAME_UPDATE__FAILURE: 'USER_USERNAME_UPDATE__FAILURE',
USER_USERNAME_UPDATE_ERROR_CLEAR: 'USER_USERNAME_UPDATE_ERROR_CLEAR', USER_USERNAME_UPDATE_ERROR_CLEAR: 'USER_USERNAME_UPDATE_ERROR_CLEAR',
USER_AVATAR_UPDATE: 'USER_AVATAR_UPDATE', USER_AVATAR_UPDATE: 'USER_AVATAR_UPDATE',
USER_AVATAR_UPDATE__SUCCESS: 'USER_AVATAR_UPDATE__SUCCESS', USER_AVATAR_UPDATE__SUCCESS: 'USER_AVATAR_UPDATE__SUCCESS',
USER_AVATAR_UPDATE__FAILURE: 'USER_AVATAR_UPDATE__FAILURE', USER_AVATAR_UPDATE__FAILURE: 'USER_AVATAR_UPDATE__FAILURE',
USER_DELETE: 'USER_DELETE', USER_DELETE: 'USER_DELETE',
USER_DELETE__SUCCESS: 'USER_DELETE__SUCCESS', USER_DELETE__SUCCESS: 'USER_DELETE__SUCCESS',
USER_DELETE__FAILURE: 'USER_DELETE__FAILURE', USER_DELETE__FAILURE: 'USER_DELETE__FAILURE',
USER_DELETE_HANDLE: 'USER_DELETE_HANDLE', USER_DELETE_HANDLE: 'USER_DELETE_HANDLE',
USER_TO_CARD_ADD: 'USER_TO_CARD_ADD', USER_TO_CARD_ADD: 'USER_TO_CARD_ADD',
USER_TO_CARD_ADD__SUCCESS: 'USER_TO_CARD_ADD__SUCCESS', USER_TO_CARD_ADD__SUCCESS: 'USER_TO_CARD_ADD__SUCCESS',
USER_TO_CARD_ADD__FAILURE: 'USER_TO_CARD_ADD__FAILURE', USER_TO_CARD_ADD__FAILURE: 'USER_TO_CARD_ADD__FAILURE',
USER_TO_CARD_ADD_HANDLE: 'USER_TO_CARD_ADD_HANDLE', USER_TO_CARD_ADD_HANDLE: 'USER_TO_CARD_ADD_HANDLE',
USER_FROM_CARD_REMOVE: 'USER_FROM_CARD_REMOVE', USER_FROM_CARD_REMOVE: 'USER_FROM_CARD_REMOVE',
USER_FROM_CARD_REMOVE__SUCCESS: 'USER_FROM_CARD_REMOVE__SUCCESS', USER_FROM_CARD_REMOVE__SUCCESS: 'USER_FROM_CARD_REMOVE__SUCCESS',
USER_FROM_CARD_REMOVE__FAILURE: 'USER_FROM_CARD_REMOVE__FAILURE', USER_FROM_CARD_REMOVE__FAILURE: 'USER_FROM_CARD_REMOVE__FAILURE',
USER_FROM_CARD_REMOVE_HANDLE: 'USER_FROM_CARD_REMOVE_HANDLE', USER_FROM_CARD_REMOVE_HANDLE: 'USER_FROM_CARD_REMOVE_HANDLE',
USER_TO_BOARD_FILTER_ADD: 'USER_TO_BOARD_FILTER_ADD', USER_TO_BOARD_FILTER_ADD: 'USER_TO_BOARD_FILTER_ADD',
USER_FROM_BOARD_FILTER_REMOVE: 'USER_FROM_BOARD_FILTER_REMOVE', USER_FROM_BOARD_FILTER_REMOVE: 'USER_FROM_BOARD_FILTER_REMOVE',
/* Projects */ /* Projects */
PROJECT_CREATE: 'PROJECT_CREATE', PROJECT_CREATE: 'PROJECT_CREATE',
PROJECT_CREATE__SUCCESS: 'PROJECT_CREATE__SUCCESS', PROJECT_CREATE__SUCCESS: 'PROJECT_CREATE__SUCCESS',
PROJECT_CREATE__FAILURE: 'PROJECT_CREATE__FAILURE', PROJECT_CREATE__FAILURE: 'PROJECT_CREATE__FAILURE',
PROJECT_CREATE_HANDLE: 'PROJECT_CREATE_HANDLE', PROJECT_CREATE_HANDLE: 'PROJECT_CREATE_HANDLE',
PROJECT_UPDATE: 'PROJECT_UPDATE', PROJECT_UPDATE: 'PROJECT_UPDATE',
PROJECT_UPDATE__SUCCESS: 'PROJECT_UPDATE__SUCCESS', PROJECT_UPDATE__SUCCESS: 'PROJECT_UPDATE__SUCCESS',
PROJECT_UPDATE__FAILURE: 'PROJECT_UPDATE__FAILURE', PROJECT_UPDATE__FAILURE: 'PROJECT_UPDATE__FAILURE',
PROJECT_UPDATE_HANDLE: 'PROJECT_UPDATE_HANDLE', PROJECT_UPDATE_HANDLE: 'PROJECT_UPDATE_HANDLE',
PROJECT_BACKGROUND_IMAGE_UPDATE: 'PROJECT_BACKGROUND_IMAGE_UPDATE', PROJECT_BACKGROUND_IMAGE_UPDATE: 'PROJECT_BACKGROUND_IMAGE_UPDATE',
PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS: 'PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS', PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS: 'PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS',
PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE: 'PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE', PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE: 'PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE',
PROJECT_DELETE: 'PROJECT_DELETE', PROJECT_DELETE: 'PROJECT_DELETE',
PROJECT_DELETE__SUCCESS: 'PROJECT_DELETE__SUCCESS', PROJECT_DELETE__SUCCESS: 'PROJECT_DELETE__SUCCESS',
PROJECT_DELETE__FAILURE: 'PROJECT_DELETE__FAILURE', PROJECT_DELETE__FAILURE: 'PROJECT_DELETE__FAILURE',
PROJECT_DELETE_HANDLE: 'PROJECT_DELETE_HANDLE', PROJECT_DELETE_HANDLE: 'PROJECT_DELETE_HANDLE',
/* Project managers */ /* Project managers */
PROJECT_MANAGER_CREATE: 'PROJECT_MANAGER_CREATE', PROJECT_MANAGER_CREATE: 'PROJECT_MANAGER_CREATE',
PROJECT_MANAGER_CREATE__SUCCESS: 'PROJECT_MANAGER_CREATE__SUCCESS', PROJECT_MANAGER_CREATE__SUCCESS: 'PROJECT_MANAGER_CREATE__SUCCESS',
PROJECT_MANAGER_CREATE__FAILURE: 'PROJECT_MANAGER_CREATE__FAILURE', PROJECT_MANAGER_CREATE__FAILURE: 'PROJECT_MANAGER_CREATE__FAILURE',
PROJECT_MANAGER_CREATE_HANDLE: 'PROJECT_MANAGER_CREATE_HANDLE', PROJECT_MANAGER_CREATE_HANDLE: 'PROJECT_MANAGER_CREATE_HANDLE',
PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH: 'PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH', PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH: 'PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH',
PROJECT_MANAGER_DELETE: 'PROJECT_MANAGER_DELETE', PROJECT_MANAGER_DELETE: 'PROJECT_MANAGER_DELETE',
PROJECT_MANAGER_DELETE__SUCCESS: 'PROJECT_MANAGER_DELETE__SUCCESS', PROJECT_MANAGER_DELETE__SUCCESS: 'PROJECT_MANAGER_DELETE__SUCCESS',
PROJECT_MANAGER_DELETE__FAILURE: 'PROJECT_MANAGER_DELETE__FAILURE', PROJECT_MANAGER_DELETE__FAILURE: 'PROJECT_MANAGER_DELETE__FAILURE',
PROJECT_MANAGER_DELETE_HANDLE: 'PROJECT_MANAGER_DELETE_HANDLE', PROJECT_MANAGER_DELETE_HANDLE: 'PROJECT_MANAGER_DELETE_HANDLE',
/* Boards */ /* Boards */
BOARD_CREATE: 'BOARD_CREATE', BOARD_CREATE: 'BOARD_CREATE',
BOARD_CREATE__SUCCESS: 'BOARD_CREATE__SUCCESS', BOARD_CREATE__SUCCESS: 'BOARD_CREATE__SUCCESS',
BOARD_CREATE__FAILURE: 'BOARD_CREATE__FAILURE', BOARD_CREATE__FAILURE: 'BOARD_CREATE__FAILURE',
BOARD_CREATE_HANDLE: 'BOARD_CREATE_HANDLE', BOARD_CREATE_HANDLE: 'BOARD_CREATE_HANDLE',
BOARD_FETCH: 'BOARD_FETCH', BOARD_FETCH: 'BOARD_FETCH',
BOARD_FETCH__SUCCESS: 'BOARD_FETCH__SUCCESS', BOARD_FETCH__SUCCESS: 'BOARD_FETCH__SUCCESS',
BOARD_FETCH__FAILURE: 'BOARD_FETCH__FAILURE', BOARD_FETCH__FAILURE: 'BOARD_FETCH__FAILURE',
BOARD_UPDATE: 'BOARD_UPDATE', BOARD_UPDATE: 'BOARD_UPDATE',
BOARD_UPDATE__SUCCESS: 'BOARD_UPDATE__SUCCESS', BOARD_UPDATE__SUCCESS: 'BOARD_UPDATE__SUCCESS',
BOARD_UPDATE__FAILURE: 'BOARD_UPDATE__FAILURE', BOARD_UPDATE__FAILURE: 'BOARD_UPDATE__FAILURE',
BOARD_UPDATE_HANDLE: 'BOARD_UPDATE_HANDLE', BOARD_UPDATE_HANDLE: 'BOARD_UPDATE_HANDLE',
BOARD_DELETE: 'BOARD_DELETE', BOARD_DELETE: 'BOARD_DELETE',
BOARD_DELETE__SUCCESS: 'BOARD_DELETE__SUCCESS', BOARD_DELETE__SUCCESS: 'BOARD_DELETE__SUCCESS',
BOARD_DELETE__FAILURE: 'BOARD_DELETE__FAILURE', BOARD_DELETE__FAILURE: 'BOARD_DELETE__FAILURE',
BOARD_DELETE_HANDLE: 'BOARD_DELETE_HANDLE', BOARD_DELETE_HANDLE: 'BOARD_DELETE_HANDLE',
/* Board memberships */ /* Board memberships */
BOARD_MEMBERSHIP_CREATE: 'BOARD_MEMBERSHIP_CREATE', BOARD_MEMBERSHIP_CREATE: 'BOARD_MEMBERSHIP_CREATE',
BOARD_MEMBERSHIP_CREATE__SUCCESS: 'BOARD_MEMBERSHIP_CREATE__SUCCESS', BOARD_MEMBERSHIP_CREATE__SUCCESS: 'BOARD_MEMBERSHIP_CREATE__SUCCESS',
BOARD_MEMBERSHIP_CREATE__FAILURE: 'BOARD_MEMBERSHIP_CREATE__FAILURE', BOARD_MEMBERSHIP_CREATE__FAILURE: 'BOARD_MEMBERSHIP_CREATE__FAILURE',
BOARD_MEMBERSHIP_CREATE_HANDLE: 'BOARD_MEMBERSHIP_CREATE_HANDLE', BOARD_MEMBERSHIP_CREATE_HANDLE: 'BOARD_MEMBERSHIP_CREATE_HANDLE',
BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH: 'BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH', BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH: 'BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH',
BOARD_MEMBERSHIP_UPDATE: 'BOARD_MEMBERSHIP_UPDATE', BOARD_MEMBERSHIP_UPDATE: 'BOARD_MEMBERSHIP_UPDATE',
BOARD_MEMBERSHIP_UPDATE__SUCCESS: 'BOARD_MEMBERSHIP_UPDATE__SUCCESS', BOARD_MEMBERSHIP_UPDATE__SUCCESS: 'BOARD_MEMBERSHIP_UPDATE__SUCCESS',
BOARD_MEMBERSHIP_UPDATE__FAILURE: 'BOARD_MEMBERSHIP_UPDATE__FAILURE', BOARD_MEMBERSHIP_UPDATE__FAILURE: 'BOARD_MEMBERSHIP_UPDATE__FAILURE',
BOARD_MEMBERSHIP_UPDATE_HANDLE: 'BOARD_MEMBERSHIP_UPDATE_HANDLE', BOARD_MEMBERSHIP_UPDATE_HANDLE: 'BOARD_MEMBERSHIP_UPDATE_HANDLE',
BOARD_MEMBERSHIP_DELETE: 'BOARD_MEMBERSHIP_DELETE', BOARD_MEMBERSHIP_DELETE: 'BOARD_MEMBERSHIP_DELETE',
BOARD_MEMBERSHIP_DELETE__SUCCESS: 'BOARD_MEMBERSHIP_DELETE__SUCCESS', BOARD_MEMBERSHIP_DELETE__SUCCESS: 'BOARD_MEMBERSHIP_DELETE__SUCCESS',
BOARD_MEMBERSHIP_DELETE__FAILURE: 'BOARD_MEMBERSHIP_DELETE__FAILURE', BOARD_MEMBERSHIP_DELETE__FAILURE: 'BOARD_MEMBERSHIP_DELETE__FAILURE',
BOARD_MEMBERSHIP_DELETE_HANDLE: 'BOARD_MEMBERSHIP_DELETE_HANDLE', BOARD_MEMBERSHIP_DELETE_HANDLE: 'BOARD_MEMBERSHIP_DELETE_HANDLE',
/* Labels */ /* Labels */
LABEL_CREATE: 'LABEL_CREATE', LABEL_CREATE: 'LABEL_CREATE',
LABEL_CREATE__SUCCESS: 'LABEL_CREATE__SUCCESS', LABEL_CREATE__SUCCESS: 'LABEL_CREATE__SUCCESS',
LABEL_CREATE__FAILURE: 'LABEL_CREATE__FAILURE', LABEL_CREATE__FAILURE: 'LABEL_CREATE__FAILURE',
LABEL_CREATE_HANDLE: 'LABEL_CREATE_HANDLE', LABEL_CREATE_HANDLE: 'LABEL_CREATE_HANDLE',
LABEL_UPDATE: 'LABEL_UPDATE', LABEL_UPDATE: 'LABEL_UPDATE',
LABEL_UPDATE__SUCCESS: 'LABEL_UPDATE__SUCCESS', LABEL_UPDATE__SUCCESS: 'LABEL_UPDATE__SUCCESS',
LABEL_UPDATE__FAILURE: 'LABEL_UPDATE__FAILURE', LABEL_UPDATE__FAILURE: 'LABEL_UPDATE__FAILURE',
LABEL_UPDATE_HANDLE: 'LABEL_UPDATE_HANDLE', LABEL_UPDATE_HANDLE: 'LABEL_UPDATE_HANDLE',
LABEL_DELETE: 'LABEL_DELETE', LABEL_DELETE: 'LABEL_DELETE',
LABEL_DELETE__SUCCESS: 'LABEL_DELETE__SUCCESS', LABEL_DELETE__SUCCESS: 'LABEL_DELETE__SUCCESS',
LABEL_DELETE__FAILURE: 'LABEL_DELETE__FAILURE', LABEL_DELETE__FAILURE: 'LABEL_DELETE__FAILURE',
LABEL_DELETE_HANDLE: 'LABEL_DELETE_HANDLE', LABEL_DELETE_HANDLE: 'LABEL_DELETE_HANDLE',
LABEL_TO_CARD_ADD: 'LABEL_TO_CARD_ADD', LABEL_TO_CARD_ADD: 'LABEL_TO_CARD_ADD',
LABEL_TO_CARD_ADD__SUCCESS: 'LABEL_TO_CARD_ADD__SUCCESS', LABEL_TO_CARD_ADD__SUCCESS: 'LABEL_TO_CARD_ADD__SUCCESS',
LABEL_TO_CARD_ADD__FAILURE: 'LABEL_TO_CARD_ADD__FAILURE', LABEL_TO_CARD_ADD__FAILURE: 'LABEL_TO_CARD_ADD__FAILURE',
LABEL_TO_CARD_ADD_HANDLE: 'LABEL_TO_CARD_ADD_HANDLE', LABEL_TO_CARD_ADD_HANDLE: 'LABEL_TO_CARD_ADD_HANDLE',
LABEL_FROM_CARD_REMOVE: 'LABEL_FROM_CARD_REMOVE', LABEL_FROM_CARD_REMOVE: 'LABEL_FROM_CARD_REMOVE',
LABEL_FROM_CARD_REMOVE__SUCCESS: 'LABEL_FROM_CARD_REMOVE__SUCCESS', LABEL_FROM_CARD_REMOVE__SUCCESS: 'LABEL_FROM_CARD_REMOVE__SUCCESS',
LABEL_FROM_CARD_REMOVE__FAILURE: 'LABEL_FROM_CARD_REMOVE__FAILURE', LABEL_FROM_CARD_REMOVE__FAILURE: 'LABEL_FROM_CARD_REMOVE__FAILURE',
LABEL_FROM_CARD_REMOVE_HANDLE: 'LABEL_FROM_CARD_REMOVE_HANDLE', LABEL_FROM_CARD_REMOVE_HANDLE: 'LABEL_FROM_CARD_REMOVE_HANDLE',
LABEL_TO_BOARD_FILTER_ADD: 'LABEL_TO_BOARD_FILTER_ADD', LABEL_TO_BOARD_FILTER_ADD: 'LABEL_TO_BOARD_FILTER_ADD',
LABEL_FROM_BOARD_FILTER_REMOVE: 'LABEL_FROM_BOARD_FILTER_REMOVE', LABEL_FROM_BOARD_FILTER_REMOVE: 'LABEL_FROM_BOARD_FILTER_REMOVE',
/* Lists */ /* Lists */
LIST_CREATE: 'LIST_CREATE', LIST_CREATE: 'LIST_CREATE',
LIST_CREATE__SUCCESS: 'LIST_CREATE__SUCCESS', LIST_CREATE__SUCCESS: 'LIST_CREATE__SUCCESS',
LIST_CREATE__FAILURE: 'LIST_CREATE__FAILURE', LIST_CREATE__FAILURE: 'LIST_CREATE__FAILURE',
LIST_CREATE_HANDLE: 'LIST_CREATE_HANDLE', LIST_CREATE_HANDLE: 'LIST_CREATE_HANDLE',
LIST_UPDATE: 'LIST_UPDATE', LIST_UPDATE: 'LIST_UPDATE',
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_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',
LIST_DELETE_HANDLE: 'LIST_DELETE_HANDLE', LIST_DELETE_HANDLE: 'LIST_DELETE_HANDLE',
/* Cards */ /* Cards */
CARD_CREATE: 'CARD_CREATE', CARD_CREATE: 'CARD_CREATE',
CARD_CREATE__SUCCESS: 'CARD_CREATE__SUCCESS', CARD_CREATE__SUCCESS: 'CARD_CREATE__SUCCESS',
CARD_CREATE__FAILURE: 'CARD_CREATE__FAILURE', CARD_CREATE__FAILURE: 'CARD_CREATE__FAILURE',
CARD_CREATE_HANDLE: 'CARD_CREATE_HANDLE', CARD_CREATE_HANDLE: 'CARD_CREATE_HANDLE',
CARD_FETCH: 'CARD_FETCH', CARD_FETCH: 'CARD_FETCH',
CARD_FETCH__SUCCESS: 'CARD_FETCH__SUCCESS', CARD_FETCH__SUCCESS: 'CARD_FETCH__SUCCESS',
CARD_FETCH__FAILURE: 'CARD_FETCH__FAILURE', CARD_FETCH__FAILURE: 'CARD_FETCH__FAILURE',
CARD_UPDATE: 'CARD_UPDATE', CARD_UPDATE: 'CARD_UPDATE',
CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS', CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS',
CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE', CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE',
CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE', CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE',
CARD_TRANSFER: 'CARD_TRANSFER', CARD_TRANSFER: '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: 'CARD_DUPLICATE',
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS', CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE', CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
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',
/* Tasks */ /* Tasks */
TASK_CREATE: 'TASK_CREATE', TASK_CREATE: 'TASK_CREATE',
TASK_CREATE__SUCCESS: 'TASK_CREATE__SUCCESS', TASK_CREATE__SUCCESS: 'TASK_CREATE__SUCCESS',
TASK_CREATE__FAILURE: 'TASK_CREATE__FAILURE', TASK_CREATE__FAILURE: 'TASK_CREATE__FAILURE',
TASK_CREATE_HANDLE: 'TASK_CREATE_HANDLE', TASK_CREATE_HANDLE: 'TASK_CREATE_HANDLE',
TASK_UPDATE: 'TASK_UPDATE', TASK_UPDATE: 'TASK_UPDATE',
TASK_UPDATE__SUCCESS: 'TASK_UPDATE__SUCCESS', TASK_UPDATE__SUCCESS: 'TASK_UPDATE__SUCCESS',
TASK_UPDATE__FAILURE: 'TASK_UPDATE__FAILURE', TASK_UPDATE__FAILURE: 'TASK_UPDATE__FAILURE',
TASK_UPDATE_HANDLE: 'TASK_UPDATE_HANDLE', TASK_UPDATE_HANDLE: 'TASK_UPDATE_HANDLE',
TASK_DELETE: 'TASK_DELETE', TASK_DELETE: 'TASK_DELETE',
TASK_DELETE__SUCCESS: 'TASK_DELETE__SUCCESS', TASK_DELETE__SUCCESS: 'TASK_DELETE__SUCCESS',
TASK_DELETE__FAILURE: 'TASK_DELETE__FAILURE', TASK_DELETE__FAILURE: 'TASK_DELETE__FAILURE',
TASK_DELETE_HANDLE: 'TASK_DELETE_HANDLE', TASK_DELETE_HANDLE: 'TASK_DELETE_HANDLE',
/* Attachments */ /* Attachments */
ATTACHMENT_CREATE: 'ATTACHMENT_CREATE', ATTACHMENT_CREATE: 'ATTACHMENT_CREATE',
ATTACHMENT_CREATE__SUCCESS: 'ATTACHMENT_CREATE__SUCCESS', ATTACHMENT_CREATE__SUCCESS: 'ATTACHMENT_CREATE__SUCCESS',
ATTACHMENT_CREATE__FAILURE: 'ATTACHMENT_CREATE__FAILURE', ATTACHMENT_CREATE__FAILURE: 'ATTACHMENT_CREATE__FAILURE',
ATTACHMENT_CREATE_HANDLE: 'ATTACHMENT_CREATE_HANDLE', ATTACHMENT_CREATE_HANDLE: 'ATTACHMENT_CREATE_HANDLE',
ATTACHMENT_UPDATE: 'ATTACHMENT_UPDATE', ATTACHMENT_UPDATE: 'ATTACHMENT_UPDATE',
ATTACHMENT_UPDATE__SUCCESS: 'ATTACHMENT_UPDATE__SUCCESS', ATTACHMENT_UPDATE__SUCCESS: 'ATTACHMENT_UPDATE__SUCCESS',
ATTACHMENT_UPDATE__FAILURE: 'ATTACHMENT_UPDATE__FAILURE', ATTACHMENT_UPDATE__FAILURE: 'ATTACHMENT_UPDATE__FAILURE',
ATTACHMENT_UPDATE_HANDLE: 'ATTACHMENT_UPDATE_HANDLE', ATTACHMENT_UPDATE_HANDLE: 'ATTACHMENT_UPDATE_HANDLE',
ATTACHMENT_DELETE: 'ATTACHMENT_DELETE', ATTACHMENT_DELETE: 'ATTACHMENT_DELETE',
ATTACHMENT_DELETE__SUCCESS: 'ATTACHMENT_DELETE__SUCCESS', ATTACHMENT_DELETE__SUCCESS: 'ATTACHMENT_DELETE__SUCCESS',
ATTACHMENT_DELETE__FAILURE: 'ATTACHMENT_DELETE__FAILURE', ATTACHMENT_DELETE__FAILURE: 'ATTACHMENT_DELETE__FAILURE',
ATTACHMENT_DELETE_HANDLE: 'ATTACHMENT_DELETE_HANDLE', ATTACHMENT_DELETE_HANDLE: 'ATTACHMENT_DELETE_HANDLE',
/* Activities */ /* Activities */
ACTIVITIES_FETCH: 'ACTIVITIES_FETCH', ACTIVITIES_FETCH: 'ACTIVITIES_FETCH',
ACTIVITIES_FETCH__SUCCESS: 'ACTIVITIES_FETCH__SUCCESS', ACTIVITIES_FETCH__SUCCESS: 'ACTIVITIES_FETCH__SUCCESS',
ACTIVITIES_FETCH__FAILURE: 'ACTIVITIES_FETCH__FAILURE', ACTIVITIES_FETCH__FAILURE: 'ACTIVITIES_FETCH__FAILURE',
ACTIVITIES_DETAILS_TOGGLE: 'ACTIVITIES_DETAILS_TOGGLE', ACTIVITIES_DETAILS_TOGGLE: 'ACTIVITIES_DETAILS_TOGGLE',
ACTIVITIES_DETAILS_TOGGLE__SUCCESS: 'ACTIVITIES_DETAILS_TOGGLE__SUCCESS', ACTIVITIES_DETAILS_TOGGLE__SUCCESS: 'ACTIVITIES_DETAILS_TOGGLE__SUCCESS',
ACTIVITIES_DETAILS_TOGGLE__FAILURE: 'ACTIVITIES_DETAILS_TOGGLE__FAILURE', ACTIVITIES_DETAILS_TOGGLE__FAILURE: 'ACTIVITIES_DETAILS_TOGGLE__FAILURE',
ACTIVITY_CREATE_HANDLE: 'ACTIVITY_CREATE_HANDLE', ACTIVITY_CREATE_HANDLE: 'ACTIVITY_CREATE_HANDLE',
ACTIVITY_UPDATE_HANDLE: 'ACTIVITY_UPDATE_HANDLE', ACTIVITY_UPDATE_HANDLE: 'ACTIVITY_UPDATE_HANDLE',
ACTIVITY_DELETE_HANDLE: 'ACTIVITY_DELETE_HANDLE', ACTIVITY_DELETE_HANDLE: 'ACTIVITY_DELETE_HANDLE',
/* Comment activities */ /* Comment activities */
COMMENT_ACTIVITY_CREATE: 'COMMENT_ACTIVITY_CREATE', COMMENT_ACTIVITY_CREATE: 'COMMENT_ACTIVITY_CREATE',
COMMENT_ACTIVITY_CREATE__SUCCESS: 'COMMENT_ACTIVITY_CREATE__SUCCESS', COMMENT_ACTIVITY_CREATE__SUCCESS: 'COMMENT_ACTIVITY_CREATE__SUCCESS',
COMMENT_ACTIVITY_CREATE__FAILURE: 'COMMENT_ACTIVITY_CREATE__FAILURE', COMMENT_ACTIVITY_CREATE__FAILURE: 'COMMENT_ACTIVITY_CREATE__FAILURE',
COMMENT_ACTIVITY_UPDATE: 'COMMENT_ACTIVITY_UPDATE', COMMENT_ACTIVITY_UPDATE: 'COMMENT_ACTIVITY_UPDATE',
COMMENT_ACTIVITY_UPDATE__SUCCESS: 'COMMENT_ACTIVITY_UPDATE__SUCCESS', COMMENT_ACTIVITY_UPDATE__SUCCESS: 'COMMENT_ACTIVITY_UPDATE__SUCCESS',
COMMENT_ACTIVITY_UPDATE__FAILURE: 'COMMENT_ACTIVITY_UPDATE__FAILURE', COMMENT_ACTIVITY_UPDATE__FAILURE: 'COMMENT_ACTIVITY_UPDATE__FAILURE',
COMMENT_ACTIVITY_DELETE: 'COMMENT_ACTIVITY_DELETE', COMMENT_ACTIVITY_DELETE: 'COMMENT_ACTIVITY_DELETE',
COMMENT_ACTIVITY_DELETE__SUCCESS: 'COMMENT_ACTIVITY_DELETE__SUCCESS', COMMENT_ACTIVITY_DELETE__SUCCESS: 'COMMENT_ACTIVITY_DELETE__SUCCESS',
COMMENT_ACTIVITY_DELETE__FAILURE: 'COMMENT_ACTIVITY_DELETE__FAILURE', COMMENT_ACTIVITY_DELETE__FAILURE: 'COMMENT_ACTIVITY_DELETE__FAILURE',
/* Notifications */ /* Notifications */
NOTIFICATION_CREATE_HANDLE: 'NOTIFICATION_CREATE_HANDLE', NOTIFICATION_CREATE_HANDLE: 'NOTIFICATION_CREATE_HANDLE',
NOTIFICATION_DELETE: 'NOTIFICATION_DELETE', NOTIFICATION_DELETE: 'NOTIFICATION_DELETE',
NOTIFICATION_DELETE__SUCCESS: 'NOTIFICATION_DELETE__SUCCESS', NOTIFICATION_DELETE__SUCCESS: 'NOTIFICATION_DELETE__SUCCESS',
NOTIFICATION_DELETE__FAILURE: 'NOTIFICATION_DELETE__FAILURE', NOTIFICATION_DELETE__FAILURE: 'NOTIFICATION_DELETE__FAILURE',
NOTIFICATION_DELETE_HANDLE: 'NOTIFICATION_DELETE_HANDLE', NOTIFICATION_DELETE_HANDLE: 'NOTIFICATION_DELETE_HANDLE',
}; };

@ -1,179 +1,179 @@
const PREFIX = '@entry'; const PREFIX = '@entry';
export default { export default {
PREFIX, PREFIX,
/* Socket */ /* Socket */
SOCKET_DISCONNECT_HANDLE: `${PREFIX}/SOCKET_DISCONNECT_HANDLE`, SOCKET_DISCONNECT_HANDLE: `${PREFIX}/SOCKET_DISCONNECT_HANDLE`,
SOCKET_RECONNECT_HANDLE: `${PREFIX}/SOCKET_RECONNECT_HANDLE`, SOCKET_RECONNECT_HANDLE: `${PREFIX}/SOCKET_RECONNECT_HANDLE`,
/* Login */ /* Login */
AUTHENTICATE: `${PREFIX}/AUTHENTICATE`, AUTHENTICATE: `${PREFIX}/AUTHENTICATE`,
USING_OIDC_AUTHENTICATE: `${PREFIX}/USING_OIDC_AUTHENTICATE`, USING_OIDC_AUTHENTICATE: `${PREFIX}/USING_OIDC_AUTHENTICATE`,
AUTHENTICATE_ERROR_CLEAR: `${PREFIX}/AUTHENTICATE_ERROR_CLEAR`, AUTHENTICATE_ERROR_CLEAR: `${PREFIX}/AUTHENTICATE_ERROR_CLEAR`,
/* Core */ /* Core */
LOGOUT: `${PREFIX}/LOGOUT`, LOGOUT: `${PREFIX}/LOGOUT`,
/* Modals */ /* Modals */
MODAL_OPEN: `${PREFIX}/MODAL_OPEN`, MODAL_OPEN: `${PREFIX}/MODAL_OPEN`,
MODAL_CLOSE: `${PREFIX}/MODAL_CLOSE`, MODAL_CLOSE: `${PREFIX}/MODAL_CLOSE`,
/* Users */ /* Users */
USER_CREATE: `${PREFIX}/USER_CREATE`, USER_CREATE: `${PREFIX}/USER_CREATE`,
USER_CREATE_HANDLE: `${PREFIX}/USER_CREATE_HANDLE`, USER_CREATE_HANDLE: `${PREFIX}/USER_CREATE_HANDLE`,
USER_CREATE_ERROR_CLEAR: `${PREFIX}/USER_CREATE_ERROR_CLEAR`, USER_CREATE_ERROR_CLEAR: `${PREFIX}/USER_CREATE_ERROR_CLEAR`,
USER_UPDATE: `${PREFIX}/USER_UPDATE`, USER_UPDATE: `${PREFIX}/USER_UPDATE`,
CURRENT_USER_UPDATE: `${PREFIX}/CURRENT_USER_UPDATE`, CURRENT_USER_UPDATE: `${PREFIX}/CURRENT_USER_UPDATE`,
USER_UPDATE_HANDLE: `${PREFIX}/USER_UPDATE_HANDLE`, USER_UPDATE_HANDLE: `${PREFIX}/USER_UPDATE_HANDLE`,
CURRENT_USER_LANGUAGE_UPDATE: `${PREFIX}/CURRENT_USER_LANGUAGE_UPDATE`, CURRENT_USER_LANGUAGE_UPDATE: `${PREFIX}/CURRENT_USER_LANGUAGE_UPDATE`,
USER_EMAIL_UPDATE: `${PREFIX}/USER_EMAIL_UPDATE`, USER_EMAIL_UPDATE: `${PREFIX}/USER_EMAIL_UPDATE`,
CURRENT_USER_EMAIL_UPDATE: `${PREFIX}/CURRENT_USER_EMAIL_UPDATE`, CURRENT_USER_EMAIL_UPDATE: `${PREFIX}/CURRENT_USER_EMAIL_UPDATE`,
USER_EMAIL_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_EMAIL_UPDATE_ERROR_CLEAR`, USER_EMAIL_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_EMAIL_UPDATE_ERROR_CLEAR`,
CURRENT_USER_EMAIL_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_EMAIL_UPDATE_ERROR_CLEAR`, CURRENT_USER_EMAIL_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_EMAIL_UPDATE_ERROR_CLEAR`,
USER_PASSWORD_UPDATE: `${PREFIX}/USER_PASSWORD_UPDATE`, USER_PASSWORD_UPDATE: `${PREFIX}/USER_PASSWORD_UPDATE`,
CURRENT_USER_PASSWORD_UPDATE: `${PREFIX}/CURRENT_USER_PASSWORD_UPDATE`, CURRENT_USER_PASSWORD_UPDATE: `${PREFIX}/CURRENT_USER_PASSWORD_UPDATE`,
USER_PASSWORD_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_PASSWORD_UPDATE_ERROR_CLEAR`, USER_PASSWORD_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_PASSWORD_UPDATE_ERROR_CLEAR`,
CURRENT_USER_PASSWORD_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_PASSWORD_UPDATE_ERROR_CLEAR`, CURRENT_USER_PASSWORD_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_PASSWORD_UPDATE_ERROR_CLEAR`,
USER_USERNAME_UPDATE: `${PREFIX}/USER_USERNAME_UPDATE`, USER_USERNAME_UPDATE: `${PREFIX}/USER_USERNAME_UPDATE`,
CURRENT_USER_USERNAME_UPDATE: `${PREFIX}/CURRENT_USER_USERNAME_UPDATE`, CURRENT_USER_USERNAME_UPDATE: `${PREFIX}/CURRENT_USER_USERNAME_UPDATE`,
USER_USERNAME_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_USERNAME_UPDATE_ERROR_CLEAR`, USER_USERNAME_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_USERNAME_UPDATE_ERROR_CLEAR`,
CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR`, CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR: `${PREFIX}/CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR`,
CURRENT_USER_AVATAR_UPDATE: `${PREFIX}/CURRENT_USER_AVATAR_UPDATE`, CURRENT_USER_AVATAR_UPDATE: `${PREFIX}/CURRENT_USER_AVATAR_UPDATE`,
USER_DELETE: `${PREFIX}/USER_DELETE`, USER_DELETE: `${PREFIX}/USER_DELETE`,
USER_DELETE_HANDLE: `${PREFIX}/USER_DELETE_HANDLE`, USER_DELETE_HANDLE: `${PREFIX}/USER_DELETE_HANDLE`,
USER_TO_CARD_ADD: `${PREFIX}/USER_TO_CARD_ADD`, USER_TO_CARD_ADD: `${PREFIX}/USER_TO_CARD_ADD`,
USER_TO_CURRENT_CARD_ADD: `${PREFIX}/USER_TO_CURRENT_CARD_ADD`, USER_TO_CURRENT_CARD_ADD: `${PREFIX}/USER_TO_CURRENT_CARD_ADD`,
USER_TO_CARD_ADD_HANDLE: `${PREFIX}/USER_TO_CARD_ADD_HANDLE`, USER_TO_CARD_ADD_HANDLE: `${PREFIX}/USER_TO_CARD_ADD_HANDLE`,
USER_FROM_CARD_REMOVE: `${PREFIX}/USER_FROM_CARD_REMOVE`, USER_FROM_CARD_REMOVE: `${PREFIX}/USER_FROM_CARD_REMOVE`,
USER_FROM_CURRENT_CARD_REMOVE: `${PREFIX}/USER_FROM_CURRENT_CARD_REMOVE`, USER_FROM_CURRENT_CARD_REMOVE: `${PREFIX}/USER_FROM_CURRENT_CARD_REMOVE`,
USER_FROM_CARD_REMOVE_HANDLE: `${PREFIX}/USER_FROM_CARD_REMOVE_HANDLE`, USER_FROM_CARD_REMOVE_HANDLE: `${PREFIX}/USER_FROM_CARD_REMOVE_HANDLE`,
USER_TO_FILTER_IN_CURRENT_BOARD_ADD: `${PREFIX}/USER_TO_FILTER_IN_CURRENT_BOARD_ADD`, USER_TO_FILTER_IN_CURRENT_BOARD_ADD: `${PREFIX}/USER_TO_FILTER_IN_CURRENT_BOARD_ADD`,
USER_FROM_FILTER_IN_CURRENT_BOARD_REMOVE: `${PREFIX}/USER_FROM_FILTER_IN_CURRENT_BOARD_REMOVE`, USER_FROM_FILTER_IN_CURRENT_BOARD_REMOVE: `${PREFIX}/USER_FROM_FILTER_IN_CURRENT_BOARD_REMOVE`,
/* Projects */ /* Projects */
PROJECT_CREATE: `${PREFIX}/PROJECT_CREATE`, PROJECT_CREATE: `${PREFIX}/PROJECT_CREATE`,
PROJECT_CREATE_HANDLE: `${PREFIX}/PROJECT_CREATE_HANDLE`, PROJECT_CREATE_HANDLE: `${PREFIX}/PROJECT_CREATE_HANDLE`,
CURRENT_PROJECT_UPDATE: `${PREFIX}/CURRENT_PROJECT_UPDATE`, CURRENT_PROJECT_UPDATE: `${PREFIX}/CURRENT_PROJECT_UPDATE`,
PROJECT_UPDATE_HANDLE: `${PREFIX}/PROJECT_UPDATE_HANDLE`, PROJECT_UPDATE_HANDLE: `${PREFIX}/PROJECT_UPDATE_HANDLE`,
CURRENT_PROJECT_BACKGROUND_IMAGE_UPDATE: `${PREFIX}/CURRENT_PROJECT_BACKGROUND_IMAGE_UPDATE`, CURRENT_PROJECT_BACKGROUND_IMAGE_UPDATE: `${PREFIX}/CURRENT_PROJECT_BACKGROUND_IMAGE_UPDATE`,
CURRENT_PROJECT_DELETE: `${PREFIX}/CURRENT_PROJECT_DELETE`, CURRENT_PROJECT_DELETE: `${PREFIX}/CURRENT_PROJECT_DELETE`,
PROJECT_DELETE_HANDLE: `${PREFIX}/PROJECT_DELETE_HANDLE`, PROJECT_DELETE_HANDLE: `${PREFIX}/PROJECT_DELETE_HANDLE`,
/* Project managers */ /* Project managers */
MANAGER_IN_CURRENT_PROJECT_CREATE: `${PREFIX}/MANAGER_IN_CURRENT_PROJECT_CREATE`, MANAGER_IN_CURRENT_PROJECT_CREATE: `${PREFIX}/MANAGER_IN_CURRENT_PROJECT_CREATE`,
PROJECT_MANAGER_CREATE_HANDLE: `${PREFIX}/PROJECT_MANAGER_CREATE_HANDLE`, PROJECT_MANAGER_CREATE_HANDLE: `${PREFIX}/PROJECT_MANAGER_CREATE_HANDLE`,
PROJECT_MANAGER_DELETE: `${PREFIX}/PROJECT_MANAGER_DELETE`, PROJECT_MANAGER_DELETE: `${PREFIX}/PROJECT_MANAGER_DELETE`,
PROJECT_MANAGER_DELETE_HANDLE: `${PREFIX}/PROJECT_MANAGER_DELETE_HANDLE`, PROJECT_MANAGER_DELETE_HANDLE: `${PREFIX}/PROJECT_MANAGER_DELETE_HANDLE`,
/* Boards */ /* Boards */
BOARD_IN_CURRENT_PROJECT_CREATE: `${PREFIX}/BOARD_IN_CURRENT_PROJECT_CREATE`, BOARD_IN_CURRENT_PROJECT_CREATE: `${PREFIX}/BOARD_IN_CURRENT_PROJECT_CREATE`,
BOARD_CREATE_HANDLE: `${PREFIX}/BOARD_CREATE_HANDLE`, BOARD_CREATE_HANDLE: `${PREFIX}/BOARD_CREATE_HANDLE`,
BOARD_FETCH: `${PREFIX}/BOARD_FETCH`, BOARD_FETCH: `${PREFIX}/BOARD_FETCH`,
BOARD_UPDATE: `${PREFIX}/BOARD_UPDATE`, BOARD_UPDATE: `${PREFIX}/BOARD_UPDATE`,
BOARD_UPDATE_HANDLE: `${PREFIX}/BOARD_UPDATE_HANDLE`, BOARD_UPDATE_HANDLE: `${PREFIX}/BOARD_UPDATE_HANDLE`,
BOARD_MOVE: `${PREFIX}/BOARD_MOVE`, BOARD_MOVE: `${PREFIX}/BOARD_MOVE`,
BOARD_DELETE: `${PREFIX}/BOARD_DELETE`, BOARD_DELETE: `${PREFIX}/BOARD_DELETE`,
BOARD_DELETE_HANDLE: `${PREFIX}/BOARD_DELETE_HANDLE`, BOARD_DELETE_HANDLE: `${PREFIX}/BOARD_DELETE_HANDLE`,
/* Board memberships */ /* Board memberships */
MEMBERSHIP_IN_CURRENT_BOARD_CREATE: `${PREFIX}/MEMBERSHIP_IN_CURRENT_BOARD_CREATE`, MEMBERSHIP_IN_CURRENT_BOARD_CREATE: `${PREFIX}/MEMBERSHIP_IN_CURRENT_BOARD_CREATE`,
BOARD_MEMBERSHIP_CREATE_HANDLE: `${PREFIX}/BOARD_MEMBERSHIP_CREATE_HANDLE`, BOARD_MEMBERSHIP_CREATE_HANDLE: `${PREFIX}/BOARD_MEMBERSHIP_CREATE_HANDLE`,
BOARD_MEMBERSHIP_UPDATE: `${PREFIX}/BOARD_MEMBERSHIP_UPDATE`, BOARD_MEMBERSHIP_UPDATE: `${PREFIX}/BOARD_MEMBERSHIP_UPDATE`,
BOARD_MEMBERSHIP_UPDATE_HANDLE: `${PREFIX}/BOARD_MEMBERSHIP_UPDATE_HANDLE`, BOARD_MEMBERSHIP_UPDATE_HANDLE: `${PREFIX}/BOARD_MEMBERSHIP_UPDATE_HANDLE`,
BOARD_MEMBERSHIP_DELETE: `${PREFIX}/BOARD_MEMBERSHIP_DELETE`, BOARD_MEMBERSHIP_DELETE: `${PREFIX}/BOARD_MEMBERSHIP_DELETE`,
BOARD_MEMBERSHIP_DELETE_HANDLE: `${PREFIX}/BOARD_MEMBERSHIP_DELETE_HANDLE`, BOARD_MEMBERSHIP_DELETE_HANDLE: `${PREFIX}/BOARD_MEMBERSHIP_DELETE_HANDLE`,
/* Labels */ /* Labels */
LABEL_IN_CURRENT_BOARD_CREATE: `${PREFIX}/LABEL_IN_CURRENT_BOARD_CREATE`, LABEL_IN_CURRENT_BOARD_CREATE: `${PREFIX}/LABEL_IN_CURRENT_BOARD_CREATE`,
LABEL_CREATE_HANDLE: `${PREFIX}/LABEL_CREATE_HANDLE`, LABEL_CREATE_HANDLE: `${PREFIX}/LABEL_CREATE_HANDLE`,
LABEL_UPDATE: `${PREFIX}/LABEL_UPDATE`, LABEL_UPDATE: `${PREFIX}/LABEL_UPDATE`,
LABEL_UPDATE_HANDLE: `${PREFIX}/LABEL_UPDATE_HANDLE`, LABEL_UPDATE_HANDLE: `${PREFIX}/LABEL_UPDATE_HANDLE`,
LABEL_MOVE: `${PREFIX}/LABEL_MOVE`, LABEL_MOVE: `${PREFIX}/LABEL_MOVE`,
LABEL_DELETE: `${PREFIX}/LABEL_DELETE`, LABEL_DELETE: `${PREFIX}/LABEL_DELETE`,
LABEL_DELETE_HANDLE: `${PREFIX}/LABEL_DELETE_HANDLE`, LABEL_DELETE_HANDLE: `${PREFIX}/LABEL_DELETE_HANDLE`,
LABEL_TO_CARD_ADD: `${PREFIX}/LABEL_TO_CARD_ADD`, LABEL_TO_CARD_ADD: `${PREFIX}/LABEL_TO_CARD_ADD`,
LABEL_TO_CURRENT_CARD_ADD: `${PREFIX}/LABEL_TO_CURRENT_CARD_ADD`, LABEL_TO_CURRENT_CARD_ADD: `${PREFIX}/LABEL_TO_CURRENT_CARD_ADD`,
LABEL_TO_CARD_ADD_HANDLE: `${PREFIX}/LABEL_TO_CARD_ADD_HANDLE`, LABEL_TO_CARD_ADD_HANDLE: `${PREFIX}/LABEL_TO_CARD_ADD_HANDLE`,
LABEL_FROM_CARD_REMOVE: `${PREFIX}/LABEL_FROM_CARD_REMOVE`, LABEL_FROM_CARD_REMOVE: `${PREFIX}/LABEL_FROM_CARD_REMOVE`,
LABEL_FROM_CURRENT_CARD_REMOVE: `${PREFIX}/LABEL_FROM_CURRENT_CARD_REMOVE`, LABEL_FROM_CURRENT_CARD_REMOVE: `${PREFIX}/LABEL_FROM_CURRENT_CARD_REMOVE`,
LABEL_FROM_CARD_REMOVE_HANDLE: `${PREFIX}/LABEL_FROM_CARD_REMOVE_HANDLE`, LABEL_FROM_CARD_REMOVE_HANDLE: `${PREFIX}/LABEL_FROM_CARD_REMOVE_HANDLE`,
LABEL_TO_FILTER_IN_CURRENT_BOARD_ADD: `${PREFIX}/LABEL_TO_FILTER_IN_CURRENT_BOARD_ADD`, LABEL_TO_FILTER_IN_CURRENT_BOARD_ADD: `${PREFIX}/LABEL_TO_FILTER_IN_CURRENT_BOARD_ADD`,
LABEL_FROM_FILTER_IN_CURRENT_BOARD_REMOVE: `${PREFIX}/LABEL_FROM_FILTER_IN_CURRENT_BOARD_REMOVE`, LABEL_FROM_FILTER_IN_CURRENT_BOARD_REMOVE: `${PREFIX}/LABEL_FROM_FILTER_IN_CURRENT_BOARD_REMOVE`,
/* Lists */ /* Lists */
LIST_IN_CURRENT_BOARD_CREATE: `${PREFIX}/LIST_IN_CURRENT_BOARD_CREATE`, LIST_IN_CURRENT_BOARD_CREATE: `${PREFIX}/LIST_IN_CURRENT_BOARD_CREATE`,
LIST_CREATE_HANDLE: `${PREFIX}/LIST_CREATE_HANDLE`, LIST_CREATE_HANDLE: `${PREFIX}/LIST_CREATE_HANDLE`,
LIST_UPDATE: `${PREFIX}/LIST_UPDATE`, LIST_UPDATE: `${PREFIX}/LIST_UPDATE`,
LIST_UPDATE_HANDLE: `${PREFIX}/LIST_UPDATE_HANDLE`, LIST_UPDATE_HANDLE: `${PREFIX}/LIST_UPDATE_HANDLE`,
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`,
/* Cards */ /* Cards */
CARD_CREATE: `${PREFIX}/CARD_CREATE`, CARD_CREATE: `${PREFIX}/CARD_CREATE`,
CARD_CREATE_HANDLE: `${PREFIX}/CARD_CREATE_HANDLE`, CARD_CREATE_HANDLE: `${PREFIX}/CARD_CREATE_HANDLE`,
CARD_UPDATE: `${PREFIX}/CARD_UPDATE`, CARD_UPDATE: `${PREFIX}/CARD_UPDATE`,
CURRENT_CARD_UPDATE: `${PREFIX}/CURRENT_CARD_UPDATE`, CURRENT_CARD_UPDATE: `${PREFIX}/CURRENT_CARD_UPDATE`,
CARD_UPDATE_HANDLE: `${PREFIX}/CARD_UPDATE_HANDLE`, CARD_UPDATE_HANDLE: `${PREFIX}/CARD_UPDATE_HANDLE`,
CARD_MOVE: `${PREFIX}/CARD_MOVE`, CARD_MOVE: `${PREFIX}/CARD_MOVE`,
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`, CARD_DUPLICATE: `${PREFIX}/CARD_DUPLICATE`,
CURRENT_CARD_DUPLICATE: `${PREFIX}/CURRENT_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`,
/* Tasks */ /* Tasks */
TASK_IN_CURRENT_CARD_CREATE: `${PREFIX}/TASK_IN_CURRENT_CARD_CREATE`, TASK_IN_CURRENT_CARD_CREATE: `${PREFIX}/TASK_IN_CURRENT_CARD_CREATE`,
TASK_CREATE_HANDLE: `${PREFIX}/TASK_CREATE_HANDLE`, TASK_CREATE_HANDLE: `${PREFIX}/TASK_CREATE_HANDLE`,
TASK_UPDATE: `${PREFIX}/TASK_UPDATE`, TASK_UPDATE: `${PREFIX}/TASK_UPDATE`,
TASK_UPDATE_HANDLE: `${PREFIX}/TASK_UPDATE_HANDLE`, TASK_UPDATE_HANDLE: `${PREFIX}/TASK_UPDATE_HANDLE`,
TASK_MOVE: `${PREFIX}/TASK_MOVE`, TASK_MOVE: `${PREFIX}/TASK_MOVE`,
TASK_DELETE: `${PREFIX}/TASK_DELETE`, TASK_DELETE: `${PREFIX}/TASK_DELETE`,
TASK_DELETE_HANDLE: `${PREFIX}/TASK_DELETE_HANDLE`, TASK_DELETE_HANDLE: `${PREFIX}/TASK_DELETE_HANDLE`,
/* Attachments */ /* Attachments */
ATTACHMENT_IN_CURRENT_CARD_CREATE: `${PREFIX}/ATTACHMENT_IN_CURRENT_CARD_CREATE`, ATTACHMENT_IN_CURRENT_CARD_CREATE: `${PREFIX}/ATTACHMENT_IN_CURRENT_CARD_CREATE`,
ATTACHMENT_CREATE_HANDLE: `${PREFIX}/ATTACHMENT_CREATE_HANDLE`, ATTACHMENT_CREATE_HANDLE: `${PREFIX}/ATTACHMENT_CREATE_HANDLE`,
ATTACHMENT_UPDATE: `${PREFIX}/ATTACHMENT_UPDATE`, ATTACHMENT_UPDATE: `${PREFIX}/ATTACHMENT_UPDATE`,
ATTACHMENT_UPDATE_HANDLE: `${PREFIX}/ATTACHMENT_UPDATE_HANDLE`, ATTACHMENT_UPDATE_HANDLE: `${PREFIX}/ATTACHMENT_UPDATE_HANDLE`,
ATTACHMENT_DELETE: `${PREFIX}/ATTACHMENT_DELETE`, ATTACHMENT_DELETE: `${PREFIX}/ATTACHMENT_DELETE`,
ATTACHMENT_DELETE_HANDLE: `${PREFIX}/ATTACHMENT_DELETE_HANDLE`, ATTACHMENT_DELETE_HANDLE: `${PREFIX}/ATTACHMENT_DELETE_HANDLE`,
/* Activities */ /* Activities */
ACTIVITIES_IN_CURRENT_CARD_FETCH: `${PREFIX}/ACTIVITIES_IN_CURRENT_CARD_FETCH`, ACTIVITIES_IN_CURRENT_CARD_FETCH: `${PREFIX}/ACTIVITIES_IN_CURRENT_CARD_FETCH`,
ACTIVITIES_DETAILS_IN_CURRENT_CARD_TOGGLE: `${PREFIX}/ACTIVITIES_DETAILS_IN_CURRENT_CARD_TOGGLE`, ACTIVITIES_DETAILS_IN_CURRENT_CARD_TOGGLE: `${PREFIX}/ACTIVITIES_DETAILS_IN_CURRENT_CARD_TOGGLE`,
ACTIVITY_CREATE_HANDLE: `${PREFIX}/ACTIVITY_CREATE_HANDLE`, ACTIVITY_CREATE_HANDLE: `${PREFIX}/ACTIVITY_CREATE_HANDLE`,
ACTIVITY_UPDATE_HANDLE: `${PREFIX}/ACTIVITY_UPDATE_HANDLE`, ACTIVITY_UPDATE_HANDLE: `${PREFIX}/ACTIVITY_UPDATE_HANDLE`,
ACTIVITY_DELETE_HANDLE: `${PREFIX}/ACTIVITY_DELETE_HANDLE`, ACTIVITY_DELETE_HANDLE: `${PREFIX}/ACTIVITY_DELETE_HANDLE`,
/* Comment activities */ /* Comment activities */
COMMENT_ACTIVITY_IN_CURRENT_CARD_CREATE: `${PREFIX}/COMMENT_ACTIVITY_IN_CURRENT_CARD_CREATE`, COMMENT_ACTIVITY_IN_CURRENT_CARD_CREATE: `${PREFIX}/COMMENT_ACTIVITY_IN_CURRENT_CARD_CREATE`,
COMMENT_ACTIVITY_UPDATE: `${PREFIX}/COMMENT_ACTIVITY_UPDATE`, COMMENT_ACTIVITY_UPDATE: `${PREFIX}/COMMENT_ACTIVITY_UPDATE`,
COMMENT_ACTIVITY_DELETE: `${PREFIX}/COMMENT_ACTIVITY_DELETE`, COMMENT_ACTIVITY_DELETE: `${PREFIX}/COMMENT_ACTIVITY_DELETE`,
/* Notifications */ /* Notifications */
NOTIFICATION_CREATE_HANDLE: `${PREFIX}/NOTIFICATION_CREATE_HANDLE`, NOTIFICATION_CREATE_HANDLE: `${PREFIX}/NOTIFICATION_CREATE_HANDLE`,
NOTIFICATION_DELETE: `${PREFIX}/NOTIFICATION_DELETE`, NOTIFICATION_DELETE: `${PREFIX}/NOTIFICATION_DELETE`,
NOTIFICATION_DELETE_HANDLE: `${PREFIX}/NOTIFICATION_DELETE_HANDLE`, NOTIFICATION_DELETE_HANDLE: `${PREFIX}/NOTIFICATION_DELETE_HANDLE`,
}; };

@ -1,80 +1,80 @@
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import selectors from '../selectors'; import selectors from '../selectors';
import entryActions from '../entry-actions'; import entryActions from '../entry-actions';
import { BoardMembershipRoles } from '../constants/Enums'; import { BoardMembershipRoles } from '../constants/Enums';
import Card from '../components/Card'; import Card from '../components/Card';
const makeMapStateToProps = () => { const makeMapStateToProps = () => {
const selectCardById = selectors.makeSelectCardById(); const selectCardById = selectors.makeSelectCardById();
const selectUsersByCardId = selectors.makeSelectUsersByCardId(); const selectUsersByCardId = selectors.makeSelectUsersByCardId();
const selectLabelsByCardId = selectors.makeSelectLabelsByCardId(); const selectLabelsByCardId = selectors.makeSelectLabelsByCardId();
const selectTasksByCardId = selectors.makeSelectTasksByCardId(); const selectTasksByCardId = selectors.makeSelectTasksByCardId();
const selectNotificationsTotalByCardId = selectors.makeSelectNotificationsTotalByCardId(); const selectNotificationsTotalByCardId = selectors.makeSelectNotificationsTotalByCardId();
return (state, { id, index }) => { return (state, { id, index }) => {
const { projectId } = selectors.selectPath(state); const { projectId } = selectors.selectPath(state);
const allProjectsToLists = selectors.selectProjectsToListsForCurrentUser(state); const allProjectsToLists = selectors.selectProjectsToListsForCurrentUser(state);
const allBoardMemberships = selectors.selectMembershipsForCurrentBoard(state); const allBoardMemberships = selectors.selectMembershipsForCurrentBoard(state);
const allLabels = selectors.selectLabelsForCurrentBoard(state); const allLabels = selectors.selectLabelsForCurrentBoard(state);
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state); const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const { name, dueDate, stopwatch, coverUrl, boardId, listId, isPersisted } = selectCardById( const { name, dueDate, stopwatch, coverUrl, boardId, listId, isPersisted } = selectCardById(
state, state,
id, id,
); );
const users = selectUsersByCardId(state, id); const users = selectUsersByCardId(state, id);
const labels = selectLabelsByCardId(state, id); const labels = selectLabelsByCardId(state, id);
const tasks = selectTasksByCardId(state, id); const tasks = selectTasksByCardId(state, id);
const notificationsTotal = selectNotificationsTotalByCardId(state, id); const notificationsTotal = selectNotificationsTotalByCardId(state, id);
const isCurrentUserEditor = const isCurrentUserEditor =
!!currentUserMembership && currentUserMembership.role === BoardMembershipRoles.EDITOR; !!currentUserMembership && currentUserMembership.role === BoardMembershipRoles.EDITOR;
return { return {
id, id,
index, index,
name, name,
dueDate, dueDate,
stopwatch, stopwatch,
coverUrl, coverUrl,
boardId, boardId,
listId, listId,
projectId, projectId,
isPersisted, isPersisted,
notificationsTotal, notificationsTotal,
users, users,
labels, labels,
tasks, tasks,
allProjectsToLists, allProjectsToLists,
allBoardMemberships, allBoardMemberships,
allLabels, allLabels,
canEdit: isCurrentUserEditor, canEdit: isCurrentUserEditor,
}; };
}; };
}; };
const mapDispatchToProps = (dispatch, { id }) => const mapDispatchToProps = (dispatch, { id }) =>
bindActionCreators( bindActionCreators(
{ {
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), 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),
onBoardFetch: entryActions.fetchBoard, onBoardFetch: entryActions.fetchBoard,
onLabelAdd: (labelId) => entryActions.addLabelToCard(labelId, id), onLabelAdd: (labelId) => entryActions.addLabelToCard(labelId, id),
onLabelRemove: (labelId) => entryActions.removeLabelFromCard(labelId, id), onLabelRemove: (labelId) => entryActions.removeLabelFromCard(labelId, id),
onLabelCreate: (data) => entryActions.createLabelInCurrentBoard(data), onLabelCreate: (data) => entryActions.createLabelInCurrentBoard(data),
onLabelUpdate: (labelId, data) => entryActions.updateLabel(labelId, data), onLabelUpdate: (labelId, data) => entryActions.updateLabel(labelId, data),
onLabelMove: (labelId, index) => entryActions.moveLabel(labelId, index), onLabelMove: (labelId, index) => entryActions.moveLabel(labelId, index),
onLabelDelete: (labelId) => entryActions.deleteLabel(labelId), onLabelDelete: (labelId) => entryActions.deleteLabel(labelId),
}, },
dispatch, dispatch,
); );
export default connect(makeMapStateToProps, mapDispatchToProps)(Card); export default connect(makeMapStateToProps, mapDispatchToProps)(Card);

@ -1,115 +1,115 @@
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { push } from '../lib/redux-router'; import { push } from '../lib/redux-router';
import selectors from '../selectors'; import selectors from '../selectors';
import entryActions from '../entry-actions'; import entryActions from '../entry-actions';
import Paths from '../constants/Paths'; import Paths from '../constants/Paths';
import { BoardMembershipRoles } from '../constants/Enums'; import { BoardMembershipRoles } from '../constants/Enums';
import CardModal from '../components/CardModal'; import CardModal from '../components/CardModal';
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const { projectId } = selectors.selectPath(state); const { projectId } = selectors.selectPath(state);
const allProjectsToLists = selectors.selectProjectsToListsForCurrentUser(state); const allProjectsToLists = selectors.selectProjectsToListsForCurrentUser(state);
const isCurrentUserManager = selectors.selectIsCurrentUserManagerForCurrentProject(state); const isCurrentUserManager = selectors.selectIsCurrentUserManagerForCurrentProject(state);
const allBoardMemberships = selectors.selectMembershipsForCurrentBoard(state); const allBoardMemberships = selectors.selectMembershipsForCurrentBoard(state);
const allLabels = selectors.selectLabelsForCurrentBoard(state); const allLabels = selectors.selectLabelsForCurrentBoard(state);
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state); const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const { const {
name, name,
description, description,
dueDate, dueDate,
stopwatch, stopwatch,
isSubscribed, isSubscribed,
isActivitiesFetching, isActivitiesFetching,
isAllActivitiesFetched, isAllActivitiesFetched,
isActivitiesDetailsVisible, isActivitiesDetailsVisible,
isActivitiesDetailsFetching, isActivitiesDetailsFetching,
boardId, boardId,
listId, listId,
} = selectors.selectCurrentCard(state); } = selectors.selectCurrentCard(state);
const users = selectors.selectUsersForCurrentCard(state); const users = selectors.selectUsersForCurrentCard(state);
const labels = selectors.selectLabelsForCurrentCard(state); const labels = selectors.selectLabelsForCurrentCard(state);
const tasks = selectors.selectTasksForCurrentCard(state); const tasks = selectors.selectTasksForCurrentCard(state);
const attachments = selectors.selectAttachmentsForCurrentCard(state); const attachments = selectors.selectAttachmentsForCurrentCard(state);
const activities = selectors.selectActivitiesForCurrentCard(state); const activities = selectors.selectActivitiesForCurrentCard(state);
let isCurrentUserEditor = false; let isCurrentUserEditor = false;
let isCurrentUserEditorOrCanComment = false; let isCurrentUserEditorOrCanComment = false;
if (currentUserMembership) { if (currentUserMembership) {
isCurrentUserEditor = currentUserMembership.role === BoardMembershipRoles.EDITOR; isCurrentUserEditor = currentUserMembership.role === BoardMembershipRoles.EDITOR;
isCurrentUserEditorOrCanComment = isCurrentUserEditor || currentUserMembership.canComment; isCurrentUserEditorOrCanComment = isCurrentUserEditor || currentUserMembership.canComment;
} }
return { return {
name, name,
description, description,
dueDate, dueDate,
stopwatch, stopwatch,
isSubscribed, isSubscribed,
isActivitiesFetching, isActivitiesFetching,
isAllActivitiesFetched, isAllActivitiesFetched,
isActivitiesDetailsVisible, isActivitiesDetailsVisible,
isActivitiesDetailsFetching, isActivitiesDetailsFetching,
listId, listId,
boardId, boardId,
projectId, projectId,
users, users,
labels, labels,
tasks, tasks,
attachments, attachments,
activities, activities,
allProjectsToLists, allProjectsToLists,
allBoardMemberships, allBoardMemberships,
allLabels, allLabels,
canEdit: isCurrentUserEditor, canEdit: isCurrentUserEditor,
canEditCommentActivities: isCurrentUserEditorOrCanComment, canEditCommentActivities: isCurrentUserEditorOrCanComment,
canEditAllCommentActivities: isCurrentUserManager, canEditAllCommentActivities: isCurrentUserManager,
}; };
}; };
const mapDispatchToProps = (dispatch) => const mapDispatchToProps = (dispatch) =>
bindActionCreators( bindActionCreators(
{ {
onUpdate: entryActions.updateCurrentCard, onUpdate: entryActions.updateCurrentCard,
onMove: entryActions.moveCurrentCard, onMove: entryActions.moveCurrentCard,
onTransfer: entryActions.transferCurrentCard, onTransfer: entryActions.transferCurrentCard,
onDuplicate: entryActions.duplicateCurrentCard, onDuplicate: entryActions.duplicateCurrentCard,
onDelete: entryActions.deleteCurrentCard, onDelete: entryActions.deleteCurrentCard,
onUserAdd: entryActions.addUserToCurrentCard, onUserAdd: entryActions.addUserToCurrentCard,
onUserRemove: entryActions.removeUserFromCurrentCard, onUserRemove: entryActions.removeUserFromCurrentCard,
onBoardFetch: entryActions.fetchBoard, onBoardFetch: entryActions.fetchBoard,
onLabelAdd: entryActions.addLabelToCurrentCard, onLabelAdd: entryActions.addLabelToCurrentCard,
onLabelRemove: entryActions.removeLabelFromCurrentCard, onLabelRemove: entryActions.removeLabelFromCurrentCard,
onLabelCreate: entryActions.createLabelInCurrentBoard, onLabelCreate: entryActions.createLabelInCurrentBoard,
onLabelUpdate: entryActions.updateLabel, onLabelUpdate: entryActions.updateLabel,
onLabelMove: entryActions.moveLabel, onLabelMove: entryActions.moveLabel,
onLabelDelete: entryActions.deleteLabel, onLabelDelete: entryActions.deleteLabel,
onTaskCreate: entryActions.createTaskInCurrentCard, onTaskCreate: entryActions.createTaskInCurrentCard,
onTaskUpdate: entryActions.updateTask, onTaskUpdate: entryActions.updateTask,
onTaskMove: entryActions.moveTask, onTaskMove: entryActions.moveTask,
onTaskDelete: entryActions.deleteTask, onTaskDelete: entryActions.deleteTask,
onAttachmentCreate: entryActions.createAttachmentInCurrentCard, onAttachmentCreate: entryActions.createAttachmentInCurrentCard,
onAttachmentUpdate: entryActions.updateAttachment, onAttachmentUpdate: entryActions.updateAttachment,
onAttachmentDelete: entryActions.deleteAttachment, onAttachmentDelete: entryActions.deleteAttachment,
onActivitiesFetch: entryActions.fetchActivitiesInCurrentCard, onActivitiesFetch: entryActions.fetchActivitiesInCurrentCard,
onActivitiesDetailsToggle: entryActions.toggleActivitiesDetailsInCurrentCard, onActivitiesDetailsToggle: entryActions.toggleActivitiesDetailsInCurrentCard,
onCommentActivityCreate: entryActions.createCommentActivityInCurrentCard, onCommentActivityCreate: entryActions.createCommentActivityInCurrentCard,
onCommentActivityUpdate: entryActions.updateCommentActivity, onCommentActivityUpdate: entryActions.updateCommentActivity,
onCommentActivityDelete: entryActions.deleteCommentActivity, onCommentActivityDelete: entryActions.deleteCommentActivity,
push, push,
}, },
dispatch, dispatch,
); );
const mergeProps = (stateProps, dispatchProps) => ({ const mergeProps = (stateProps, dispatchProps) => ({
...stateProps, ...stateProps,
...omit(dispatchProps, 'push'), ...omit(dispatchProps, 'push'),
onClose: () => dispatchProps.push(Paths.BOARDS.replace(':id', stateProps.boardId)), onClose: () => dispatchProps.push(Paths.BOARDS.replace(':id', stateProps.boardId)),
}); });
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(CardModal); export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(CardModal);

@ -1,123 +1,123 @@
import EntryActionTypes from '../constants/EntryActionTypes'; import EntryActionTypes from '../constants/EntryActionTypes';
const createCard = (listId, data, autoOpen) => ({ const createCard = (listId, data, autoOpen) => ({
type: EntryActionTypes.CARD_CREATE, type: EntryActionTypes.CARD_CREATE,
payload: { payload: {
listId, listId,
data, data,
autoOpen, autoOpen,
}, },
}); });
const handleCardCreate = (card) => ({ const handleCardCreate = (card) => ({
type: EntryActionTypes.CARD_CREATE_HANDLE, type: EntryActionTypes.CARD_CREATE_HANDLE,
payload: { payload: {
card, card,
}, },
}); });
const updateCard = (id, data) => ({ const updateCard = (id, data) => ({
type: EntryActionTypes.CARD_UPDATE, type: EntryActionTypes.CARD_UPDATE,
payload: { payload: {
id, id,
data, data,
}, },
}); });
const updateCurrentCard = (data) => ({ const updateCurrentCard = (data) => ({
type: EntryActionTypes.CURRENT_CARD_UPDATE, type: EntryActionTypes.CURRENT_CARD_UPDATE,
payload: { payload: {
data, data,
}, },
}); });
const handleCardUpdate = (card) => ({ const handleCardUpdate = (card) => ({
type: EntryActionTypes.CARD_UPDATE_HANDLE, type: EntryActionTypes.CARD_UPDATE_HANDLE,
payload: { payload: {
card, card,
}, },
}); });
const moveCard = (id, listId, index = 0) => ({ const moveCard = (id, listId, index = 0) => ({
type: EntryActionTypes.CARD_MOVE, type: EntryActionTypes.CARD_MOVE,
payload: { payload: {
id, id,
listId, listId,
index, index,
}, },
}); });
const moveCurrentCard = (listId, index = 0) => ({ const moveCurrentCard = (listId, index = 0) => ({
type: EntryActionTypes.CURRENT_CARD_MOVE, type: EntryActionTypes.CURRENT_CARD_MOVE,
payload: { payload: {
listId, listId,
index, index,
}, },
}); });
const transferCard = (id, boardId, listId, index = 0) => ({ const transferCard = (id, boardId, listId, index = 0) => ({
type: EntryActionTypes.CARD_TRANSFER, type: EntryActionTypes.CARD_TRANSFER,
payload: { payload: {
id, id,
boardId, boardId,
listId, listId,
index, index,
}, },
}); });
const transferCurrentCard = (boardId, listId, index = 0) => ({ const transferCurrentCard = (boardId, listId, index = 0) => ({
type: EntryActionTypes.CURRENT_CARD_TRANSFER, type: EntryActionTypes.CURRENT_CARD_TRANSFER,
payload: { payload: {
boardId, boardId,
listId, listId,
index, index,
}, },
}); });
const duplicateCard = (id) => ({ const duplicateCard = (id) => ({
type: EntryActionTypes.CARD_DUPLICATE, type: EntryActionTypes.CARD_DUPLICATE,
payload: { payload: {
id, id,
}, },
}); });
const duplicateCurrentCard = () => ({ const duplicateCurrentCard = () => ({
type: EntryActionTypes.CURRENT_CARD_DUPLICATE, type: EntryActionTypes.CURRENT_CARD_DUPLICATE,
payload: {}, payload: {},
}); });
const deleteCard = (id) => ({ const deleteCard = (id) => ({
type: EntryActionTypes.CARD_DELETE, type: EntryActionTypes.CARD_DELETE,
payload: { payload: {
id, id,
}, },
}); });
const deleteCurrentCard = () => ({ const deleteCurrentCard = () => ({
type: EntryActionTypes.CURRENT_CARD_DELETE, type: EntryActionTypes.CURRENT_CARD_DELETE,
payload: {}, payload: {},
}); });
const handleCardDelete = (card) => ({ const handleCardDelete = (card) => ({
type: EntryActionTypes.CARD_DELETE_HANDLE, type: EntryActionTypes.CARD_DELETE_HANDLE,
payload: { payload: {
card, card,
}, },
}); });
export default { export default {
createCard, createCard,
handleCardCreate, handleCardCreate,
updateCard, updateCard,
updateCurrentCard, updateCurrentCard,
handleCardUpdate, handleCardUpdate,
moveCard, moveCard,
moveCurrentCard, moveCurrentCard,
transferCard, transferCard,
transferCurrentCard, transferCurrentCard,
duplicateCard, duplicateCard,
duplicateCurrentCard, duplicateCurrentCard,
deleteCard, deleteCard,
deleteCurrentCard, deleteCurrentCard,
handleCardDelete, handleCardDelete,
}; };

@ -1,239 +1,239 @@
export default { export default {
format: { format: {
date: 'M/d/yyyy', date: 'M/d/yyyy',
time: 'p', time: 'p',
dateTime: '$t(format:date) $t(format:time)', dateTime: '$t(format:date) $t(format:time)',
longDate: 'MMM d', longDate: 'MMM d',
longDateTime: "MMMM d 'at' p", longDateTime: "MMMM d 'at' p",
fullDate: 'MMM d, y', fullDate: 'MMM d, y',
fullDateTime: "MMMM d, y 'at' p", fullDateTime: "MMMM d, y 'at' p",
}, },
translation: { translation: {
common: { common: {
aboutPlanka: 'About Planka', aboutPlanka: 'About Planka',
account: 'Account', account: 'Account',
actions: 'Actions', actions: 'Actions',
addAttachment_title: 'Add Attachment', addAttachment_title: 'Add Attachment',
addComment: 'Add comment', addComment: 'Add comment',
addManager_title: 'Add Manager', addManager_title: 'Add Manager',
addMember_title: 'Add Member', addMember_title: 'Add Member',
addUser_title: 'Add User', addUser_title: 'Add User',
administrator: 'Administrator', administrator: 'Administrator',
all: 'All', all: 'All',
allChangesWillBeAutomaticallySavedAfterConnectionRestored: allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'All changes will be automatically saved<br />after connection restored.', 'All changes will be automatically saved<br />after connection restored.',
areYouSureYouWantToDeleteThisAttachment: 'Are you sure you want to delete this attachment?', areYouSureYouWantToDeleteThisAttachment: 'Are you sure you want to delete this attachment?',
areYouSureYouWantToDeleteThisBoard: 'Are you sure you want to delete this board?', areYouSureYouWantToDeleteThisBoard: 'Are you sure you want to delete this board?',
areYouSureYouWantToDeleteThisCard: 'Are you sure you want to delete this card?', areYouSureYouWantToDeleteThisCard: 'Are you sure you want to delete this card?',
areYouSureYouWantToDeleteThisComment: 'Are you sure you want to delete this comment?', areYouSureYouWantToDeleteThisComment: 'Are you sure you want to delete this comment?',
areYouSureYouWantToDeleteThisLabel: 'Are you sure you want to delete this label?', areYouSureYouWantToDeleteThisLabel: 'Are you sure you want to delete this label?',
areYouSureYouWantToDeleteThisList: 'Are you sure you want to delete this list?', areYouSureYouWantToDeleteThisList: 'Are you sure you want to delete this list?',
areYouSureYouWantToDeleteThisProject: 'Are you sure you want to delete this project?', areYouSureYouWantToDeleteThisProject: 'Are you sure you want to delete this project?',
areYouSureYouWantToDeleteThisTask: 'Are you sure you want to delete this task?', areYouSureYouWantToDeleteThisTask: 'Are you sure you want to delete this task?',
areYouSureYouWantToDeleteThisUser: 'Are you sure you want to delete this user?', areYouSureYouWantToDeleteThisUser: 'Are you sure you want to delete this user?',
areYouSureYouWantToLeaveBoard: 'Are you sure you want to leave the board?', areYouSureYouWantToLeaveBoard: 'Are you sure you want to leave the board?',
areYouSureYouWantToLeaveProject: 'Are you sure you want to leave the project?', areYouSureYouWantToLeaveProject: 'Are you sure you want to leave the project?',
areYouSureYouWantToRemoveThisManagerFromProject: areYouSureYouWantToRemoveThisManagerFromProject:
'Are you sure you want to remove this manager from the project?', 'Are you sure you want to remove this manager from the project?',
areYouSureYouWantToRemoveThisMemberFromBoard: areYouSureYouWantToRemoveThisMemberFromBoard:
'Are you sure you want to remove this member from the board?', 'Are you sure you want to remove this member from the board?',
attachment: 'Attachment', attachment: 'Attachment',
attachments: 'Attachments', attachments: 'Attachments',
authentication: 'Authentication', authentication: 'Authentication',
background: 'Background', background: 'Background',
board: 'Board', board: 'Board',
boardNotFound_title: 'Board Not Found', boardNotFound_title: 'Board Not Found',
canComment: 'Can comment', canComment: 'Can comment',
canEditContentOfBoard: 'Can edit the content of the board.', canEditContentOfBoard: 'Can edit the content of the board.',
canOnlyViewBoard: 'Can only view the board.', canOnlyViewBoard: 'Can only view the board.',
cardActions_title: 'Card Actions', cardActions_title: 'Card Actions',
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', 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.',
createProject_title: 'Create Project', createProject_title: 'Create Project',
createTextFile_title: 'Create Text File', createTextFile_title: 'Create Text File',
currentPassword: 'Current password', currentPassword: 'Current password',
dangerZone_title: 'Danger Zone', dangerZone_title: 'Danger Zone',
date: 'Date', date: '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',
deleteCard_title: 'Delete Card', deleteCard_title: 'Delete Card',
deleteComment_title: 'Delete Comment', deleteComment_title: 'Delete Comment',
deleteLabel_title: 'Delete Label', deleteLabel_title: 'Delete Label',
deleteList_title: 'Delete List', deleteList_title: 'Delete List',
deleteProject_title: 'Delete Project', deleteProject_title: 'Delete Project',
deleteTask_title: 'Delete Task', deleteTask_title: 'Delete Task',
deleteUser_title: 'Delete User', deleteUser_title: 'Delete User',
description: 'Description', description: 'Description',
detectAutomatically: 'Detect automatically', detectAutomatically: 'Detect automatically',
dropFileToUpload: 'Drop file to upload', dropFileToUpload: 'Drop file to upload',
editor: 'Editor', editor: 'Editor',
editAttachment_title: 'Edit Attachment', editAttachment_title: 'Edit Attachment',
editAvatar_title: 'Edit Avatar', editAvatar_title: 'Edit Avatar',
editBoard_title: 'Edit Board', editBoard_title: 'Edit Board',
editDueDate_title: 'Edit Due Date', editDueDate_title: 'Edit Due Date',
editEmail_title: 'Edit E-mail', editEmail_title: 'Edit E-mail',
editInformation_title: 'Edit Information', editInformation_title: 'Edit Information',
editLabel_title: 'Edit Label', editLabel_title: 'Edit Label',
editPassword_title: 'Edit Password', editPassword_title: 'Edit Password',
editPermissions_title: 'Edit Permissions', editPermissions_title: 'Edit Permissions',
editStopwatch_title: 'Edit Stopwatch', editStopwatch_title: 'Edit Stopwatch',
editUsername_title: 'Edit Username', editUsername_title: 'Edit Username',
email: 'E-mail', email: 'E-mail',
emailAlreadyInUse: 'E-mail already in use', emailAlreadyInUse: 'E-mail already in use',
enterCardTitle: 'Enter card title... [Ctrl+Enter] to auto-open.', enterCardTitle: 'Enter card title... [Ctrl+Enter] to auto-open.',
enterDescription: 'Enter description...', enterDescription: 'Enter description...',
enterFilename: 'Enter filename', enterFilename: 'Enter filename',
enterListTitle: 'Enter list title...', enterListTitle: 'Enter list title...',
enterProjectTitle: 'Enter project title', enterProjectTitle: 'Enter project title',
enterTaskDescription: 'Enter task description...', enterTaskDescription: 'Enter task description...',
filterByLabels_title: 'Filter By Labels', filterByLabels_title: 'Filter By Labels',
filterByMembers_title: 'Filter By Members', filterByMembers_title: 'Filter By Members',
fromComputer_title: 'From Computer', fromComputer_title: 'From Computer',
fromTrello: 'From Trello', fromTrello: 'From Trello',
general: 'General', general: 'General',
hours: 'Hours', hours: 'Hours',
importBoard_title: 'Import Board', importBoard_title: 'Import Board',
invalidCurrentPassword: 'Invalid current password', invalidCurrentPassword: 'Invalid current password',
labels: 'Labels', labels: 'Labels',
language: 'Language', language: 'Language',
leaveBoard_title: 'Leave Board', leaveBoard_title: 'Leave Board',
leaveProject_title: 'Leave Project', leaveProject_title: 'Leave Project',
list: 'List', list: 'List',
listActions_title: 'List Actions', listActions_title: 'List Actions',
managers: 'Managers', managers: 'Managers',
members: 'Members', members: 'Members',
minutes: 'Minutes', minutes: 'Minutes',
moveCard_title: 'Move Card', moveCard_title: 'Move Card',
name: 'Name', name: 'Name',
newEmail: 'New e-mail', newEmail: 'New e-mail',
newPassword: 'New password', newPassword: 'New password',
newUsername: 'New username', newUsername: 'New username',
noConnectionToServer: 'No connection to server', noConnectionToServer: 'No connection to server',
noBoards: 'No boards', noBoards: 'No boards',
noLists: 'No lists', noLists: 'No lists',
noProjects: 'No projects', noProjects: 'No projects',
notifications: 'Notifications', notifications: 'Notifications',
noUnreadNotifications: 'No unread notifications.', noUnreadNotifications: 'No unread notifications.',
openBoard_title: 'Open Board', openBoard_title: 'Open Board',
optional_inline: 'optional', optional_inline: 'optional',
organization: 'Organization', organization: 'Organization',
phone: 'Phone', phone: 'Phone',
preferences: 'Preferences', preferences: 'Preferences',
pressPasteShortcutToAddAttachmentFromClipboard: pressPasteShortcutToAddAttachmentFromClipboard:
'Tip: press Ctrl-V (Cmd-V on Mac) to add an attachment from the clipboard.', 'Tip: press Ctrl-V (Cmd-V on Mac) to add an attachment from the clipboard.',
project: 'Project', project: 'Project',
projectNotFound_title: 'Project Not Found', projectNotFound_title: 'Project Not Found',
removeManager_title: 'Remove Manager', removeManager_title: 'Remove Manager',
removeMember_title: 'Remove Member', removeMember_title: 'Remove Member',
searchLabels: 'Search labels...', searchLabels: 'Search labels...',
searchMembers: 'Search members...', searchMembers: 'Search members...',
searchUsers: 'Search users...', searchUsers: 'Search users...',
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',
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',
tasks: 'Tasks', tasks: 'Tasks',
thereIsNoPreviewAvailableForThisAttachment: thereIsNoPreviewAvailableForThisAttachment:
'There is no preview available for this attachment.', 'There is no preview available for this attachment.',
time: 'Time', time: 'Time',
title: 'Title', title: 'Title',
userActions_title: 'User Actions', userActions_title: 'User Actions',
userAddedThisCardToList: '<0>{{user}}</0><1> added this card to {{list}}</1>', userAddedThisCardToList: '<0>{{user}}</0><1> added this card to {{list}}</1>',
userLeftNewCommentToCard: '{{user}} left a new comment «{{comment}}» to <2>{{card}}</2>', userLeftNewCommentToCard: '{{user}} left a new comment «{{comment}}» to <2>{{card}}</2>',
userMovedCardFromListToList: '{{user}} moved <2>{{card}}</2> from {{fromList}} to {{toList}}', userMovedCardFromListToList: '{{user}} moved <2>{{card}}</2> from {{fromList}} to {{toList}}',
userMovedThisCardFromListToList: userMovedThisCardFromListToList:
'<0>{{user}}</0><1> moved this card from {{fromList}} to {{toList}}</1>', '<0>{{user}}</0><1> moved this card from {{fromList}} to {{toList}}</1>',
username: 'Username', username: 'Username',
usernameAlreadyInUse: 'Username already in use', usernameAlreadyInUse: 'Username already in use',
users: 'Users', users: 'Users',
version: 'Version', version: 'Version',
viewer: 'Viewer', viewer: 'Viewer',
writeComment: 'Write a comment...', writeComment: 'Write a comment...',
}, },
action: { action: {
addAnotherCard: 'Add another card', addAnotherCard: 'Add another card',
addAnotherList: 'Add another list', addAnotherList: 'Add another list',
addAnotherTask: 'Add another task', addAnotherTask: 'Add another task',
addCard: 'Add card', addCard: 'Add card',
addCard_title: 'Add Card', addCard_title: 'Add Card',
addComment: 'Add comment', addComment: 'Add comment',
addList: 'Add list', addList: 'Add list',
addMember: 'Add member', addMember: 'Add member',
addMoreDetailedDescription: 'Add more detailed description', addMoreDetailedDescription: 'Add more detailed description',
addTask: 'Add task', addTask: 'Add task',
addToCard: 'Add to card', addToCard: 'Add to card',
addUser: 'Add user', addUser: 'Add user',
createBoard: 'Create board', createBoard: 'Create board',
createFile: 'Create file', createFile: 'Create file',
createLabel: 'Create label', createLabel: 'Create label',
createNewLabel: 'Create new label', createNewLabel: 'Create new label',
createProject: 'Create project', createProject: 'Create project',
delete: 'Delete', delete: 'Delete',
deleteAttachment: 'Delete attachment', deleteAttachment: 'Delete attachment',
deleteAvatar: 'Delete avatar', deleteAvatar: 'Delete avatar',
deleteBoard: 'Delete board', deleteBoard: 'Delete board',
deleteCard: 'Delete card', deleteCard: 'Delete card',
deleteCard_title: 'Delete Card', deleteCard_title: 'Delete Card',
deleteComment: 'Delete comment', deleteComment: 'Delete comment',
deleteImage: 'Delete image', deleteImage: 'Delete image',
deleteLabel: 'Delete label', deleteLabel: 'Delete label',
deleteList: 'Delete list', deleteList: 'Delete list',
deleteList_title: 'Delete List', deleteList_title: 'Delete List',
deleteProject: 'Delete project', deleteProject: 'Delete project',
deleteProject_title: 'Delete Project', deleteProject_title: 'Delete Project',
deleteTask: 'Delete task', deleteTask: 'Delete task',
deleteTask_title: 'Delete Task', deleteTask_title: 'Delete Task',
deleteUser: 'Delete user', deleteUser: 'Delete user',
duplicate: 'Duplicate', duplicate: 'Duplicate',
duplicateCard_title: 'Duplicate Card', 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',
editEmail_title: 'Edit E-mail', editEmail_title: 'Edit E-mail',
editInformation_title: 'Edit Information', editInformation_title: 'Edit Information',
editPassword_title: 'Edit Password', editPassword_title: 'Edit Password',
editPermissions: 'Edit permissions', editPermissions: 'Edit permissions',
editStopwatch_title: 'Edit Stopwatch', editStopwatch_title: 'Edit Stopwatch',
editTitle_title: 'Edit Title', editTitle_title: 'Edit Title',
editUsername_title: 'Edit Username', editUsername_title: 'Edit Username',
hideDetails: 'Hide details', hideDetails: 'Hide details',
import: 'Import', import: 'Import',
leaveBoard: 'Leave board', leaveBoard: 'Leave board',
leaveProject: 'Leave project', leaveProject: 'Leave project',
logOut_title: 'Log Out', logOut_title: 'Log Out',
makeCover_title: 'Make Cover', makeCover_title: 'Make Cover',
move: 'Move', move: 'Move',
moveCard_title: 'Move Card', moveCard_title: 'Move Card',
remove: 'Remove', remove: 'Remove',
removeBackground: 'Remove background', removeBackground: 'Remove background',
removeCover_title: 'Remove Cover', removeCover_title: 'Remove Cover',
removeFromBoard: 'Remove from board', removeFromBoard: 'Remove from board',
removeFromProject: 'Remove from project', removeFromProject: 'Remove from project',
removeManager: 'Remove manager', removeManager: 'Remove manager',
removeMember: 'Remove member', removeMember: 'Remove member',
save: 'Save', save: 'Save',
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',
start: 'Start', start: 'Start',
stop: 'Stop', stop: 'Stop',
subscribe: 'Subscribe', subscribe: 'Subscribe',
unsubscribe: 'Unsubscribe', unsubscribe: 'Unsubscribe',
uploadNewAvatar: 'Upload new avatar', uploadNewAvatar: 'Upload new avatar',
uploadNewImage: 'Upload new image', uploadNewImage: 'Upload new image',
}, },
}, },
}; };

@ -1,201 +1,201 @@
import dateFns from 'date-fns/locale/fr'; import dateFns from 'date-fns/locale/fr';
export default { export default {
dateFns, dateFns,
format: { format: {
date: 'P', date: 'P',
time: 'HH:mm', time: 'HH:mm',
dateTime: '$t(format:date) $t(format:time)', dateTime: '$t(format:date) $t(format:time)',
longDate: 'd MMM', longDate: 'd MMM',
longDateTime: "d MMMM 'à' p", longDateTime: "d MMMM 'à' p",
fullDate: 'd MMM y', fullDate: 'd MMM y',
fullDateTime: "d MMMM y 'à' p", fullDateTime: "d MMMM y 'à' p",
}, },
translation: { translation: {
common: { common: {
account: 'Compte', account: 'Compte',
actions: 'Actions', actions: 'Actions',
addAttachment_title: 'Ajouter une pièce jointe', addAttachment_title: 'Ajouter une pièce jointe',
addComment: 'Ajouter un commentaire', addComment: 'Ajouter un commentaire',
addMember_title: 'Ajouter un membre', addMember_title: 'Ajouter un membre',
addUser_title: 'Ajouter un utilisateur', addUser_title: 'Ajouter un utilisateur',
administrator: 'Administrateur', administrator: 'Administrateur',
all: 'Tout', all: 'Tout',
allChangesWillBeAutomaticallySavedAfterConnectionRestored: allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'Toutes les modifications seront automatiquement enregistrées<br />une fois la connexion rétablie.', 'Toutes les modifications seront automatiquement enregistrées<br />une fois la connexion rétablie.',
areYouSureYouWantToDeleteThisAttachment: 'Voulez-vous vraiment supprimer cette pièce jointe?', areYouSureYouWantToDeleteThisAttachment: 'Voulez-vous vraiment supprimer cette pièce jointe?',
areYouSureYouWantToDeleteThisBoard: 'Êtes-vous sûr de vouloir supprimer ce forum?', areYouSureYouWantToDeleteThisBoard: 'Êtes-vous sûr de vouloir supprimer ce forum?',
areYouSureYouWantToDeleteThisCard: 'Voulez-vous vraiment supprimer cette carte?', areYouSureYouWantToDeleteThisCard: 'Voulez-vous vraiment supprimer cette carte?',
areYouSureYouWantToDeleteThisComment: 'Êtes-vous sûr de vouloir supprimer ce commentaire?', areYouSureYouWantToDeleteThisComment: 'Êtes-vous sûr de vouloir supprimer ce commentaire?',
areYouSureYouWantToDeleteThisLabel: 'Voulez-vous vraiment supprimer ce libellé?', areYouSureYouWantToDeleteThisLabel: 'Voulez-vous vraiment supprimer ce libellé?',
areYouSureYouWantToDeleteThisList: 'Êtes-vous sûr de vouloir supprimer cette liste?', areYouSureYouWantToDeleteThisList: 'Êtes-vous sûr de vouloir supprimer cette liste?',
areYouSureYouWantToDeleteThisProject: 'Êtes-vous sûr de vouloir supprimer ce projet?', areYouSureYouWantToDeleteThisProject: 'Êtes-vous sûr de vouloir supprimer ce projet?',
areYouSureYouWantToDeleteThisTask: 'Êtes-vous sûr de vouloir supprimer cette tâche?', areYouSureYouWantToDeleteThisTask: 'Êtes-vous sûr de vouloir supprimer cette tâche?',
areYouSureYouWantToDeleteThisUser: 'Êtes-vous sûr de vouloir supprimer cet utilisateur?', areYouSureYouWantToDeleteThisUser: 'Êtes-vous sûr de vouloir supprimer cet utilisateur?',
areYouSureYouWantToRemoveThisMemberFromProject: areYouSureYouWantToRemoveThisMemberFromProject:
'Êtes-vous sûr de vouloir supprimer ce membre du projet?', 'Êtes-vous sûr de vouloir supprimer ce membre du projet?',
attachment: 'Attachement', attachment: 'Attachement',
attachments: 'Pièces jointes', attachments: 'Pièces jointes',
authentication: 'Authentification', authentication: 'Authentification',
board: 'Tableau', board: 'Tableau',
boardNotFound_title: 'Carte non trouvée', boardNotFound_title: 'Carte non trouvée',
cardActions_title: 'Actions sur la carte', cardActions_title: 'Actions sur la carte',
cardNotFound_title: 'Carte non trouvée', cardNotFound_title: 'Carte non trouvée',
cardOrActionAreDeleted: "La carte ou l'action sont supprimées.", cardOrActionAreDeleted: "La carte ou l'action sont supprimées.",
color: 'Couleur', color: 'Couleur',
createBoard_title: 'Créer un tableau', createBoard_title: 'Créer un tableau',
createLabel_title: 'Créer une étiquette', createLabel_title: 'Créer une étiquette',
createNewOneOrSelectExistingOne: 'Créez-en un nouveau ou sélectionnez<br />un existant.', createNewOneOrSelectExistingOne: 'Créez-en un nouveau ou sélectionnez<br />un existant.',
createProject_title: 'Créer un projet', createProject_title: 'Créer un projet',
createTextFile_title: 'Créer un fichier texte', createTextFile_title: 'Créer un fichier texte',
currentPassword: 'Mot de passe actuel', currentPassword: 'Mot de passe actuel',
date: 'Date', date: 'Date',
dueDate_title: "Date d'échéance", dueDate_title: "Date d'échéance",
deleteAttachment_title: 'Supprimer la pièce jointe', deleteAttachment_title: 'Supprimer la pièce jointe',
deleteBoard_title: 'Supprimer le tableau', deleteBoard_title: 'Supprimer le tableau',
deleteCard_title: 'Supprimer la carte', deleteCard_title: 'Supprimer la carte',
deleteComment_title: 'Supprimer le commentaire', deleteComment_title: 'Supprimer le commentaire',
deleteLabel_title: "Supprimer l'étiquette", deleteLabel_title: "Supprimer l'étiquette",
deleteList_title: 'Supprimer la liste', deleteList_title: 'Supprimer la liste',
deleteProject_title: 'Supprimer le projet', deleteProject_title: 'Supprimer le projet',
deleteTask_title: 'Supprimer la tâche', deleteTask_title: 'Supprimer la tâche',
deleteUser_title: "Supprimer l'utilisateur", deleteUser_title: "Supprimer l'utilisateur",
description: 'Description', description: 'Description',
dropFileToUpload: 'Déposer le fichier à télécharger', dropFileToUpload: 'Déposer le fichier à télécharger',
editAttachment_title: 'Modifier la pièce jointe', editAttachment_title: 'Modifier la pièce jointe',
editAvatar_title: 'Modifier Avatar', editAvatar_title: 'Modifier Avatar',
editBoard_title: 'Modifier le tableau', editBoard_title: 'Modifier le tableau',
editDueDate_title: "Modifier la date d'échéance", editDueDate_title: "Modifier la date d'échéance",
editEmail_title: "Modifier l'e-mail", editEmail_title: "Modifier l'e-mail",
editLabel_title: "Modifier l'étiquette", editLabel_title: "Modifier l'étiquette",
editPassword_title: 'Modifier le mot de passe', editPassword_title: 'Modifier le mot de passe',
editStopwatch_title: 'Modifier la minuterie', editStopwatch_title: 'Modifier la minuterie',
editUsername_title: "Modifier le nom d'utilisateur", editUsername_title: "Modifier le nom d'utilisateur",
email: 'E-mail', email: 'E-mail',
emailAlreadyInUse: 'Email déjà utilisé', emailAlreadyInUse: 'Email déjà utilisé',
enterCardTitle: 'Entrer le titre de la carte...', enterCardTitle: 'Entrer le titre de la carte...',
enterDescription: 'Entrez la description...', enterDescription: 'Entrez la description...',
enterFilename: 'Entrez le nom du fichier', enterFilename: 'Entrez le nom du fichier',
enterListTitle: 'Entrer le titre de la liste...', enterListTitle: 'Entrer le titre de la liste...',
enterProjectTitle: 'Saisir le titre du projet', enterProjectTitle: 'Saisir le titre du projet',
enterTaskDescription: 'Saisir la description de la tâche...', enterTaskDescription: 'Saisir la description de la tâche...',
filterByLabels_title: 'Filtrer par libellés', filterByLabels_title: 'Filtrer par libellés',
filterByMembers_title: 'Filtrer par membres', filterByMembers_title: 'Filtrer par membres',
fromComputer_title: "Depuis l'ordinateur", fromComputer_title: "Depuis l'ordinateur",
hours: 'Les heures', hours: 'Les heures',
invalidCurrentPassword: 'Mot de passe actuel invalide', invalidCurrentPassword: 'Mot de passe actuel invalide',
labels: 'Étiquettes', labels: 'Étiquettes',
list: 'Lister', list: 'Lister',
listActions_title: 'Liste des actions', listActions_title: 'Liste des actions',
members: 'Membres', members: 'Membres',
minutes: 'Minutes', minutes: 'Minutes',
moveCard_title: 'Déplacer la carte', moveCard_title: 'Déplacer la carte',
name: 'Nom', name: 'Nom',
newEmail: 'Nouveau courriel', newEmail: 'Nouveau courriel',
newPassword: 'Nouveau mot de passe', newPassword: 'Nouveau mot de passe',
newUsername: "Nouveau nom d'utilisateur", newUsername: "Nouveau nom d'utilisateur",
noConnectionToServer: 'Pas de connexion au serveur', noConnectionToServer: 'Pas de connexion au serveur',
noBoards: 'Pas de planches', noBoards: 'Pas de planches',
noLists: 'Pas de listes', noLists: 'Pas de listes',
noProjects: 'Pas de projets', noProjects: 'Pas de projets',
notifications: 'Notifications', notifications: 'Notifications',
noUnreadNotifications: 'Aucune notification non lue.', noUnreadNotifications: 'Aucune notification non lue.',
openBoard_title: 'Open Board', openBoard_title: 'Open Board',
optional_inline: 'optionnel', optional_inline: 'optionnel',
organization: 'Organisation', organization: 'Organisation',
phone: 'Téléphone', phone: 'Téléphone',
preferences: 'Préférences', preferences: 'Préférences',
pressPasteShortcutToAddAttachmentFromClipboard: 'Conseil', pressPasteShortcutToAddAttachmentFromClipboard: 'Conseil',
project: 'Projet', project: 'Projet',
projectNotFound_title: 'Projet introuvable', projectNotFound_title: 'Projet introuvable',
removeMember_title: 'Supprimer le membre', removeMember_title: 'Supprimer le membre',
seconds: 'Secondes', seconds: 'Secondes',
selectBoard: 'Sélectionner une carte', selectBoard: 'Sélectionner une carte',
selectList: 'Sélectionner une liste', selectList: 'Sélectionner une liste',
selectProject: 'Sélectionner un projet', selectProject: 'Sélectionner un projet',
settings: 'Réglages', settings: 'Réglages',
stopwatch: 'Minuteur', stopwatch: 'Minuteur',
subscribeToMyOwnCardsByDefault: 'Abonnez-vous à mes propres cartes par défaut', subscribeToMyOwnCardsByDefault: 'Abonnez-vous à mes propres cartes par défaut',
taskActions_title: 'Actions de tâche', taskActions_title: 'Actions de tâche',
tasks: 'Tâches', tasks: 'Tâches',
time: 'Temps', time: 'Temps',
title: 'Titre', title: 'Titre',
userActions_title: "Actions de l'utilisateur", userActions_title: "Actions de l'utilisateur",
userAddedThisCardToList: '<0> {{user}} </0> <1> a ajouté cette carte à {{list}} </1>', userAddedThisCardToList: '<0> {{user}} </0> <1> a ajouté cette carte à {{list}} </1>',
userLeftNewCommentToCard: userLeftNewCommentToCard:
'{{user}} a laissé un nouveau commentaire {{comment}} à <2> {{card}} </2>', '{{user}} a laissé un nouveau commentaire {{comment}} à <2> {{card}} </2>',
userMovedCardFromListToList: userMovedCardFromListToList:
'{{user}} a déplacé <2> {{card}} </2> de {{fromList}} vers {{toList}}', '{{user}} a déplacé <2> {{card}} </2> de {{fromList}} vers {{toList}}',
userMovedThisCardFromListToList: userMovedThisCardFromListToList:
'<0> {{user}} </0> <1> a déplacé cette carte de {{fromList}} vers {{toList}} </1>', '<0> {{user}} </0> <1> a déplacé cette carte de {{fromList}} vers {{toList}} </1>',
username: "Nom d'utilisateur", username: "Nom d'utilisateur",
usernameAlreadyInUse: "Nom d'utilisateur déjà utilisé", usernameAlreadyInUse: "Nom d'utilisateur déjà utilisé",
users: 'Utilisateurs', users: 'Utilisateurs',
writeComment: 'Écrire un commentaire...', writeComment: 'Écrire un commentaire...',
}, },
action: { action: {
addAnotherCard: 'Ajouter une autre carte', addAnotherCard: 'Ajouter une autre carte',
addAnotherList: 'Ajouter une autre liste', addAnotherList: 'Ajouter une autre liste',
addAnotherTask: 'Ajouter une autre tâche', addAnotherTask: 'Ajouter une autre tâche',
addCard: 'Ajouter une carte', addCard: 'Ajouter une carte',
addCard_title: 'Ajouter une carte', addCard_title: 'Ajouter une carte',
addComment: 'Ajouter un commentaire', addComment: 'Ajouter un commentaire',
addList: 'Ajouter la liste', addList: 'Ajouter la liste',
addMoreDetailedDescription: 'Ajouter une description plus détaillée', addMoreDetailedDescription: 'Ajouter une description plus détaillée',
addTask: 'Ajouter une tâche', addTask: 'Ajouter une tâche',
addToCard: 'Ajouter à la carte', addToCard: 'Ajouter à la carte',
addUser: 'Ajouter un utilisateur', addUser: 'Ajouter un utilisateur',
createBoard: 'Créer un tableau', createBoard: 'Créer un tableau',
createFile: 'Créer un fichier', createFile: 'Créer un fichier',
createLabel: 'Créer une étiquette', createLabel: 'Créer une étiquette',
createNewLabel: 'Créer une nouvelle étiquette', createNewLabel: 'Créer une nouvelle étiquette',
createProject: 'Créer un projet', createProject: 'Créer un projet',
delete: 'Supprimer', delete: 'Supprimer',
deleteAttachment: 'Supprimer la pièce jointe', deleteAttachment: 'Supprimer la pièce jointe',
deleteAvatar: "Supprimer l'avatar", deleteAvatar: "Supprimer l'avatar",
deleteBoard: 'Supprimer le tableau', deleteBoard: 'Supprimer le tableau',
deleteCard: 'Supprimer la carte', deleteCard: 'Supprimer la carte',
deleteCard_title: 'Supprimer la carte', deleteCard_title: 'Supprimer la carte',
deleteComment: 'Supprimer le commentaire', deleteComment: 'Supprimer le commentaire',
deleteImage: "Supprimer l'image", deleteImage: "Supprimer l'image",
deleteLabel: "Supprimer l'étiquette", deleteLabel: "Supprimer l'étiquette",
deleteList: 'Supprimer la liste', deleteList: 'Supprimer la liste',
deleteList_title: 'Supprimer la liste', deleteList_title: 'Supprimer la liste',
deleteProject: 'Supprimer le projet', deleteProject: 'Supprimer le projet',
deleteProject_title: 'Supprimer le projet', deleteProject_title: 'Supprimer le projet',
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', 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',
editEmail_title: "Modifier l'e-mail", editEmail_title: "Modifier l'e-mail",
editPassword_title: 'Modifier le mot de passe', editPassword_title: 'Modifier le mot de passe',
editStopwatch_title: 'Modifier la minuterie', editStopwatch_title: 'Modifier la minuterie',
editTitle_title: 'Modifier le titre', editTitle_title: 'Modifier le titre',
editUsername_title: "Modifier le nom d'utilisateur", editUsername_title: "Modifier le nom d'utilisateur",
logOut_title: 'Se déconnecter', logOut_title: 'Se déconnecter',
makeCover_title: 'Faire la jaquette', makeCover_title: 'Faire la jaquette',
move: 'Déplacer', move: 'Déplacer',
moveCard_title: 'Déplacer la carte', moveCard_title: 'Déplacer la carte',
remove: 'Supprimer', remove: 'Supprimer',
removeBackground: "Supprimer l'arrière-plan", removeBackground: "Supprimer l'arrière-plan",
removeCover_title: 'Supprimer la jaquette', removeCover_title: 'Supprimer la jaquette',
removeFromProject: 'Supprimer du projet', removeFromProject: 'Supprimer du projet',
removeMember: 'Supprimer le membre', removeMember: 'Supprimer le membre',
save: 'Sauvegarder', save: 'Sauvegarder',
showAllAttachments: 'Afficher toutes les pièces jointes ({{hidden}} masquées)', showAllAttachments: 'Afficher toutes les pièces jointes ({{hidden}} masquées)',
showFewerAttachments: 'Afficher moins de pièces jointes', showFewerAttachments: 'Afficher moins de pièces jointes',
start: 'Début', start: 'Début',
stop: 'Arrêter', stop: 'Arrêter',
subscribe: "S'abonner", subscribe: "S'abonner",
unsubscribe: 'Se désabonner', unsubscribe: 'Se désabonner',
uploadNewAvatar: 'Télécharger un nouvel avatar', uploadNewAvatar: 'Télécharger un nouvel avatar',
uploadNewImage: 'Télécharger une nouvelle image', uploadNewImage: 'Télécharger une nouvelle image',
}, },
}, },
}; };

@ -1,358 +1,358 @@
import pick from 'lodash/pick'; 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';
import ActionTypes from '../constants/ActionTypes'; import ActionTypes from '../constants/ActionTypes';
import Config from '../constants/Config'; import Config from '../constants/Config';
import { ActivityTypes } from '../constants/Enums'; import { ActivityTypes } from '../constants/Enums';
export default class extends BaseModel { export default class extends BaseModel {
static modelName = 'Card'; static modelName = 'Card';
static fields = { static fields = {
id: attr(), id: attr(),
position: attr(), position: attr(),
name: attr(), name: attr(),
description: attr(), description: attr(),
dueDate: attr(), dueDate: attr(),
stopwatch: attr(), stopwatch: attr(),
isSubscribed: attr({ isSubscribed: attr({
getDefault: () => false, getDefault: () => false,
}), }),
isActivitiesFetching: attr({ isActivitiesFetching: attr({
getDefault: () => false, getDefault: () => false,
}), }),
isAllActivitiesFetched: attr({ isAllActivitiesFetched: attr({
getDefault: () => false, getDefault: () => false,
}), }),
isActivitiesDetailsVisible: attr({ isActivitiesDetailsVisible: attr({
getDefault: () => false, getDefault: () => false,
}), }),
isActivitiesDetailsFetching: attr({ isActivitiesDetailsFetching: attr({
getDefault: () => false, getDefault: () => false,
}), }),
boardId: fk({ boardId: fk({
to: 'Board', to: 'Board',
as: 'board', as: 'board',
relatedName: 'cards', relatedName: 'cards',
}), }),
listId: fk({ listId: fk({
to: 'List', to: 'List',
as: 'list', as: 'list',
relatedName: 'cards', relatedName: 'cards',
}), }),
coverAttachmentId: oneToOne({ coverAttachmentId: oneToOne({
to: 'Attachment', to: 'Attachment',
as: 'coverAttachment', as: 'coverAttachment',
relatedName: 'coveredCard', relatedName: 'coveredCard',
}), }),
users: many('User', 'cards'), users: many('User', 'cards'),
labels: many('Label', 'cards'), labels: many('Label', 'cards'),
}; };
static reducer({ type, payload }, Card) { static reducer({ type, payload }, Card) {
switch (type) { switch (type) {
case ActionTypes.LOCATION_CHANGE_HANDLE: case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE: case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE: case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.cards) { if (payload.cards) {
payload.cards.forEach((card) => { payload.cards.forEach((card) => {
Card.upsert(card); Card.upsert(card);
}); });
} }
if (payload.cardMemberships) { if (payload.cardMemberships) {
payload.cardMemberships.forEach(({ cardId, userId }) => { payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId); Card.withId(cardId).users.add(userId);
}); });
} }
if (payload.cardLabels) { if (payload.cardLabels) {
payload.cardLabels.forEach(({ cardId, labelId }) => { payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId); Card.withId(cardId).labels.add(labelId);
}); });
} }
break; break;
case ActionTypes.SOCKET_RECONNECT_HANDLE: case ActionTypes.SOCKET_RECONNECT_HANDLE:
Card.all() Card.all()
.toModelArray() .toModelArray()
.forEach((cardModel) => { .forEach((cardModel) => {
cardModel.deleteWithClearable(); cardModel.deleteWithClearable();
}); });
if (payload.cards) { if (payload.cards) {
payload.cards.forEach((card) => { payload.cards.forEach((card) => {
Card.upsert(card); Card.upsert(card);
}); });
} }
if (payload.cardMemberships) { if (payload.cardMemberships) {
payload.cardMemberships.forEach(({ cardId, userId }) => { payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId); Card.withId(cardId).users.add(userId);
}); });
} }
if (payload.cardLabels) { if (payload.cardLabels) {
payload.cardLabels.forEach(({ cardId, labelId }) => { payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId); Card.withId(cardId).labels.add(labelId);
}); });
} }
break; break;
case ActionTypes.USER_TO_CARD_ADD: { case ActionTypes.USER_TO_CARD_ADD: {
const cardModel = Card.withId(payload.cardId); const cardModel = Card.withId(payload.cardId);
cardModel.users.add(payload.id); cardModel.users.add(payload.id);
if (payload.isCurrent) { if (payload.isCurrent) {
cardModel.isSubscribed = true; cardModel.isSubscribed = true;
} }
break; break;
} }
case ActionTypes.USER_TO_CARD_ADD__SUCCESS: case ActionTypes.USER_TO_CARD_ADD__SUCCESS:
case ActionTypes.USER_TO_CARD_ADD_HANDLE: case ActionTypes.USER_TO_CARD_ADD_HANDLE:
try { try {
Card.withId(payload.cardMembership.cardId).users.add(payload.cardMembership.userId); Card.withId(payload.cardMembership.cardId).users.add(payload.cardMembership.userId);
} catch {} // eslint-disable-line no-empty } catch {} // eslint-disable-line no-empty
break; break;
case ActionTypes.USER_FROM_CARD_REMOVE: case ActionTypes.USER_FROM_CARD_REMOVE:
Card.withId(payload.cardId).users.remove(payload.id); Card.withId(payload.cardId).users.remove(payload.id);
break; break;
case ActionTypes.USER_FROM_CARD_REMOVE__SUCCESS: case ActionTypes.USER_FROM_CARD_REMOVE__SUCCESS:
case ActionTypes.USER_FROM_CARD_REMOVE_HANDLE: case ActionTypes.USER_FROM_CARD_REMOVE_HANDLE:
try { try {
Card.withId(payload.cardMembership.cardId).users.remove(payload.cardMembership.userId); Card.withId(payload.cardMembership.cardId).users.remove(payload.cardMembership.userId);
} catch {} // eslint-disable-line no-empty } catch {} // eslint-disable-line no-empty
break; break;
case ActionTypes.BOARD_FETCH__SUCCESS: case ActionTypes.BOARD_FETCH__SUCCESS:
payload.cards.forEach((card) => { payload.cards.forEach((card) => {
Card.upsert(card); Card.upsert(card);
}); });
payload.cardMemberships.forEach(({ cardId, userId }) => { payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId); Card.withId(cardId).users.add(userId);
}); });
payload.cardLabels.forEach(({ cardId, labelId }) => { payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId); Card.withId(cardId).labels.add(labelId);
}); });
break; break;
case ActionTypes.LABEL_TO_CARD_ADD: case ActionTypes.LABEL_TO_CARD_ADD:
Card.withId(payload.cardId).labels.add(payload.id); Card.withId(payload.cardId).labels.add(payload.id);
break; break;
case ActionTypes.LABEL_TO_CARD_ADD__SUCCESS: case ActionTypes.LABEL_TO_CARD_ADD__SUCCESS:
case ActionTypes.LABEL_TO_CARD_ADD_HANDLE: case ActionTypes.LABEL_TO_CARD_ADD_HANDLE:
try { try {
Card.withId(payload.cardLabel.cardId).labels.add(payload.cardLabel.labelId); Card.withId(payload.cardLabel.cardId).labels.add(payload.cardLabel.labelId);
} catch {} // eslint-disable-line no-empty } catch {} // eslint-disable-line no-empty
break; break;
case ActionTypes.LABEL_FROM_CARD_REMOVE: case ActionTypes.LABEL_FROM_CARD_REMOVE:
Card.withId(payload.cardId).labels.remove(payload.id); Card.withId(payload.cardId).labels.remove(payload.id);
break; break;
case ActionTypes.LABEL_FROM_CARD_REMOVE__SUCCESS: case ActionTypes.LABEL_FROM_CARD_REMOVE__SUCCESS:
case ActionTypes.LABEL_FROM_CARD_REMOVE_HANDLE: case ActionTypes.LABEL_FROM_CARD_REMOVE_HANDLE:
try { try {
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; break;
case ActionTypes.CARD_CREATE: case ActionTypes.CARD_CREATE:
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);
break; break;
case ActionTypes.CARD_CREATE__SUCCESS: case ActionTypes.CARD_CREATE__SUCCESS:
Card.withId(payload.localId).delete(); Card.withId(payload.localId).delete();
Card.upsert(payload.card); Card.upsert(payload.card);
break; break;
case ActionTypes.CARD_CREATE_HANDLE: { case ActionTypes.CARD_CREATE_HANDLE: {
const cardModel = Card.upsert(payload.card); const cardModel = Card.upsert(payload.card);
payload.cardMemberships.forEach(({ userId }) => { payload.cardMemberships.forEach(({ userId }) => {
cardModel.users.add(userId); cardModel.users.add(userId);
}); });
payload.cardLabels.forEach(({ labelId }) => { payload.cardLabels.forEach(({ labelId }) => {
cardModel.labels.add(labelId); cardModel.labels.add(labelId);
}); });
break; 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: { case ActionTypes.CARD_DUPLICATE: {
const cardModel = Card.withId(payload.id); const cardModel = Card.withId(payload.id);
const nextCardModel = Card.upsert({ const nextCardModel = Card.upsert({
...pick(cardModel.ref, [ ...pick(cardModel.ref, [
'boardId', 'boardId',
'listId', 'listId',
'position', 'position',
'name', 'name',
'description', 'description',
'dueDate', 'dueDate',
'stopwatch', 'stopwatch',
]), ]),
...payload.card, ...payload.card,
}); });
cardModel.users.toRefArray().forEach(({ id }) => { cardModel.users.toRefArray().forEach(({ id }) => {
nextCardModel.users.add(id); nextCardModel.users.add(id);
}); });
cardModel.labels.toRefArray().forEach(({ id }) => { cardModel.labels.toRefArray().forEach(({ id }) => {
nextCardModel.labels.add(id); nextCardModel.labels.add(id);
}); });
break; break;
} }
case ActionTypes.CARD_DUPLICATE__SUCCESS: { case ActionTypes.CARD_DUPLICATE__SUCCESS: {
Card.withId(payload.localId).deleteWithRelated(); Card.withId(payload.localId).deleteWithRelated();
const cardModel = Card.upsert(payload.card); const cardModel = Card.upsert(payload.card);
payload.cardMemberships.forEach(({ userId }) => { payload.cardMemberships.forEach(({ userId }) => {
cardModel.users.add(userId); cardModel.users.add(userId);
}); });
payload.cardLabels.forEach(({ labelId }) => { payload.cardLabels.forEach(({ labelId }) => {
cardModel.labels.add(labelId); cardModel.labels.add(labelId);
}); });
break; break;
} }
case ActionTypes.CARD_DELETE: case ActionTypes.CARD_DELETE:
Card.withId(payload.id).deleteWithRelated(); Card.withId(payload.id).deleteWithRelated();
break; break;
case ActionTypes.CARD_DELETE__SUCCESS: case ActionTypes.CARD_DELETE__SUCCESS:
case ActionTypes.CARD_DELETE_HANDLE: { case ActionTypes.CARD_DELETE_HANDLE: {
const cardModel = Card.withId(payload.card.id); const cardModel = Card.withId(payload.card.id);
if (cardModel) { if (cardModel) {
cardModel.deleteWithRelated(); cardModel.deleteWithRelated();
} }
break; break;
} }
case ActionTypes.ACTIVITIES_FETCH: case ActionTypes.ACTIVITIES_FETCH:
Card.withId(payload.cardId).update({ Card.withId(payload.cardId).update({
isActivitiesFetching: true, isActivitiesFetching: true,
}); });
break; break;
case ActionTypes.ACTIVITIES_FETCH__SUCCESS: case ActionTypes.ACTIVITIES_FETCH__SUCCESS:
Card.withId(payload.cardId).update({ Card.withId(payload.cardId).update({
isActivitiesFetching: false, isActivitiesFetching: false,
isAllActivitiesFetched: payload.activities.length < Config.ACTIVITIES_LIMIT, isAllActivitiesFetched: payload.activities.length < Config.ACTIVITIES_LIMIT,
}); });
break; break;
case ActionTypes.ACTIVITIES_DETAILS_TOGGLE: { case ActionTypes.ACTIVITIES_DETAILS_TOGGLE: {
const cardModel = Card.withId(payload.cardId); const cardModel = Card.withId(payload.cardId);
cardModel.isActivitiesDetailsVisible = payload.isVisible; cardModel.isActivitiesDetailsVisible = payload.isVisible;
if (payload.isVisible) { if (payload.isVisible) {
cardModel.isActivitiesDetailsFetching = true; cardModel.isActivitiesDetailsFetching = true;
} }
break; break;
} }
case ActionTypes.ACTIVITIES_DETAILS_TOGGLE__SUCCESS: { case ActionTypes.ACTIVITIES_DETAILS_TOGGLE__SUCCESS: {
const cardModel = Card.withId(payload.cardId); const cardModel = Card.withId(payload.cardId);
cardModel.update({ cardModel.update({
isAllActivitiesFetched: payload.activities.length < Config.ACTIVITIES_LIMIT, isAllActivitiesFetched: payload.activities.length < Config.ACTIVITIES_LIMIT,
isActivitiesDetailsFetching: false, isActivitiesDetailsFetching: false,
}); });
cardModel.deleteActivities(); cardModel.deleteActivities();
break; break;
} }
case ActionTypes.NOTIFICATION_CREATE_HANDLE: case ActionTypes.NOTIFICATION_CREATE_HANDLE:
payload.cards.forEach((card) => { payload.cards.forEach((card) => {
Card.upsert(card); Card.upsert(card);
}); });
break; break;
default: default:
} }
} }
getOrderedTasksQuerySet() { getOrderedTasksQuerySet() {
return this.tasks.orderBy('position'); return this.tasks.orderBy('position');
} }
getOrderedAttachmentsQuerySet() { getOrderedAttachmentsQuerySet() {
return this.attachments.orderBy('createdAt', false); return this.attachments.orderBy('createdAt', false);
} }
getFilteredOrderedInCardActivitiesQuerySet() { getFilteredOrderedInCardActivitiesQuerySet() {
const filter = { const filter = {
isInCard: true, isInCard: true,
}; };
if (!this.isActivitiesDetailsVisible) { if (!this.isActivitiesDetailsVisible) {
filter.type = ActivityTypes.COMMENT_CARD; filter.type = ActivityTypes.COMMENT_CARD;
} }
return this.activities.filter(filter).orderBy('createdAt', false); return this.activities.filter(filter).orderBy('createdAt', false);
} }
getUnreadNotificationsQuerySet() { getUnreadNotificationsQuerySet() {
return this.notifications.filter({ return this.notifications.filter({
isRead: false, isRead: false,
}); });
} }
isAvailableForUser(userId) { isAvailableForUser(userId) {
return this.board && this.board.isAvailableForUser(userId); return this.board && this.board.isAvailableForUser(userId);
} }
deleteClearable() { deleteClearable() {
this.users.clear(); this.users.clear();
this.labels.clear(); this.labels.clear();
} }
deleteActivities() { deleteActivities() {
this.activities.toModelArray().forEach((activityModel) => { this.activities.toModelArray().forEach((activityModel) => {
if (activityModel.notification) { if (activityModel.notification) {
activityModel.update({ activityModel.update({
isInCard: false, isInCard: false,
}); });
} else { } else {
activityModel.delete(); activityModel.delete();
} }
}); });
} }
deleteRelated() { deleteRelated() {
this.deleteClearable(); this.deleteClearable();
this.tasks.delete(); this.tasks.delete();
this.attachments.delete(); this.attachments.delete();
this.deleteActivities(); this.deleteActivities();
} }
deleteWithClearable() { deleteWithClearable() {
this.deleteClearable(); this.deleteClearable();
this.delete(); this.delete();
} }
deleteWithRelated() { deleteWithRelated() {
this.deleteRelated(); this.deleteRelated();
this.delete(); this.delete();
} }
} }

@ -1,100 +1,100 @@
import { attr, fk } from 'redux-orm'; import { attr, fk } from 'redux-orm';
import { createLocalId } from '../utils/local-id'; import { createLocalId } from '../utils/local-id';
import BaseModel from './BaseModel'; import BaseModel from './BaseModel';
import ActionTypes from '../constants/ActionTypes'; import ActionTypes from '../constants/ActionTypes';
export default class extends BaseModel { export default class extends BaseModel {
static modelName = 'Task'; static modelName = 'Task';
static fields = { static fields = {
id: attr(), id: attr(),
position: attr(), position: attr(),
name: attr(), name: attr(),
isCompleted: attr({ isCompleted: attr({
getDefault: () => false, getDefault: () => false,
}), }),
cardId: fk({ cardId: fk({
to: 'Card', to: 'Card',
as: 'card', as: 'card',
relatedName: 'tasks', relatedName: 'tasks',
}), }),
}; };
static reducer({ type, payload }, Task) { static reducer({ type, payload }, Task) {
switch (type) { switch (type) {
case ActionTypes.LOCATION_CHANGE_HANDLE: case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE: case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE: case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.tasks) { if (payload.tasks) {
payload.tasks.forEach((task) => { payload.tasks.forEach((task) => {
Task.upsert(task); Task.upsert(task);
}); });
} }
break; break;
case ActionTypes.SOCKET_RECONNECT_HANDLE: case ActionTypes.SOCKET_RECONNECT_HANDLE:
Task.all().delete(); Task.all().delete();
if (payload.tasks) { if (payload.tasks) {
payload.tasks.forEach((task) => { payload.tasks.forEach((task) => {
Task.upsert(task); Task.upsert(task);
}); });
} }
break; break;
case ActionTypes.BOARD_FETCH__SUCCESS: case ActionTypes.BOARD_FETCH__SUCCESS:
case ActionTypes.CARD_CREATE_HANDLE: case ActionTypes.CARD_CREATE_HANDLE:
case ActionTypes.CARD_DUPLICATE__SUCCESS: case ActionTypes.CARD_DUPLICATE__SUCCESS:
payload.tasks.forEach((task) => { payload.tasks.forEach((task) => {
Task.upsert(task); Task.upsert(task);
}); });
break; break;
case ActionTypes.CARD_DUPLICATE: case ActionTypes.CARD_DUPLICATE:
payload.taskIds.forEach((taskId, index) => { payload.taskIds.forEach((taskId, index) => {
const taskModel = Task.withId(taskId); const taskModel = Task.withId(taskId);
Task.upsert({ Task.upsert({
...taskModel.ref, ...taskModel.ref,
id: `${createLocalId()}-${index}`, // TODO: hack? id: `${createLocalId()}-${index}`, // TODO: hack?
cardId: payload.card.id, cardId: payload.card.id,
}); });
}); });
break; break;
case ActionTypes.TASK_CREATE: case ActionTypes.TASK_CREATE:
case ActionTypes.TASK_CREATE_HANDLE: case ActionTypes.TASK_CREATE_HANDLE:
case ActionTypes.TASK_UPDATE__SUCCESS: case ActionTypes.TASK_UPDATE__SUCCESS:
case ActionTypes.TASK_UPDATE_HANDLE: case ActionTypes.TASK_UPDATE_HANDLE:
Task.upsert(payload.task); Task.upsert(payload.task);
break; break;
case ActionTypes.TASK_CREATE__SUCCESS: case ActionTypes.TASK_CREATE__SUCCESS:
Task.withId(payload.localId).delete(); Task.withId(payload.localId).delete();
Task.upsert(payload.task); Task.upsert(payload.task);
break; break;
case ActionTypes.TASK_UPDATE: case ActionTypes.TASK_UPDATE:
Task.withId(payload.id).update(payload.data); Task.withId(payload.id).update(payload.data);
break; break;
case ActionTypes.TASK_DELETE: case ActionTypes.TASK_DELETE:
Task.withId(payload.id).delete(); Task.withId(payload.id).delete();
break; break;
case ActionTypes.TASK_DELETE__SUCCESS: case ActionTypes.TASK_DELETE__SUCCESS:
case ActionTypes.TASK_DELETE_HANDLE: { case ActionTypes.TASK_DELETE_HANDLE: {
const taskModel = Task.withId(payload.task.id); const taskModel = Task.withId(payload.task.id);
if (taskModel) { if (taskModel) {
taskModel.delete(); taskModel.delete();
} }
break; break;
} }
default: default:
} }
} }
} }

@ -1,225 +1,225 @@
import { call, put, select } from 'redux-saga/effects'; import { call, put, select } from 'redux-saga/effects';
import { goToBoard, goToCard } from './router'; import { goToBoard, goToCard } from './router';
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 i18n from '../../../i18n'; 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) {
const { boardId } = yield select(selectors.selectListById, listId); const { boardId } = yield select(selectors.selectListById, listId);
const nextData = { const nextData = {
...data, ...data,
position: yield select(selectors.selectNextCardPosition, listId), position: yield select(selectors.selectNextCardPosition, listId),
}; };
const localId = yield call(createLocalId); const localId = yield call(createLocalId);
yield put( yield put(
actions.createCard({ actions.createCard({
...nextData, ...nextData,
boardId, boardId,
listId, listId,
id: localId, id: localId,
}), }),
); );
let card; let card;
try { try {
({ item: card } = yield call(request, api.createCard, listId, nextData)); ({ item: card } = yield call(request, api.createCard, listId, nextData));
} catch (error) { } catch (error) {
yield put(actions.createCard.failure(localId, error)); yield put(actions.createCard.failure(localId, error));
return; return;
} }
yield put(actions.createCard.success(localId, card)); yield put(actions.createCard.success(localId, card));
if (autoOpen) { if (autoOpen) {
yield call(goToCard, card.id); yield call(goToCard, card.id);
} }
} }
export function* handleCardCreate({ id }) { export function* handleCardCreate({ id }) {
let card; let card;
let cardMemberships; let cardMemberships;
let cardLabels; let cardLabels;
let tasks; let tasks;
let attachments; let attachments;
try { try {
({ ({
item: card, item: card,
included: { cardMemberships, cardLabels, tasks, attachments }, included: { cardMemberships, cardLabels, tasks, attachments },
} = yield call(request, api.getCard, id)); } = yield call(request, api.getCard, id));
} catch (error) { } catch (error) {
return; return;
} }
yield put(actions.handleCardCreate(card, cardMemberships, cardLabels, tasks, attachments)); yield put(actions.handleCardCreate(card, cardMemberships, cardLabels, tasks, attachments));
} }
export function* updateCard(id, data) { export function* updateCard(id, data) {
yield put(actions.updateCard(id, data)); yield put(actions.updateCard(id, data));
let card; let card;
try { try {
({ item: card } = yield call(request, api.updateCard, id, data)); ({ item: card } = yield call(request, api.updateCard, id, data));
} catch (error) { } catch (error) {
yield put(actions.updateCard.failure(id, error)); yield put(actions.updateCard.failure(id, error));
return; return;
} }
yield put(actions.updateCard.success(card)); yield put(actions.updateCard.success(card));
} }
export function* updateCurrentCard(data) { export function* updateCurrentCard(data) {
const { cardId } = yield select(selectors.selectPath); const { cardId } = yield select(selectors.selectPath);
yield call(updateCard, cardId, data); yield call(updateCard, cardId, data);
} }
// TODO: handle card transfer // TODO: handle card transfer
export function* handleCardUpdate(card) { 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) {
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, {
listId, listId,
position, position,
}); });
} }
export function* moveCurrentCard(listId, index) { export function* moveCurrentCard(listId, index) {
const { cardId } = yield select(selectors.selectPath); const { cardId } = yield select(selectors.selectPath);
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) {
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);
if (id === currentCardId) { if (id === currentCardId) {
yield call(goToBoard, currentBoardId); yield call(goToBoard, currentBoardId);
} }
yield call(updateCard, id, { yield call(updateCard, id, {
boardId, boardId,
listId, listId,
position, position,
}); });
} }
export function* transferCurrentCard(boardId, listId, index) { export function* transferCurrentCard(boardId, listId, index) {
const { cardId } = yield select(selectors.selectPath); const { cardId } = yield select(selectors.selectPath);
yield call(transferCard, cardId, boardId, listId, index); yield call(transferCard, cardId, boardId, listId, index);
} }
export function* duplicateCard(id) { export function* duplicateCard(id) {
const { listId, name } = yield select(selectors.selectCardById, id); const { listId, name } = yield select(selectors.selectCardById, id);
const index = yield select(selectors.selectCardIndexById, id); const index = yield select(selectors.selectCardIndexById, id);
const nextData = { const nextData = {
position: yield select(selectors.selectNextCardPosition, listId, index + 1), position: yield select(selectors.selectNextCardPosition, listId, index + 1),
name: `${name} (${i18n.t('common.copy', { name: `${name} (${i18n.t('common.copy', {
context: 'inline', context: 'inline',
})})`, })})`,
}; };
const localId = yield call(createLocalId); const localId = yield call(createLocalId);
const taskIds = yield select(selectors.selectTaskIdsByCardId, id); const taskIds = yield select(selectors.selectTaskIdsByCardId, id);
yield put( yield put(
actions.duplicateCard( actions.duplicateCard(
id, id,
{ {
...nextData, ...nextData,
id: localId, id: localId,
}, },
taskIds, taskIds,
), ),
); );
let card; let card;
let cardMemberships; let cardMemberships;
let cardLabels; let cardLabels;
let tasks; let tasks;
try { try {
({ ({
item: card, item: card,
included: { cardMemberships, cardLabels, tasks }, included: { cardMemberships, cardLabels, tasks },
} = yield call(request, api.duplicateCard, id, nextData)); } = yield call(request, api.duplicateCard, id, nextData));
} catch (error) { } catch (error) {
yield put(actions.duplicateCard.failure(localId, error)); yield put(actions.duplicateCard.failure(localId, error));
return; return;
} }
yield put(actions.duplicateCard.success(localId, card, cardMemberships, cardLabels, tasks)); yield put(actions.duplicateCard.success(localId, card, cardMemberships, cardLabels, tasks));
} }
export function* duplicateCurrentCard() { export function* duplicateCurrentCard() {
const { cardId } = yield select(selectors.selectPath); const { cardId } = yield select(selectors.selectPath);
yield call(duplicateCard, cardId); 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);
if (id === cardId) { if (id === cardId) {
yield call(goToBoard, boardId); yield call(goToBoard, boardId);
} }
yield put(actions.deleteCard(id)); yield put(actions.deleteCard(id));
let card; let card;
try { try {
({ item: card } = yield call(request, api.deleteCard, id)); ({ item: card } = yield call(request, api.deleteCard, id));
} catch (error) { } catch (error) {
yield put(actions.deleteCard.failure(id, error)); yield put(actions.deleteCard.failure(id, error));
return; return;
} }
yield put(actions.deleteCard.success(card)); yield put(actions.deleteCard.success(card));
} }
export function* deleteCurrentCard() { export function* deleteCurrentCard() {
const { cardId } = yield select(selectors.selectPath); const { cardId } = yield select(selectors.selectPath);
yield call(deleteCard, cardId); yield call(deleteCard, cardId);
} }
export function* handleCardDelete(card) { export function* handleCardDelete(card) {
const { cardId, boardId } = yield select(selectors.selectPath); const { cardId, boardId } = yield select(selectors.selectPath);
if (card.id === cardId) { if (card.id === cardId) {
yield call(goToBoard, boardId); yield call(goToBoard, boardId);
} }
yield put(actions.handleCardDelete(card)); yield put(actions.handleCardDelete(card));
} }
export default { export default {
createCard, createCard,
handleCardCreate, handleCardCreate,
updateCard, updateCard,
updateCurrentCard, updateCurrentCard,
handleCardUpdate, handleCardUpdate,
moveCard, moveCard,
moveCurrentCard, moveCurrentCard,
transferCard, transferCard,
transferCurrentCard, transferCurrentCard,
duplicateCard, duplicateCard,
duplicateCurrentCard, duplicateCurrentCard,
deleteCard, deleteCard,
deleteCurrentCard, deleteCurrentCard,
handleCardDelete, handleCardDelete,
}; };

@ -1,43 +1,43 @@
import { all, takeEvery } from 'redux-saga/effects'; import { all, takeEvery } from 'redux-saga/effects';
import services from '../services'; import services from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes'; import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* cardsWatchers() { export default function* cardsWatchers() {
yield all([ yield all([
takeEvery(EntryActionTypes.CARD_CREATE, ({ payload: { listId, data, autoOpen } }) => takeEvery(EntryActionTypes.CARD_CREATE, ({ payload: { listId, data, autoOpen } }) =>
services.createCard(listId, data, autoOpen), services.createCard(listId, data, autoOpen),
), ),
takeEvery(EntryActionTypes.CARD_CREATE_HANDLE, ({ payload: { card } }) => takeEvery(EntryActionTypes.CARD_CREATE_HANDLE, ({ payload: { card } }) =>
services.handleCardCreate(card), services.handleCardCreate(card),
), ),
takeEvery(EntryActionTypes.CARD_UPDATE, ({ payload: { id, data } }) => takeEvery(EntryActionTypes.CARD_UPDATE, ({ payload: { id, data } }) =>
services.updateCard(id, data), services.updateCard(id, data),
), ),
takeEvery(EntryActionTypes.CURRENT_CARD_UPDATE, ({ payload: { data } }) => takeEvery(EntryActionTypes.CURRENT_CARD_UPDATE, ({ payload: { data } }) =>
services.updateCurrentCard(data), services.updateCurrentCard(data),
), ),
takeEvery(EntryActionTypes.CARD_UPDATE_HANDLE, ({ payload: { card } }) => takeEvery(EntryActionTypes.CARD_UPDATE_HANDLE, ({ payload: { card } }) =>
services.handleCardUpdate(card), services.handleCardUpdate(card),
), ),
takeEvery(EntryActionTypes.CARD_MOVE, ({ payload: { id, listId, index } }) => takeEvery(EntryActionTypes.CARD_MOVE, ({ payload: { id, listId, index } }) =>
services.moveCard(id, listId, index), services.moveCard(id, listId, index),
), ),
takeEvery(EntryActionTypes.CURRENT_CARD_MOVE, ({ payload: { listId, index } }) => takeEvery(EntryActionTypes.CURRENT_CARD_MOVE, ({ payload: { listId, index } }) =>
services.moveCurrentCard(listId, index), services.moveCurrentCard(listId, index),
), ),
takeEvery(EntryActionTypes.CARD_TRANSFER, ({ payload: { id, boardId, listId, index } }) => takeEvery(EntryActionTypes.CARD_TRANSFER, ({ payload: { id, boardId, listId, index } }) =>
services.transferCard(id, boardId, listId, index), services.transferCard(id, boardId, listId, index),
), ),
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.CARD_DUPLICATE, ({ payload: { id } }) => services.duplicateCard(id)),
takeEvery(EntryActionTypes.CURRENT_CARD_DUPLICATE, () => services.duplicateCurrentCard()), 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),
), ),
]); ]);
} }

@ -1,350 +1,350 @@
import { createSelector } from 'redux-orm'; import { createSelector } from 'redux-orm';
import orm from '../orm'; import orm from '../orm';
import { selectPath } from './router'; import { selectPath } from './router';
import { selectCurrentUserId } from './users'; import { selectCurrentUserId } from './users';
import { isLocalId } from '../utils/local-id'; import { isLocalId } from '../utils/local-id';
export const makeSelectCardById = () => export const makeSelectCardById = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return { return {
...cardModel.ref, ...cardModel.ref,
coverUrl: cardModel.coverAttachment && cardModel.coverAttachment.coverUrl, coverUrl: cardModel.coverAttachment && cardModel.coverAttachment.coverUrl,
isPersisted: !isLocalId(id), isPersisted: !isLocalId(id),
}; };
}, },
); );
export const selectCardById = makeSelectCardById(); export const selectCardById = makeSelectCardById();
export const makeSelectCardIndexById = () => export const makeSelectCardIndexById = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
const cardModels = cardModel.list.getFilteredOrderedCardsModelArray(); const cardModels = cardModel.list.getFilteredOrderedCardsModelArray();
return cardModels.findIndex((cardModelItem) => cardModelItem.id === cardModel.id); return cardModels.findIndex((cardModelItem) => cardModelItem.id === cardModel.id);
}, },
); );
export const selectCardIndexById = makeSelectCardIndexById(); export const selectCardIndexById = makeSelectCardIndexById();
export const makeSelectUsersByCardId = () => export const makeSelectUsersByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.users.toRefArray(); return cardModel.users.toRefArray();
}, },
); );
export const selectUsersByCardId = makeSelectUsersByCardId(); export const selectUsersByCardId = makeSelectUsersByCardId();
export const makeSelectLabelsByCardId = () => export const makeSelectLabelsByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.labels.toRefArray(); return cardModel.labels.toRefArray();
}, },
); );
export const selectLabelsByCardId = makeSelectLabelsByCardId(); export const selectLabelsByCardId = makeSelectLabelsByCardId();
export const makeSelectTaskIdsByCardId = () => export const makeSelectTaskIdsByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel return cardModel
.getOrderedTasksQuerySet() .getOrderedTasksQuerySet()
.toRefArray() .toRefArray()
.map((task) => task.id); .map((task) => task.id);
}, },
); );
export const selectTaskIdsByCardId = makeSelectTaskIdsByCardId(); export const selectTaskIdsByCardId = makeSelectTaskIdsByCardId();
export const makeSelectTasksByCardId = () => export const makeSelectTasksByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.getOrderedTasksQuerySet().toRefArray(); return cardModel.getOrderedTasksQuerySet().toRefArray();
}, },
); );
export const selectTasksByCardId = makeSelectTasksByCardId(); export const selectTasksByCardId = makeSelectTasksByCardId();
export const makeSelectLastActivityIdByCardId = () => export const makeSelectLastActivityIdByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
const lastActivityModel = cardModel.getFilteredOrderedInCardActivitiesQuerySet().last(); const lastActivityModel = cardModel.getFilteredOrderedInCardActivitiesQuerySet().last();
return lastActivityModel && lastActivityModel.id; return lastActivityModel && lastActivityModel.id;
}, },
); );
export const selectLastActivityIdByCardId = makeSelectLastActivityIdByCardId(); export const selectLastActivityIdByCardId = makeSelectLastActivityIdByCardId();
export const makeSelectNotificationsByCardId = () => export const makeSelectNotificationsByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.getUnreadNotificationsQuerySet().toRefArray(); return cardModel.getUnreadNotificationsQuerySet().toRefArray();
}, },
); );
export const selectNotificationsByCardId = makeSelectNotificationsByCardId(); export const selectNotificationsByCardId = makeSelectNotificationsByCardId();
export const makeSelectNotificationsTotalByCardId = () => export const makeSelectNotificationsTotalByCardId = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,
({ Card }, id) => { ({ Card }, id) => {
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.getUnreadNotificationsQuerySet().count(); return cardModel.getUnreadNotificationsQuerySet().count();
}, },
); );
export const selectNotificationsTotalByCardId = makeSelectNotificationsTotalByCardId(); export const selectNotificationsTotalByCardId = makeSelectNotificationsTotalByCardId();
export const selectCurrentCard = createSelector( export const selectCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
({ Card }, id) => { ({ Card }, id) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.ref; return cardModel.ref;
}, },
); );
export const selectUsersForCurrentCard = createSelector( export const selectUsersForCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
({ Card }, id) => { ({ Card }, id) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.users.toRefArray(); return cardModel.users.toRefArray();
}, },
); );
export const selectLabelsForCurrentCard = createSelector( export const selectLabelsForCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
({ Card }, id) => { ({ Card }, id) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel.labels.toRefArray(); return cardModel.labels.toRefArray();
}, },
); );
export const selectTasksForCurrentCard = createSelector( export const selectTasksForCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
({ Card }, id) => { ({ Card }, id) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel return cardModel
.getOrderedTasksQuerySet() .getOrderedTasksQuerySet()
.toRefArray() .toRefArray()
.map((task) => ({ .map((task) => ({
...task, ...task,
isPersisted: !isLocalId(task.id), isPersisted: !isLocalId(task.id),
})); }));
}, },
); );
export const selectAttachmentsForCurrentCard = createSelector( export const selectAttachmentsForCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
({ Card }, id) => { ({ Card }, id) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel return cardModel
.getOrderedAttachmentsQuerySet() .getOrderedAttachmentsQuerySet()
.toRefArray() .toRefArray()
.map((attachment) => ({ .map((attachment) => ({
...attachment, ...attachment,
isCover: attachment.id === cardModel.coverAttachmentId, isCover: attachment.id === cardModel.coverAttachmentId,
isPersisted: !isLocalId(attachment.id), isPersisted: !isLocalId(attachment.id),
})); }));
}, },
); );
export const selectActivitiesForCurrentCard = createSelector( export const selectActivitiesForCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
(state) => selectCurrentUserId(state), (state) => selectCurrentUserId(state),
({ Card }, id, currentUserId) => { ({ Card }, id, currentUserId) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel return cardModel
.getFilteredOrderedInCardActivitiesQuerySet() .getFilteredOrderedInCardActivitiesQuerySet()
.toModelArray() .toModelArray()
.map((activityModel) => ({ .map((activityModel) => ({
...activityModel.ref, ...activityModel.ref,
isPersisted: !isLocalId(activityModel.id), isPersisted: !isLocalId(activityModel.id),
user: { user: {
...activityModel.user.ref, ...activityModel.user.ref,
isCurrent: activityModel.user.id === currentUserId, isCurrent: activityModel.user.id === currentUserId,
}, },
})); }));
}, },
); );
export const selectNotificationIdsForCurrentCard = createSelector( export const selectNotificationIdsForCurrentCard = createSelector(
orm, orm,
(state) => selectPath(state).cardId, (state) => selectPath(state).cardId,
({ Card }, id) => { ({ Card }, id) => {
if (!id) { if (!id) {
return id; return id;
} }
const cardModel = Card.withId(id); const cardModel = Card.withId(id);
if (!cardModel) { if (!cardModel) {
return cardModel; return cardModel;
} }
return cardModel return cardModel
.getUnreadNotificationsQuerySet() .getUnreadNotificationsQuerySet()
.toRefArray() .toRefArray()
.map((notification) => notification.id); .map((notification) => notification.id);
}, },
); );
export default { export default {
makeSelectCardById, makeSelectCardById,
selectCardById, selectCardById,
makeSelectCardIndexById, makeSelectCardIndexById,
selectCardIndexById, selectCardIndexById,
makeSelectUsersByCardId, makeSelectUsersByCardId,
selectUsersByCardId, selectUsersByCardId,
makeSelectLabelsByCardId, makeSelectLabelsByCardId,
selectLabelsByCardId, selectLabelsByCardId,
makeSelectTaskIdsByCardId, makeSelectTaskIdsByCardId,
selectTaskIdsByCardId, selectTaskIdsByCardId,
makeSelectTasksByCardId, makeSelectTasksByCardId,
selectTasksByCardId, selectTasksByCardId,
makeSelectLastActivityIdByCardId, makeSelectLastActivityIdByCardId,
selectLastActivityIdByCardId, selectLastActivityIdByCardId,
makeSelectNotificationsByCardId, makeSelectNotificationsByCardId,
selectNotificationsByCardId, selectNotificationsByCardId,
makeSelectNotificationsTotalByCardId, makeSelectNotificationsTotalByCardId,
selectNotificationsTotalByCardId, selectNotificationsTotalByCardId,
selectCurrentCard, selectCurrentCard,
selectUsersForCurrentCard, selectUsersForCurrentCard,
selectLabelsForCurrentCard, selectLabelsForCurrentCard,
selectTasksForCurrentCard, selectTasksForCurrentCard,
selectAttachmentsForCurrentCard, selectAttachmentsForCurrentCard,
selectActivitiesForCurrentCard, selectActivitiesForCurrentCard,
selectNotificationIdsForCurrentCard, selectNotificationIdsForCurrentCard,
}; };

8244
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,69 +1,61 @@
{ {
"name": "planka", "name": "planka",
"version": "0.0.1-beta.1", "version": "0.0.1-beta.1",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/plankanban/planka.git" "url": "https://github.com/plankanban/planka.git"
}, },
"license": "AGPL-3.0", "license": "AGPL-3.0",
"author": "Maksim Eltyshev", "author": "Maksim Eltyshev",
"scripts": { "scripts": {
"client:build": "npm run build --prefix client", "client:build": "npm run build --prefix client",
"client:lint": "npm run lint --prefix client", "client:lint": "npm run lint --prefix client",
"client:start": "npm start --prefix client", "client:start": "npm start --prefix client",
"client:test": "npm test --prefix client", "client:test": "npm test --prefix client",
"docker:build": "docker build -t ghcr.io/plankanban/planka:local -f Dockerfile .", "docker:build": "docker build -t ghcr.io/plankanban/planka:local -f Dockerfile .",
"docker:build:base": "docker build -t ghcr.io/plankanban/planka:base-local -f Dockerfile.base .", "docker:build:base": "docker build -t ghcr.io/plankanban/planka:base-local -f Dockerfile.base .",
"postinstall": "(cd server && npm i && cd ../client && npm i)", "postinstall": "(cd server && npm i && cd ../client && npm i)",
"lint": "npm run server:lint && npm run client:lint", "lint": "npm run server:lint && npm run client:lint",
"prepare": "husky install", "prepare": "husky install",
"server:console": "npm run console --prefix server", "server:console": "npm run console --prefix server",
"server:db:init": "npm run db:init --prefix server", "server:db:init": "npm run db:init --prefix server",
"server:db:migrate": "npm run db:migrate --prefix server", "server:db:migrate": "npm run db:migrate --prefix server",
"server:db:seed": "npm run db:seed --prefix server", "server:db:seed": "npm run db:seed --prefix server",
"server:lint": "npm run lint --prefix server", "server:lint": "npm run lint --prefix server",
"server:start": "npm start --prefix server", "server:start": "npm start --prefix server",
"server:start:prod": "npm run start:prod --prefix server", "server:start:prod": "npm run start:prod --prefix server",
"server:test": "npm test --prefix server", "server:test": "npm test --prefix server",
"start": "concurrently -n server,client \"npm run server:start\" \"npm run client:start\"", "start": "concurrently -n server,client \"npm run server:start\" \"npm run client:start\"",
"test": "npm run server:test && npm run client:test" "test": "npm run server:test && npm run client:test"
}, },
"lint-staged": { "prettier": {
"client/**/*.{js,jsx}": [ "printWidth": 100,
"npm run client:lint" "singleQuote": true,
], "trailingComma": "all"
"server/**/*.js": [ },
"npm run server:lint" "eslintConfig": {
] "plugins": [
}, "prettier"
"prettier": { ],
"printWidth": 100, "extends": [
"singleQuote": true, "plugin:prettier/recommended"
"trailingComma": "all" ],
}, "rules": {
"eslintConfig": { "no-undef": "off",
"plugins": [ "prettier/prettier": "error"
"prettier" }
], },
"extends": [ "dependencies": {
"plugin:prettier/recommended" "concurrently": "^8.2.2",
], "husky": "^8.0.3",
"rules": { "lint-staged": "^15.1.0"
"no-undef": "off", },
"prettier/prettier": "error" "devDependencies": {
} "eslint": "^8.53.0",
}, "eslint-config-prettier": "^9.0.0",
"dependencies": { "eslint-plugin-prettier": "^5.0.1",
"concurrently": "^8.2.2", "prettier": "^3.1.0"
"husky": "^8.0.3", }
"lint-staged": "^15.1.0" }
},
"devDependencies": {
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"prettier": "^3.1.0"
}
}

@ -1,82 +1,82 @@
const Errors = { const Errors = {
NOT_ENOUGH_RIGHTS: { NOT_ENOUGH_RIGHTS: {
notEnoughRights: 'Not enough rights', notEnoughRights: 'Not enough rights',
}, },
CARD_NOT_FOUND: { CARD_NOT_FOUND: {
cardNotFound: 'Card not found', cardNotFound: 'Card not found',
}, },
}; };
module.exports = { module.exports = {
inputs: { inputs: {
id: { id: {
type: 'string', type: 'string',
regex: /^[0-9]+$/, regex: /^[0-9]+$/,
required: true, required: true,
}, },
position: { position: {
type: 'number', type: 'number',
required: true, required: true,
}, },
name: { name: {
type: 'string', type: 'string',
}, },
}, },
exits: { exits: {
notEnoughRights: { notEnoughRights: {
responseType: 'forbidden', responseType: 'forbidden',
}, },
cardNotFound: { cardNotFound: {
responseType: 'notFound', responseType: 'notFound',
}, },
}, },
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { card, list, board } = await sails.helpers.cards const { card, list, board } = await sails.helpers.cards
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: card.boardId,
userId: currentUser.id, userId: currentUser.id,
}); });
if (!boardMembership) { if (!boardMembership) {
throw Errors.CARD_NOT_FOUND; // Forbidden throw Errors.CARD_NOT_FOUND; // Forbidden
} }
if (boardMembership.role !== BoardMembership.Roles.EDITOR) { if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
throw Errors.NOT_ENOUGH_RIGHTS; throw Errors.NOT_ENOUGH_RIGHTS;
} }
const values = _.pick(inputs, ['position', 'name']); const values = _.pick(inputs, ['position', 'name']);
const { const {
card: nextCard, card: nextCard,
cardMemberships, cardMemberships,
cardLabels, cardLabels,
tasks, tasks,
} = await sails.helpers.cards.duplicateOne.with({ } = await sails.helpers.cards.duplicateOne.with({
board, board,
list, list,
record: card, record: card,
values: { values: {
...values, ...values,
creatorUser: currentUser, creatorUser: currentUser,
}, },
request: this.req, request: this.req,
}); });
return { return {
item: nextCard, item: nextCard,
included: { included: {
cardMemberships, cardMemberships,
cardLabels, cardLabels,
tasks, tasks,
}, },
}; };
}, },
}; };

@ -1,156 +1,156 @@
const POSITION_GAP = 65535; // TODO: move to config const POSITION_GAP = 65535; // TODO: move to config
module.exports = { module.exports = {
inputs: { inputs: {
user: { user: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
trelloBoard: { trelloBoard: {
type: 'json', type: 'json',
required: true, required: true,
}, },
}, },
async fn(inputs) { async fn(inputs) {
const trelloToPlankaLabels = {}; const trelloToPlankaLabels = {};
const getTrelloLists = () => inputs.trelloBoard.lists.filter((list) => !list.closed); const getTrelloLists = () => inputs.trelloBoard.lists.filter((list) => !list.closed);
const getUsedTrelloLabels = () => { const getUsedTrelloLabels = () => {
const result = {}; const result = {};
inputs.trelloBoard.cards inputs.trelloBoard.cards
.map((card) => card.labels) .map((card) => card.labels)
.flat() .flat()
.forEach((label) => { .forEach((label) => {
result[label.id] = label; result[label.id] = label;
}); });
return Object.values(result); return Object.values(result);
}; };
const getTrelloCardsOfList = (listId) => const getTrelloCardsOfList = (listId) =>
inputs.trelloBoard.cards.filter((card) => card.idList === listId && !card.closed); inputs.trelloBoard.cards.filter((card) => card.idList === listId && !card.closed);
const getAllTrelloCheckItemsOfCard = (cardId) => const getAllTrelloCheckItemsOfCard = (cardId) =>
inputs.trelloBoard.checklists inputs.trelloBoard.checklists
.filter((checklist) => checklist.idCard === cardId) .filter((checklist) => checklist.idCard === cardId)
.map((checklist) => checklist.checkItems) .map((checklist) => checklist.checkItems)
.flat(); .flat();
const getTrelloCommentsOfCard = (cardId) => const getTrelloCommentsOfCard = (cardId) =>
inputs.trelloBoard.actions.filter( inputs.trelloBoard.actions.filter(
(action) => (action) =>
action.type === 'commentCard' && action.type === 'commentCard' &&
action.data && action.data &&
action.data.card && action.data.card &&
action.data.card.id === cardId, action.data.card.id === cardId,
); );
const getPlankaLabelColor = (trelloLabelColor) => const getPlankaLabelColor = (trelloLabelColor) =>
Label.COLORS.find((color) => color.indexOf(trelloLabelColor) !== -1) || 'desert-sand'; Label.COLORS.find((color) => color.indexOf(trelloLabelColor) !== -1) || 'desert-sand';
const importCardLabels = async (plankaCard, trelloCard) => { const importCardLabels = async (plankaCard, trelloCard) => {
return Promise.all( return Promise.all(
trelloCard.labels.map(async (trelloLabel) => { trelloCard.labels.map(async (trelloLabel) => {
return CardLabel.create({ return CardLabel.create({
cardId: plankaCard.id, cardId: plankaCard.id,
labelId: trelloToPlankaLabels[trelloLabel.id].id, labelId: trelloToPlankaLabels[trelloLabel.id].id,
}); });
}), }),
); );
}; };
const importTasks = async (plankaCard, trelloCard) => { const importTasks = async (plankaCard, trelloCard) => {
// TODO find workaround for tasks/checklist mismapping, see issue trello2planka#5 // TODO find workaround for tasks/checklist mismapping, see issue trello2planka#5
return Promise.all( return Promise.all(
getAllTrelloCheckItemsOfCard(trelloCard.id).map(async (trelloCheckItem) => { getAllTrelloCheckItemsOfCard(trelloCard.id).map(async (trelloCheckItem) => {
return Task.create({ return Task.create({
cardId: plankaCard.id, cardId: plankaCard.id,
position: trelloCheckItem.pos, position: trelloCheckItem.pos,
name: trelloCheckItem.name, name: trelloCheckItem.name,
isCompleted: trelloCheckItem.state === 'complete', isCompleted: trelloCheckItem.state === 'complete',
}).fetch(); }).fetch();
}), }),
); );
}; };
const importComments = async (plankaCard, trelloCard) => { const importComments = async (plankaCard, trelloCard) => {
const trelloComments = getTrelloCommentsOfCard(trelloCard.id); const trelloComments = getTrelloCommentsOfCard(trelloCard.id);
trelloComments.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); trelloComments.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
return Promise.all( return Promise.all(
trelloComments.map(async (trelloComment) => { trelloComments.map(async (trelloComment) => {
return Action.create({ return Action.create({
cardId: plankaCard.id, cardId: plankaCard.id,
userId: inputs.user.id, userId: inputs.user.id,
type: 'commentCard', type: 'commentCard',
data: { data: {
text: text:
`${trelloComment.data.text}\n\n---\n*Note: imported comment, originally posted by ` + `${trelloComment.data.text}\n\n---\n*Note: imported comment, originally posted by ` +
`\n${trelloComment.memberCreator.fullName} (${trelloComment.memberCreator.username}) on ${trelloComment.date}*`, `\n${trelloComment.memberCreator.fullName} (${trelloComment.memberCreator.username}) on ${trelloComment.date}*`,
}, },
}).fetch(); }).fetch();
}), }),
); );
}; };
const importCards = async (plankaList, trelloList) => { const importCards = async (plankaList, trelloList) => {
return Promise.all( return Promise.all(
getTrelloCardsOfList(trelloList.id).map(async (trelloCard) => { getTrelloCardsOfList(trelloList.id).map(async (trelloCard) => {
const plankaCard = await Card.create({ const plankaCard = await Card.create({
boardId: inputs.board.id, boardId: inputs.board.id,
listId: plankaList.id, listId: plankaList.id,
creatorUserId: inputs.user.id, creatorUserId: inputs.user.id,
position: trelloCard.pos, position: trelloCard.pos,
name: trelloCard.name, name: trelloCard.name,
description: trelloCard.desc || null, description: trelloCard.desc || null,
dueDate: trelloCard.due, dueDate: trelloCard.due,
}).fetch(); }).fetch();
await importCardLabels(plankaCard, trelloCard); await importCardLabels(plankaCard, trelloCard);
await importTasks(plankaCard, trelloCard); await importTasks(plankaCard, trelloCard);
await importComments(plankaCard, trelloCard); await importComments(plankaCard, trelloCard);
return plankaCard; return plankaCard;
}), }),
); );
}; };
const importLabels = async () => { const importLabels = async () => {
return Promise.all( return Promise.all(
getUsedTrelloLabels().map(async (trelloLabel, index) => { getUsedTrelloLabels().map(async (trelloLabel, index) => {
const plankaLabel = await Label.create({ const plankaLabel = await Label.create({
boardId: inputs.board.id, boardId: inputs.board.id,
position: POSITION_GAP * (index + 1), position: POSITION_GAP * (index + 1),
name: trelloLabel.name || null, name: trelloLabel.name || null,
color: getPlankaLabelColor(trelloLabel.color), color: getPlankaLabelColor(trelloLabel.color),
}).fetch(); }).fetch();
trelloToPlankaLabels[trelloLabel.id] = plankaLabel; trelloToPlankaLabels[trelloLabel.id] = plankaLabel;
}), }),
); );
}; };
const importLists = async () => { const importLists = async () => {
return Promise.all( return Promise.all(
getTrelloLists().map(async (trelloList) => { getTrelloLists().map(async (trelloList) => {
const plankaList = await List.create({ const plankaList = await List.create({
boardId: inputs.board.id, boardId: inputs.board.id,
name: trelloList.name, name: trelloList.name,
position: trelloList.pos, position: trelloList.pos,
}).fetch(); }).fetch();
return importCards(plankaList, trelloList); return importCards(plankaList, trelloList);
}), }),
); );
}; };
await importLabels(); await importLabels();
await importLists(); await importLists();
}, },
}; };

@ -1,144 +1,144 @@
const valuesValidator = (value) => { const valuesValidator = (value) => {
if (!_.isPlainObject(value)) { if (!_.isPlainObject(value)) {
return false; return false;
} }
if (!_.isUndefined(value.position) && !_.isFinite(value.position)) { if (!_.isUndefined(value.position) && !_.isFinite(value.position)) {
return false; return false;
} }
if (!_.isPlainObject(value.creatorUser)) { if (!_.isPlainObject(value.creatorUser)) {
return false; return false;
} }
return true; return true;
}; };
module.exports = { module.exports = {
inputs: { inputs: {
record: { record: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
values: { values: {
type: 'ref', type: 'ref',
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: { list: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
request: { request: {
type: 'ref', type: 'ref',
}, },
}, },
async fn(inputs) { async fn(inputs) {
const { values } = inputs; const { values } = inputs;
const cards = await sails.helpers.lists.getCards(inputs.record.listId); const cards = await sails.helpers.lists.getCards(inputs.record.listId);
const { position, repositions } = sails.helpers.utils.insertToPositionables( const { position, repositions } = sails.helpers.utils.insertToPositionables(
values.position, values.position,
cards, cards,
); );
repositions.forEach(async ({ id, position: nextPosition }) => { repositions.forEach(async ({ id, position: nextPosition }) => {
await Card.update({ await Card.update({
id, id,
listId: inputs.record.listId, listId: inputs.record.listId,
}).set({ }).set({
position: nextPosition, position: nextPosition,
}); });
sails.sockets.broadcast(`board:${inputs.record.boardId}`, 'cardUpdate', { sails.sockets.broadcast(`board:${inputs.record.boardId}`, 'cardUpdate', {
item: { item: {
id, id,
position: nextPosition, position: nextPosition,
}, },
}); });
}); });
const card = await Card.create({ const card = await Card.create({
..._.pick(inputs.record, [ ..._.pick(inputs.record, [
'boardId', 'boardId',
'listId', 'listId',
'name', 'name',
'description', 'description',
'dueDate', 'dueDate',
'stopwatch', 'stopwatch',
]), ]),
...values, ...values,
position, position,
creatorUserId: values.creatorUser.id, creatorUserId: values.creatorUser.id,
}).fetch(); }).fetch();
const cardMemberships = await sails.helpers.cards.getCardMemberships(inputs.record.id); const cardMemberships = await sails.helpers.cards.getCardMemberships(inputs.record.id);
const cardMembershipsValues = cardMemberships.map((cardMembership) => ({ const cardMembershipsValues = cardMemberships.map((cardMembership) => ({
..._.pick(cardMembership, ['userId']), ..._.pick(cardMembership, ['userId']),
cardId: card.id, cardId: card.id,
})); }));
const nextCardMemberships = await CardMembership.createEach(cardMembershipsValues).fetch(); const nextCardMemberships = await CardMembership.createEach(cardMembershipsValues).fetch();
const cardLabels = await sails.helpers.cards.getCardLabels(inputs.record.id); const cardLabels = await sails.helpers.cards.getCardLabels(inputs.record.id);
const cardLabelsValues = cardLabels.map((cardLabel) => ({ const cardLabelsValues = cardLabels.map((cardLabel) => ({
..._.pick(cardLabel, ['labelId']), ..._.pick(cardLabel, ['labelId']),
cardId: card.id, cardId: card.id,
})); }));
const nextCardLabels = await CardLabel.createEach(cardLabelsValues).fetch(); const nextCardLabels = await CardLabel.createEach(cardLabelsValues).fetch();
const tasks = await sails.helpers.cards.getTasks(inputs.record.id); const tasks = await sails.helpers.cards.getTasks(inputs.record.id);
const tasksValues = tasks.map((task) => ({ const tasksValues = tasks.map((task) => ({
..._.pick(task, ['position', 'name', 'isCompleted']), ..._.pick(task, ['position', 'name', 'isCompleted']),
cardId: card.id, cardId: card.id,
})); }));
const nextTasks = await Task.createEach(tasksValues).fetch(); const nextTasks = await Task.createEach(tasksValues).fetch();
sails.sockets.broadcast( sails.sockets.broadcast(
`board:${card.boardId}`, `board:${card.boardId}`,
'cardCreate', 'cardCreate',
{ {
item: card, item: card,
}, },
inputs.request, inputs.request,
); );
if (values.creatorUser.subscribeToOwnCards) { if (values.creatorUser.subscribeToOwnCards) {
await CardSubscription.create({ await CardSubscription.create({
cardId: card.id, cardId: card.id,
userId: card.creatorUserId, userId: card.creatorUserId,
}).tolerate('E_UNIQUE'); }).tolerate('E_UNIQUE');
sails.sockets.broadcast(`user:${card.creatorUserId}`, 'cardUpdate', { sails.sockets.broadcast(`user:${card.creatorUserId}`, 'cardUpdate', {
item: { item: {
id: card.id, id: card.id,
isSubscribed: true, isSubscribed: true,
}, },
}); });
} }
await sails.helpers.actions.createOne.with({ await sails.helpers.actions.createOne.with({
values: { values: {
card, card,
type: Action.Types.CREATE_CARD, // TODO: introduce separate type? type: Action.Types.CREATE_CARD, // TODO: introduce separate type?
data: { data: {
list: _.pick(inputs.list, ['id', 'name']), list: _.pick(inputs.list, ['id', 'name']),
}, },
user: values.creatorUser, user: values.creatorUser,
}, },
board: inputs.board, board: inputs.board,
}); });
return { return {
card, card,
cardMemberships: nextCardMemberships, cardMemberships: nextCardMemberships,
cardLabels: nextCardLabels, cardLabels: nextCardLabels,
tasks: nextTasks, tasks: nextTasks,
}; };
}, },
}; };

@ -1,97 +1,97 @@
/** /**
* Route Mappings * Route Mappings
* (sails.config.routes) * (sails.config.routes)
* *
* Your routes tell Sails what to do each time it receives a request. * Your routes tell Sails what to do each time it receives a request.
* *
* For more information on configuring custom routes, check out: * For more information on configuring custom routes, check out:
* https://sailsjs.com/anatomy/config/routes-js * https://sailsjs.com/anatomy/config/routes-js
*/ */
module.exports.routes = { module.exports.routes = {
'GET /api/config': 'show-config', 'GET /api/config': 'show-config',
'POST /api/access-tokens': 'access-tokens/create', 'POST /api/access-tokens': 'access-tokens/create',
'POST /api/access-tokens/exchange-using-oidc': 'access-tokens/exchange-using-oidc', 'POST /api/access-tokens/exchange-using-oidc': 'access-tokens/exchange-using-oidc',
'DELETE /api/access-tokens/me': 'access-tokens/delete', 'DELETE /api/access-tokens/me': 'access-tokens/delete',
'GET /api/users': 'users/index', 'GET /api/users': 'users/index',
'POST /api/users': 'users/create', 'POST /api/users': 'users/create',
'GET /api/users/:id': 'users/show', 'GET /api/users/:id': 'users/show',
'PATCH /api/users/:id': 'users/update', 'PATCH /api/users/:id': 'users/update',
'PATCH /api/users/:id/email': 'users/update-email', 'PATCH /api/users/:id/email': 'users/update-email',
'PATCH /api/users/:id/password': 'users/update-password', 'PATCH /api/users/:id/password': 'users/update-password',
'PATCH /api/users/:id/username': 'users/update-username', 'PATCH /api/users/:id/username': 'users/update-username',
'POST /api/users/:id/avatar': 'users/update-avatar', 'POST /api/users/:id/avatar': 'users/update-avatar',
'DELETE /api/users/:id': 'users/delete', 'DELETE /api/users/:id': 'users/delete',
'GET /api/projects': 'projects/index', 'GET /api/projects': 'projects/index',
'POST /api/projects': 'projects/create', 'POST /api/projects': 'projects/create',
'GET /api/projects/:id': 'projects/show', 'GET /api/projects/:id': 'projects/show',
'PATCH /api/projects/:id': 'projects/update', 'PATCH /api/projects/:id': 'projects/update',
'POST /api/projects/:id/background-image': 'projects/update-background-image', 'POST /api/projects/:id/background-image': 'projects/update-background-image',
'DELETE /api/projects/:id': 'projects/delete', 'DELETE /api/projects/:id': 'projects/delete',
'POST /api/projects/:projectId/managers': 'project-managers/create', 'POST /api/projects/:projectId/managers': 'project-managers/create',
'DELETE /api/project-managers/:id': 'project-managers/delete', 'DELETE /api/project-managers/:id': 'project-managers/delete',
'POST /api/projects/:projectId/boards': 'boards/create', 'POST /api/projects/:projectId/boards': 'boards/create',
'GET /api/boards/:id': 'boards/show', 'GET /api/boards/:id': 'boards/show',
'PATCH /api/boards/:id': 'boards/update', 'PATCH /api/boards/:id': 'boards/update',
'DELETE /api/boards/:id': 'boards/delete', 'DELETE /api/boards/:id': 'boards/delete',
'POST /api/boards/:boardId/memberships': 'board-memberships/create', 'POST /api/boards/:boardId/memberships': 'board-memberships/create',
'PATCH /api/board-memberships/:id': 'board-memberships/update', 'PATCH /api/board-memberships/:id': 'board-memberships/update',
'DELETE /api/board-memberships/:id': 'board-memberships/delete', 'DELETE /api/board-memberships/:id': 'board-memberships/delete',
'POST /api/boards/:boardId/labels': 'labels/create', 'POST /api/boards/:boardId/labels': 'labels/create',
'PATCH /api/labels/:id': 'labels/update', 'PATCH /api/labels/:id': 'labels/update',
'DELETE /api/labels/:id': 'labels/delete', 'DELETE /api/labels/:id': 'labels/delete',
'POST /api/boards/:boardId/lists': 'lists/create', 'POST /api/boards/:boardId/lists': 'lists/create',
'PATCH /api/lists/:id': 'lists/update', 'PATCH /api/lists/:id': 'lists/update',
'DELETE /api/lists/:id': 'lists/delete', 'DELETE /api/lists/:id': 'lists/delete',
'POST /api/lists/:listId/cards': 'cards/create', 'POST /api/lists/:listId/cards': 'cards/create',
'GET /api/cards/:id': 'cards/show', 'GET /api/cards/:id': 'cards/show',
'PATCH /api/cards/:id': 'cards/update', 'PATCH /api/cards/:id': 'cards/update',
'POST /api/cards/:id/duplicate': 'cards/duplicate', 'POST /api/cards/:id/duplicate': 'cards/duplicate',
'DELETE /api/cards/:id': 'cards/delete', 'DELETE /api/cards/:id': 'cards/delete',
'POST /api/cards/:cardId/memberships': 'card-memberships/create', 'POST /api/cards/:cardId/memberships': 'card-memberships/create',
'DELETE /api/cards/:cardId/memberships': 'card-memberships/delete', 'DELETE /api/cards/:cardId/memberships': 'card-memberships/delete',
'POST /api/cards/:cardId/labels': 'card-labels/create', 'POST /api/cards/:cardId/labels': 'card-labels/create',
'DELETE /api/cards/:cardId/labels/:labelId': 'card-labels/delete', 'DELETE /api/cards/:cardId/labels/:labelId': 'card-labels/delete',
'POST /api/cards/:cardId/tasks': 'tasks/create', 'POST /api/cards/:cardId/tasks': 'tasks/create',
'PATCH /api/tasks/:id': 'tasks/update', 'PATCH /api/tasks/:id': 'tasks/update',
'DELETE /api/tasks/:id': 'tasks/delete', 'DELETE /api/tasks/:id': 'tasks/delete',
'POST /api/cards/:cardId/attachments': 'attachments/create', 'POST /api/cards/:cardId/attachments': 'attachments/create',
'PATCH /api/attachments/:id': 'attachments/update', 'PATCH /api/attachments/:id': 'attachments/update',
'DELETE /api/attachments/:id': 'attachments/delete', 'DELETE /api/attachments/:id': 'attachments/delete',
'GET /api/cards/:cardId/actions': 'actions/index', 'GET /api/cards/:cardId/actions': 'actions/index',
'POST /api/cards/:cardId/comment-actions': 'comment-actions/create', 'POST /api/cards/:cardId/comment-actions': 'comment-actions/create',
'PATCH /api/comment-actions/:id': 'comment-actions/update', 'PATCH /api/comment-actions/:id': 'comment-actions/update',
'DELETE /api/comment-actions/:id': 'comment-actions/delete', 'DELETE /api/comment-actions/:id': 'comment-actions/delete',
'GET /api/notifications': 'notifications/index', 'GET /api/notifications': 'notifications/index',
'GET /api/notifications/:id': 'notifications/show', 'GET /api/notifications/:id': 'notifications/show',
'PATCH /api/notifications/:ids': 'notifications/update', 'PATCH /api/notifications/:ids': 'notifications/update',
'GET /attachments/:id/download/:filename': { 'GET /attachments/:id/download/:filename': {
action: 'attachments/download', action: 'attachments/download',
skipAssets: false, skipAssets: false,
}, },
'GET /attachments/:id/download/thumbnails/cover-256.:extension': { 'GET /attachments/:id/download/thumbnails/cover-256.:extension': {
action: 'attachments/download-thumbnail', action: 'attachments/download-thumbnail',
skipAssets: false, skipAssets: false,
}, },
'GET /*': { 'GET /*': {
view: 'index', view: 'index',
skipAssets: true, skipAssets: true,
}, },
}; };

@ -1,19 +1,19 @@
module.exports.up = (knex) => module.exports.up = (knex) =>
knex.schema.createTable('card_label', (table) => { knex.schema.createTable('card_label', (table) => {
/* Columns */ /* Columns */
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()')); table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
table.bigInteger('card_id').notNullable(); table.bigInteger('card_id').notNullable();
table.bigInteger('label_id').notNullable(); table.bigInteger('label_id').notNullable();
table.timestamp('created_at', true); table.timestamp('created_at', true);
table.timestamp('updated_at', true); table.timestamp('updated_at', true);
/* Indexes */ /* Indexes */
table.unique(['card_id', 'label_id']); table.unique(['card_id', 'label_id']);
table.index('label_id'); table.index('label_id');
}); });
module.exports.down = (knex) => knex.schema.dropTable('card_label'); module.exports.down = (knex) => knex.schema.dropTable('card_label');

@ -1,44 +1,44 @@
module.exports.up = async (knex) => { module.exports.up = async (knex) => {
await knex.schema.createTable('identity_provider_user', (table) => { await knex.schema.createTable('identity_provider_user', (table) => {
/* Columns */ /* Columns */
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()')); table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
table.bigInteger('user_id').notNullable(); table.bigInteger('user_id').notNullable();
table.text('issuer').notNullable(); table.text('issuer').notNullable();
table.text('sub').notNullable(); table.text('sub').notNullable();
table.timestamp('created_at', true); table.timestamp('created_at', true);
table.timestamp('updated_at', true); table.timestamp('updated_at', true);
/* Indexes */ /* Indexes */
table.unique(['issuer', 'sub']); table.unique(['issuer', 'sub']);
table.index('user_id'); table.index('user_id');
}); });
await knex.schema.table('user_account', (table) => { await knex.schema.table('user_account', (table) => {
/* Columns */ /* Columns */
table.boolean('is_sso').notNullable().default(false); table.boolean('is_sso').notNullable().default(false);
/* Modifications */ /* Modifications */
table.setNullable('password'); table.setNullable('password');
}); });
return knex.schema.alterTable('user_account', (table) => { return knex.schema.alterTable('user_account', (table) => {
table.boolean('is_sso').notNullable().alter(); table.boolean('is_sso').notNullable().alter();
}); });
}; };
module.exports.down = async (knex) => { module.exports.down = async (knex) => {
await knex.schema.dropTable('identity_provider_user'); await knex.schema.dropTable('identity_provider_user');
return knex.schema.table('user_account', (table) => { return knex.schema.table('user_account', (table) => {
table.dropColumn('is_sso'); table.dropColumn('is_sso');
table.dropNullable('password'); table.dropNullable('password');
}); });
}; };

Loading…
Cancel
Save