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
name: planka
description: A Helm chart to deploy Planka and it's dependencies.
# 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
# to be deployed.
#
# 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
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# 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.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.23
# 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
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.2"
dependencies:
- alias: postgresql
condition: postgresql.enabled
name: postgresql
repository: &bitnami-repo https://charts.bitnami.com/bitnami
version: 12.5.1
apiVersion: v2
name: planka
description: A Helm chart to deploy Planka and it's dependencies.
# 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
# to be deployed.
#
# 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
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# 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.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.23
# 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
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.2"
dependencies:
- alias: postgresql
condition: postgresql.enabled
name: postgresql
repository: &bitnami-repo https://charts.bitnami.com/bitnami
version: 12.5.1

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

@ -1,182 +1,182 @@
# Default values for planka.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: ghcr.io/plankanban/planka
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
# Generate a secret using openssl rand -base64 45
secretkey: ""
# Base url for Planka. Will override `ingress.hosts[0].host`
# Defaults to `http://localhost:3000` if ingress is disabled.
baseUrl: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 1337
## @param service.containerPort Planka HTTP container port
## If empty will default to 1337
##
containerPort: 1337
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
# Used to set planka BASE_URL if no `baseurl` is provided.
- host: planka.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: planka-tls
# hosts:
# - planka.local
resources: {}
# 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
# 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:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
postgresql:
enabled: true
auth:
database: planka
username: planka
password: ""
postgresPassword: ""
replicationPassword: ""
# existingSecret: planka-postgresql
serviceBindings:
enabled: true
## Set this if you disable the built-in postgresql deployment
dburl:
## PVC-based data storage configuration
persistence:
enabled: false
# existingClaim: netbox-data
# storageClass: "-"
accessMode: ReadWriteOnce
size: 10Gi
## OpenID Identity Management configuration
##
## Example:
## ---------------
## oidc:
## enabled: true
## clientId: sxxaAIAxVXlCxTmc1YLHBbQr8NL8MqLI2DUbt42d
## clientSecret: om4RTMRVHRszU7bqxB7RZNkHIzA8e4sGYWxeCwIMYQXPwEBWe4SY5a0wwCe9ltB3zrq5f0dnFnp34cEHD7QSMHsKvV9AiV5Z7eqDraMnv0I8IFivmuV5wovAECAYreSI
## issuerUrl: https://auth.local/application/o/planka/
## admin:
## roles:
## - planka-admin
##
## ---------------
## NOTE: A minimal configuration requires setting `clientId`, `clientSecret` and `issuerUrl`. (plus `admin.roles` for administrators)
## ref: https://docs.planka.cloud/docs/Configuration/OIDC
##
oidc:
## @param oidc.enabled Enable single sign-on (SSO) with OpenID Connect (OIDC)
##
enabled: false
## OIDC credentials
## @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.
##
## NOTE: Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`
##
clientId: ""
clientSecret: ""
## @param oidc.existingSecret Name of an existing secret containing OIDC credentials
## NOTE: Must contain key `clientId` and `clientSecret`
## NOTE: When it's set, the `clientId` and `clientSecret` parameters are ignored
##
existingSecret: ""
## @param oidc.issuerUrl The OpenID connect metadata document endpoint
##
issuerUrl: ""
## @param oidc.scopes A list of scopes required for OIDC client.
## If empty will default to `openid`, `profile` and `email`
## NOTE: Planka needs the email and name claims
##
scopes: []
## Admin permissions configuration
admin:
## @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.
## If empty will default to `false`
##
ignoreRoles: false
## @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`
##
rolesAttribute: groups
## @param oidc.admin.roles The names of the admin groups
##
roles: []
# - planka-admin
# Default values for planka.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: ghcr.io/plankanban/planka
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
# Generate a secret using openssl rand -base64 45
secretkey: ""
# Base url for Planka. Will override `ingress.hosts[0].host`
# Defaults to `http://localhost:3000` if ingress is disabled.
baseUrl: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 1337
## @param service.containerPort Planka HTTP container port
## If empty will default to 1337
##
containerPort: 1337
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
# Used to set planka BASE_URL if no `baseurl` is provided.
- host: planka.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: planka-tls
# hosts:
# - planka.local
resources: {}
# 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
# 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:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
postgresql:
enabled: true
auth:
database: planka
username: planka
password: ""
postgresPassword: ""
replicationPassword: ""
# existingSecret: planka-postgresql
serviceBindings:
enabled: true
## Set this if you disable the built-in postgresql deployment
dburl:
## PVC-based data storage configuration
persistence:
enabled: false
# existingClaim: netbox-data
# storageClass: "-"
accessMode: ReadWriteOnce
size: 10Gi
## OpenID Identity Management configuration
##
## Example:
## ---------------
## oidc:
## enabled: true
## clientId: sxxaAIAxVXlCxTmc1YLHBbQr8NL8MqLI2DUbt42d
## clientSecret: om4RTMRVHRszU7bqxB7RZNkHIzA8e4sGYWxeCwIMYQXPwEBWe4SY5a0wwCe9ltB3zrq5f0dnFnp34cEHD7QSMHsKvV9AiV5Z7eqDraMnv0I8IFivmuV5wovAECAYreSI
## issuerUrl: https://auth.local/application/o/planka/
## admin:
## roles:
## - planka-admin
##
## ---------------
## NOTE: A minimal configuration requires setting `clientId`, `clientSecret` and `issuerUrl`. (plus `admin.roles` for administrators)
## ref: https://docs.planka.cloud/docs/Configuration/OIDC
##
oidc:
## @param oidc.enabled Enable single sign-on (SSO) with OpenID Connect (OIDC)
##
enabled: false
## OIDC credentials
## @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.
##
## NOTE: Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`
##
clientId: ""
clientSecret: ""
## @param oidc.existingSecret Name of an existing secret containing OIDC credentials
## NOTE: Must contain key `clientId` and `clientSecret`
## NOTE: When it's set, the `clientId` and `clientSecret` parameters are ignored
##
existingSecret: ""
## @param oidc.issuerUrl The OpenID connect metadata document endpoint
##
issuerUrl: ""
## @param oidc.scopes A list of scopes required for OIDC client.
## If empty will default to `openid`, `profile` and `email`
## NOTE: Planka needs the email and name claims
##
scopes: []
## Admin permissions configuration
admin:
## @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.
## If empty will default to `false`
##
ignoreRoles: false
## @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`
##
rolesAttribute: groups
## @param oidc.admin.roles The names of the admin groups
##
roles: []
# - 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';
const createCard = (card) => ({
type: ActionTypes.CARD_CREATE,
payload: {
card,
},
});
createCard.success = (localId, card) => ({
type: ActionTypes.CARD_CREATE__SUCCESS,
payload: {
localId,
card,
},
});
createCard.failure = (localId, error) => ({
type: ActionTypes.CARD_CREATE__FAILURE,
payload: {
localId,
error,
},
});
const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({
type: ActionTypes.CARD_CREATE_HANDLE,
payload: {
card,
cardMemberships,
cardLabels,
tasks,
attachments,
},
});
const updateCard = (id, data) => ({
type: ActionTypes.CARD_UPDATE,
payload: {
id,
data,
},
});
updateCard.success = (card) => ({
type: ActionTypes.CARD_UPDATE__SUCCESS,
payload: {
card,
},
});
updateCard.failure = (id, error) => ({
type: ActionTypes.CARD_UPDATE__FAILURE,
payload: {
id,
error,
},
});
const handleCardUpdate = (card) => ({
type: ActionTypes.CARD_UPDATE_HANDLE,
payload: {
card,
},
});
const duplicateCard = (id, card, taskIds) => ({
type: ActionTypes.CARD_DUPLICATE,
payload: {
id,
card,
taskIds,
},
});
duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({
type: ActionTypes.CARD_DUPLICATE__SUCCESS,
payload: {
localId,
card,
cardMemberships,
cardLabels,
tasks,
},
});
duplicateCard.failure = (id, error) => ({
type: ActionTypes.CARD_DUPLICATE__FAILURE,
payload: {
id,
error,
},
});
const deleteCard = (id) => ({
type: ActionTypes.CARD_DELETE,
payload: {
id,
},
});
deleteCard.success = (card) => ({
type: ActionTypes.CARD_DELETE__SUCCESS,
payload: {
card,
},
});
deleteCard.failure = (id, error) => ({
type: ActionTypes.CARD_DELETE__FAILURE,
payload: {
id,
error,
},
});
const handleCardDelete = (card) => ({
type: ActionTypes.CARD_DELETE_HANDLE,
payload: {
card,
},
});
export default {
createCard,
handleCardCreate,
updateCard,
handleCardUpdate,
duplicateCard,
deleteCard,
handleCardDelete,
};
import ActionTypes from '../constants/ActionTypes';
const createCard = (card) => ({
type: ActionTypes.CARD_CREATE,
payload: {
card,
},
});
createCard.success = (localId, card) => ({
type: ActionTypes.CARD_CREATE__SUCCESS,
payload: {
localId,
card,
},
});
createCard.failure = (localId, error) => ({
type: ActionTypes.CARD_CREATE__FAILURE,
payload: {
localId,
error,
},
});
const handleCardCreate = (card, cardMemberships, cardLabels, tasks, attachments) => ({
type: ActionTypes.CARD_CREATE_HANDLE,
payload: {
card,
cardMemberships,
cardLabels,
tasks,
attachments,
},
});
const updateCard = (id, data) => ({
type: ActionTypes.CARD_UPDATE,
payload: {
id,
data,
},
});
updateCard.success = (card) => ({
type: ActionTypes.CARD_UPDATE__SUCCESS,
payload: {
card,
},
});
updateCard.failure = (id, error) => ({
type: ActionTypes.CARD_UPDATE__FAILURE,
payload: {
id,
error,
},
});
const handleCardUpdate = (card) => ({
type: ActionTypes.CARD_UPDATE_HANDLE,
payload: {
card,
},
});
const duplicateCard = (id, card, taskIds) => ({
type: ActionTypes.CARD_DUPLICATE,
payload: {
id,
card,
taskIds,
},
});
duplicateCard.success = (localId, card, cardMemberships, cardLabels, tasks) => ({
type: ActionTypes.CARD_DUPLICATE__SUCCESS,
payload: {
localId,
card,
cardMemberships,
cardLabels,
tasks,
},
});
duplicateCard.failure = (id, error) => ({
type: ActionTypes.CARD_DUPLICATE__FAILURE,
payload: {
id,
error,
},
});
const deleteCard = (id) => ({
type: ActionTypes.CARD_DELETE,
payload: {
id,
},
});
deleteCard.success = (card) => ({
type: ActionTypes.CARD_DELETE__SUCCESS,
payload: {
card,
},
});
deleteCard.failure = (id, error) => ({
type: ActionTypes.CARD_DELETE__FAILURE,
payload: {
id,
error,
},
});
const handleCardDelete = (card) => ({
type: ActionTypes.CARD_DELETE_HANDLE,
payload: {
card,
},
});
export default {
createCard,
handleCardCreate,
updateCard,
handleCardUpdate,
duplicateCard,
deleteCard,
handleCardDelete,
};

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

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

8244
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,69 +1,61 @@
{
"name": "planka",
"version": "0.0.1-beta.1",
"private": true,
"homepage": "https://plankanban.github.io/planka",
"repository": {
"type": "git",
"url": "https://github.com/plankanban/planka.git"
},
"license": "AGPL-3.0",
"author": "Maksim Eltyshev",
"scripts": {
"client:build": "npm run build --prefix client",
"client:lint": "npm run lint --prefix client",
"client:start": "npm start --prefix client",
"client:test": "npm test --prefix client",
"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 .",
"postinstall": "(cd server && npm i && cd ../client && npm i)",
"lint": "npm run server:lint && npm run client:lint",
"prepare": "husky install",
"server:console": "npm run console --prefix server",
"server:db:init": "npm run db:init --prefix server",
"server:db:migrate": "npm run db:migrate --prefix server",
"server:db:seed": "npm run db:seed --prefix server",
"server:lint": "npm run lint --prefix server",
"server:start": "npm start --prefix server",
"server:start:prod": "npm run start:prod --prefix server",
"server:test": "npm test --prefix server",
"start": "concurrently -n server,client \"npm run server:start\" \"npm run client:start\"",
"test": "npm run server:test && npm run client:test"
},
"lint-staged": {
"client/**/*.{js,jsx}": [
"npm run client:lint"
],
"server/**/*.js": [
"npm run server:lint"
]
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
},
"eslintConfig": {
"plugins": [
"prettier"
],
"extends": [
"plugin:prettier/recommended"
],
"rules": {
"no-undef": "off",
"prettier/prettier": "error"
}
},
"dependencies": {
"concurrently": "^8.2.2",
"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"
}
}
{
"name": "planka",
"version": "0.0.1-beta.1",
"private": true,
"homepage": "https://plankanban.github.io/planka",
"repository": {
"type": "git",
"url": "https://github.com/plankanban/planka.git"
},
"license": "AGPL-3.0",
"author": "Maksim Eltyshev",
"scripts": {
"client:build": "npm run build --prefix client",
"client:lint": "npm run lint --prefix client",
"client:start": "npm start --prefix client",
"client:test": "npm test --prefix client",
"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 .",
"postinstall": "(cd server && npm i && cd ../client && npm i)",
"lint": "npm run server:lint && npm run client:lint",
"prepare": "husky install",
"server:console": "npm run console --prefix server",
"server:db:init": "npm run db:init --prefix server",
"server:db:migrate": "npm run db:migrate --prefix server",
"server:db:seed": "npm run db:seed --prefix server",
"server:lint": "npm run lint --prefix server",
"server:start": "npm start --prefix server",
"server:start:prod": "npm run start:prod --prefix server",
"server:test": "npm test --prefix server",
"start": "concurrently -n server,client \"npm run server:start\" \"npm run client:start\"",
"test": "npm run server:test && npm run client:test"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
},
"eslintConfig": {
"plugins": [
"prettier"
],
"extends": [
"plugin:prettier/recommended"
],
"rules": {
"no-undef": "off",
"prettier/prettier": "error"
}
},
"dependencies": {
"concurrently": "^8.2.2",
"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 = {
NOT_ENOUGH_RIGHTS: {
notEnoughRights: 'Not enough rights',
},
CARD_NOT_FOUND: {
cardNotFound: 'Card not found',
},
};
module.exports = {
inputs: {
id: {
type: 'string',
regex: /^[0-9]+$/,
required: true,
},
position: {
type: 'number',
required: true,
},
name: {
type: 'string',
},
},
exits: {
notEnoughRights: {
responseType: 'forbidden',
},
cardNotFound: {
responseType: 'notFound',
},
},
async fn(inputs) {
const { currentUser } = this.req;
const { card, list, board } = await sails.helpers.cards
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
userId: currentUser.id,
});
if (!boardMembership) {
throw Errors.CARD_NOT_FOUND; // Forbidden
}
if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
throw Errors.NOT_ENOUGH_RIGHTS;
}
const values = _.pick(inputs, ['position', 'name']);
const {
card: nextCard,
cardMemberships,
cardLabels,
tasks,
} = await sails.helpers.cards.duplicateOne.with({
board,
list,
record: card,
values: {
...values,
creatorUser: currentUser,
},
request: this.req,
});
return {
item: nextCard,
included: {
cardMemberships,
cardLabels,
tasks,
},
};
},
};
const Errors = {
NOT_ENOUGH_RIGHTS: {
notEnoughRights: 'Not enough rights',
},
CARD_NOT_FOUND: {
cardNotFound: 'Card not found',
},
};
module.exports = {
inputs: {
id: {
type: 'string',
regex: /^[0-9]+$/,
required: true,
},
position: {
type: 'number',
required: true,
},
name: {
type: 'string',
},
},
exits: {
notEnoughRights: {
responseType: 'forbidden',
},
cardNotFound: {
responseType: 'notFound',
},
},
async fn(inputs) {
const { currentUser } = this.req;
const { card, list, board } = await sails.helpers.cards
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
userId: currentUser.id,
});
if (!boardMembership) {
throw Errors.CARD_NOT_FOUND; // Forbidden
}
if (boardMembership.role !== BoardMembership.Roles.EDITOR) {
throw Errors.NOT_ENOUGH_RIGHTS;
}
const values = _.pick(inputs, ['position', 'name']);
const {
card: nextCard,
cardMemberships,
cardLabels,
tasks,
} = await sails.helpers.cards.duplicateOne.with({
board,
list,
record: card,
values: {
...values,
creatorUser: currentUser,
},
request: this.req,
});
return {
item: nextCard,
included: {
cardMemberships,
cardLabels,
tasks,
},
};
},
};

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

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

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

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

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

Loading…
Cancel
Save