From 1602a10fd0e15b791f6790dbc2488c05b1a361e2 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 8 Jun 2022 10:14:47 +0200 Subject: [PATCH 001/172] (build) improved docker image building time --- CHANGELOG.md | 2 ++ Dockerfile | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c34ce5f26..899aff2f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## next deploy +- Improved docker image building time + ## v5.4.4 2022 June 8 - Check shopping cart items are valid before online payment diff --git a/Dockerfile b/Dockerfile index 884d2bc65..472103020 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,8 +44,7 @@ RUN bundle config --global frozen 1 # Install gems in a cache efficient way WORKDIR /tmp -COPY Gemfile /tmp/ -COPY Gemfile.lock /tmp/ +COPY Gemfile* /tmp/ RUN bundle config set --local without 'development test doc' && bundle install && bundle binstubs --all # Prepare the application directories @@ -72,16 +71,16 @@ COPY docker/database.yml /usr/src/app/config/database.yml COPY . /usr/src/app # Volumes (the folders are created by setup.sh) -VOLUME /usr/src/app/invoices -VOLUME /usr/src/app/payment_schedules -VOLUME /usr/src/app/exports -VOLUME /usr/src/app/imports -VOLUME /usr/src/app/public -VOLUME /usr/src/app/public/uploads -VOLUME /usr/src/app/public/packs -VOLUME /usr/src/app/accounting -VOLUME /usr/src/app/proof_of_identity_files -VOLUME /var/log/supervisor +VOLUME /usr/src/app/invoices \ + /usr/src/app/payment_schedules \ + /usr/src/app/exports \ + /usr/src/app/imports \ + /usr/src/app/public \ + /usr/src/app/public/uploads \ + /usr/src/app/public/packs \ + /usr/src/app/accounting \ + /usr/src/app/proof_of_identity_files \ + /var/log/supervisor # Expose port 3000 to the Docker host, so we can access it from the outside EXPOSE 3000 From 0b64e08e66061ddc168ab111d2f80426f19b1728 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 8 Jun 2022 10:21:16 +0200 Subject: [PATCH 002/172] (deploy) Use relative paths in mount scripts --- CHANGELOG.md | 1 + scripts/mount-payment-schedules.sh | 4 +--- scripts/mount-proof-of-identity-files.sh | 4 +--- scripts/mount-webpack.sh | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 899aff2f2..1cee1ac4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## next deploy - Improved docker image building time +- Use relative paths in mount scripts ## v5.4.4 2022 June 8 diff --git a/scripts/mount-payment-schedules.sh b/scripts/mount-payment-schedules.sh index a785e32b8..5355ca1bc 100644 --- a/scripts/mount-payment-schedules.sh +++ b/scripts/mount-payment-schedules.sh @@ -20,11 +20,9 @@ config() add_mount() { if [[ ! $(yq eval ".services.$SERVICE.volumes.[] | select (. == \"*payment_schedules\")" docker-compose.yml) ]]; then - # shellcheck disable=SC2016 - # we don't want to expand ${PWD} # change docker-compose.yml permissions for fix yq can't modify file issue chmod 666 docker-compose.yml - yq -i eval ".services.$SERVICE.volumes += [\"\${PWD}/payment_schedules:/usr/src/app/payment_schedules\"]" docker-compose.yml + yq -i eval ".services.$SERVICE.volumes += [\"\./payment_schedules:/usr/src/app/payment_schedules\"]" docker-compose.yml chmod 644 docker-compose.yml fi } diff --git a/scripts/mount-proof-of-identity-files.sh b/scripts/mount-proof-of-identity-files.sh index 02d4ff297..95231cafb 100644 --- a/scripts/mount-proof-of-identity-files.sh +++ b/scripts/mount-proof-of-identity-files.sh @@ -20,11 +20,9 @@ config() add_mount() { if [[ ! $(yq eval ".services.$SERVICE.volumes.[] | select (. == \"*proof_of_identity_files\")" docker-compose.yml) ]]; then - # shellcheck disable=SC2016 - # we don't want to expand ${PWD} # change docker-compose.yml permissions for fix yq can't modify file issue chmod 666 docker-compose.yml - yq -i eval ".services.$SERVICE.volumes += [\"\${PWD}/proof_of_identity_files:/usr/src/app/proof_of_identity_files\"]" docker-compose.yml + yq -i eval ".services.$SERVICE.volumes += [\"\./proof_of_identity_files:/usr/src/app/proof_of_identity_files\"]" docker-compose.yml chmod 644 docker-compose.yml fi } diff --git a/scripts/mount-webpack.sh b/scripts/mount-webpack.sh index 3e7097e8d..8cd716bba 100644 --- a/scripts/mount-webpack.sh +++ b/scripts/mount-webpack.sh @@ -20,7 +20,7 @@ config() change_mount() { if [[ $(yq eval ".services.$SERVICE.volumes.[] | select (. == \"*assets\")" docker-compose.yml) ]]; then - yq -i eval ".services.$SERVICE.volumes.[] |= select(. == \"*assets\") |= \"\${PWD}/public/packs:/usr/src/app/public/packs\"" docker-compose.yml + yq -i eval ".services.$SERVICE.volumes.[] |= select(. == \"*assets\") |= \"\./public/packs:/usr/src/app/public/packs\"" docker-compose.yml echo "Service volume was replaced for $SERVICE: /assets changed to /packs" fi } From 7cc31e3872ee7bf3b92c45bbb64d228c9f464364 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 8 Jun 2022 20:56:51 +0200 Subject: [PATCH 003/172] (bug) unable to generate the secret key base during the setup --- CHANGELOG.md | 1 + Dockerfile | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cee1ac4b..21f8f275e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Improved docker image building time - Use relative paths in mount scripts +- Fix a bug: unable to generate the secret key base during the setup ## v5.4.4 2022 June 8 diff --git a/Dockerfile b/Dockerfile index 472103020..fbb984244 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,9 @@ RUN bundle config set --local without 'development test doc' && bundle install & # Prepare the application directories RUN mkdir -p /var/log/supervisor && \ mkdir -p /usr/src/app/tmp/sockets && \ - mkdir -p /usr/src/app/tmp/pids + mkdir -p /usr/src/app/tmp/pids && \ + mkdir -p /usr/src/app/tmp/cache && \ + chmod -R a+w /usr/src/app/tmp # Install Javascript packages WORKDIR /usr/src/app From a02c10ef63117b533cd16120047629e4d58e6967 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 13 Jun 2022 10:05:03 +0200 Subject: [PATCH 004/172] (bug) elevate new_packs folder creation during upgrade, if needed --- setup/upgrade.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup/upgrade.sh b/setup/upgrade.sh index a966faa06..8235fe344 100644 --- a/setup/upgrade.sh +++ b/setup/upgrade.sh @@ -221,7 +221,11 @@ compile_assets() fi PG_NET_ID=$(docker inspect "$PG_ID" -f "{{json .NetworkSettings.Networks }}" | jq -r '.[] .NetworkID') clean_env_file - mkdir -p public/new_packs + if ! mkdir -p public/new_packs; then + # if, for any reason, the directory cannot be created, we create it with sudo privileges and changes the ownership to the current user + elevate_cmd mkdir -p public/new_packs + elevate_cmd chown "$(id -u):$(id -g)" public/new_packs + fi # shellcheck disable=SC2068 if ! docker run --user "$(id -u):$(id -g)" --rm --env-file ./config/env ${ENV_ARGS[@]} --link "$PG_ID" --net "$PG_NET_ID" -v "${PWD}/public/new_packs:/usr/src/app/public/packs" "$IMAGE" bundle exec rake assets:precompile; then printf "\e[93m[ ⚠ ] Something may have went wrong while compiling the assets, please check the logs above.\e[39m\n" From eba9c4380988104e6c2e8222a5ad5fa35cf01a68 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 13 Jun 2022 10:37:04 +0200 Subject: [PATCH 005/172] (feat) setup: auto configure the main domain --- CHANGELOG.md | 1 + setup/setup.sh | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f8f275e..306a8debf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Improved docker image building time - Use relative paths in mount scripts +- During the setup, auto configure the main domain - Fix a bug: unable to generate the secret key base during the setup ## v5.4.4 2022 June 8 diff --git a/setup/setup.sh b/setup/setup.sh index 3447d2642..c1bcf4c3a 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -317,6 +317,11 @@ get_md_anchor() configure_env_file() { + # pre-configure the main domain + if [ "${MAIN_DOMAIN[0]}" != "" ]; then + sed -i.bak "s/DEFAULT_HOST=.*/DEFAULT_HOST=${MAIN_DOMAIN[0]}/g" "$FABMANAGER_PATH/config/env" + fi + printf "\n\nWe will now configure the environment variables.\n" echo "This allows you to customize Fab-manager's appearance and behavior." read -rp "Proceed? (Y/n) " confirm Date: Mon, 13 Jun 2022 14:11:43 +0200 Subject: [PATCH 006/172] Run the docker image with the system user --- CHANGELOG.md | 4 +++- Dockerfile | 4 +++- scripts/set-docker-user.sh | 36 ++++++++++++++++++++++++++++++++++++ setup/docker-compose.yml | 5 +---- setup/setup.sh | 3 +++ 5 files changed, 46 insertions(+), 6 deletions(-) create mode 100755 scripts/set-docker-user.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 306a8debf..29265d7ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ - Improved docker image building time - Use relative paths in mount scripts -- During the setup, auto configure the main domain +- Run the docker image with the system user +- During the setup, autoconfigure the main domain - Fix a bug: unable to generate the secret key base during the setup +- [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/set-docker-user.sh | bash` ## v5.4.4 2022 June 8 diff --git a/Dockerfile b/Dockerfile index fbb984244..f78221d74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,9 @@ RUN mkdir -p /var/log/supervisor && \ mkdir -p /usr/src/app/tmp/sockets && \ mkdir -p /usr/src/app/tmp/pids && \ mkdir -p /usr/src/app/tmp/cache && \ - chmod -R a+w /usr/src/app/tmp + mkdir -p /usr/src/app/log && \ + chmod -R a+w /usr/src/app/tmp && \ + chmod -R a+w /usr/src/app/log # Install Javascript packages WORKDIR /usr/src/app diff --git a/scripts/set-docker-user.sh b/scripts/set-docker-user.sh new file mode 100755 index 000000000..4140b7894 --- /dev/null +++ b/scripts/set-docker-user.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +yq() { + docker run --rm -i -v "${PWD}:/workdir" mikefarah/yq:4 "$@" +} + +config() +{ + echo -ne "Checking user... " + if [[ "$(whoami)" != "root" ]] && ! groups | grep docker + then + echo "Please add your current user to the docker group OR run this script as root." + echo "current user is not allowed to use docker, exiting..." + exit 1 + fi + SERVICE="$(yq eval '.services.*.image | select(. == "sleede/fab-manager*") | path | .[-2]' docker-compose.yml)" + echo -e "\n" +} + +set_user() +{ + CURRENT_VALUE=$(yq eval ".services.$SERVICE.user" docker-compose.yml) + USER_ID="$(id -u):$(id -g)" + if [[ "$CURRENT_VALUE" == "USER_ID" || "$CURRENT_VALUE" == "null" ]]; then + yq -i eval ".services.$SERVICE.user |= \"$USER_ID\"" docker-compose.yml + echo "Service user was set to $USER_ID for $SERVICE" + fi +} + +proceed() +{ + config + set_user +} + +proceed "$@" diff --git a/setup/docker-compose.yml b/setup/docker-compose.yml index af9005c38..a10eb6b76 100644 --- a/setup/docker-compose.yml +++ b/setup/docker-compose.yml @@ -7,6 +7,7 @@ services: RACK_ENV: production env_file: - ./config/env + user: 1000:100 volumes: - ./public/packs:/usr/src/app/public/packs - ./public/uploads:/usr/src/app/public/uploads @@ -23,7 +24,6 @@ services: - redis - elasticsearch restart: always - postgres: image: postgres:9.6 volumes: @@ -31,7 +31,6 @@ services: restart: always environment: POSTGRES_HOST_AUTH_METHOD: trust - elasticsearch: image: elasticsearch:5.6 environment: @@ -44,13 +43,11 @@ services: - ./elasticsearch/config:/usr/share/elasticsearch/config - ./elasticsearch:/usr/share/elasticsearch/data restart: always - redis: image: redis:6-alpine volumes: - ./redis:/data restart: always - nginx: image: nginx:latest ports: diff --git a/setup/setup.sh b/setup/setup.sh index c1bcf4c3a..fad858f01 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -298,6 +298,9 @@ prepare_docker() fi fi + # set the current user in the docker-compose.yml, as the owner of the process + sed -i.bak "s/USER_ID/$(id -u):$(id -g)/g" "$FABMANAGER_PATH/docker-compose.yml" + cd "$FABMANAGER_PATH" && docker-compose pull } From 4fe0d6ecba44167cf4254a9d7e8d4a6bd21865fd Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 13 Jun 2022 15:22:01 +0200 Subject: [PATCH 007/172] (doc) installing behind a proxy --- CHANGELOG.md | 1 + doc/README.md | 2 ++ doc/proxy.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 doc/proxy.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 29265d7ab..6185d9f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## next deploy +- Documentation for installing behind a proxy - Improved docker image building time - Use relative paths in mount scripts - Run the docker image with the system user diff --git a/doc/README.md b/doc/README.md index 7ee2daf00..38d64ea1b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -20,6 +20,8 @@ The following guide describes what you can do and how to use Fab-manager. The following guides are designed for the people that perform software maintenance. - [Setup and update a production environment](production_readme.md) +- [Installing behind a corporate proxy](proxy.md) + - [Configuring the environment variables](environment.md) - [Known issues with Fab-Manager](known-issues.md) diff --git a/doc/proxy.md b/doc/proxy.md new file mode 100644 index 000000000..b1a69b427 --- /dev/null +++ b/doc/proxy.md @@ -0,0 +1,53 @@ +# Install Fab-manager behind a proxy + +## Configure docker service + +As root, create a service configuration file for docker: + +```bash +mkdir -p /etc/systemd/system/docker.service.d +vi /etc/systemd/system/docker.service.d/http-proxy.conf +``` + +```unit file (systemd) +[Service] +Environment="HTTP_PROXY=http://proxy.example.com:8080/" +Environment="HTTPS_PROXY=https://proxy.example.com:8080/" +Environment="NO_PROXY=localhost,example.com" +``` + +```bash +systemctl daemon-reload +systemctl restart docker +``` + +# Configure docker client + +As root, create a configuration file for the docker client: + +```bash +mkdir -p /root/.docker +vi /root/.docker/config.json +``` + +```json +{ + "proxies": { + "default": { + "httpProxy": "http://proxy.example.com:8080", + "httpsProxy": "https://proxy.example.com:8080", + "noProxy": "localhost,example.com" + } + } +} +``` + +## Configure your terminal and run the installation + +See the [production guide](production_readme.md) for more information and options about the setup command. + +```bash +export http_proxy="http://proxy.example.com:8080" https_proxy="https://proxy.example.com:8080" no_proxy="localhost,example.com" +\curl -sSL setup.fab.mn | bash +``` + From 8a93160628665cd526887ea8043df1dc0b9d65bf Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 13 Jun 2022 15:35:52 +0200 Subject: [PATCH 008/172] (setup) ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http --- CHANGELOG.md | 1 + setup/setup.sh | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6185d9f3c..909cc3031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Use relative paths in mount scripts - Run the docker image with the system user - During the setup, autoconfigure the main domain +- During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http - Fix a bug: unable to generate the secret key base during the setup - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/set-docker-user.sh | bash` diff --git a/setup/setup.sh b/setup/setup.sh index fad858f01..d69a433cc 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -355,6 +355,16 @@ configure_env_file() # we automatically generate the SECRET_KEY_BASE secret=$(docker-compose -f "$FABMANAGER_PATH/docker-compose.yml" run --user "$(id -u):$(id -g)" --rm "$SERVICE" bundle exec rake secret) sed -i.bak "s/SECRET_KEY_BASE=/SECRET_KEY_BASE=$secret/g" "$FABMANAGER_PATH/config/env" + + # if DEFAULT_PROTOCOL was set to http, ALLOW_INSECURE_HTTP is probably required + if grep "^DEFAULT_PROTOCOL=http$" "$FABMANAGER_PATH/config/env"; then + get_md_anchor "$doc" "ALLOW_INSECURE_HTTP" + printf "You have set \e[1mDEFAULT_PROTOCOL\e[21m to \e[1mhttp\e[21m.\n" + read -rp "Do you want to allow insecure HTTP? (y/N) " confirm Date: Mon, 13 Jun 2022 17:03:35 +0200 Subject: [PATCH 009/172] (setup) Ability to install behind a proxy --- CHANGELOG.md | 1 + doc/proxy.md | 1 + setup/proxy/.npmrc | 1 + setup/proxy/gitconfig | 2 ++ setup/setup.sh | 37 +++++++++++++++++++++++++++++++------ 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 setup/proxy/.npmrc create mode 100644 setup/proxy/gitconfig diff --git a/CHANGELOG.md b/CHANGELOG.md index 909cc3031..b0f3778cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## next deploy - Documentation for installing behind a proxy +- Ability to install behind a proxy - Improved docker image building time - Use relative paths in mount scripts - Run the docker image with the system user diff --git a/doc/proxy.md b/doc/proxy.md index b1a69b427..648659b9b 100644 --- a/doc/proxy.md +++ b/doc/proxy.md @@ -51,3 +51,4 @@ export http_proxy="http://proxy.example.com:8080" https_proxy="https://proxy.exa \curl -sSL setup.fab.mn | bash ``` +During the installation, if you have correctly exported the variable `http_proxy`, you will be prompted to enter your proxy CA certificate, if needed. diff --git a/setup/proxy/.npmrc b/setup/proxy/.npmrc new file mode 100644 index 000000000..e47515cd7 --- /dev/null +++ b/setup/proxy/.npmrc @@ -0,0 +1 @@ +cafile=/etc/ssl/certs/ca-cert-proxy.pem diff --git a/setup/proxy/gitconfig b/setup/proxy/gitconfig new file mode 100644 index 000000000..c2acf845b --- /dev/null +++ b/setup/proxy/gitconfig @@ -0,0 +1,2 @@ +[http] + sslCAInfo = /etc/ssl/certs/ca-cert-proxy.pem diff --git a/setup/setup.sh b/setup/setup.sh index d69a433cc..a002b6107 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -128,6 +128,15 @@ read_email() config() { SERVICE="fabmanager" + # http_proxy should have been exported externally + # shellcheck disable=SC2154 + if [ "$http_proxy" != "" ]; then + read -rp "You seems to be behind a proxy. Do you want to configure a custom CA certificate? (Y/n) " confirm "$FABMANAGER_PATH/docker-compose.yml" + + # proxy + if [ "$CERTIFICATE" != "" ]; then + mkdir -p "$FABMANAGER_PATH/config/proxy" + echo "$CERTIFICATE" > "$FABMANAGER_PATH/config/proxy/certificate.pem" + \curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/setup/proxy/.npmrc > "$FABMANAGER_PATH/config/proxy/.npmrc" + \curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/setup/proxy/gitconfig > "$FABMANAGER_PATH/config/proxy/gitconfig" + fi } yq() { @@ -301,6 +318,14 @@ prepare_docker() # set the current user in the docker-compose.yml, as the owner of the process sed -i.bak "s/USER_ID/$(id -u):$(id -g)/g" "$FABMANAGER_PATH/docker-compose.yml" + # if a certificate was provided, modify the docker-compose.yml file to use it + if [ "$CERTIFICATE" != "" ]; then + echo "Using the certificate provided..." + yq -i eval ".services.$SERVICE.volumes += [\"\./config/proxy/certificate.pem:/etc/ssl/certs/ca-cert-proxy.pem\"]" docker-compose.yml + yq -i eval ".services.$SERVICE.volumes += [\"\./config/proxy/.npmrc:/usr/src/app/.npmrc\"]" docker-compose.yml + yq -i eval ".services.$SERVICE.volumes += [\"\./config/proxy/gitconfig:/etc/gitconfig\"]" docker-compose.yml + fi + cd "$FABMANAGER_PATH" && docker-compose pull } @@ -353,7 +378,7 @@ configure_env_file() fi done # we automatically generate the SECRET_KEY_BASE - secret=$(docker-compose -f "$FABMANAGER_PATH/docker-compose.yml" run --user "$(id -u):$(id -g)" --rm "$SERVICE" bundle exec rake secret) + secret=$(docker-compose -f "$FABMANAGER_PATH/docker-compose.yml" run --rm "$SERVICE" bundle exec rake secret) sed -i.bak "s/SECRET_KEY_BASE=/SECRET_KEY_BASE=$secret/g" "$FABMANAGER_PATH/config/env" # if DEFAULT_PROTOCOL was set to http, ALLOW_INSECURE_HTTP is probably required @@ -393,22 +418,22 @@ setup_assets_and_databases() read -rp "Continue? (Y/n) " confirm Date: Mon, 13 Jun 2022 17:54:57 +0200 Subject: [PATCH 010/172] fix setup behind proxy --- setup/setup.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/setup/setup.sh b/setup/setup.sh index a002b6107..05a118766 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -134,7 +134,7 @@ config() read -rp "You seems to be behind a proxy. Do you want to configure a custom CA certificate? (Y/n) " confirm /dev/null; then get_md_anchor "$doc" "ALLOW_INSECURE_HTTP" printf "You have set \e[1mDEFAULT_PROTOCOL\e[21m to \e[1mhttp\e[21m.\n" - read -rp "Do you want to allow insecure HTTP? (y/N) " confirm Date: Tue, 14 Jun 2022 13:43:07 +0200 Subject: [PATCH 011/172] (bug) error message during the setup: the input device is not a TTY --- CHANGELOG.md | 1 + setup/setup.sh | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0f3778cf..8de7ff07b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - During the setup, autoconfigure the main domain - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http - Fix a bug: unable to generate the secret key base during the setup +- Fix a bug: error message during the setup: the input device is not a TTY - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/set-docker-user.sh | bash` ## v5.4.4 2022 June 8 diff --git a/setup/setup.sh b/setup/setup.sh index 05a118766..bc8c1f018 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -418,22 +418,22 @@ setup_assets_and_databases() read -rp "Continue? (Y/n) " confirm Date: Tue, 14 Jun 2022 15:38:28 +0200 Subject: [PATCH 012/172] (bug) Cannot open an HTTP server: socket.error reported errno.EACCES (13) --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f78221d74..f5e99e2a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,7 +54,8 @@ RUN mkdir -p /var/log/supervisor && \ mkdir -p /usr/src/app/tmp/cache && \ mkdir -p /usr/src/app/log && \ chmod -R a+w /usr/src/app/tmp && \ - chmod -R a+w /usr/src/app/log + chmod -R a+w /usr/src/app/log && \ + chmod -R a+w /var/run # Install Javascript packages WORKDIR /usr/src/app From 2208504e8e2c4a36e9c34299b5b77d695a69fbd5 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 14 Jun 2022 16:16:59 +0200 Subject: [PATCH 013/172] (bug) fix USER_ID variable in docker-compose.yml --- setup/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/docker-compose.yml b/setup/docker-compose.yml index a10eb6b76..6a24a9c51 100644 --- a/setup/docker-compose.yml +++ b/setup/docker-compose.yml @@ -7,7 +7,7 @@ services: RACK_ENV: production env_file: - ./config/env - user: 1000:100 + user: USER_ID volumes: - ./public/packs:/usr/src/app/public/packs - ./public/uploads:/usr/src/app/public/uploads From 9ba177bf7bbaa7ed8e4ee8b0040c8464c4056e90 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 14 Jun 2022 16:22:42 +0200 Subject: [PATCH 014/172] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8de7ff07b..857434e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http - Fix a bug: unable to generate the secret key base during the setup - Fix a bug: error message during the setup: the input device is not a TTY +- Fix a bug: when Fab-manager was installed as non-root user, unable to compile the assets during the upgrade - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/set-docker-user.sh | bash` ## v5.4.4 2022 June 8 From a5cdd33e0b60291f87ccfbbfc425c88987280972 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 14 Jun 2022 16:25:36 +0200 Subject: [PATCH 015/172] (bug) run the upgrade commands w/ the appropriate user --- setup/upgrade.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setup/upgrade.sh b/setup/upgrade.sh index 8235fe344..c35dff083 100644 --- a/setup/upgrade.sh +++ b/setup/upgrade.sh @@ -162,9 +162,9 @@ version_error() # set $VERSION version_check() { - VERSION=$(docker-compose exec --user "$(id -u):$(id -g)" -T "$SERVICE" cat .fabmanager-version 2>/dev/null) + VERSION=$(docker-compose exec -T "$SERVICE" cat .fabmanager-version 2>/dev/null) if [[ $? = 1 ]]; then - VERSION=$(docker-compose exec --user "$(id -u):$(id -g)" -T "$SERVICE" cat package.json | jq -r '.version') + VERSION=$(docker-compose exec -T "$SERVICE" cat package.json | jq -r '.version') fi target_version if [ "$TARGET" = 'custom' ]; then return; fi @@ -227,7 +227,7 @@ compile_assets() elevate_cmd chown "$(id -u):$(id -g)" public/new_packs fi # shellcheck disable=SC2068 - if ! docker run --user "$(id -u):$(id -g)" --rm --env-file ./config/env ${ENV_ARGS[@]} --link "$PG_ID" --net "$PG_NET_ID" -v "${PWD}/public/new_packs:/usr/src/app/public/packs" "$IMAGE" bundle exec rake assets:precompile; then + if ! docker run --user "0:0" --rm --env-file ./config/env ${ENV_ARGS[@]} --link "$PG_ID" --net "$PG_NET_ID" -v "${PWD}/public/new_packs:/usr/src/app/public/packs" "$IMAGE" bundle exec rake assets:precompile; then printf "\e[93m[ ⚠ ] Something may have went wrong while compiling the assets, please check the logs above.\e[39m\n" [[ "$YES_ALL" = "true" ]] && confirm="y" || read -rp ":: Ignore and continue? (Y/n) " confirm Date: Thu, 14 Apr 2022 18:30:56 +0200 Subject: [PATCH 016/172] (wip) React component [EventCard] --- .../components/events/event-card.tsx | 131 +++++++++++++ app/frontend/src/stylesheets/application.scss | 1 + .../src/stylesheets/modules/events/event.scss | 182 ++++++++++++++++++ app/frontend/templates/events/index.html | 2 + app/frontend/templates/home/events.html | 4 + 5 files changed, 320 insertions(+) create mode 100644 app/frontend/src/javascript/components/events/event-card.tsx create mode 100644 app/frontend/src/stylesheets/modules/events/event.scss diff --git a/app/frontend/src/javascript/components/events/event-card.tsx b/app/frontend/src/javascript/components/events/event-card.tsx new file mode 100644 index 000000000..1eaf3938a --- /dev/null +++ b/app/frontend/src/javascript/components/events/event-card.tsx @@ -0,0 +1,131 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { react2angular } from 'react2angular'; +import { IApplication } from '../../models/application'; +import { Loader } from '../base/loader'; +import { Event } from '../../models/event'; +import FormatLib from '../../lib/format'; + +declare const Application: IApplication; + +interface EventCardProps { + event: Event, + cardType: 'sm' | 'md' | 'lg' +} + +export const EventCard: React.FC = ({ event, cardType = 'sm' }) => { + const { t } = useTranslation('public'); + console.log(event); + /** + * Format description to remove HTML tags and set a maximum character count + */ + const formatText = (text: string, count: number) => { + text = text.replace(/(<\/p>|<\/h4>|<\/h5>|<\/h6>|<\/pre>|<\/blockquote>)/g, '\n'); + text = text.replace(//g, '\n'); + text = text.replace(/<\/?\w+[^>]*>/g, ''); + text = text.replace(/\n+/g, '
'); + if (text.length > count) { + return text.slice(0, count) + '...'; + } + return text; + }; + + /** + * Return the formatted localized date of the event + */ + const formatDate = (): string => { + // FIXME: typeof event.all_day = sting ? + return event.all_day === 'true' + ? t('app.public.home.from_date_to_date', { START: FormatLib.date(event.start_date), END: FormatLib.date(event.end_date) }) + : t('app.public.home.on_the_date', { DATE: FormatLib.date(event.start_date) }); + }; + + /** + * Return the formatted localized hours of the event + */ + const formatTime = (): string => { + // FIXME: typeof event.all_day = sting ? + return event.all_day === 'true' + ? t('app.public.home.all_day') + : t('app.public.home.from_time_to_time', { START: FormatLib.time(event.start_date), END: FormatLib.time(event.end_date) }); + }; + + /** + * Link to event by id + */ + const showEvent = (id: number) => { + // TODO: ??? + console.log(id); + }; + + return ( +
showEvent(event.id)}> +
+ {event.event_image + ? + : + } +
+
+
+

{event?.title}

+ {event.category.name} +
+ {cardType !== 'sm' && +

+ } +
+
+ {cardType !== 'md' &&

{formatDate()}

} +
+ {cardType === 'sm' && + event.event_themes.map(theme => { + return (
+ +
{theme.name}
+
); + }) + } + {(cardType === 'sm' && event.age_range) && +
+ +
{event.age_range?.name}
+
+ } + {cardType === 'md' && +
+ +
{formatDate()}
+
+ } +
+ {cardType !== 'sm' && } +
{formatTime()}
+
+
+ {cardType !== 'sm' && } + {event.nb_free_places > 0 &&
{t('app.public.home.still_available') + event.nb_free_places}
} + {event.nb_total_places > 0 && event.nb_free_places <= 0 &&
{t('app.public.home.event_full')}
} + {!event.nb_total_places &&
{t('app.public.home.without_reservation')}
} +
+
+ {cardType !== 'sm' && } + {event.amount === 0 &&
{t('app.public.home.free_admission')}
} + {/* TODO: Display currency ? */} + {event.amount > 0 &&
{t('app.public.home.full_price') + event.amount}
} +
+
+
+
+ ); +}; + +const EventCardWrapper: React.FC = ({ event, cardType }) => { + return ( + + + + ); +}; + +Application.Components.component('eventCard', react2angular(EventCardWrapper, ['event', 'cardType'])); diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index 81270007f..c134acab2 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -29,6 +29,7 @@ @import "modules/base/fab-text-editor"; @import "modules/base/labelled-input"; @import "modules/calendar/calendar"; +@import "modules/events/event"; @import "modules/form/form-input"; @import "modules/form/form-item"; @import "modules/form/form-rich-text"; diff --git a/app/frontend/src/stylesheets/modules/events/event.scss b/app/frontend/src/stylesheets/modules/events/event.scss new file mode 100644 index 000000000..b2c4ddc08 --- /dev/null +++ b/app/frontend/src/stylesheets/modules/events/event.scss @@ -0,0 +1,182 @@ +.event { + &-card { + border: 1px solid var(--gray-soft-dark); + border-radius: 5px; + overflow: hidden; + color: var(--gray-hard-darkest); + &:hover { + color: var(--gray-hard-darkest); + cursor: pointer; + & .event-card-picture {opacity: 0.7;} + } + + &-picture { + display: flex; + justify-content: center; + align-items: center; + background-color: #fff; + font-size: 8rem; + color: var(--gray-soft-light); + transition: opacity 0.4s ease-out; + img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + &-desc { + padding: 15px 15px 30px; + header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 1rem; + p { + margin: 0; + font-size: 2rem; + font-weight: 900; + line-height: 1.3; + text-transform: uppercase; + } + } + p { margin: 0; } + } + &-info { + margin-top: auto; + padding: 15px 30px; + .grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + &-item { + height: 20px; + display: flex; + align-items: center; + i { + width: 16px; + height: 16px; + margin-right: 15px; + font-size: 16px; + text-align: center; + color: var(--main); + } + } + } + } + } + + // Card size specific + &-card--sm { + display: grid; + grid-template-columns: 1fr max-content; + .event-card-desc { + grid-area: 1/1/2/2; + padding-bottom: 10px; + header p { font-size: 1.6rem; } + } + .event-card-picture { + grid-area: 1/2/3/3; + width: 160px; + display: none; + border-left: 1px solid var(--gray-soft-dark); + @media (min-width: 500px) { + display: flex; + } + } + .event-card-info { + grid-area: 2/1/3/2; + padding: 0 15px 15px; + & > p { + font-size: 1.8rem; + font-weight: 600; + color: var(--main); + } + .grid { + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 5px 10px; + &-item i { + width: 12px; + height: 12px; + margin-right: 6px; + font-size: 12px; + text-align: center; + color: var(--gray-hard-light); + } + } + } + } + &-card--md { + display: grid; + grid-template-rows: min-content 1fr min-content; + .event-card-picture { + height: 250px; + border-bottom: 1px solid var(--gray-soft-dark); + } + .event-card-info { + padding-bottom: 30px; + border-top: 1px solid var(--gray-soft-dark); + } + } + &-card--lg {} + + // layout specific + &-home { + h4 { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + column-gap: 1rem; + i { margin-right: 1rem;} + } + &-list { + display: grid; + grid-template-columns: 1fr; + gap: 30px; + margin-bottom: 5rem; + @media (min-width: 480px) { + grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + } + } + } + + &-focus { + display: grid; + margin-bottom: 50px; + .event-card { + border: 1px solid var(--gray-hard); + &-info { padding-top: 0; } + &-picture { + border-bottom: 1px solid var(--gray-hard); + img { max-height: 300px; } + } + } + + @media (min-width: 992px) { + grid-template-columns: repeat(auto-fill, minmax(425px, 1fr)); + gap: 15px; + .event-card { + grid-column: span 2; + display: grid; + grid-template-columns: 3fr 2fr; + &-desc { grid-area: 1/1/2/2; } + &-info { grid-area: 2/1/3/2; } + &-picture { + grid-area: 1/2/3/3; + border-bottom: none; + border-left: 1px solid var(--gray-hard); + img { max-height: none; } + } + } + } + } + + &-month-list { + display: grid; + grid-template-columns: 1fr; + gap: 15px; + margin-bottom: 2rem; + @media (min-width: 768px) { + grid-template-columns: repeat(auto-fill, minmax(425px, 1fr)); + } + } +} diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 385fb90bb..f261f3b14 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -74,6 +74,8 @@ + + diff --git a/app/frontend/templates/home/events.html b/app/frontend/templates/home/events.html index a05e6c2de..12e491f23 100644 --- a/app/frontend/templates/home/events.html +++ b/app/frontend/templates/home/events.html @@ -50,4 +50,8 @@ + +
+ +
From f30c5019484c7f7eb5fb8b7ef1b2452f00fdc789 Mon Sep 17 00:00:00 2001 From: vincent Date: Fri, 15 Apr 2022 11:50:53 +0200 Subject: [PATCH 017/172] (wip) React component [EventCard] --- .../components/events/event-card.tsx | 30 ++++----- .../src/stylesheets/modules/events/event.scss | 62 +++++++++++-------- app/frontend/templates/events/index.html | 43 +++---------- app/frontend/templates/home/events.html | 46 +------------- 4 files changed, 61 insertions(+), 120 deletions(-) diff --git a/app/frontend/src/javascript/components/events/event-card.tsx b/app/frontend/src/javascript/components/events/event-card.tsx index 1eaf3938a..064a7034b 100644 --- a/app/frontend/src/javascript/components/events/event-card.tsx +++ b/app/frontend/src/javascript/components/events/event-card.tsx @@ -13,9 +13,9 @@ interface EventCardProps { cardType: 'sm' | 'md' | 'lg' } -export const EventCard: React.FC = ({ event, cardType = 'sm' }) => { +export const EventCard: React.FC = ({ event, cardType = 'md' }) => { const { t } = useTranslation('public'); - console.log(event); + /** * Format description to remove HTML tags and set a maximum character count */ @@ -23,10 +23,10 @@ export const EventCard: React.FC = ({ event, cardType = 'sm' }) text = text.replace(/(<\/p>|<\/h4>|<\/h5>|<\/h6>|<\/pre>|<\/blockquote>)/g, '\n'); text = text.replace(//g, '\n'); text = text.replace(/<\/?\w+[^>]*>/g, ''); - text = text.replace(/\n+/g, '
'); if (text.length > count) { - return text.slice(0, count) + '...'; + text = text.slice(0, count) + '…'; } + text = text.replace(/\n+/g, '
'); return text; }; @@ -51,28 +51,30 @@ export const EventCard: React.FC = ({ event, cardType = 'sm' }) }; /** - * Link to event by id + * TODO: Link to event by id ? */ const showEvent = (id: number) => { - // TODO: ??? - console.log(id); + console.log(window.location.href + '/' + id); }; return (
showEvent(event.id)}> -
- {event.event_image - ? - : - } -
+ {event.event_image + ?
+ +
+ : cardType !== 'sm' && +
+ +
+ }

{event?.title}

{event.category.name}
{cardType !== 'sm' && -

+

}
diff --git a/app/frontend/src/stylesheets/modules/events/event.scss b/app/frontend/src/stylesheets/modules/events/event.scss index b2c4ddc08..2bedd8a46 100644 --- a/app/frontend/src/stylesheets/modules/events/event.scss +++ b/app/frontend/src/stylesheets/modules/events/event.scss @@ -6,8 +6,10 @@ color: var(--gray-hard-darkest); &:hover { color: var(--gray-hard-darkest); + border-color: var(--gray-hard); cursor: pointer; - & .event-card-picture {opacity: 0.7;} + .event-card-picture, + .event-card-info { border-color: var(--gray-hard); } } &-picture { @@ -17,7 +19,6 @@ background-color: #fff; font-size: 8rem; color: var(--gray-soft-light); - transition: opacity 0.4s ease-out; img { width: 100%; height: 100%; @@ -43,7 +44,7 @@ } &-info { margin-top: auto; - padding: 15px 30px; + padding: 15px 20px; .grid { display: grid; grid-template-columns: repeat(2, 1fr); @@ -55,7 +56,7 @@ i { width: 16px; height: 16px; - margin-right: 15px; + margin-right: 10px; font-size: 16px; text-align: center; color: var(--main); @@ -117,7 +118,35 @@ border-top: 1px solid var(--gray-soft-dark); } } - &-card--lg {} + &-card--lg { + border: 1px solid var(--gray-hard); + .event-card-info { + padding-top: 0; + & > p { + font-size: 1.8rem; + font-weight: 600; + color: var(--main); + } + } + .event-card-picture { + border-bottom: 1px solid var(--gray-hard); + img { max-height: 300px; } + } + + @media (min-width: 992px) { + grid-column: span 2; + display: grid; + grid-template-columns: 3fr 2fr; + .event-card-desc { grid-area: 1/1/2/2; } + .event-card-info { grid-area: 2/1/3/2; } + .event-card-picture { + grid-area: 1/2/3/3; + border-bottom: none; + border-left: 1px solid var(--gray-hard); + img { max-height: none; } + } + } + } // layout specific &-home { @@ -142,35 +171,14 @@ &-focus { display: grid; margin-bottom: 50px; - .event-card { - border: 1px solid var(--gray-hard); - &-info { padding-top: 0; } - &-picture { - border-bottom: 1px solid var(--gray-hard); - img { max-height: 300px; } - } - } @media (min-width: 992px) { grid-template-columns: repeat(auto-fill, minmax(425px, 1fr)); gap: 15px; - .event-card { - grid-column: span 2; - display: grid; - grid-template-columns: 3fr 2fr; - &-desc { grid-area: 1/1/2/2; } - &-info { grid-area: 2/1/3/2; } - &-picture { - grid-area: 1/2/3/3; - border-bottom: none; - border-left: 1px solid var(--gray-hard); - img { max-height: none; } - } - } } } - &-month-list { + &-monthList { display: grid; grid-template-columns: 1fr; gap: 15px; diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index f261f3b14..1ea6e9e72 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -40,43 +40,20 @@
+
+ + + +
+

{{monthNames[month.split(',')[0] - 1]}}, {{month.split(',')[1]}}

-
@@ -88,5 +65,3 @@ - - diff --git a/app/frontend/templates/home/events.html b/app/frontend/templates/home/events.html index 12e491f23..45a549b93 100644 --- a/app/frontend/templates/home/events.html +++ b/app/frontend/templates/home/events.html @@ -6,52 +6,8 @@ -
-
-
- - -
- -
-

{{event.title}}

- {{event.category.name}} -

-
- -
-
- -
{{ 'app.public.home.from_date_to_date' | translate:{START:(event.start_date | amDateFormat:'L'), END:(event.end_date | amDateFormat:'L')} }}
-
{{ 'app.public.home.on_the_date' | translate:{DATE:(event.start_date | amDateFormat:'L')} }}
-
-
- -
- {{ 'app.public.home.all_day' }} - {{ 'app.public.home.from_time_to_time' | translate:{START:(event.start_date | amDateFormat:'LT'), END:(event.end_date | amDateFormat:'LT')} }} -
-
-
- -
- {{ 'app.public.home.still_available' | translate }} {{event.nb_free_places}} - {{ 'app.public.home.without_reservation' }} - {{ 'app.public.home.event_full' }} -
-
-
- -
- {{ 'app.public.home.free_admission' }} - {{ 'app.public.home.full_price' | translate }} {{event.amount | currency}} -
-
-
-
-
-
+
From 103bf80e814c2c2822c6f4ccd2c51b3b313a7f01 Mon Sep 17 00:00:00 2001 From: vincent Date: Tue, 19 Apr 2022 16:59:48 +0200 Subject: [PATCH 018/172] Standardize card layout --- .../components/events/event-card.tsx | 64 ++++++++++--------- .../src/stylesheets/modules/events/event.scss | 54 ++++++++++------ app/frontend/templates/events/index.html | 11 ++-- app/frontend/templates/home/events.html | 8 ++- app/views/api/events/_event.json.jbuilder | 2 +- 5 files changed, 80 insertions(+), 59 deletions(-) diff --git a/app/frontend/src/javascript/components/events/event-card.tsx b/app/frontend/src/javascript/components/events/event-card.tsx index 064a7034b..12db5d72e 100644 --- a/app/frontend/src/javascript/components/events/event-card.tsx +++ b/app/frontend/src/javascript/components/events/event-card.tsx @@ -13,7 +13,7 @@ interface EventCardProps { cardType: 'sm' | 'md' | 'lg' } -export const EventCard: React.FC = ({ event, cardType = 'md' }) => { +export const EventCard: React.FC = ({ event, cardType }) => { const { t } = useTranslation('public'); /** @@ -34,31 +34,27 @@ export const EventCard: React.FC = ({ event, cardType = 'md' }) * Return the formatted localized date of the event */ const formatDate = (): string => { - // FIXME: typeof event.all_day = sting ? - return event.all_day === 'true' - ? t('app.public.home.from_date_to_date', { START: FormatLib.date(event.start_date), END: FormatLib.date(event.end_date) }) - : t('app.public.home.on_the_date', { DATE: FormatLib.date(event.start_date) }); + const startDate = new Date(event.start_date); + const endDate = new Date(event.end_date); + const singleDayEvent = startDate.getFullYear() === endDate.getFullYear() && + startDate.getMonth() === endDate.getMonth() && + startDate.getDate() === endDate.getDate(); + return singleDayEvent + ? t('app.public.home.on_the_date', { DATE: FormatLib.date(event.start_date) }) + : t('app.public.home.from_date_to_date', { START: FormatLib.date(event.start_date), END: FormatLib.date(event.end_date) }); }; /** * Return the formatted localized hours of the event */ const formatTime = (): string => { - // FIXME: typeof event.all_day = sting ? - return event.all_day === 'true' + return event.all_day ? t('app.public.home.all_day') : t('app.public.home.from_time_to_time', { START: FormatLib.time(event.start_date), END: FormatLib.time(event.end_date) }); }; - /** - * TODO: Link to event by id ? - */ - const showEvent = (id: number) => { - console.log(window.location.href + '/' + id); - }; - return ( -
showEvent(event.id)}> +
{event.event_image ?
@@ -70,17 +66,22 @@ export const EventCard: React.FC = ({ event, cardType = 'md' }) }
-

{event?.title}

{event.category.name} +

{event?.title}

{cardType !== 'sm' &&

}
- {cardType !== 'md' &&

{formatDate()}

} + {cardType !== 'md' && +

+ {formatDate()} + {formatTime()} +

+ }
- {cardType === 'sm' && + {cardType !== 'md' && event.event_themes.map(theme => { return (
@@ -88,33 +89,34 @@ export const EventCard: React.FC = ({ event, cardType = 'md' })
); }) } - {(cardType === 'sm' && event.age_range) && + {(cardType !== 'md' && event.age_range) &&
{event.age_range?.name}
} {cardType === 'md' && -
- -
{formatDate()}
-
+ <> +
+ +
{formatDate()}
+
+
+ +
{formatTime()}
+
+ }
- {cardType !== 'sm' && } -
{formatTime()}
-
-
- {cardType !== 'sm' && } + {event.nb_free_places > 0 &&
{t('app.public.home.still_available') + event.nb_free_places}
} {event.nb_total_places > 0 && event.nb_free_places <= 0 &&
{t('app.public.home.event_full')}
} {!event.nb_total_places &&
{t('app.public.home.without_reservation')}
}
- {cardType !== 'sm' && } + {event.amount === 0 &&
{t('app.public.home.free_admission')}
} - {/* TODO: Display currency ? */} - {event.amount > 0 &&
{t('app.public.home.full_price') + event.amount}
} + {event.amount > 0 &&
{t('app.public.home.full_price') + FormatLib.price(event.amount)}
}
diff --git a/app/frontend/src/stylesheets/modules/events/event.scss b/app/frontend/src/stylesheets/modules/events/event.scss index 2bedd8a46..2010f5cd7 100644 --- a/app/frontend/src/stylesheets/modules/events/event.scss +++ b/app/frontend/src/stylesheets/modules/events/event.scss @@ -28,9 +28,6 @@ &-desc { padding: 15px 15px 30px; header { - display: flex; - justify-content: space-between; - align-items: flex-start; margin-bottom: 1rem; p { margin: 0; @@ -39,18 +36,38 @@ line-height: 1.3; text-transform: uppercase; } + .badge { + min-width: auto; + max-width: min(16ch, 33%); + white-space: normal; + word-break: break-word; + margin: 0 0 10px 15px; + float: right; + } } p { margin: 0; } } &-info { - margin-top: auto; padding: 15px 20px; + & > p { + font-size: 1.8rem; + font-weight: 600; + color: var(--main); + span { + display: block; + font-size: 0.75em; + font-weight: 400; + line-height: 1.2; + text-transform: lowercase; + } + } .grid { display: grid; grid-template-columns: repeat(2, 1fr); + align-items: baseline; gap: 15px; &-item { - height: 20px; + min-height: 20px; display: flex; align-items: center; i { @@ -61,6 +78,7 @@ text-align: center; color: var(--main); } + h6 { margin: 0; } } } } @@ -69,11 +87,15 @@ // Card size specific &-card--sm { display: grid; + grid-template-rows: max-content 1fr; grid-template-columns: 1fr max-content; .event-card-desc { grid-area: 1/1/2/2; padding-bottom: 10px; - header p { font-size: 1.6rem; } + header { + margin-bottom: 0; + p { font-size: 1.6rem; } + } } .event-card-picture { grid-area: 1/2/3/3; @@ -87,21 +109,15 @@ .event-card-info { grid-area: 2/1/3/2; padding: 0 15px 15px; - & > p { - font-size: 1.8rem; - font-weight: 600; - color: var(--main); - } .grid { - grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); - gap: 5px 10px; + gap: 10px 15px; &-item i { width: 12px; height: 12px; - margin-right: 6px; + //margin-right: 6px; font-size: 12px; text-align: center; - color: var(--gray-hard-light); + //color: var(--gray-hard-light); } } } @@ -114,6 +130,7 @@ border-bottom: 1px solid var(--gray-soft-dark); } .event-card-info { + margin-top: auto; padding-bottom: 30px; border-top: 1px solid var(--gray-soft-dark); } @@ -122,11 +139,6 @@ border: 1px solid var(--gray-hard); .event-card-info { padding-top: 0; - & > p { - font-size: 1.8rem; - font-weight: 600; - color: var(--main); - } } .event-card-picture { border-bottom: 1px solid var(--gray-hard); @@ -184,7 +196,7 @@ gap: 15px; margin-bottom: 2rem; @media (min-width: 768px) { - grid-template-columns: repeat(auto-fill, minmax(425px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(430px, 1fr)); } } } diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 1ea6e9e72..5b2f065f5 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -42,16 +42,19 @@
- - +

{{monthNames[month.split(',')[0] - 1]}}, {{month.split(',')[1]}}

- - + +
diff --git a/app/frontend/templates/home/events.html b/app/frontend/templates/home/events.html index 45a549b93..1baf3a860 100644 --- a/app/frontend/templates/home/events.html +++ b/app/frontend/templates/home/events.html @@ -7,7 +7,11 @@
- - + +
diff --git a/app/views/api/events/_event.json.jbuilder b/app/views/api/events/_event.json.jbuilder index a919d1f06..c5813e1ec 100644 --- a/app/views/api/events/_event.json.jbuilder +++ b/app/views/api/events/_event.json.jbuilder @@ -32,7 +32,7 @@ json.end_time event.availability.end_at json.month t('date.month_names')[event.availability.start_at.month] json.month_id event.availability.start_at.month json.year event.availability.start_at.year -json.all_day event.availability.start_at.hour.zero? ? 'true' : 'false' +json.all_day event.availability.start_at.hour.zero? json.availability do json.id event.availability.id json.start_at event.availability.start_at From 61080a5188140df3dc8c9eb34b64dea9ebb384ea Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 14 Jun 2022 17:50:04 +0200 Subject: [PATCH 019/172] (feat) feature the first event --- Procfile | 2 +- app/frontend/src/javascript/controllers/events.js.erb | 6 ++++-- app/frontend/templates/events/index.html | 9 ++++++--- app/services/event_service.rb | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Procfile b/Procfile index 434aa4a6c..64f020c5e 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ -#web: bundle exec rails server puma -p $PORT +web: bundle exec rails server puma -p $PORT worker: bundle exec sidekiq -C ./config/sidekiq.yml webpack: bin/webpacker-dev-server diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index 16a3012b5..008c9a748 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -72,6 +72,7 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve // reinitialize results datasets $scope.page = 1; $scope.eventsGroupByMonth = {}; + $scope.featuredEevent = null; $scope.events = []; $scope.monthOrder = []; $scope.noMoreResults = false; @@ -111,13 +112,14 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve */ const groupEvents = function (events) { if (events.length > 0) { - const eventsGroupedByMonth = _.groupBy(events, function (obj) { + const eventsGroupedByMonth = _.groupBy(events.slice(1), function (obj) { return _.map(['month_id', 'year'], function (key) { return obj[key]; }); }); $scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth); - return $scope.monthOrder = Object.keys($scope.eventsGroupByMonth); + $scope.monthOrder = Object.keys($scope.eventsGroupByMonth); + $scope.featuredEevent = events[0]; } }; diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 5b2f065f5..02b436682 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -40,9 +40,12 @@
-
- - +
+ +
diff --git a/app/services/event_service.rb b/app/services/event_service.rb index 6747aca36..860391532 100644 --- a/app/services/event_service.rb +++ b/app/services/event_service.rb @@ -37,8 +37,8 @@ class EventService start_at = DateTime.new(start_date.year, start_date.month, start_date.day, 0, 0, 0, start_date.zone) end_at = DateTime.new(end_date.year, end_date.month, end_date.day, 23, 59, 59, end_date.zone) else - start_at = DateTime.new(start_date.year, start_date.month, start_date.day, start_time.hour, start_time.min, start_time.sec, start_date.zone) - end_at = DateTime.new(end_date.year, end_date.month, end_date.day, end_time.hour, end_time.min, end_time.sec, end_date.zone) + start_at = DateTime.new(start_date.year, start_date.month, start_date.day, start_time&.hour, start_time&.min, start_time&.sec, start_date.zone) + end_at = DateTime.new(end_date.year, end_date.month, end_date.day, end_time&.hour, end_time&.min, end_time&.sec, end_date.zone) end { start_at: start_at, end_at: end_at } end From 48e1cf782ffd8d7cbcdb0055944bd48e94cfaacf Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 09:42:39 +0200 Subject: [PATCH 020/172] (bug) unable to edit an event --- CHANGELOG.md | 1 + app/frontend/templates/events/_form.html | 6 +++--- app/models/event.rb | 4 ++++ app/views/api/events/_event.json.jbuilder | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 857434e80..64bae9a67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Run the docker image with the system user - During the setup, autoconfigure the main domain - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http +- Fix a bug: unable to edit an event - Fix a bug: unable to generate the secret key base during the setup - Fix a bug: error message during the setup: the input device is not a TTY - Fix a bug: when Fab-manager was installed as non-root user, unable to compile the assets during the upgrade diff --git a/app/frontend/templates/events/_form.html b/app/frontend/templates/events/_form.html index f6e7e2ac2..bbbc8b682 100644 --- a/app/frontend/templates/events/_form.html +++ b/app/frontend/templates/events/_form.html @@ -138,10 +138,10 @@
@@ -180,7 +180,7 @@
-
+
diff --git a/app/models/event.rb b/app/models/event.rb index 2e5a251c2..c0a31bd7b 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -84,6 +84,10 @@ class Event < ApplicationRecord end end + def all_day? + availability.start_at.hour.zero? + end + private def event_recurrence diff --git a/app/views/api/events/_event.json.jbuilder b/app/views/api/events/_event.json.jbuilder index c5813e1ec..c94072877 100644 --- a/app/views/api/events/_event.json.jbuilder +++ b/app/views/api/events/_event.json.jbuilder @@ -32,7 +32,7 @@ json.end_time event.availability.end_at json.month t('date.month_names')[event.availability.start_at.month] json.month_id event.availability.start_at.month json.year event.availability.start_at.year -json.all_day event.availability.start_at.hour.zero? +json.all_day event.all_day? json.availability do json.id event.availability.id json.start_at event.availability.start_at From a8ca94ce3292def03c0ec8c856f72d74fb84c59a Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 09:59:19 +0200 Subject: [PATCH 021/172] (bug) times are not shown in admin/events monitoring page --- CHANGELOG.md | 1 + app/frontend/templates/admin/events/monitoring.html | 6 +++--- app/policies/event_policy.rb | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64bae9a67..07c0b8c14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - During the setup, autoconfigure the main domain - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http - Fix a bug: unable to edit an event +- Fix a bug: times are not shown in admin/events monitoring page - Fix a bug: unable to generate the secret key base during the setup - Fix a bug: error message during the setup: the input device is not a TTY - Fix a bug: when Fab-manager was installed as non-root user, unable to compile the assets during the upgrade diff --git a/app/frontend/templates/admin/events/monitoring.html b/app/frontend/templates/admin/events/monitoring.html index 3324372ef..7d77bf9ed 100644 --- a/app/frontend/templates/admin/events/monitoring.html +++ b/app/frontend/templates/admin/events/monitoring.html @@ -28,7 +28,7 @@ {{ 'app.admin.events.on_DATE' | translate:{DATE:(event.start_date | amDateFormat:'LL')} }} - + {{ 'app.admin.events.from_TIME' | translate:{TIME:(event.start_date | amDateFormat:'LT')} }} {{ 'app.admin.events.to_time' }} {{event.end_date | amDateFormat:'LT'}} @@ -39,8 +39,8 @@ {{'app.admin.events.from_DATE' | translate:{DATE:(event.start_date | amDateFormat:'LL')} }} {{'app.admin.events.to_date' | translate}} {{event.end_date | amDateFormat:'LL'}} -
- +
+ {{ 'app.admin.events.from_TIME' | translate:{TIME:(event.start_date | amDateFormat:'LT')} }} {{ 'app.admin.events.to_time' }} {{event.end_date | amDateFormat:'LT'}} diff --git a/app/policies/event_policy.rb b/app/policies/event_policy.rb index 1a5a51df5..8e350a944 100644 --- a/app/policies/event_policy.rb +++ b/app/policies/event_policy.rb @@ -6,12 +6,12 @@ class EventPolicy < ApplicationPolicy class Scope < Scope def resolve if user.nil? || (user && !user.admin? && !user.manager?) - scope.includes(:event_image, :event_files, :availability, :category) + scope.includes(:event_image, :event_files, :availability, :category, :event_price_categories, :age_range, :events_event_themes, :event_themes) .where('availabilities.start_at >= ?', DateTime.current) .order('availabilities.start_at ASC') .references(:availabilities) else - scope.includes(:event_image, :event_files, :availability, :category) + scope.includes(:event_image, :event_files, :availability, :category, :event_price_categories, :age_range, :events_event_themes, :event_themes) .references(:availabilities) end end From 31fe9dea05e7627c40e78eaad8044c8c8fb88f34 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 10:55:08 +0200 Subject: [PATCH 022/172] (feat) feature the next event in the event page --- CHANGELOG.md | 1 + app/frontend/src/javascript/controllers/events.js.erb | 4 ++-- app/frontend/templates/events/index.html | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c0b8c14..9773ea1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## next deploy +- Feature the next event in the event page - Documentation for installing behind a proxy - Ability to install behind a proxy - Improved docker image building time diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index 008c9a748..3786901f7 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -112,14 +112,14 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve */ const groupEvents = function (events) { if (events.length > 0) { - const eventsGroupedByMonth = _.groupBy(events.slice(1), function (obj) { + const eventsGroupedByMonth = _.groupBy(events, function (obj) { return _.map(['month_id', 'year'], function (key) { return obj[key]; }); }); $scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth); $scope.monthOrder = Object.keys($scope.eventsGroupByMonth); - $scope.featuredEevent = events[0]; + $scope.featuredEevent = _.minBy(events.filter(e => moment(e.start_date).isAfter()), e => e.start_date); } }; diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 02b436682..9616005ee 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -56,6 +56,7 @@ event="event" card-type="'sm'" ng-repeat="event in eventsGroupByMonth[month].slice(3*$index, 3*$index + 3)" + ng-if="event.id !== featuredEevent.id" ui-sref="app.public.events_show({id: event.id})">
From 96c825769c3e61e831beb3303711b1cbb1d62f9f Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:00:05 +0200 Subject: [PATCH 023/172] (wip) fix events style: no more 3 in rows --- app/frontend/templates/events/index.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 9616005ee..8152eab70 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -40,7 +40,7 @@
-
+

{{monthNames[month.split(',')[0] - 1]}}, {{month.split(',')[1]}}

-
+
From 503ae22c0b24ff7c1fef658ba8d90a851f7e4689 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:26:44 +0200 Subject: [PATCH 024/172] (feat) admins can scroll to the featured event --- app/frontend/src/javascript/controllers/events.js.erb | 10 ++++++++++ app/frontend/templates/events/index.html | 6 +++++- config/locales/app.public.en.yml | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index 3786901f7..7a6098be2 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -95,6 +95,16 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve */ $scope.onSingleDay = function (event) { moment(event.start_date).isSame(event.end_date, 'day'); }; + /** + * Move down the viewport to the featured event + */ + $scope.scrollToFeaturedEvent = function () { + const card = document.getElementsByClassName('featured-event')[0]; + if (card) { + card.childNodes[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } + /* PRIVATE SCOPE */ /** diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 8152eab70..77e9e9d97 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -47,6 +47,9 @@ ui-sref="app.public.events_show({id: featuredEevent.id})">
+
+ +

{{monthNames[month.split(',')[0] - 1]}}, {{month.split(',')[1]}}

@@ -55,7 +58,8 @@
diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index d997c59aa..820d97c21 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -289,6 +289,7 @@ en: full_price_: "Full price:" to_date: "to" #eg. from 01/01 to 01/05 all_themes: "All themes" + show_featured: "Show the featured event" #details and booking of an event events_show: event_description: "Event description" From 3b63ffa53263ab5759ad3e8b4742a235d9d996da Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:29:44 +0200 Subject: [PATCH 025/172] (bug) fix events list display --- app/frontend/templates/events/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/frontend/templates/events/index.html b/app/frontend/templates/events/index.html index 77e9e9d97..b45ebb330 100644 --- a/app/frontend/templates/events/index.html +++ b/app/frontend/templates/events/index.html @@ -54,9 +54,10 @@

{{monthNames[month.split(',')[0] - 1]}}, {{month.split(',')[1]}}

-
+
Date: Wed, 15 Jun 2022 12:33:29 +0200 Subject: [PATCH 026/172] New translations app.public.en.yml (Spanish) --- config/locales/app.public.es.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/app.public.es.yml b/config/locales/app.public.es.yml index 01b13d611..e6c66c915 100644 --- a/config/locales/app.public.es.yml +++ b/config/locales/app.public.es.yml @@ -289,6 +289,7 @@ es: full_price_: "Precio completo:" to_date: "to" #eg. from 01/01 to 01/05 all_themes: "All themes" + show_featured: "Show the featured event" #details and booking of an event events_show: event_description: "Descripción del evento" From d2a6e2dbd172e71633b244879d79d20615b2f081 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:33:36 +0200 Subject: [PATCH 027/172] New translations app.public.en.yml (French) --- config/locales/app.public.fr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index 05969d158..d2e1703d2 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -289,6 +289,7 @@ fr: full_price_: "Plein tarif :" to_date: "au" #eg. from 01/01 to 01/05 all_themes: "Toutes les thématiques" + show_featured: "Show the featured event" #details and booking of an event events_show: event_description: "Description de l’événement" From 87f1040aba22f798fb9ceafb0d6cdf21a9ecf981 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:33:38 +0200 Subject: [PATCH 028/172] New translations app.public.en.yml (German) --- config/locales/app.public.de.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/app.public.de.yml b/config/locales/app.public.de.yml index ef3071553..23033f8bd 100644 --- a/config/locales/app.public.de.yml +++ b/config/locales/app.public.de.yml @@ -289,6 +289,7 @@ de: full_price_: "Voller Preis:" to_date: "bis" #eg. from 01/01 to 01/05 all_themes: "Alle Themen" + show_featured: "Show the featured event" #details and booking of an event events_show: event_description: "Beschreibung" From 5c208b5da7606bdd9e5509151c8af62b292dc5db Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:33:40 +0200 Subject: [PATCH 029/172] New translations app.public.en.yml (Norwegian) --- config/locales/app.public.no.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/app.public.no.yml b/config/locales/app.public.no.yml index 5a6858e5f..1bcc93952 100644 --- a/config/locales/app.public.no.yml +++ b/config/locales/app.public.no.yml @@ -289,6 +289,7 @@ full_price_: "Full pris:" to_date: "til" #eg. from 01/01 to 01/05 all_themes: "Alle temaer" + show_featured: "Show the featured event" #details and booking of an event events_show: event_description: "Arrangementsbeskrivelse" From d5daf73903368632a2ea7053e00bb0b219c8f2bc Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:33:41 +0200 Subject: [PATCH 030/172] New translations app.public.en.yml (Portuguese) --- config/locales/app.public.pt.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/app.public.pt.yml b/config/locales/app.public.pt.yml index 4056e0b7e..a7de009d4 100755 --- a/config/locales/app.public.pt.yml +++ b/config/locales/app.public.pt.yml @@ -289,6 +289,7 @@ pt: full_price_: "Valor inteira:" to_date: "até" #eg. from 01/01 to 01/05 all_themes: "Todos os temas" + show_featured: "Show the featured event" #details and booking of an event events_show: event_description: "Descrição do evento" From 10e71144552ac6360198c91cd6bfb9a291e13ec7 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:33:43 +0200 Subject: [PATCH 031/172] New translations app.public.en.yml (Zulu) --- config/locales/app.public.zu.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/app.public.zu.yml b/config/locales/app.public.zu.yml index ea9677f8f..50b94ff5e 100644 --- a/config/locales/app.public.zu.yml +++ b/config/locales/app.public.zu.yml @@ -289,6 +289,7 @@ zu: full_price_: "crwdns9243:0crwdne9243:0" to_date: "crwdns19622:0crwdne19622:0" #eg. from 01/01 to 01/05 all_themes: "crwdns19624:0crwdne19624:0" + show_featured: "crwdns23126:0crwdne23126:0" #details and booking of an event events_show: event_description: "crwdns9247:0crwdne9247:0" From 53b11b0de55388c961d2a7ea20aece420a4a18b1 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 12:35:50 +0200 Subject: [PATCH 032/172] New translations app.public.en.yml (French) --- config/locales/app.public.fr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index d2e1703d2..236f8b34b 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -289,7 +289,7 @@ fr: full_price_: "Plein tarif :" to_date: "au" #eg. from 01/01 to 01/05 all_themes: "Toutes les thématiques" - show_featured: "Show the featured event" + show_featured: "Afficher l'événement du moment" #details and booking of an event events_show: event_description: "Description de l’événement" From ed34279cef390f607ad314ba05a444b1b9d4d038 Mon Sep 17 00:00:00 2001 From: vincent Date: Wed, 8 Jun 2022 10:35:53 +0200 Subject: [PATCH 033/172] unable non-required [text-setting] --- .../src/javascript/directives/settings/text-setting.js | 4 ++++ app/frontend/templates/admin/settings/home_page.html | 1 + app/frontend/templates/admin/settings/text.html | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/frontend/src/javascript/directives/settings/text-setting.js b/app/frontend/src/javascript/directives/settings/text-setting.js index d664b9347..2d0be3eda 100644 --- a/app/frontend/src/javascript/directives/settings/text-setting.js +++ b/app/frontend/src/javascript/directives/settings/text-setting.js @@ -21,6 +21,10 @@ Application.Directives.directive('textSetting', ['Setting', 'growl', '_t', if (typeof $scope.type === 'undefined') { $scope.type = 'text'; } + // 'required' default to true + if (typeof $scope.required === 'undefined') { + $scope.required = true; + } // The setting $scope.setting = { name: $scope.name, diff --git a/app/frontend/templates/admin/settings/home_page.html b/app/frontend/templates/admin/settings/home_page.html index 4b4c858c1..dfa3073af 100644 --- a/app/frontend/templates/admin/settings/home_page.html +++ b/app/frontend/templates/admin/settings/home_page.html @@ -29,6 +29,7 @@
diff --git a/app/frontend/templates/admin/settings/text.html b/app/frontend/templates/admin/settings/text.html index 8ecc824d7..ffcb1966e 100644 --- a/app/frontend/templates/admin/settings/text.html +++ b/app/frontend/templates/admin/settings/text.html @@ -10,7 +10,7 @@ id="setting-{{setting.name}}" placeholder="{{placeholder}}" ng-model="setting.value" - ng-required="true" + ng-required="required" ng-minlength="minLength" ng-maxlength="maxLength" ng-trim="false" From 727f2d3d807761c0e3f2fa1c65d19c2ea0daa21b Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 13:37:07 +0200 Subject: [PATCH 034/172] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9773ea1b0..b855591ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Run the docker image with the system user - During the setup, autoconfigure the main domain - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http +- Fix a bug: unable to set the twitter input empty - Fix a bug: unable to edit an event - Fix a bug: times are not shown in admin/events monitoring page - Fix a bug: unable to generate the secret key base during the setup From ca9eec857927dadbbf2f89035b209789ea9e678d Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 24 May 2022 17:55:19 +0200 Subject: [PATCH 035/172] (lint) add rule to check component class naming --- .eslintrc | 6 ++++-- .../base/text-editor/fab-text-editor.tsx | 1 + .../components/machines/machine-card.tsx | 8 +++++--- .../machines/pending-training-modal.tsx | 5 +++-- .../payment-schedule/payment-schedules-table.tsx | 16 +++++++++++----- .../payment/abstract-payment-modal.tsx | 2 +- .../javascript/components/plans/plan-card.tsx | 8 +++++--- .../components/pricing/editable-price.tsx | 9 ++++++++- app/frontend/src/stylesheets/application.scss | 2 +- .../payment-schedules-table.scss | 2 +- ...nt-modal.scss => abstract-payment-modal.scss} | 2 +- package.json | 1 + yarn.lock | 5 +++++ 13 files changed, 47 insertions(+), 20 deletions(-) rename app/frontend/src/stylesheets/modules/payment/{payment-modal.scss => abstract-payment-modal.scss} (98%) diff --git a/.eslintrc b/.eslintrc index 0524aabd3..72fee9de8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,7 +6,9 @@ ], "rules": { "semi": ["error", "always"], - "no-use-before-define": "off" + "no-use-before-define": "off", + "fabmanager/component-class-named-as-component": "error", + "import/no-default-export": "error" }, "globals": { "Application": true, @@ -19,7 +21,7 @@ "$": true, "KeyboardEvent": true }, - "plugins": ["html-erb"], + "plugins": ["html-erb", "fabmanager"], "overrides": [ { "files": ["**/*.ts", "**/*.tsx"], diff --git a/app/frontend/src/javascript/components/base/text-editor/fab-text-editor.tsx b/app/frontend/src/javascript/components/base/text-editor/fab-text-editor.tsx index bf7414244..37db695fc 100644 --- a/app/frontend/src/javascript/components/base/text-editor/fab-text-editor.tsx +++ b/app/frontend/src/javascript/components/base/text-editor/fab-text-editor.tsx @@ -101,4 +101,5 @@ export const FabTextEditor: React.ForwardRefRenderFunction = ({ user, machine, onShowMachine, onReserveMachine, onError, onSuccess, onLoginRequested, onEnrollRequested, canProposePacks }) => { +const MachineCard: React.FC = ({ user, machine, onShowMachine, onReserveMachine, onError, onSuccess, onLoginRequested, onEnrollRequested, canProposePacks }) => { const { t } = useTranslation('public'); // shall we display a loader to prevent double-clicking, while the machine details are loading? @@ -82,10 +82,12 @@ const MachineCardComponent: React.FC = ({ user, machine, onSho ); }; -export const MachineCard: React.FC = ({ user, machine, onShowMachine, onReserveMachine, onError, onSuccess, onLoginRequested, onEnrollRequested, canProposePacks }) => { +const MachineCardWrapper: React.FC = ({ user, machine, onShowMachine, onReserveMachine, onError, onSuccess, onLoginRequested, onEnrollRequested, canProposePacks }) => { return ( - + ); }; + +export { MachineCardWrapper as MachineCard }; diff --git a/app/frontend/src/javascript/components/machines/pending-training-modal.tsx b/app/frontend/src/javascript/components/machines/pending-training-modal.tsx index e1f32fd2a..051eab884 100644 --- a/app/frontend/src/javascript/components/machines/pending-training-modal.tsx +++ b/app/frontend/src/javascript/components/machines/pending-training-modal.tsx @@ -3,11 +3,12 @@ import { FabModal } from '../base/fab-modal'; import { useTranslation } from 'react-i18next'; import { HtmlTranslate } from '../base/html-translate'; import FormatLib from '../../lib/format'; +import { TDateISO } from '../../typings/date-iso'; interface PendingTrainingModalProps { isOpen: boolean, toggleModal: () => void, - nextReservation: Date, + nextReservation: TDateISO, } /** @@ -20,7 +21,7 @@ export const PendingTrainingModal: React.FC = ({ isOp /** * Return the formatted localized date for the given date */ - const formatDateTime = (date: Date): string => { + const formatDateTime = (date: TDateISO): string => { return t('app.logged.pending_training_modal.DATE_TIME', { DATE: FormatLib.date(date), TIME: FormatLib.time(date) }); }; diff --git a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx index 9164c9555..3236bf49b 100644 --- a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx +++ b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx @@ -26,7 +26,7 @@ interface PaymentSchedulesTableProps { /** * This component shows a list of all payment schedules with their associated deadlines (aka. PaymentScheduleItem) and invoices */ -const PaymentSchedulesTableComponent: React.FC = ({ paymentSchedules, showCustomer, refreshList, operator, onError, onCardUpdateSuccess }) => { +const PaymentSchedulesTable: React.FC = ({ paymentSchedules, showCustomer, refreshList, operator, onError, onCardUpdateSuccess }) => { const { t } = useTranslation('shared'); // for each payment schedule: are the details (all deadlines) shown or hidden? @@ -68,8 +68,10 @@ const PaymentSchedulesTableComponent: React.FC = ({ */ const expandCollapseIcon = (paymentScheduleId: number): JSX.Element => { if (isExpanded(paymentScheduleId)) { + // eslint-disable-next-line fabmanager/component-class-named-as-component return ; } else { + // eslint-disable-next-line fabmanager/component-class-named-as-component return ; } }; @@ -93,6 +95,7 @@ const PaymentSchedulesTableComponent: React.FC = ({ const downloadScheduleButton = (id: number): JSX.Element => { const link = `api/payment_schedules/${id}/download`; return ( + // eslint-disable-next-line fabmanager/component-class-named-as-component {t('app.shared.schedules_table.download')} @@ -109,6 +112,7 @@ const PaymentSchedulesTableComponent: React.FC = ({ const key = `app.shared.schedules_table.method_${item.payment_method}`; res += ` (${t(key)})`; } + // eslint-disable-next-line fabmanager/component-class-named-as-component return {res}; }; @@ -121,7 +125,7 @@ const PaymentSchedulesTableComponent: React.FC = ({ const renderPaymentSchedulesTable = (): ReactElement => { return ( - +
{groups?.map(group => - - - {showCustomer && } + + + + {showCustomer && } @@ -159,9 +159,9 @@ const PaymentSchedulesTable: React.FC = ({ paymentSc
@@ -212,12 +216,14 @@ const PaymentSchedulesTableComponent: React.FC = ({ return
; } }; -PaymentSchedulesTableComponent.defaultProps = { showCustomer: false }; +PaymentSchedulesTable.defaultProps = { showCustomer: false }; -export const PaymentSchedulesTable: React.FC = ({ paymentSchedules, showCustomer, refreshList, operator, onError, onCardUpdateSuccess }) => { +const PaymentSchedulesTableWrapper: React.FC = ({ paymentSchedules, showCustomer, refreshList, operator, onError, onCardUpdateSuccess }) => { return ( - + ); }; + +export { PaymentSchedulesTableWrapper as PaymentSchedulesTable }; diff --git a/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx b/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx index f031ecca7..42596f0be 100644 --- a/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx +++ b/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx @@ -203,7 +203,7 @@ export const AbstractPaymentModal: React.FC = ({ isOp width={modalSize} closeButton={false} customFooter={logoFooter} - className={`payment-modal ${className || ''}`}> + className={`abstract-payment-modal ${className || ''}`}> {ready &&
= ({ plan, userId, subscribedPlanId, operator, onSelectPlan, isSelected, onLoginRequested, canSelectPlan }) => { +const PlanCard: React.FC = ({ plan, userId, subscribedPlanId, operator, onSelectPlan, isSelected, onLoginRequested, canSelectPlan }) => { const { t } = useTranslation('public'); /** * Return the formatted localized amount of the given plan (eg. 20.5 => "20,50 €") @@ -144,10 +144,12 @@ const PlanCardComponent: React.FC = ({ plan, userId, subscribedPl ); }; -export const PlanCard: React.FC = ({ plan, userId, subscribedPlanId, operator, onSelectPlan, isSelected, onLoginRequested, canSelectPlan }) => { +const PlanCardWrapper: React.FC = ({ plan, userId, subscribedPlanId, operator, onSelectPlan, isSelected, onLoginRequested, canSelectPlan }) => { return ( - + ); }; + +export { PlanCardWrapper as PlanCard }; diff --git a/app/frontend/src/javascript/components/pricing/editable-price.tsx b/app/frontend/src/javascript/components/pricing/editable-price.tsx index f6226d33f..d24fb7c20 100644 --- a/app/frontend/src/javascript/components/pricing/editable-price.tsx +++ b/app/frontend/src/javascript/components/pricing/editable-price.tsx @@ -30,6 +30,13 @@ export const EditablePrice: React.FC = ({ price, onSave }) = toggleEdit(); }; + /** + * Callback triggered when the user input a new price + */ + const handleChangePrice = (value: string): void => { + setTempPrice(value); + }; + /** * Enable or disable the edit mode */ @@ -41,7 +48,7 @@ export const EditablePrice: React.FC = ({ price, onSave }) = {!edit && {FormatLib.price(price.amount)}} {edit && - + } className="approve-button" onClick={handleValidateEdit} /> } className="cancel-button" onClick={toggleEdit} /> } diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index c134acab2..fdc00c5d8 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -44,7 +44,7 @@ @import "modules/payment-schedule/payment-schedules-list"; @import "modules/payment-schedule/payment-schedules-table"; @import "modules/payment/local-payment/local-payment-modal"; -@import "modules/payment/payment-modal"; +@import "modules/payment/abstract-payment-modal"; @import "modules/payment/payzen/payzen-keys-form"; @import "modules/payment/payzen/payzen-modal"; @import "modules/payment/payzen/payzen-settings"; diff --git a/app/frontend/src/stylesheets/modules/payment-schedule/payment-schedules-table.scss b/app/frontend/src/stylesheets/modules/payment-schedule/payment-schedules-table.scss index 86121e97a..4d4b0d7d7 100644 --- a/app/frontend/src/stylesheets/modules/payment-schedule/payment-schedules-table.scss +++ b/app/frontend/src/stylesheets/modules/payment-schedule/payment-schedules-table.scss @@ -1,4 +1,4 @@ -.schedules-table { +.payment-schedules-table { table-layout: fixed; border: 1px solid #e9e9e9; border-top: 0; diff --git a/app/frontend/src/stylesheets/modules/payment/payment-modal.scss b/app/frontend/src/stylesheets/modules/payment/abstract-payment-modal.scss similarity index 98% rename from app/frontend/src/stylesheets/modules/payment/payment-modal.scss rename to app/frontend/src/stylesheets/modules/payment/abstract-payment-modal.scss index ad905afb0..1483a71b4 100644 --- a/app/frontend/src/stylesheets/modules/payment/payment-modal.scss +++ b/app/frontend/src/stylesheets/modules/payment/abstract-payment-modal.scss @@ -1,4 +1,4 @@ -.payment-modal { +.abstract-payment-modal { .fab-modal-content { padding-bottom: 0; } diff --git a/package.json b/package.json index 942fc73db..305ade0b1 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@typescript-eslint/parser": "^5.17.0", "eslint": "~8.12.0", "eslint-config-standard": "~17.0.0-1", + "eslint-plugin-fabmanager": "^0.0.13", "eslint-plugin-html-erb": "^1.0.1", "eslint-plugin-import": "~2.25.4", "eslint-plugin-n": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index 463c8cbdd..af07a1863 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4082,6 +4082,11 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" +eslint-plugin-fabmanager@^0.0.13: + version "0.0.13" + resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.0.13.tgz#bf7b8beee1485a354ecfc42e2649501da25d5996" + integrity sha512-cYJecCNMG/2hJ41HM6GrfnMwb9MaMQXob7V90U8kkdt3Os4rf//cv/6lTWJiX0Wpnp9nVbQavkPKX5cJ8gYA/w== + eslint-plugin-html-erb@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-html-erb/-/eslint-plugin-html-erb-1.0.1.tgz#f4a55070d5eabe365e13dba587a1d779bfe36eb6" From 0f15127f2287b05abe87500b4bf4158ece84fde1 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 25 May 2022 14:37:25 +0200 Subject: [PATCH 036/172] updated eslint rules dependency --- .eslintrc | 2 +- .../src/javascript/components/form/abstract-form-item.tsx | 3 +-- .../components/payment/abstract-payment-modal.tsx | 2 +- .../form/{form-item.scss => abstract-form-item.scss} | 2 +- .../modules/payment/abstract-payment-modal.scss | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 7 files changed, 10 insertions(+), 11 deletions(-) rename app/frontend/src/stylesheets/modules/form/{form-item.scss => abstract-form-item.scss} (99%) diff --git a/.eslintrc b/.eslintrc index 72fee9de8..ee2cd2df7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,7 +7,7 @@ "rules": { "semi": ["error", "always"], "no-use-before-define": "off", - "fabmanager/component-class-named-as-component": "error", + "fabmanager/component-class-named-as-component": ["error", { "ignoreAbstractKeyword": true }], "import/no-default-export": "error" }, "globals": { diff --git a/app/frontend/src/javascript/components/form/abstract-form-item.tsx b/app/frontend/src/javascript/components/form/abstract-form-item.tsx index 4db1c62bf..15ef6467a 100644 --- a/app/frontend/src/javascript/components/form/abstract-form-item.tsx +++ b/app/frontend/src/javascript/components/form/abstract-form-item.tsx @@ -40,7 +40,6 @@ export const AbstractFormItem = ({ id, label, // Compose classnames from props const classNames = [ - 'form-item', `${className || ''}`, `${isDirty && fieldError ? 'is-incorrect' : ''}`, `${isDirty && warning ? 'is-warned' : ''}`, @@ -59,7 +58,7 @@ export const AbstractFormItem = ({ id, label, } return ( -
{space.name} {prices.length && } - = ({ onSuccess, onError }) => { +export const ProfileCustomFieldsList: React.FC = ({ onSuccess, onError }) => { const { t } = useTranslation('admin'); const [profileCustomFields, setProfileCustomFields] = useState>([]); diff --git a/app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx b/app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx index 33d6fbb47..80107f534 100644 --- a/app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx +++ b/app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx @@ -30,7 +30,7 @@ export const DeleteProofOfIdentityTypeModal: React.FC + className="delete-proof-of-identity-type-modal">

{t('app.admin.settings.compte.do_you_really_want_to_delete_this_proof_of_identity_type')}

); diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx b/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx index bd712a6a2..92590710b 100644 --- a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx +++ b/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx @@ -29,7 +29,7 @@ interface FilesType { /** * This component upload the proof of identity file of member */ -const ProofOfIdentityFiles: React.FC = ({ currentUser, onSuccess, onError }) => { +export const ProofOfIdentityFiles: React.FC = ({ currentUser, onSuccess, onError }) => { const { t } = useTranslation('admin'); const maxProofOfIdentityFileSizeMb = (Fablab.maxProofOfIdentityFileSize / 1024 / 1024).toFixed(); diff --git a/app/frontend/src/javascript/components/select-gateway-modal.tsx b/app/frontend/src/javascript/components/select-gateway-modal.tsx index a8b68b6e3..54ebab5a4 100644 --- a/app/frontend/src/javascript/components/select-gateway-modal.tsx +++ b/app/frontend/src/javascript/components/select-gateway-modal.tsx @@ -7,7 +7,7 @@ import React, { BaseSyntheticEvent, useEffect, useState } from 'react'; import { react2angular } from 'react2angular'; import { useTranslation } from 'react-i18next'; import { StripeKeysForm } from './payment/stripe/stripe-keys-form'; -import { PayZenKeysForm } from './payment/payzen/payzen-keys-form'; +import { PayzenKeysForm } from './payment/payzen/payzen-keys-form'; import { FabModal, ModalSize } from './base/fab-modal'; import { Loader } from './base/loader'; import { User } from '../models/user'; @@ -26,7 +26,7 @@ interface SelectGatewayModalModalProps { onSuccess: (results: Map) => void, } -const SelectGatewayModal: React.FC = ({ isOpen, toggleModal, onError, onSuccess }) => { +export const SelectGatewayModal: React.FC = ({ isOpen, toggleModal, onError, onSuccess }) => { const { t } = useTranslation('admin'); const [preventConfirmGateway, setPreventConfirmGateway] = useState(true); @@ -117,7 +117,7 @@ const SelectGatewayModal: React.FC = ({ isOpen, to isOpen={isOpen} toggleModal={toggleModal} width={ModalSize.medium} - className="gateway-modal" + className="select-gateway-modal" confirmButton={t('app.admin.invoices.payment.gateway_modal.confirm_button')} onConfirm={onGatewayConfirmed} preventConfirm={preventConfirmGateway}> @@ -131,7 +131,7 @@ const SelectGatewayModal: React.FC = ({ isOpen, to {selectedGateway === Gateway.Stripe && } - {selectedGateway === Gateway.PayZen && } + {selectedGateway === Gateway.PayZen && } ); }; diff --git a/app/frontend/src/javascript/components/settings/boolean-setting.tsx b/app/frontend/src/javascript/components/settings/boolean-setting.tsx index fa16efadd..752014ece 100644 --- a/app/frontend/src/javascript/components/settings/boolean-setting.tsx +++ b/app/frontend/src/javascript/components/settings/boolean-setting.tsx @@ -97,7 +97,7 @@ export const BooleanSetting: React.FC = ({ name, label, cla }; return ( -
+
{!hideSave && {t('app.admin.check_list_setting.save')} } @@ -105,7 +105,7 @@ export const BooleanSetting: React.FC = ({ name, label, cla ); }; -export const BooleanSettingWrapper: React.FC = ({ onChange, onSuccess, onError, label, className, name, hideSave, onBeforeSave }) => { +const BooleanSettingWrapper: React.FC = ({ onChange, onSuccess, onError, label, className, name, hideSave, onBeforeSave }) => { return ( diff --git a/app/frontend/src/javascript/components/settings/check-list-setting.tsx b/app/frontend/src/javascript/components/settings/check-list-setting.tsx index 446c9b510..06435c974 100644 --- a/app/frontend/src/javascript/components/settings/check-list-setting.tsx +++ b/app/frontend/src/javascript/components/settings/check-list-setting.tsx @@ -98,7 +98,7 @@ export const CheckListSetting: React.FC = ({ name, label, ); }; -export const CheckListSettingWrapper: React.FC = ({ availableOptions, onSuccess, onError, label, className, name, hideSave, defaultValue, onChange }) => { +const CheckListSettingWrapper: React.FC = ({ availableOptions, onSuccess, onError, label, className, name, hideSave, defaultValue, onChange }) => { return ( diff --git a/app/frontend/src/javascript/components/settings/user-validation-setting.tsx b/app/frontend/src/javascript/components/settings/user-validation-setting.tsx index 6bd4b9bd7..49d1a8353 100644 --- a/app/frontend/src/javascript/components/settings/user-validation-setting.tsx +++ b/app/frontend/src/javascript/components/settings/user-validation-setting.tsx @@ -19,7 +19,7 @@ interface UserValidationSettingProps { /** * This component allows to configure user validation required setting. */ -const UserValidationSetting: React.FC = ({ onSuccess, onError }) => { +export const UserValidationSetting: React.FC = ({ onSuccess, onError }) => { const { t } = useTranslation('admin'); const [userValidationRequired, setUserValidationRequired] = useState('false'); diff --git a/app/frontend/src/javascript/components/subscriptions/free-extend-modal.tsx b/app/frontend/src/javascript/components/subscriptions/free-extend-modal.tsx index 2f878d5f5..51b191ae2 100644 --- a/app/frontend/src/javascript/components/subscriptions/free-extend-modal.tsx +++ b/app/frontend/src/javascript/components/subscriptions/free-extend-modal.tsx @@ -26,7 +26,7 @@ interface FreeExtendModalProps { /** * Modal dialog shown to extend the current subscription of a customer, for free */ -const FreeExtendModal: React.FC = ({ isOpen, toggleModal, subscription, customerId, onError, onSuccess }) => { +export const FreeExtendModal: React.FC = ({ isOpen, toggleModal, subscription, customerId, onError, onSuccess }) => { // we do not render the modal if the subscription was not provided if (!subscription) return null; diff --git a/app/frontend/src/javascript/components/subscriptions/renew-modal.tsx b/app/frontend/src/javascript/components/subscriptions/renew-modal.tsx index 6356bfa5f..ef3922f54 100644 --- a/app/frontend/src/javascript/components/subscriptions/renew-modal.tsx +++ b/app/frontend/src/javascript/components/subscriptions/renew-modal.tsx @@ -35,7 +35,7 @@ interface RenewModalProps { /** * Modal dialog shown to renew the current subscription of a customer, for free */ -const RenewModal: React.FC = ({ isOpen, toggleModal, subscription, customer, operator, onError, onSuccess }) => { +export const RenewModal: React.FC = ({ isOpen, toggleModal, subscription, customer, operator, onError, onSuccess }) => { // we do not render the modal if the subscription was not provided if (!subscription) return null; diff --git a/app/frontend/src/javascript/components/subscriptions/subscribe-modal.tsx b/app/frontend/src/javascript/components/subscriptions/subscribe-modal.tsx index 2ca4b5c5f..006e92356 100644 --- a/app/frontend/src/javascript/components/subscriptions/subscribe-modal.tsx +++ b/app/frontend/src/javascript/components/subscriptions/subscribe-modal.tsx @@ -39,7 +39,7 @@ type selectOption = { value: number, label: string }; /** * Modal dialog shown to create a subscription for the given customer */ -const SubscribeModal: React.FC = ({ isOpen, toggleModal, customer, operator, onError, onSuccess }) => { +export const SubscribeModal: React.FC = ({ isOpen, toggleModal, customer, operator, onError, onSuccess }) => { const { t } = useTranslation('admin'); const [selectedPlan, setSelectedPlan] = useState(null); diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index fdc00c5d8..fea428780 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -30,8 +30,8 @@ @import "modules/base/labelled-input"; @import "modules/calendar/calendar"; @import "modules/events/event"; +@import "modules/form/abstract-form-item"; @import "modules/form/form-input"; -@import "modules/form/form-item"; @import "modules/form/form-rich-text"; @import "modules/form/form-switch"; @import "modules/group/change-group"; @@ -45,10 +45,11 @@ @import "modules/payment-schedule/payment-schedules-table"; @import "modules/payment/local-payment/local-payment-modal"; @import "modules/payment/abstract-payment-modal"; +@import "modules/payment/payzen/payzen-card-update-modal"; @import "modules/payment/payzen/payzen-keys-form"; @import "modules/payment/payzen/payzen-modal"; @import "modules/payment/payzen/payzen-settings"; -@import "modules/payment/payzen/payzen-update-card-modal"; +@import "modules/payment/stripe/stripe-card-update-modal"; @import "modules/payment/stripe/stripe-confirm"; @import "modules/payment/stripe/stripe-keys-form"; @import "modules/payment/stripe/stripe-modal"; diff --git a/app/frontend/src/stylesheets/modules/base/fab-text-editor.scss b/app/frontend/src/stylesheets/modules/base/fab-text-editor.scss index 034a1d583..daf2d2f2a 100644 --- a/app/frontend/src/stylesheets/modules/base/fab-text-editor.scss +++ b/app/frontend/src/stylesheets/modules/base/fab-text-editor.scss @@ -1,4 +1,4 @@ -.fab-textEditor { +.fab-text-editor { position: relative; margin-bottom: 1.6rem; padding-bottom: 1.6rem; diff --git a/app/frontend/src/stylesheets/modules/base/labelled-input.scss b/app/frontend/src/stylesheets/modules/base/labelled-input.scss index 3f8381b4d..3ccfb595e 100644 --- a/app/frontend/src/stylesheets/modules/base/labelled-input.scss +++ b/app/frontend/src/stylesheets/modules/base/labelled-input.scss @@ -1,4 +1,4 @@ -.input-with-label { +.labelled-input { position: relative; display: inline-table; border-collapse: separate; diff --git a/app/frontend/src/stylesheets/modules/payment/payzen/payzen-update-card-modal.scss b/app/frontend/src/stylesheets/modules/payment/payzen/payzen-card-update-modal.scss similarity index 97% rename from app/frontend/src/stylesheets/modules/payment/payzen/payzen-update-card-modal.scss rename to app/frontend/src/stylesheets/modules/payment/payzen/payzen-card-update-modal.scss index 413e5e43a..fb59142ef 100644 --- a/app/frontend/src/stylesheets/modules/payment/payzen/payzen-update-card-modal.scss +++ b/app/frontend/src/stylesheets/modules/payment/payzen/payzen-card-update-modal.scss @@ -1,4 +1,4 @@ -.payzen-update-card-modal { +.payzen-card-update-modal { .card-form { background-color: #f4f3f3; border: 1px solid #ddd; diff --git a/app/frontend/src/stylesheets/modules/payment/stripe/stripe-update-card-modal.scss b/app/frontend/src/stylesheets/modules/payment/stripe/stripe-card-update-modal.scss similarity index 97% rename from app/frontend/src/stylesheets/modules/payment/stripe/stripe-update-card-modal.scss rename to app/frontend/src/stylesheets/modules/payment/stripe/stripe-card-update-modal.scss index ba5bbf02b..cada094d0 100644 --- a/app/frontend/src/stylesheets/modules/payment/stripe/stripe-update-card-modal.scss +++ b/app/frontend/src/stylesheets/modules/payment/stripe/stripe-card-update-modal.scss @@ -1,4 +1,4 @@ -.stripe-update-card-modal { +.stripe-card-update-modal { .fab-modal-content { .card-form { background-color: #f4f3f3; diff --git a/app/frontend/src/stylesheets/modules/select-gateway-modal.scss b/app/frontend/src/stylesheets/modules/select-gateway-modal.scss index 971a07989..bdfc920e8 100644 --- a/app/frontend/src/stylesheets/modules/select-gateway-modal.scss +++ b/app/frontend/src/stylesheets/modules/select-gateway-modal.scss @@ -1,4 +1,4 @@ -.gateway-modal { +.select-gateway-modal { .info-gateway { border: 1px solid #bce8f1; border-radius: 4px; diff --git a/lib/tasks/fablab/auth.rake b/lib/tasks/fablab/auth.rake index 437025dfd..c88053114 100644 --- a/lib/tasks/fablab/auth.rake +++ b/lib/tasks/fablab/auth.rake @@ -6,13 +6,13 @@ namespace :fablab do desc 'switch the active authentication provider' task :switch_provider, [:provider] => :environment do |_task, args| + providers = AuthProvider.all.inject('') { |str, item| str + item[:name] + ', ' } unless args.provider - puts "\e[0;31mERROR\e[0m: You must pass a provider name to activate" + puts "\e[0;31mERROR\e[0m: You must pass a provider name to activate. Available providers are: #{providers[0..-3]}" next end if AuthProvider.find_by(name: args.provider).nil? - providers = AuthProvider.all.inject('') { |str, item| str + item[:name] + ', ' } puts "\e[0;31mERROR\e[0m: the provider '#{args.provider}' does not exists. Available providers are: #{providers[0..-3]}" next end diff --git a/package.json b/package.json index 207aaeb55..1d8b1daf0 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@typescript-eslint/parser": "^5.17.0", "eslint": "~8.12.0", "eslint-config-standard": "~17.0.0-1", - "eslint-plugin-fabmanager": "^0.0.21", + "eslint-plugin-fabmanager": "^0.0.23", "eslint-plugin-html-erb": "^1.0.1", "eslint-plugin-import": "~2.25.4", "eslint-plugin-n": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index 2c2ab0422..7d2fb3946 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4082,10 +4082,10 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-fabmanager@^0.0.21: - version "0.0.21" - resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.0.21.tgz#c2f2ccfbafc2f202adefdf73bb2650604a53541c" - integrity sha512-sy8SW7K0NSpm/l8sWJ75NAJfQuMLDZ98LnrYfnHGt119CeulaylgqYniyFnKRxUrYJH2c9dO2vBhiTFgFTeJUg== +eslint-plugin-fabmanager@^0.0.23: + version "0.0.23" + resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.0.23.tgz#7475999e23f9f658186c172c40a73e15587930e4" + integrity sha512-yXxJWPvUyhszBp/jL+aW4I9Qm43kkGH140Pm1EfEBnFkGaC63/U4UX+XSA2ibSocvw2Gqi8igEACEIf6/H4GYw== eslint-plugin-html-erb@^1.0.1: version "1.0.1" From 28f873c871acc5163a7c63cb497d17f6968cb48f Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 1 Jun 2022 16:44:48 +0200 Subject: [PATCH 039/172] (code) rule to enforce component named like filename --- .eslintrc | 3 ++- ...ce-button.tsx => configure-extended-prices-button.tsx} | 0 .../components/pricing/spaces/spaces-pricing.tsx | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 5 files changed, 8 insertions(+), 7 deletions(-) rename app/frontend/src/javascript/components/pricing/spaces/{configure-extended-price-button.tsx => configure-extended-prices-button.tsx} (100%) diff --git a/.eslintrc b/.eslintrc index fcb11d3d4..9896c6461 100644 --- a/.eslintrc +++ b/.eslintrc @@ -51,7 +51,8 @@ "rules": { "import/no-default-export": "error", "import/no-unused-modules": ["error", { "missingExports": true }], - "fabmanager/component-class-named-as-component": ["error", { "ignoreAbstractKeyword": true }] + "fabmanager/component-class-named-as-component": ["error", { "ignoreAbstractKeyword": true }], + "fabmanager/component-named-like-file": "error" } }, { diff --git a/app/frontend/src/javascript/components/pricing/spaces/configure-extended-price-button.tsx b/app/frontend/src/javascript/components/pricing/spaces/configure-extended-prices-button.tsx similarity index 100% rename from app/frontend/src/javascript/components/pricing/spaces/configure-extended-price-button.tsx rename to app/frontend/src/javascript/components/pricing/spaces/configure-extended-prices-button.tsx diff --git a/app/frontend/src/javascript/components/pricing/spaces/spaces-pricing.tsx b/app/frontend/src/javascript/components/pricing/spaces/spaces-pricing.tsx index 6a494aac0..3bca4d6f6 100644 --- a/app/frontend/src/javascript/components/pricing/spaces/spaces-pricing.tsx +++ b/app/frontend/src/javascript/components/pricing/spaces/spaces-pricing.tsx @@ -10,7 +10,7 @@ import { Group } from '../../../models/group'; import { IApplication } from '../../../models/application'; import { Space } from '../../../models/space'; import { EditablePrice } from '../editable-price'; -import { ConfigureExtendedPricesButton } from './configure-extended-price-button'; +import { ConfigureExtendedPricesButton } from './configure-extended-prices-button'; import PriceAPI from '../../../api/price'; import { Price } from '../../../models/price'; import { useImmer } from 'use-immer'; diff --git a/package.json b/package.json index 1d8b1daf0..fd3f7f1ff 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@typescript-eslint/parser": "^5.17.0", "eslint": "~8.12.0", "eslint-config-standard": "~17.0.0-1", - "eslint-plugin-fabmanager": "^0.0.23", + "eslint-plugin-fabmanager": "^0.1.2", "eslint-plugin-html-erb": "^1.0.1", "eslint-plugin-import": "~2.25.4", "eslint-plugin-n": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index 7d2fb3946..631d11212 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4082,10 +4082,10 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-fabmanager@^0.0.23: - version "0.0.23" - resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.0.23.tgz#7475999e23f9f658186c172c40a73e15587930e4" - integrity sha512-yXxJWPvUyhszBp/jL+aW4I9Qm43kkGH140Pm1EfEBnFkGaC63/U4UX+XSA2ibSocvw2Gqi8igEACEIf6/H4GYw== +eslint-plugin-fabmanager@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.1.2.tgz#b38f068b00946c85c51610ecc5b6ea8dd7359a91" + integrity sha512-P+Vd6CjSIWMt4DS8Aax0KWletxzcJqTsXIZ/rxdyKwrGpfw5YrJVGxV849oFyQskzcz8v/WMhRp8A98f1rU+2A== eslint-plugin-html-erb@^1.0.1: version "1.0.1" From f40f80dd1f543e9a84038b61452dcf04f8c26e41 Mon Sep 17 00:00:00 2001 From: vincent Date: Wed, 15 Jun 2022 13:48:42 +0200 Subject: [PATCH 040/172] Fix canceled event label's translation --- CHANGELOG.md | 1 + app/frontend/src/stylesheets/app.layout.scss | 2 +- app/frontend/templates/admin/calendar/calendar.html | 2 +- app/frontend/templates/admin/members/edit.html | 2 +- app/frontend/templates/dashboard/trainings.html | 4 ++-- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b855591ae..3f84f3352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Fix a bug: error message during the setup: the input device is not a TTY - Fix a bug: when Fab-manager was installed as non-root user, unable to compile the assets during the upgrade - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/set-docker-user.sh | bash` +- Fix canceled event label's translation ## v5.4.4 2022 June 8 diff --git a/app/frontend/src/stylesheets/app.layout.scss b/app/frontend/src/stylesheets/app.layout.scss index 5ef13ba35..00ccea00d 100644 --- a/app/frontend/src/stylesheets/app.layout.scss +++ b/app/frontend/src/stylesheets/app.layout.scss @@ -420,7 +420,7 @@ body.container { } &:before { - content: "Annulée"; + content: attr(data-label); display: inline-block; background-color: #c44242; border-radius: 0.25em; diff --git a/app/frontend/templates/admin/calendar/calendar.html b/app/frontend/templates/admin/calendar/calendar.html index 821c70b40..f7a132f7d 100644 --- a/app/frontend/templates/admin/calendar/calendar.html +++ b/app/frontend/templates/admin/calendar/calendar.html @@ -83,7 +83,7 @@
    -
  • +
  • {{r.user.name}} {{ 'app.admin.calendar.deleted_user' }} - {{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }} diff --git a/app/frontend/templates/admin/members/edit.html b/app/frontend/templates/admin/members/edit.html index cb22dc095..a4373b635 100644 --- a/app/frontend/templates/admin/members/edit.html +++ b/app/frontend/templates/admin/members/edit.html @@ -136,7 +136,7 @@
    -
  • +
  • {{r.reservable.name}} - {{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }}
diff --git a/app/frontend/templates/dashboard/trainings.html b/app/frontend/templates/dashboard/trainings.html index 6209b0a3b..9516ac27a 100644 --- a/app/frontend/templates/dashboard/trainings.html +++ b/app/frontend/templates/dashboard/trainings.html @@ -39,7 +39,7 @@
    -
  • +
  • {{r.reservable.name}} - {{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }}
@@ -54,7 +54,7 @@
    -
  • +
  • {{r.reservable.name}} - {{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }}
From cd17c17f46101ca0baced8d149dc033d967720b5 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 14:02:43 +0200 Subject: [PATCH 041/172] (code) add component-documentation rule --- .eslintrc | 3 ++- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.eslintrc b/.eslintrc index 9896c6461..f23fe11ca 100644 --- a/.eslintrc +++ b/.eslintrc @@ -52,7 +52,8 @@ "import/no-default-export": "error", "import/no-unused-modules": ["error", { "missingExports": true }], "fabmanager/component-class-named-as-component": ["error", { "ignoreAbstractKeyword": true }], - "fabmanager/component-named-like-file": "error" + "fabmanager/component-named-like-file": "error", + "fabmanager/component-documentation": "error" } }, { diff --git a/package.json b/package.json index fd3f7f1ff..639d5bae2 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@typescript-eslint/parser": "^5.17.0", "eslint": "~8.12.0", "eslint-config-standard": "~17.0.0-1", - "eslint-plugin-fabmanager": "^0.1.2", + "eslint-plugin-fabmanager": "^0.2.2", "eslint-plugin-html-erb": "^1.0.1", "eslint-plugin-import": "~2.25.4", "eslint-plugin-n": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index 631d11212..c7bf0fb77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4082,10 +4082,10 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-fabmanager@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.1.2.tgz#b38f068b00946c85c51610ecc5b6ea8dd7359a91" - integrity sha512-P+Vd6CjSIWMt4DS8Aax0KWletxzcJqTsXIZ/rxdyKwrGpfw5YrJVGxV849oFyQskzcz8v/WMhRp8A98f1rU+2A== +eslint-plugin-fabmanager@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.2.2.tgz#395acebad1d4b5e98e2a4843f0970740081592c5" + integrity sha512-GWAJzJhr59s3+/A3amSu4UtmeoUPB3pkzxFOEeAHKdkkZ5WfIS3Hlfx7gKe9CdC7Moli0VK+ZZ7KT2Xcc26S4A== eslint-plugin-html-erb@^1.0.1: version "1.0.1" From 98d091cf8758629457b857fc645dc7e331546270 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 15 Jun 2022 18:03:00 +0200 Subject: [PATCH 042/172] added somes rules --- .eslintrc | 5 ++++- package.json | 2 +- yarn.lock | 15 +++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.eslintrc b/.eslintrc index f23fe11ca..089ae0a20 100644 --- a/.eslintrc +++ b/.eslintrc @@ -53,7 +53,10 @@ "import/no-unused-modules": ["error", { "missingExports": true }], "fabmanager/component-class-named-as-component": ["error", { "ignoreAbstractKeyword": true }], "fabmanager/component-named-like-file": "error", - "fabmanager/component-documentation": "error" + "fabmanager/component-documentation": "error", + "fabmanager/component-methods-documentation": "error", + "fabmanager/no-bootstrap": "error", + "fabmanager/no-utilities": "error" } }, { diff --git a/package.json b/package.json index 639d5bae2..b0c7b1dac 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@typescript-eslint/parser": "^5.17.0", "eslint": "~8.12.0", "eslint-config-standard": "~17.0.0-1", - "eslint-plugin-fabmanager": "^0.2.2", + "eslint-plugin-fabmanager": "^0.4.2", "eslint-plugin-html-erb": "^1.0.1", "eslint-plugin-import": "~2.25.4", "eslint-plugin-n": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index c7bf0fb77..020d09a3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4082,10 +4082,12 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-fabmanager@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.2.2.tgz#395acebad1d4b5e98e2a4843f0970740081592c5" - integrity sha512-GWAJzJhr59s3+/A3amSu4UtmeoUPB3pkzxFOEeAHKdkkZ5WfIS3Hlfx7gKe9CdC7Moli0VK+ZZ7KT2Xcc26S4A== +eslint-plugin-fabmanager@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.4.2.tgz#87f18b13a29fe77dcbb9cb8c4a996b0b108f8a07" + integrity sha512-3SKm4YXXzrgQYnaCmk0/QLh+2YaCSMAZFazC7fFB57BBo0PR70YMrGNoyiEW4oQ52qYNeIMwlFHHNPEgWfcwsw== + dependencies: + requireindex "^1.2.0" eslint-plugin-html-erb@^1.0.1: version "1.0.1" @@ -6791,6 +6793,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +requireindex@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" From d45c03d36eb867a0cd79cd111aa199012da48516 Mon Sep 17 00:00:00 2001 From: Du Peng Date: Wed, 15 Jun 2022 19:44:55 +0200 Subject: [PATCH 043/172] Override angular currency filter, use Intl.NumberFormat to format amount --- CHANGELOG.md | 1 + app/frontend/src/javascript/filters/filters.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f84f3352..1640764a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Run the docker image with the system user - During the setup, autoconfigure the main domain - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http +- Override angular currency filter, use Intl.NumberFormat to format amount - Fix a bug: unable to set the twitter input empty - Fix a bug: unable to edit an event - Fix a bug: times are not shown in admin/events monitoring page diff --git a/app/frontend/src/javascript/filters/filters.js b/app/frontend/src/javascript/filters/filters.js index ad943d5a4..832760e4a 100644 --- a/app/frontend/src/javascript/filters/filters.js +++ b/app/frontend/src/javascript/filters/filters.js @@ -350,3 +350,12 @@ Application.Filters.filter('filterDisabled', [function () { } }; }]); + +Application.Filters.filter('currency', [function ($locale) { + return function (amount) { + // if null or undefined pass it through + return (amount == null) + ? amount + : new Intl.NumberFormat(Fablab.intl_locale, { style: 'currency', currency: Fablab.intl_currency }).format(amount); + }; +}]); From 32259e8876dabf7996baab51bde20515e8fb0334 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 20 Jun 2022 10:18:50 +0200 Subject: [PATCH 044/172] (bug) birthday, phone, address not marked as required --- CHANGELOG.md | 2 ++ .../javascript/components/user/user-profile-form.tsx | 11 ++++++++++- app/frontend/src/stylesheets/application.scss | 1 - 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b855591ae..217f591bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - Run the docker image with the system user - During the setup, autoconfigure the main domain - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http +- Fix a bug: the birthdate was not marked as required, in the profile edition form +- Fix a bug: when the phone or the address were required, they were not marked as this, in the profile edition form - Fix a bug: unable to set the twitter input empty - Fix a bug: unable to edit an event - Fix a bug: times are not shown in admin/events monitoring page diff --git a/app/frontend/src/javascript/components/user/user-profile-form.tsx b/app/frontend/src/javascript/components/user/user-profile-form.tsx index 1114f6876..040335ba7 100644 --- a/app/frontend/src/javascript/components/user/user-profile-form.tsx +++ b/app/frontend/src/javascript/components/user/user-profile-form.tsx @@ -28,6 +28,8 @@ import TagAPI from '../../api/tag'; import { FormMultiSelect } from '../form/form-multi-select'; import ProfileCustomFieldAPI from '../../api/profile-custom-field'; import { ProfileCustomField } from '../../models/profile-custom-field'; +import { SettingName } from '../../models/setting'; +import SettingAPI from '../../api/setting'; declare const Application: IApplication; @@ -68,6 +70,7 @@ export const UserProfileForm: React.FC = ({ action, size, const [groups, setGroups] = useState([]); const [termsAndConditions, setTermsAndConditions] = useState(null); const [profileCustomFields, setProfileCustomFields] = useState([]); + const [requiredFieldsSettings, setRequiredFieldsSettings] = useState>(new Map()); useEffect(() => { AuthProviderAPI.active().then(data => { @@ -94,6 +97,9 @@ export const UserProfileForm: React.FC = ({ action, size, }); setValue('invoicing_profile_attributes.user_profile_custom_fields_attributes', userProfileCustomFields); }).catch(error => onError(error)); + SettingAPI.query([SettingName.PhoneRequired, SettingName.AddressRequired]) + .then(settings => setRequiredFieldsSettings(settings)) + .catch(error => onError(error)); }, []); /** @@ -202,6 +208,7 @@ export const UserProfileForm: React.FC = ({ action, size, register={register} label={t('app.shared.user_profile_form.date_of_birth')} disabled={isDisabled} + rules={{ required: true }} type="date" /> = ({ action, size, pattern: { value: phoneRegex, message: t('app.shared.user_profile_form.phone_number_invalid') - } + }, + required: requiredFieldsSettings.get(SettingName.PhoneRequired) === 'true' }} disabled={isDisabled} formState={formState} @@ -222,6 +230,7 @@ export const UserProfileForm: React.FC = ({ action, size,
diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index fea428780..0575162e7 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -53,7 +53,6 @@ @import "modules/payment/stripe/stripe-confirm"; @import "modules/payment/stripe/stripe-keys-form"; @import "modules/payment/stripe/stripe-modal"; -@import "modules/payment/stripe/stripe-update-card-modal"; @import "modules/plan-categories/delete-plan-category"; @import "modules/plan-categories/manage-plan-category"; @import "modules/plan-categories/plan-categories-list"; From 37bc2b3d434e6af599fc6145420b19f0f998ba0c Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 20 Jun 2022 10:49:44 +0200 Subject: [PATCH 045/172] (bug) the birthday was not shown in user edition form --- CHANGELOG.md | 1 + app/frontend/src/javascript/controllers/admin/members.js | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 217f591bd..69f0e46d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - During the setup, ask to set ALLOW_INSECURE_HTTP if DEFAULT_PROTOCOL was set to http - Fix a bug: the birthdate was not marked as required, in the profile edition form - Fix a bug: when the phone or the address were required, they were not marked as this, in the profile edition form +- Fix a bug: the birthday was not shown in user edition form - Fix a bug: unable to set the twitter input empty - Fix a bug: unable to edit an event - Fix a bug: times are not shown in admin/events monitoring page diff --git a/app/frontend/src/javascript/controllers/admin/members.js b/app/frontend/src/javascript/controllers/admin/members.js index 7647a9da5..237c8a97b 100644 --- a/app/frontend/src/javascript/controllers/admin/members.js +++ b/app/frontend/src/javascript/controllers/admin/members.js @@ -929,9 +929,6 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', const initialize = function () { CSRF.setMetaTags(); - // init the birthdate to JS object - $scope.user.statistic_profile_attributes.birthday = moment($scope.user.statistic_profile_attributes.birthday).toDate(); - // the user subscription if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) { $scope.subscription = $scope.user.subscription; From 203332934274d31bb060159627dad85a6f66ff0a Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 20 Jun 2022 15:35:38 +0200 Subject: [PATCH 046/172] linted all front code, excepted profile-completion/ and prrofile-custom-fields/ --- .eslintrc | 2 +- .../oauth2-data-mapping-form.tsx | 4 ++++ .../openid-connect-data-mapping-form.tsx | 4 ++++ .../openid-connect-form.tsx | 3 +++ .../components/base/text-editor/menu-bar.tsx | 4 ++-- .../components/events/event-card.tsx | 3 +++ .../components/group/change-group.tsx | 3 +++ .../components/machines/machine-card.tsx | 7 +++++-- .../components/machines/machines-filters.tsx | 3 +++ .../components/machines/reserve-button.tsx | 14 ++++++++------ .../payment-schedules-table.tsx | 3 +++ .../update-payment-mean-modal.tsx | 3 +++ .../components/payment/card-payment-modal.tsx | 10 ++++++---- .../local-payment/local-payment-modal.tsx | 10 ++++++---- .../payzen/payzen-card-update-modal.tsx | 3 +++ .../components/payment/payzen/payzen-form.tsx | 5 ++++- .../payment/payzen/payzen-keys-form.tsx | 8 +++++--- .../stripe/stripe-card-update-modal.tsx | 5 ++++- .../payment/stripe/stripe-keys-form.tsx | 8 +++++--- .../payment/stripe/stripe-modal.tsx | 2 +- .../components/payment/update-card-modal.tsx | 8 +++++--- .../plan-categories/delete-plan-category.tsx | 8 +++++--- .../plan-categories/manage-plan-category.tsx | 10 ++++++---- .../plan-categories/plan-category-form.tsx | 11 ++++++++--- .../components/plans/plans-filter.tsx | 3 +++ .../prepaid-packs/packs-summary.tsx | 19 +++++++++++++------ .../pricing/machines/delete-pack.tsx | 8 +++++--- .../profile-form-option.tsx | 7 +++++++ .../components/settings/boolean-setting.tsx | 8 ++++---- .../settings/user-validation-setting.tsx | 7 ++++--- .../components/socials/edit-socials.tsx | 10 ++++++++++ .../components/socials/fab-socials.tsx | 19 ++++++++++++++++--- .../components/user/user-validation.tsx | 7 ++++--- .../src/javascript/typings/date-iso.d.ts | 8 -------- app/frontend/src/stylesheets/application.scss | 3 +++ .../modules/base/fab-text-editor.scss | 2 +- .../payzen/payzen-card-update-modal.scss | 2 +- .../modules/payment/payzen/payzen-modal.scss | 2 +- .../stripe/stripe-card-update-modal.scss | 2 ++ .../modules/payment/stripe/stripe-modal.scss | 2 ++ .../plan-categories/manage-plan-category.scss | 6 ++++++ .../modules/prepaid-packs/packs-summary.scss | 7 +++++++ .../modules/settings/boolean-setting.scss | 18 ++++++++++++++++++ .../settings/user-validation-setting.scss | 8 ++++++++ .../modules/socials/fab-socials.scss | 7 +++++++ .../modules/user/user-validation.scss | 11 +++++++++++ package.json | 2 +- yarn.lock | 8 ++++---- 48 files changed, 238 insertions(+), 79 deletions(-) create mode 100644 app/frontend/src/stylesheets/modules/settings/boolean-setting.scss create mode 100644 app/frontend/src/stylesheets/modules/settings/user-validation-setting.scss create mode 100644 app/frontend/src/stylesheets/modules/socials/fab-socials.scss create mode 100644 app/frontend/src/stylesheets/modules/user/user-validation.scss diff --git a/.eslintrc b/.eslintrc index 089ae0a20..bd4107a0c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,7 +6,7 @@ ], "rules": { "semi": ["error", "always"], - "no-use-before-define": "off", + "no-use-before-define": "off" }, "globals": { "Application": true, diff --git a/app/frontend/src/javascript/components/authentication-provider/oauth2-data-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/oauth2-data-mapping-form.tsx index 24ede2224..5fd93d8d2 100644 --- a/app/frontend/src/javascript/components/authentication-provider/oauth2-data-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/oauth2-data-mapping-form.tsx @@ -13,6 +13,10 @@ interface Oauth2DataMappingFormProps { index: number, } +/** + * Partial form to set the data mapping for an OAuth 2.0 provider. + * The data mapping is the way to bind data from the authentication provider API to the Fab-manager's database + */ export const Oauth2DataMappingForm = ({ register, control, index }: Oauth2DataMappingFormProps) => { const { t } = useTranslation('admin'); diff --git a/app/frontend/src/javascript/components/authentication-provider/openid-connect-data-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/openid-connect-data-mapping-form.tsx index 8a8eb410d..fad276dff 100644 --- a/app/frontend/src/javascript/components/authentication-provider/openid-connect-data-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/openid-connect-data-mapping-form.tsx @@ -16,6 +16,10 @@ interface OpenidConnectDataMappingFormProps { index: number, } +/** + * Partial form to set the data mapping for an OpenID Connect provider. + * The data mapping is the way to bind data from the OIDC claims to the Fab-manager's database + */ export const OpenidConnectDataMappingForm = ({ register, setValue, currentFormValues, index }: OpenidConnectDataMappingFormProps) => { const { t } = useTranslation('admin'); diff --git a/app/frontend/src/javascript/components/authentication-provider/openid-connect-form.tsx b/app/frontend/src/javascript/components/authentication-provider/openid-connect-form.tsx index af8e29231..28532652d 100644 --- a/app/frontend/src/javascript/components/authentication-provider/openid-connect-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/openid-connect-form.tsx @@ -20,6 +20,9 @@ interface OpenidConnectFormProps { setValue: UseFormSetValue, } +/** + * Partial form to fill the OpenID Connect (OIDC) settings for a new/existing authentication provider. + */ export const OpenidConnectForm = ({ register, control, currentFormValues, formState, setValue }: OpenidConnectFormProps) => { const { t } = useTranslation('admin'); diff --git a/app/frontend/src/javascript/components/base/text-editor/menu-bar.tsx b/app/frontend/src/javascript/components/base/text-editor/menu-bar.tsx index b28c7e476..503b71323 100644 --- a/app/frontend/src/javascript/components/base/text-editor/menu-bar.tsx +++ b/app/frontend/src/javascript/components/base/text-editor/menu-bar.tsx @@ -168,7 +168,7 @@ export const MenuBar: React.FC = ({ editor, paragraphTools, video, > - + ) } - { (video || image) && } + { (video || image) && } { video && (<>
@@ -252,14 +252,14 @@ export const MenuBar: React.FC = ({ editor, paragraphTools, video, } { submenu === 'video' && (<> -
{t('app.shared.text_editor.add_video')}
+
{t('app.shared.text_editor.menu_bar.add_video')}
- + @@ -268,9 +268,9 @@ export const MenuBar: React.FC = ({ editor, paragraphTools, video, } { submenu === 'image' && (<> -
{t('app.shared.text_editor.add_image')}
+
{t('app.shared.text_editor.menu_bar.add_image')}
- + diff --git a/app/frontend/src/javascript/components/events/event-card.tsx b/app/frontend/src/javascript/components/events/event-card.tsx index fc834976a..af58fd386 100644 --- a/app/frontend/src/javascript/components/events/event-card.tsx +++ b/app/frontend/src/javascript/components/events/event-card.tsx @@ -43,8 +43,8 @@ export const EventCard: React.FC = ({ event, cardType }) => { startDate.getMonth() === endDate.getMonth() && startDate.getDate() === endDate.getDate(); return singleDayEvent - ? t('app.public.home.on_the_date', { DATE: FormatLib.date(event.start_date) }) - : t('app.public.home.from_date_to_date', { START: FormatLib.date(event.start_date), END: FormatLib.date(event.end_date) }); + ? t('app.public.event_card.on_the_date', { DATE: FormatLib.date(event.start_date) }) + : t('app.public.event_card.from_date_to_date', { START: FormatLib.date(event.start_date), END: FormatLib.date(event.end_date) }); }; /** @@ -52,8 +52,8 @@ export const EventCard: React.FC = ({ event, cardType }) => { */ const formatTime = (): string => { return event.all_day - ? t('app.public.home.all_day') - : t('app.public.home.from_time_to_time', { START: FormatLib.time(event.start_date), END: FormatLib.time(event.end_date) }); + ? t('app.public.event_card.all_day') + : t('app.public.event_card.from_time_to_time', { START: FormatLib.time(event.start_date), END: FormatLib.time(event.end_date) }); }; return ( @@ -112,14 +112,14 @@ export const EventCard: React.FC = ({ event, cardType }) => { }
- {event.nb_free_places > 0 &&
{t('app.public.home.still_available') + event.nb_free_places}
} - {event.nb_total_places > 0 && event.nb_free_places <= 0 &&
{t('app.public.home.event_full')}
} - {!event.nb_total_places &&
{t('app.public.home.without_reservation')}
} + {event.nb_free_places > 0 &&
{t('app.public.event_card.still_available') + event.nb_free_places}
} + {event.nb_total_places > 0 && event.nb_free_places <= 0 &&
{t('app.public.event_card.event_full')}
} + {!event.nb_total_places &&
{t('app.public.event_card.without_reservation')}
}
- {event.amount === 0 &&
{t('app.public.home.free_admission')}
} - {event.amount > 0 &&
{t('app.public.home.full_price') + FormatLib.price(event.amount)}
} + {event.amount === 0 &&
{t('app.public.event_card.free_admission')}
} + {event.amount > 0 &&
{t('app.public.event_card.full_price') + FormatLib.price(event.amount)}
}
diff --git a/app/frontend/src/javascript/components/form/README.md b/app/frontend/src/javascript/components/form/README.md index 831788909..a4e5d0124 100644 --- a/app/frontend/src/javascript/components/form/README.md +++ b/app/frontend/src/javascript/components/form/README.md @@ -2,7 +2,7 @@ This directory is holding the inputs components for usage within forms controlled by [React-hook-form](https://react-hook-form.com/). -All these components must have [props](https://reactjs.org/docs/components-and-props.html) that inherits from [FormComponent](../models/form-component.ts) +All these components must have [props](https://reactjs.org/docs/components-and-props.html) that inherit from [FormComponent](../models/form-component.ts) or from [FormControlledComponent](../models/form-component.ts). Please look at the existing components for examples. diff --git a/app/frontend/src/javascript/components/payment-schedule/payment-schedule-summary.tsx b/app/frontend/src/javascript/components/payment-schedule/payment-schedule-summary.tsx index 342d425c5..e888eb371 100644 --- a/app/frontend/src/javascript/components/payment-schedule/payment-schedule-summary.tsx +++ b/app/frontend/src/javascript/components/payment-schedule/payment-schedule-summary.tsx @@ -40,29 +40,29 @@ export const PaymentScheduleSummary: React.FC = ({ return (
-

{ t('app.shared.cart.your_payment_schedule') }

+

{ t('app.shared.payment_schedule_summary.your_payment_schedule') }

{hasEqualDeadlines() &&
  • - {t('app.shared.cart.NUMBER_monthly_payment_of_AMOUNT', { NUMBER: schedule.items.length, AMOUNT: FormatLib.price(schedule.items[0].amount) })} + {t('app.shared.payment_schedule_summary.NUMBER_monthly_payment_of_AMOUNT', { NUMBER: schedule.items.length, AMOUNT: FormatLib.price(schedule.items[0].amount) })} - {t('app.shared.cart.first_debit')} + {t('app.shared.payment_schedule_summary.first_debit')}
} {!hasEqualDeadlines() &&
  • - {t('app.shared.cart.monthly_payment_NUMBER', { NUMBER: 1 })} + {t('app.shared.payment_schedule_summary.monthly_payment_NUMBER', { NUMBER: 1 })} {FormatLib.price(schedule.items[0].amount)} - {t('app.shared.cart.debit')} + {t('app.shared.payment_schedule_summary.debit')}
  • - {t('app.shared.cart.NUMBER_monthly_payment_of_AMOUNT', { NUMBER: schedule.items.length - 1, AMOUNT: FormatLib.price(schedule.items[1].amount) })} + {t('app.shared.payment_schedule_summary.NUMBER_monthly_payment_of_AMOUNT', { NUMBER: schedule.items.length - 1, AMOUNT: FormatLib.price(schedule.items[1].amount) })}
} - - + +
    {schedule.items.map(item => (
  • diff --git a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-dashboard.tsx b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-dashboard.tsx index 35ab00f03..440e5ec55 100644 --- a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-dashboard.tsx +++ b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-dashboard.tsx @@ -66,7 +66,7 @@ export const PaymentSchedulesDashboard: React.FC * after a successful card update, provide a success message to the end-user */ const handleCardUpdateSuccess = (): void => { - onCardUpdateSuccess(t('app.logged.dashboard.payment_schedules.card_updated_success')); + onCardUpdateSuccess(t('app.logged.dashboard.payment_schedules_dashboard.card_updated_success')); }; /** @@ -85,7 +85,7 @@ export const PaymentSchedulesDashboard: React.FC return (
    - {!hasSchedules() &&
    {t('app.logged.dashboard.payment_schedules.no_payment_schedules')}
    } + {!hasSchedules() &&
    {t('app.logged.dashboard.payment_schedules_dashboard.no_payment_schedules')}
    } {hasSchedules() &&
    operator={currentUser} onError={onError} onCardUpdateSuccess={handleCardUpdateSuccess} /> - {hasMoreSchedules() && {t('app.logged.dashboard.payment_schedules.load_more')}} + {hasMoreSchedules() && {t('app.logged.dashboard.payment_schedules_dashboard.load_more')}}
    }
    ); diff --git a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-list.tsx b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-list.tsx index 231be3750..420ef1d53 100644 --- a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-list.tsx +++ b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-list.tsx @@ -93,19 +93,19 @@ export const PaymentSchedulesList: React.FC = ({ curr * after a successful card update, provide a success message to the operator */ const handleCardUpdateSuccess = (): void => { - onCardUpdateSuccess(t('app.admin.invoices.payment_schedules.card_updated_success')); + onCardUpdateSuccess(t('app.admin.invoices.payment_schedules_list.card_updated_success')); }; return (

    - {t('app.admin.invoices.payment_schedules.filter_schedules')} + {t('app.admin.invoices.payment_schedules_list.filter_schedules')}

    - {!hasSchedules() &&
    {t('app.admin.invoices.payment_schedules.no_payment_schedules')}
    } + {!hasSchedules() &&
    {t('app.admin.invoices.payment_schedules_list.no_payment_schedules')}
    } {hasSchedules() &&
    = ({ curr operator={currentUser} onError={onError} onCardUpdateSuccess={handleCardUpdateSuccess} /> - {hasMoreSchedules() && {t('app.admin.invoices.payment_schedules.load_more')}} + {hasMoreSchedules() && {t('app.admin.invoices.payment_schedules_list.load_more')}}
    }
    ); diff --git a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx index e1e259f12..911913036 100644 --- a/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx +++ b/app/frontend/src/javascript/components/payment-schedule/payment-schedules-table.tsx @@ -98,7 +98,7 @@ const PaymentSchedulesTable: React.FC = ({ paymentSc // eslint-disable-next-line fabmanager/component-class-named-as-component - {t('app.shared.schedules_table.download')} + {t('app.shared.payment_schedules_table.download')} ); }; @@ -107,7 +107,7 @@ const PaymentSchedulesTable: React.FC = ({ paymentSc * Return the human-readable string for the status of the provided deadline. */ const formatState = (item: PaymentScheduleItem, schedule: PaymentSchedule): JSX.Element => { - let res = t(`app.shared.schedules_table.state_${item.state}${item.state === 'pending' ? '_' + schedule.payment_method : ''}`); + let res = t(`app.shared.payment_schedules_table.state_${item.state}${item.state === 'pending' ? '_' + schedule.payment_method : ''}`); if (item.state === PaymentScheduleItemState.Paid) { const key = `app.shared.schedules_table.method_${item.payment_method}`; res += ` (${t(key)})`; @@ -132,10 +132,10 @@ const PaymentSchedulesTable: React.FC = ({ paymentSc
- {t('app.shared.schedules_table.schedule_num')}{t('app.shared.schedules_table.date')}{t('app.shared.schedules_table.price')}{t('app.shared.schedules_table.customer')}{t('app.shared.payment_schedules_table.schedule_num')}{t('app.shared.payment_schedules_table.date')}{t('app.shared.payment_schedules_table.price')}{t('app.shared.payment_schedules_table.customer')}
- - - + + + diff --git a/app/frontend/src/javascript/components/payment-schedule/select-schedule.tsx b/app/frontend/src/javascript/components/payment-schedule/select-schedule.tsx index 03419ab61..a056d5990 100644 --- a/app/frontend/src/javascript/components/payment-schedule/select-schedule.tsx +++ b/app/frontend/src/javascript/components/payment-schedule/select-schedule.tsx @@ -25,7 +25,7 @@ export const SelectSchedule: React.FC = ({ show, selected, return (
{show &&
- +
}
diff --git a/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx b/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx index f031ecca7..3fe99104f 100644 --- a/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx +++ b/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx @@ -220,13 +220,13 @@ export const AbstractPaymentModal: React.FC = ({ isOp {errors} } {hasPaymentScheduleInfo() &&
- +
} {hasCgv() &&
-
} @@ -235,8 +235,8 @@ export const AbstractPaymentModal: React.FC = ({ isOp disabled={!canSubmit()} form={formId} className="validate-btn"> - {remainingPrice > 0 && t('app.shared.payment.confirm_payment_of_', { AMOUNT: FormatLib.price(remainingPrice) })} - {remainingPrice === 0 && t('app.shared.payment.validate')} + {remainingPrice > 0 && t('app.shared.abstract_payment_modal.confirm_payment_of_', { AMOUNT: FormatLib.price(remainingPrice) })} + {remainingPrice === 0 && t('app.shared.abstract_payment_modal.validate')} } {submitState &&
diff --git a/app/frontend/src/javascript/components/payment/card-payment-modal.tsx b/app/frontend/src/javascript/components/payment/card-payment-modal.tsx index cb6b5847b..971f39278 100644 --- a/app/frontend/src/javascript/components/payment/card-payment-modal.tsx +++ b/app/frontend/src/javascript/components/payment/card-payment-modal.tsx @@ -80,10 +80,10 @@ const CardPaymentModal: React.FC = ({ isOpen, toggleModal return renderPayZenModal(); case null: case undefined: - onError(t('app.shared.payment_modal.online_payment_disabled')); + onError(t('app.shared.card_payment_modal.online_payment_disabled')); return
; default: - onError(t('app.shared.payment_modal.unexpected_error')); + onError(t('app.shared.card_payment_modal.unexpected_error')); console.error(`[PaymentModal] Unimplemented gateway: ${gateway.value}`); return
; } diff --git a/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx b/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx index 80b36ec6c..c127a3ec5 100644 --- a/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx +++ b/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx @@ -54,7 +54,7 @@ export const LocalPaymentForm: React.FC = ({ onSubmit, onSucce const methodToOption = (value: scheduleMethod): selectOption => { if (!value) return { value, label: '' }; - return { value, label: t(`app.admin.local_payment.method_${value}`) }; + return { value, label: t(`app.admin.local_payment_form.method_${value}`) }; }; /** @@ -77,7 +77,7 @@ export const LocalPaymentForm: React.FC = ({ onSubmit, onSucce try { const online = await SettingAPI.get(SettingName.OnlinePaymentModule); if (online.value !== 'true') { - return onError(t('app.admin.local_payment.online_payment_disabled')); + return onError(t('app.admin.local_payment_form.online_payment_disabled')); } return toggleOnlinePaymentModal(); } catch (e) { @@ -119,19 +119,19 @@ export const LocalPaymentForm: React.FC = ({ onSubmit, onSucce return ( - {!paymentSchedule && !isFreeOfCharge() &&

{t('app.admin.local_payment.about_to_cash')}

} - {!paymentSchedule && isFreeOfCharge() &&

{t('app.admin.local_payment.about_to_confirm', { ITEM: mainItemType() })}

} + {!paymentSchedule && !isFreeOfCharge() &&

{t('app.admin.local_payment_form.about_to_cash')}

} + {!paymentSchedule && isFreeOfCharge() &&

{t('app.admin.local_payment_form.about_to_confirm', { ITEM: mainItemType() })}

} {paymentSchedule &&
- - - {method === 'card' &&

{t('app.admin.local_payment.card_collection_info')}

} - {method === 'check' &&

{t('app.admin.local_payment.check_collection_info', { DEADLINES: paymentSchedule.items.length })}

} + {method === 'card' &&

{t('app.admin.local_payment_form.card_collection_info')}

} + {method === 'check' &&

{t('app.admin.local_payment_form.check_collection_info', { DEADLINES: paymentSchedule.items.length })}

} {method === 'transfer' && }
diff --git a/app/frontend/src/javascript/components/payment/local-payment/local-payment-modal.tsx b/app/frontend/src/javascript/components/payment/local-payment/local-payment-modal.tsx index 242cc576b..9d0ee2032 100644 --- a/app/frontend/src/javascript/components/payment/local-payment/local-payment-modal.tsx +++ b/app/frontend/src/javascript/components/payment/local-payment/local-payment-modal.tsx @@ -76,7 +76,7 @@ const LocalPaymentModal: React.FC = ({ isOpen, toggleMod isOpen={isOpen} toggleModal={toggleModal} logoFooter={logoFooter()} - title={isFreeOfCharge() ? t('app.admin.local_payment.validate_cart') : t('app.admin.local_payment.offline_payment')} + title={isFreeOfCharge() ? t('app.admin.local_payment_modal.validate_cart') : t('app.admin.local_payment_modal.offline_payment')} formId="local-payment-form" formClassName="local-payment-form" currentUser={currentUser} diff --git a/app/frontend/src/javascript/components/payment/payzen/payzen-keys-form.tsx b/app/frontend/src/javascript/components/payment/payzen/payzen-keys-form.tsx index 7d3d9613c..ccb271223 100644 --- a/app/frontend/src/javascript/components/payment/payzen/payzen-keys-form.tsx +++ b/app/frontend/src/javascript/components/payment/payzen/payzen-keys-form.tsx @@ -139,13 +139,13 @@ const PayzenKeysForm: React.FC = ({ onValidKeys, onInvalidK return (
- +
- {t('app.admin.invoices.payment.client_keys')} + {t('app.admin.invoices.payzen_keys_form.client_keys')}
- + } defaultValue={settings.get(SettingName.PayZenPublicKey)} @@ -158,11 +158,11 @@ const PayzenKeysForm: React.FC = ({ onValidKeys, onInvalidK
- {t('app.admin.invoices.payment.api_keys')} + {t('app.admin.invoices.payzen_keys_form.api_keys')} {hasApiAddOn() && {restApiAddOn}}
- + } @@ -172,7 +172,7 @@ const PayzenKeysForm: React.FC = ({ onValidKeys, onInvalidK required />
- + } defaultValue={settings.get(SettingName.PayZenPassword)} @@ -181,7 +181,7 @@ const PayzenKeysForm: React.FC = ({ onValidKeys, onInvalidK required />
- + } @@ -191,7 +191,7 @@ const PayzenKeysForm: React.FC = ({ onValidKeys, onInvalidK required />
- + } defaultValue={settings.get(SettingName.PayZenHmacKey)} diff --git a/app/frontend/src/javascript/components/payment/stripe/stripe-confirm-modal.tsx b/app/frontend/src/javascript/components/payment/stripe/stripe-confirm-modal.tsx index 32aa0991c..57d4f3c61 100644 --- a/app/frontend/src/javascript/components/payment/stripe/stripe-confirm-modal.tsx +++ b/app/frontend/src/javascript/components/payment/stripe/stripe-confirm-modal.tsx @@ -43,11 +43,11 @@ export const StripeConfirmModal: React.FC = ({ isOpen, }; return ( - {item && } diff --git a/app/frontend/src/javascript/components/payment/stripe/stripe-form.tsx b/app/frontend/src/javascript/components/payment/stripe/stripe-form.tsx index b3436df1e..0343e4bb7 100644 --- a/app/frontend/src/javascript/components/payment/stripe/stripe-form.tsx +++ b/app/frontend/src/javascript/components/payment/stripe/stripe-form.tsx @@ -66,7 +66,7 @@ export const StripeForm: React.FC = ({ onSubmit, onSuccess, on if (response.error.statusText) { onError(response.error.statusText); } else { - onError(`${t('app.shared.messages.payment_card_error')} ${response.error}`); + onError(`${t('app.shared.stripe_form.payment_card_error')} ${response.error}`); } } else if ('requires_action' in response) { if (response.type === 'payment') { diff --git a/app/frontend/src/javascript/components/payment/stripe/stripe-keys-form.tsx b/app/frontend/src/javascript/components/payment/stripe/stripe-keys-form.tsx index 42a6c562b..ee01742ce 100644 --- a/app/frontend/src/javascript/components/payment/stripe/stripe-keys-form.tsx +++ b/app/frontend/src/javascript/components/payment/stripe/stripe-keys-form.tsx @@ -123,11 +123,11 @@ const StripeKeysForm: React.FC = ({ onValidKeys, onInvalidK return (
- +
- + } defaultValue={publicKey} @@ -138,7 +138,7 @@ const StripeKeysForm: React.FC = ({ onValidKeys, onInvalidK required />
- + } defaultValue={secretKey} diff --git a/app/frontend/src/javascript/components/plan-categories/delete-plan-category.tsx b/app/frontend/src/javascript/components/plan-categories/delete-plan-category.tsx index 2ab1d4500..cba8a1afc 100644 --- a/app/frontend/src/javascript/components/plan-categories/delete-plan-category.tsx +++ b/app/frontend/src/javascript/components/plan-categories/delete-plan-category.tsx @@ -34,9 +34,9 @@ const DeletePlanCategory: React.FC = ({ onSuccess, onEr */ const onDeleteConfirmed = (): void => { PlanCategoryAPI.destroy(category.id).then(() => { - onSuccess(t('app.admin.manage_plan_category.delete_category.success')); + onSuccess(t('app.admin.delete_plan_category.success')); }).catch((error) => { - onError(t('app.admin.manage_plan_category.delete_category.error') + error); + onError(t('app.admin.delete_plan_category.error') + error); }); toggleDeletionModal(); }; @@ -44,13 +44,13 @@ const DeletePlanCategory: React.FC = ({ onSuccess, onEr return (
} onClick={toggleDeletionModal} /> - - {t('app.admin.manage_plan_category.delete_category.confirm')} + {t('app.admin.delete_plan_category.confirm')}
); diff --git a/app/frontend/src/javascript/components/plan-categories/manage-plan-category.tsx b/app/frontend/src/javascript/components/plan-categories/manage-plan-category.tsx index d347c5da3..1eca41b38 100644 --- a/app/frontend/src/javascript/components/plan-categories/manage-plan-category.tsx +++ b/app/frontend/src/javascript/components/plan-categories/manage-plan-category.tsx @@ -63,7 +63,7 @@ const ManagePlanCategory: React.FC = ({ category, actio icon={} className="create-button" onClick={toggleModal}> - {t('app.admin.manage_plan_category.create_category.title')} + {t('app.admin.manage_plan_category.create')} ); case 'update': @@ -77,7 +77,7 @@ const ManagePlanCategory: React.FC = ({ category, actio return (
{ toggleBtn() } - = ({ action, category, o switch (action) { case 'create': PlanCategoryAPI.create(data).then(() => { - onSuccess(t('app.admin.manage_plan_category.create_category.success')); + onSuccess(t('app.admin.plan_category_form.create.success')); }).catch((error) => { - onError(t('app.admin.manage_plan_category.create_category.error') + error); + onError(t('app.admin.plan_category_form.create.error') + error); }); break; case 'update': PlanCategoryAPI.update(data).then(() => { - onSuccess(t('app.admin.manage_plan_category.update_category.success')); + onSuccess(t('app.admin.plan_category_form.update.success')); }).catch((error) => { - onError(t('app.admin.manage_plan_category.update_category.error') + error); + onError(t('app.admin.plan_category_form.update.error') + error); }); break; } @@ -48,15 +48,15 @@ const PlanCategoryForm: React.FC = ({ action, category, o return ( - + - + - + - {t('app.admin.manage_plan_category.info')} + {t('app.admin.plan_category_form.info')} - {t(`app.admin.manage_plan_category.${action}_category.cta`)} + {t(`app.admin.plan_category_form.${action}.cta`)} ); }; diff --git a/app/frontend/src/javascript/components/plans/plan-card.tsx b/app/frontend/src/javascript/components/plans/plan-card.tsx index 5dba414c3..eff5127cc 100644 --- a/app/frontend/src/javascript/components/plans/plan-card.tsx +++ b/app/frontend/src/javascript/components/plans/plan-card.tsx @@ -105,7 +105,7 @@ const PlanCard: React.FC = ({ plan, userId, subscribedPlanId, ope
{canBeScheduled() &&
-
{t('app.public.plans.AMOUNT_per_month', { AMOUNT: monthlyAmount() })}
+
{t('app.public.plan_card.AMOUNT_per_month', { AMOUNT: monthlyAmount() })}
{duration()}
} @@ -118,25 +118,25 @@ const PlanCard: React.FC = ({ plan, userId, subscribedPlanId, ope
{hasDescription() &&
} - {hasAttachment() && { t('app.public.plans.more_information') }} + {hasAttachment() && { t('app.public.plan_card.more_information') }} {mustLogin() &&
- +
} {canSubscribeForMe() &&
{!hasSubscribedToThisPlan() && } {hasSubscribedToThisPlan() && }
} {canSubscribeForOther() &&
}
diff --git a/app/frontend/src/javascript/components/pricing/spaces/extended-price-form.tsx b/app/frontend/src/javascript/components/pricing/spaces/extended-price-form.tsx index dbc8f4790..b9a2e0251 100644 --- a/app/frontend/src/javascript/components/pricing/spaces/extended-price-form.tsx +++ b/app/frontend/src/javascript/components/pricing/spaces/extended-price-form.tsx @@ -14,7 +14,7 @@ interface ExtendedPriceFormProps { } /** - * A form component to create/edit a extended price. + * A form component to create/edit an extended price. * The form validation must be created elsewhere, using the attribute form={formId}. */ export const ExtendedPriceForm: React.FC = ({ formId, onSubmit, price }) => { diff --git a/app/frontend/src/javascript/components/profile-completion/profile-form-option.tsx b/app/frontend/src/javascript/components/profile-completion/profile-form-option.tsx index 54260c948..ee5a31494 100644 --- a/app/frontend/src/javascript/components/profile-completion/profile-form-option.tsx +++ b/app/frontend/src/javascript/components/profile-completion/profile-form-option.tsx @@ -21,7 +21,7 @@ interface ProfileFormOptionProps { } /** - * After first logged-in from a SSO, the user has two options: + * After first logged-in from an SSO, the user has two options: * - complete his profile (*) ; * - bind his profile to his existing account ; * (*) This component handle the first case. diff --git a/app/frontend/src/stylesheets/modules/base/labelled-input.scss b/app/frontend/src/stylesheets/modules/base/labelled-input.scss index 3ccfb595e..1bd866c82 100644 --- a/app/frontend/src/stylesheets/modules/base/labelled-input.scss +++ b/app/frontend/src/stylesheets/modules/base/labelled-input.scss @@ -4,7 +4,7 @@ border-collapse: separate; box-sizing: border-box; - label.label { + label { padding: 6px 12px; font-size: 16px; font-weight: 400; @@ -22,7 +22,7 @@ border-right: 0; } - input.input { + input { padding: 6px 12px; height: 38px; font-size: 16px; diff --git a/app/frontend/templates/admin/invoices/index.html b/app/frontend/templates/admin/invoices/index.html index 2b30e830a..12032a167 100644 --- a/app/frontend/templates/admin/invoices/index.html +++ b/app/frontend/templates/admin/invoices/index.html @@ -34,7 +34,7 @@ - + @@ -58,7 +58,7 @@ - + diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 1bb659ced..8d932aa1e 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -447,26 +447,29 @@ en: description: "Description" significance: "Significance" manage_plan_category: + create: "New category" + update: "Edit the category" + plan_category_form: name: "Name" description: "Description" significance: "Significance" info: "Categories will be shown ordered by signifiance. The higher you set the significance, the first the category will be shown." - create_category: + create: title: "New category" cta: "Create the category" success: "The new category was successfully created" error: "Unable to create the category: " - update_category: + update: title: "Edit the category" cta: "Validate" success: "The category was successfully updated" error: "Unable to update the category: " - delete_category: - title: "Delete a category" - confirm: "Are you sure you want to delete this category? If you do, the plans associated with this category won't be sorted anymore." - cta: "Delete" - success: "The category was successfully deleted" - error: "Unable to delete the category: " + delete_plan_category: + title: "Delete a category" + confirm: "Are you sure you want to delete this category? If you do, the plans associated with this category won't be sorted anymore." + cta: "Delete" + success: "The category was successfully deleted" + error: "Unable to delete the category: " #ajouter un code promotionnel coupons_new: add_a_coupon: "Add a coupon" @@ -513,7 +516,7 @@ en: credit_note: "Credit note" display_more_invoices: "Display more invoices..." no_invoices_for_now: "No invoices for now." - payment_schedules_list: "Payment schedules" + payment_schedules: "Payment schedules" invoicing_settings: "Invoicing settings" warning_invoices_disabled: "Warning: invoices are not enabled. No invoices will be generated by Fab-manager. Nevertheless, you must correctly fill the information below, especially VAT." change_logo: "Change logo" @@ -748,20 +751,27 @@ en: end_date: "End date" vat_rate: "VAT rate" amount: "Total amount" + payzen_keys_form: + payzen_keys_info_html: "

To be able to collect online payments, you must configure the PayZen identifiers and keys.

Retrieve them from your merchant back office.

" + client_keys: "Client key" + payzen_keys: "PayZen keys" + payzen_username: "Username" + payzen_password: "Password" + payzen_endpoint: "REST API server name" + payzen_hmac: "HMAC-SHA-256 key" + stripe_keys_form: + stripe_keys_info_html: "

To be able to collect online payments, you must configure the Stripe API keys.

Retrieve them from your dashboard.

Updating these keys will trigger a synchronization of all users on Stripe, this may take some time. You'll receive a notification when it's done.

" + public_key: "Public key" + secret_key: "Secret key" payment: payment_settings: "Payment settings" online_payment: "Online payment" online_payment_info_html: "You can enable your members to book directly online, paying by card. Alternatively, you can restrict the booking and payment processes for administrators and managers." enable_online_payment: "Enable online payment" stripe_keys: "Stripe keys" - stripe_keys_info_html: "

To be able to collect online payments, you must configure the Stripe API keys.

Retrieve them from your dashboard.

Updating these keys will trigger a synchronization of all users on Stripe, this may take some time. You'll receive a notification when it's done.

" - public_key: "Public key" - secret_key: "Secret key" error_check_keys: "Error: please check your Stripe keys." stripe_keys_saved: "Stripe keys successfully saved." error_saving_stripe_keys: "Unable to save the Stripe keys. Please try again later." - payzen_keys_info_html: "

To be able to collect online payments, you must configure the PayZen identifiers and keys.

Retrieve them from your merchant back office.

" - client_keys: "Client key" api_keys: "API keys" edit_keys: "Edit keys" currency: "Currency" @@ -770,11 +780,6 @@ en: stripe_currency: "Stripe currency" gateway_configuration_error: "An error occurred while configuring the payment gateway: " payzen: - payzen_keys: "PayZen keys" - payzen_username: "Username" - payzen_password: "Password" - payzen_endpoint: "REST API server name" - payzen_hmac: "HMAC-SHA-256 key" payzen_public_key: "Client public key" currency: "Currency" payzen_currency: "PayZen currency" @@ -791,7 +796,7 @@ en: stripe: "Stripe" payzen: "PayZen" confirm_button: "Validate the gateway" - payment_schedules: + payment_schedules_list: filter_schedules: "Filter schedules" no_payment_schedules: "No payment schedules to display" load_more: "Load more" @@ -1646,9 +1651,7 @@ en: report_will_be_destroyed: "Once the report has been processed, it will be deleted. This can't be undone, continue?" report_removed: "The report has been deleted" failed_to_remove: "An error occurred, unable to delete the report" - local_payment: - validate_cart: "Validate my cart" - offline_payment: "Payment on site" + local_payment_form: about_to_cash: "You're about to confirm the cashing by an external payment mean. Please do not click on the button below until you have fully cashed the requested payment." about_to_confirm: "You're about to confirm your {ITEM, select, subscription{subscription} other{reservation}}." payment_method: "Payment method" @@ -1659,6 +1662,9 @@ en: check_collection_info: "By validating, you confirm that you have {DEADLINES} checks, allowing you to collect all the monthly payments." transfer_collection_info: "

By validating, you confirm that you set up {DEADLINES} bank direct debits, allowing you to collect all the monthly payments.

Please note: the bank transfers are not automatically handled by Fab-manager.

" online_payment_disabled: "Online payment is not available. You cannot collect this payment schedule by online card." + local_payment_modal: + validate_cart: "Validate my cart" + offline_payment: "Payment on site" check_list_setting: save: 'Save' customization_of_SETTING_successfully_saved: "Customization of the {SETTING} successfully saved." diff --git a/config/locales/app.logged.en.yml b/config/locales/app.logged.en.yml index fc1335864..3640529f0 100644 --- a/config/locales/app.logged.en.yml +++ b/config/locales/app.logged.en.yml @@ -128,7 +128,7 @@ en: download_the_invoice: "Download the invoice" download_the_credit_note: "Download the refund invoice" no_invoices_for_now: "No invoices for now." - payment_schedules: + payment_schedules_dashboard: no_payment_schedules: "No payment schedules to display" load_more: "Load more" card_updated_success: "Your card was successfully updated" diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index 820d97c21..00f7bdd4a 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -150,15 +150,16 @@ en: #next events summary on the home page fablab_s_next_events: "Fablab's next events" every_events: "Every events" - from_date_to_date: "From {START} to {END}" + event_card: on_the_date: "On the {DATE}" + from_date_to_date: "From {START} to {END}" from_time_to_time: "From {START} to {END}" + all_day: "All day" + still_available: "Available place(s): " + event_full: "Event full" without_reservation: "Without reservation" free_admission: "Free admission" full_price: "Full price: " - event_full: "Event full" - still_available: "Available place(s): " - all_day: "All day" #projects gallery projects_list: the_fablab_projects: "The Fab Lab projects" @@ -244,16 +245,17 @@ en: unauthorized_operation: "Unauthorized operation" confirmation_required: "Confirmation required" the_training_cant_be_deleted_because_it_is_already_reserved_by_some_users: "The training can't be deleted because it's already reserved by some users." + plan_card: + AMOUNT_per_month: "{AMOUNT} / month" + i_subscribe_online: "I subscribe online" + more_information: "More information" + i_choose_that_plan: "I choose that plan" + i_already_subscribed: "I already subscribed" #summary of the subscriptions plans: subscriptions: "Subscriptions" - i_choose_that_plan: "I choose that plan" - i_subscribe_online: "I subscribe online" - i_already_subscribed: "I already subscribed" - more_information: "More information" your_subscription_expires_on_the_DATE: "Your subscription expires on the {DATE}" no_plans: "No plans are available for your group" - AMOUNT_per_month: "{AMOUNT} / month" my_group: "My group" his_group: "User's group" he_wants_to_change_group: "Change group" diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index 24a96fa18..d28068c56 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -21,7 +21,6 @@ en: messages: you_will_lose_any_unsaved_modification_if_you_quit_this_page: "You will lose any unsaved modification if you quit this page" you_will_lose_any_unsaved_modification_if_you_reload_this_page: "You will lose any unsaved modification if you reload this page" - payment_card_error: "A problem has occurred with your credit card:" payment_card_declined: "Your card was declined." change_group: title: "{OPERATOR, select, self{My group} other{User's group}}" @@ -29,15 +28,22 @@ en: cancel: "Cancel" validate: "Validate group change" success: "Group successfully changed" + stripe_form: + payment_card_error: "A problem occurred with your payment card:" #text editor text_editor: - text_placeholder: "Type something…" - link_placeholder: "Paste link…" - url_placeholder: "Paste url…" - new_tab: "Open in a new tab" - add_link: "Insert a link" - add_video: "Embed a video" - add_image: "Insert an image" + fab_text_editor: + text_placeholder: "Type something…" + menu_bar: + link_placeholder: "Paste link…" + url_placeholder: "Paste url…" + new_tab: "Open in a new tab" + add_link: "Insert a link" + add_video: "Embed a video" + add_image: "Insert an image" + #modal dialog + fab_modal: + close: "Close" fab_socials: follow_us: "Follow us" networks_update_success: "Social networks update successful" @@ -161,7 +167,7 @@ en: start_typing: "Start typing..." member_not_validated: "Attention:
The member has not validated." #payment modal - payment: + abstract_payment_modal: online_payment: "Online payment" i_have_read_and_accept_: "I have read, and accept " _the_general_terms_and_conditions: "the general terms and conditions." @@ -405,6 +411,15 @@ en: default_places: "Default maximum tickets" default_places_is_required: "Default maximum tickets is required." disable_space: "Disable space" + payment_schedule_summary: + your_payment_schedule: "Your payment schedule" + NUMBER_monthly_payment_of_AMOUNT: "{NUMBER} monthly {NUMBER, plural, =1{payment} other{payments}} of {AMOUNT}" + first_debit: "First debit on the day of the order." + monthly_payment_NUMBER: "{NUMBER}{NUMBER, plural, =1{st} =2{nd} =3{rd} other{th}} monthly payment: " + debit: "Debit on the day of the order." + view_full_schedule: "View the complete payment schedule" + select_schedule: + monthly_payment: "Monthly payment" #shopping cart module for reservations cart: summary: "Summary" @@ -422,13 +437,6 @@ en: cost_of_the_subscription: "Cost of the subscription" subscription_price: "Subscription price" you_ve_just_selected_a_subscription_html: "You've just selected a subscription:" - monthly_payment: "Monthly payment" - your_payment_schedule: "Your payment schedule" - monthly_payment_NUMBER: "{NUMBER}{NUMBER, plural, =1{st} =2{nd} =3{rd} other{th}} monthly payment: " - NUMBER_monthly_payment_of_AMOUNT: "{NUMBER} monthly {NUMBER, plural, =1{payment} other{payments}} of {AMOUNT}" - first_debit: "First debit on the day of the order." - debit: "Debit on the day of the order." - view_full_schedule: "View the complete payment schedule" confirm_and_pay: "Confirm and pay" you_have_settled_the_following_TYPE: "You have settled the following {TYPE, select, Machine{machine slots} Training{training} other{elements}}:" you_have_settled_a_: "You have settled a" @@ -483,12 +491,15 @@ en: what_to_do: "What do you want to do?" tour: "Start the feature tour" guide: "Open the user's manual" + stripe_confirm_modal: + resolve_action: "Resolve the action" + ok_button: "OK" # 2nd factor authentication for card payments stripe_confirm: pending: "Pending for action..." success: "Thank you, your card setup is complete. The payment will be proceeded shortly." # the summary table of all payment schedules - schedules_table: + payment_schedules_table: schedule_num: "Schedule #" date: "Date" price: "Price" @@ -524,7 +535,7 @@ en: confirm_bank_transfer: "Confirm the bank transfer" confirm_bank_transfer_body: "You must confirm the receipt of {AMOUNT} for the deadline of {DATE}. By confirming the bank transfer, an invoice will be generated for this due date." confirm_cancel_subscription: "You're about to cancel this payment schedule and the related subscription. Are you sure?" - payment_modal: + card_payment_modal: online_payment_disabled: "Online payment is not available. Please contact the FabLab's reception directly." unexpected_error: "An error occurred. Please report this issue to the Fab-Manager's team." update_card_modal: diff --git a/package.json b/package.json index 9bfb02857..245bea3c0 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@typescript-eslint/parser": "^5.17.0", "eslint": "~8.12.0", "eslint-config-standard": "~17.0.0-1", - "eslint-plugin-fabmanager": "^0.5.3", + "eslint-plugin-fabmanager": "^0.5.5", "eslint-plugin-html-erb": "^1.0.1", "eslint-plugin-import": "~2.25.4", "eslint-plugin-n": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index 385970eea..a5c08fa21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4082,10 +4082,10 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-fabmanager@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.5.3.tgz#28576314182943c999c4a42d4d9ff8a1a9eefa82" - integrity sha512-lubHoF0oSiCn1MaByyB3N7YS1QU0t+mxirDILlkPA/9m/puSF32AvMGd2ztKzYdYfPIOQlgHIO+dUEYBTRTc0A== +eslint-plugin-fabmanager@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-fabmanager/-/eslint-plugin-fabmanager-0.5.5.tgz#675ab9e34fed0d92a3f290d1c38ba377d43d3a77" + integrity sha512-5rXy6UHYkT5Ql0m4nQZ0X+JgxYUMJqppG1ECQqMlp2IqBgJGaHePdcIXdXe8i0pXRfF7VmlIL0pN3ZaOxkm6sw== dependencies: requireindex "^1.2.0" From f28b66068f5a3a29e9245a5f1cf5411ec2bafadb Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Jun 2022 12:18:25 +0200 Subject: [PATCH 049/172] tidied up the flowing components --- .../src/javascript/components/{ => events}/event-themes.tsx | 0 .../javascript/components/{ => payment}/select-gateway-modal.tsx | 0 .../src/javascript/components/{ => payment}/wallet-info.tsx | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename app/frontend/src/javascript/components/{ => events}/event-themes.tsx (100%) rename app/frontend/src/javascript/components/{ => payment}/select-gateway-modal.tsx (100%) rename app/frontend/src/javascript/components/{ => payment}/wallet-info.tsx (100%) diff --git a/app/frontend/src/javascript/components/event-themes.tsx b/app/frontend/src/javascript/components/events/event-themes.tsx similarity index 100% rename from app/frontend/src/javascript/components/event-themes.tsx rename to app/frontend/src/javascript/components/events/event-themes.tsx diff --git a/app/frontend/src/javascript/components/select-gateway-modal.tsx b/app/frontend/src/javascript/components/payment/select-gateway-modal.tsx similarity index 100% rename from app/frontend/src/javascript/components/select-gateway-modal.tsx rename to app/frontend/src/javascript/components/payment/select-gateway-modal.tsx diff --git a/app/frontend/src/javascript/components/wallet-info.tsx b/app/frontend/src/javascript/components/payment/wallet-info.tsx similarity index 100% rename from app/frontend/src/javascript/components/wallet-info.tsx rename to app/frontend/src/javascript/components/payment/wallet-info.tsx From 00fb384ba57755e28dc88595da42d1d8b8841207 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Jun 2022 12:18:54 +0200 Subject: [PATCH 050/172] fixed imports --- .../src/javascript/components/README.md | 1 + .../components/events/event-themes.tsx | 10 ++++----- .../payment/abstract-payment-modal.tsx | 2 +- .../payment/select-gateway-modal.tsx | 22 +++++++++---------- .../components/payment/wallet-info.tsx | 16 +++++++------- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/app/frontend/src/javascript/components/README.md b/app/frontend/src/javascript/components/README.md index 211988894..0781904ff 100644 --- a/app/frontend/src/javascript/components/README.md +++ b/app/frontend/src/javascript/components/README.md @@ -14,4 +14,5 @@ These components must be written using the following conventions: - Depending on if we want to use the `` wrapper or not, we can export the component directly or wrap it in a `` wrapper. - When a component is used in angularJS, the wrapper is required. The component must be named like `const Foo` (no export if not used in React) and must have a `const FooWrapper` at the end of its file, which wraps the component in a ``. - Translations must be grouped per component. For example, the `FooBar` component must have its translations in the `config/locales/app.$SCOPE.en.yml` file, under the `foo_bar` key. +- Most of these rules are validated by eslint-plugin-fabmanager. Please ensure you write eslint valid code, and think twice you have a very good reason before disabling any rule. diff --git a/app/frontend/src/javascript/components/events/event-themes.tsx b/app/frontend/src/javascript/components/events/event-themes.tsx index 5414cbc22..1c4d4eae2 100644 --- a/app/frontend/src/javascript/components/events/event-themes.tsx +++ b/app/frontend/src/javascript/components/events/event-themes.tsx @@ -2,11 +2,11 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Select from 'react-select'; import { react2angular } from 'react2angular'; -import { Loader } from './base/loader'; -import { Event } from '../models/event'; -import { EventTheme } from '../models/event-theme'; -import { IApplication } from '../models/application'; -import EventThemeAPI from '../api/event-theme'; +import { Loader } from '../base/loader'; +import { Event } from '../../models/event'; +import { EventTheme } from '../../models/event-theme'; +import { IApplication } from '../../models/application'; +import EventThemeAPI from '../../api/event-theme'; declare const Application: IApplication; diff --git a/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx b/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx index 3fe99104f..c40892a02 100644 --- a/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx +++ b/app/frontend/src/javascript/components/payment/abstract-payment-modal.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent, ReactNode, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import WalletLib from '../../lib/wallet'; -import { WalletInfo } from '../wallet-info'; +import { WalletInfo } from './wallet-info'; import { FabModal, ModalSize } from '../base/fab-modal'; import { HtmlTranslate } from '../base/html-translate'; import { CustomAsset, CustomAssetName } from '../../models/custom-asset'; diff --git a/app/frontend/src/javascript/components/payment/select-gateway-modal.tsx b/app/frontend/src/javascript/components/payment/select-gateway-modal.tsx index b095ac72a..cdd8dc3df 100644 --- a/app/frontend/src/javascript/components/payment/select-gateway-modal.tsx +++ b/app/frontend/src/javascript/components/payment/select-gateway-modal.tsx @@ -6,15 +6,15 @@ import React, { BaseSyntheticEvent, useEffect, useState } from 'react'; import { react2angular } from 'react2angular'; import { useTranslation } from 'react-i18next'; -import { StripeKeysForm } from './payment/stripe/stripe-keys-form'; -import { PayzenKeysForm } from './payment/payzen/payzen-keys-form'; -import { FabModal, ModalSize } from './base/fab-modal'; -import { Loader } from './base/loader'; -import { User } from '../models/user'; -import { Gateway } from '../models/gateway'; -import { SettingBulkResult, SettingName } from '../models/setting'; -import { IApplication } from '../models/application'; -import SettingAPI from '../api/setting'; +import { StripeKeysForm } from './stripe/stripe-keys-form'; +import { PayzenKeysForm } from './payzen/payzen-keys-form'; +import { FabModal, ModalSize } from '../base/fab-modal'; +import { Loader } from '../base/loader'; +import { User } from '../../models/user'; +import { Gateway } from '../../models/gateway'; +import { SettingBulkResult, SettingName } from '../../models/setting'; +import { IApplication } from '../../models/application'; +import SettingAPI from '../../api/setting'; declare const Application: IApplication; @@ -139,10 +139,10 @@ export const SelectGatewayModal: React.FC = ({ isO ); }; -const SelectGatewayModalWrapper: React.FC = ({ isOpen, toggleModal, currentUser, onSuccess, onError }) => { +const SelectGatewayModalWrapper: React.FC = (props) => { return ( - + ); }; diff --git a/app/frontend/src/javascript/components/payment/wallet-info.tsx b/app/frontend/src/javascript/components/payment/wallet-info.tsx index 052808b72..e7f9e28e0 100644 --- a/app/frontend/src/javascript/components/payment/wallet-info.tsx +++ b/app/frontend/src/javascript/components/payment/wallet-info.tsx @@ -1,14 +1,14 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { react2angular } from 'react2angular'; -import { IApplication } from '../models/application'; -import '../lib/i18n'; -import { Loader } from './base/loader'; -import { User } from '../models/user'; -import { Wallet } from '../models/wallet'; -import WalletLib from '../lib/wallet'; -import { ShoppingCart } from '../models/payment'; -import FormatLib from '../lib/format'; +import { IApplication } from '../../models/application'; +import '../../lib/i18n'; +import { Loader } from '../base/loader'; +import { User } from '../../models/user'; +import { Wallet } from '../../models/wallet'; +import WalletLib from '../../lib/wallet'; +import { ShoppingCart } from '../../models/payment'; +import FormatLib from '../../lib/format'; declare const Application: IApplication; From 18aba05f2595e833ad5048d471b75a92c4ba3680 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Jun 2022 14:10:51 +0200 Subject: [PATCH 051/172] linted profile-custom-fields --- .../profile-custom-fields-list.tsx | 64 ++++++++++++++----- app/frontend/src/stylesheets/application.scss | 1 + .../profile-custom-fields-list.scss | 39 +++++++++++ config/locales/app.admin.en.yml | 10 +-- 4 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 app/frontend/src/stylesheets/modules/profile-custom-fields/profile-custom-fields-list.scss diff --git a/app/frontend/src/javascript/components/profile-custom-fields/profile-custom-fields-list.tsx b/app/frontend/src/javascript/components/profile-custom-fields/profile-custom-fields-list.tsx index be014f841..c2fc04839 100644 --- a/app/frontend/src/javascript/components/profile-custom-fields/profile-custom-fields-list.tsx +++ b/app/frontend/src/javascript/components/profile-custom-fields/profile-custom-fields-list.tsx @@ -7,6 +7,7 @@ import { Loader } from '../base/loader'; import { IApplication } from '../../models/application'; import { ProfileCustomField } from '../../models/profile-custom-field'; import ProfileCustomFieldAPI from '../../api/profile-custom-field'; +import { FabButton } from '../base/fab-button'; declare const Application: IApplication; @@ -31,6 +32,9 @@ export const ProfileCustomFieldsList: React.FC = ( }); }, []); + /** + * Save the new state of the given custom field to the API + */ const saveProfileCustomField = (profileCustomField: ProfileCustomField) => { ProfileCustomFieldAPI.update(profileCustomField).then(data => { const newFields = profileCustomFields.map(f => { @@ -43,9 +47,9 @@ export const ProfileCustomFieldsList: React.FC = ( if (profileCustomFieldToEdit) { setProfileCustomFieldToEdit(null); } - onSuccess(t('app.admin.settings.account.organization_profile_custom_field_successfully_updated')); + onSuccess(t('app.admin.settings.account.profile_custom_fields_list.field_successfully_updated')); }).catch(err => { - onError(t('app.admin.settings.account.organization_profile_custom_field_unable_to_update') + err); + onError(t('app.admin.settings.account.profile_custom_fields_list.unable_to_update') + err); }); }; @@ -63,12 +67,19 @@ export const ProfileCustomFieldsList: React.FC = ( }; }; + /** + * Callback triggered when the user clicks on the 'edit field' button. + * Opens the edition form for the given custom field + */ const editProfileCustomFieldLabel = (profileCustomField: ProfileCustomField) => { return () => { setProfileCustomFieldToEdit(_.clone(profileCustomField)); }; }; + /** + * Callback triggered when the input "label" is changed: updates the according state + */ const onChangeProfileCustomFieldLabel = (e: BaseSyntheticEvent) => { const { value } = e.target; setProfileCustomFieldToEdit({ @@ -77,16 +88,22 @@ export const ProfileCustomFieldsList: React.FC = ( }); }; + /** + * Save the currently edited custom field + */ const saveProfileCustomFieldLabel = () => { saveProfileCustomField(profileCustomFieldToEdit); }; + /** + * Closes the edition form for the currently edited custom field + */ const cancelEditProfileCustomFieldLabel = () => { setProfileCustomFieldToEdit(null); }; return ( -
{t('app.shared.schedules_table.deadline')}{t('app.shared.schedules_table.amount')}{t('app.shared.schedules_table.state')}{t('app.shared.payment_schedules_table.deadline')}{t('app.shared.payment_schedules_table.amount')}{t('app.shared.payment_schedules_table.state')}
+
@@ -101,31 +118,44 @@ export const ProfileCustomFieldsList: React.FC = ( - - ); diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index b1898bd43..adc691dfe 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -75,6 +75,7 @@ @import "modules/pricing/spaces/spaces-pricing"; @import "modules/profile-completion/completion-header-info"; @import "modules/profile-completion/profile-form-option"; +@import "modules/profile-custom-fields/profile-custom-fields-list"; @import "modules/select-gateway-modal"; @import "modules/settings/boolean-setting"; @import "modules/settings/check-list-setting"; diff --git a/app/frontend/src/stylesheets/modules/profile-custom-fields/profile-custom-fields-list.scss b/app/frontend/src/stylesheets/modules/profile-custom-fields/profile-custom-fields-list.scss new file mode 100644 index 000000000..06d648afb --- /dev/null +++ b/app/frontend/src/stylesheets/modules/profile-custom-fields/profile-custom-fields-list.scss @@ -0,0 +1,39 @@ +.profile-custom-fields-list { + width: 100%; + margin-bottom: 20px; + + .edit-field-button { + margin-right: 5px; + float: right; + } + + .edit-field-label-input { + width: 80%; + height: 38px; + } + + .buttons { + float: right; + + .save-field-label { + color: #ffffff; + background-color: #5cb85c; + border-color: #4cae4c; + margin-right: 5px; + } + + .cancel-field-edition { + margin-right: 5px; + } + } + + td.activated > label, + td.required > label { + margin-right: 15px; + } + + td.activated > .switch, + td.required > .switch { + vertical-align: middle; + } +} diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 8d932aa1e..e96947605 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -1566,11 +1566,11 @@ en: organization: "Organization" organization_profile_custom_fields_info: "You can display additional fields for users who declare themselves to be an organization. You can also choose to make them mandatory at account creation." organization_profile_custom_fields_alert: "Attention: the activated fields will be automatically displayed on the issued invoices. Once configured, please do not modify them." - organization_profile_custom_field: - required: "Confirmation required" - actived: "Activate the field" - organization_profile_custom_field_successfully_updated: "The organization field has been updated." - organization_profile_custom_field_unable_to_update: "Impossible to modify the field : " + profile_custom_fields_list: + field_successfully_updated: "The organization field has been updated." + unable_to_update: "Impossible to modify the field : " + required: "Confirmation required" + actived: "Activate the field" home: show_upcoming_events: "Show upcoming events" upcoming_events: From 21ee80ab1962382ffd8d46b32f12891842a66d3a Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Jun 2022 14:39:26 +0200 Subject: [PATCH 052/172] rename proof-of-identity to supporting-documents --- .../delete-proof-of-identity-type-modal.tsx | 16 +++++++++++----- .../proof-of-identity-files.tsx | 8 ++++---- .../proof-of-identity-refusal-form.tsx | 0 .../proof-of-identity-refusal-modal.tsx | 0 .../proof-of-identity-type-form.tsx | 0 .../proof-of-identity-type-modal.tsx | 0 .../proof-of-identity-types-list.tsx | 4 ++-- .../proof-of-identity-validation.tsx | 6 +++--- 8 files changed, 20 insertions(+), 14 deletions(-) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/delete-proof-of-identity-type-modal.tsx (59%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-files.tsx (96%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-refusal-form.tsx (100%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-refusal-modal.tsx (100%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-type-form.tsx (100%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-type-modal.tsx (100%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-types-list.tsx (98%) rename app/frontend/src/javascript/components/{proof-of-identity => supporting-documents}/proof-of-identity-validation.tsx (95%) diff --git a/app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx b/app/frontend/src/javascript/components/supporting-documents/delete-proof-of-identity-type-modal.tsx similarity index 59% rename from app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx rename to app/frontend/src/javascript/components/supporting-documents/delete-proof-of-identity-type-modal.tsx index 50bb40110..8c8e9117d 100644 --- a/app/frontend/src/javascript/components/proof-of-identity/delete-proof-of-identity-type-modal.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/delete-proof-of-identity-type-modal.tsx @@ -11,27 +11,33 @@ interface DeleteProofOfIdentityTypeModalProps { onError: (message: string) => void, } +/** + * Modal dialog to remove a requested type of supporting documents + */ export const DeleteProofOfIdentityTypeModal: React.FC = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypeId, onError }) => { const { t } = useTranslation('admin'); + /** + * The user has confirmed the deletion of the requested type of supporting documents + */ const handleDeleteProofOfIdentityType = async (): Promise => { try { await ProofOfIdentityTypeAPI.destroy(proofOfIdentityTypeId); - onSuccess(t('app.admin.settings.account.proof_of_identity_type_deleted')); + onSuccess(t('app.admin.settings.account.delete_proof_of_identity_type_modal.deleted')); } catch (e) { - onError(t('app.admin.settings.account.proof_of_identity_type_unable_to_delete') + e); + onError(t('app.admin.settings.account.delete_proof_of_identity_type_modal.unable_to_delete') + e); } }; return ( - -

{t('app.admin.settings.account.do_you_really_want_to_delete_this_proof_of_identity_type')}

+

{t('app.admin.settings.account.delete_proof_of_identity_type_modal.confirm_delete_proof_of_identity')}

); }; diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx similarity index 96% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx index 92590710b..e1a030f71 100644 --- a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-files.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx @@ -27,20 +27,20 @@ interface FilesType { } /** - * This component upload the proof of identity file of member + * This component upload the supporting documents file of the member */ export const ProofOfIdentityFiles: React.FC = ({ currentUser, onSuccess, onError }) => { const { t } = useTranslation('admin'); const maxProofOfIdentityFileSizeMb = (Fablab.maxProofOfIdentityFileSize / 1024 / 1024).toFixed(); - // list of proof of identity type + // list of supporting documents type const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState>([]); const [proofOfIdentityFiles, setProofOfIdentityFiles] = useState>([]); const [files, setFiles] = useState({}); const [errors, setErrors] = useState>([]); - // get proof of identity type and files + // get supporting documents type and files useEffect(() => { ProofOfIdentityTypeAPI.index({ group_id: currentUser.group_id }).then(tData => { setProofOfIdentityTypes(tData); @@ -59,7 +59,7 @@ export const ProofOfIdentityFiles: React.FC = ({ curr }; /** - * Check if the current collection of proof of identity types is empty or not. + * Check if the current collection of supporting documents types is empty or not. */ const hasProofOfIdentityTypes = (): boolean => { return proofOfIdentityTypes.length > 0; diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-refusal-form.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-form.tsx similarity index 100% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-refusal-form.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-form.tsx diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-refusal-modal.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-modal.tsx similarity index 100% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-refusal-modal.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-modal.tsx diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-type-form.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-form.tsx similarity index 100% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-type-form.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-form.tsx diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-type-modal.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-modal.tsx similarity index 100% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-type-modal.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-modal.tsx diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-types-list.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx similarity index 98% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-types-list.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx index 493c3d26f..9f83b75b4 100644 --- a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-types-list.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx @@ -25,7 +25,7 @@ interface ProofOfIdentityTypesListProps { const ProofOfIdentityTypesList: React.FC = ({ onSuccess, onError }) => { const { t } = useTranslation('admin'); - // list of displayed proof of identity type + // list of displayed supporting documents type const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState>([]); const [proofOfIdentityType, setProofOfIdentityType] = useState(null); const [proofOfIdentityTypeOrder, setProofOfIdentityTypeOrder] = useState(null); @@ -45,7 +45,7 @@ const ProofOfIdentityTypesList: React.FC = ({ onS }, []); /** - * Check if the current collection of proof of identity types is empty or not. + * Check if the current collection of supporting documents types is empty or not. */ const hasProofOfIdentityTypes = (): boolean => { return proofOfIdentityTypes.length > 0; diff --git a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-validation.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx similarity index 95% rename from app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-validation.tsx rename to app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx index de26ee14d..8fe7a6047 100644 --- a/app/frontend/src/javascript/components/proof-of-identity/proof-of-identity-validation.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx @@ -21,12 +21,12 @@ interface ProofOfIdentityValidationProps { } /** - * This component shows a list of proof of identity file of member, admin can download and valid + * This component shows a list of supporting documents file of member, admin can download and valid **/ const ProofOfIdentityValidation: React.FC = ({ operator, member, onSuccess, onError }) => { const { t } = useTranslation('admin'); - // list of proof of identity type + // list of supporting documents type const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState>([]); const [proofOfIdentityFiles, setProofOfIdentityFiles] = useState>([]); const [modalIsOpen, setModalIsOpen] = useState(false); @@ -46,7 +46,7 @@ const ProofOfIdentityValidation: React.FC = ({ o }; /** - * Check if the current collection of proof of identity types is empty or not. + * Check if the current collection of supporting documents types is empty or not. */ const hasProofOfIdentityTypes = (): boolean => { return proofOfIdentityTypes.length > 0; From 4d6af2c1c0dcbac8d0dcfd0ab235f7ee2e72c26c Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Jun 2022 17:49:04 +0200 Subject: [PATCH 053/172] (wip) linting supporting-documents (ex proof-of-identify) remaining: type-form, type-modal, types-list --- ...elete-supporting-documents-type-modal.tsx} | 16 +- .../proof-of-identity-files.tsx | 173 --------------- .../proof-of-identity-refusal-modal.tsx | 63 ------ .../proof-of-identity-types-list.tsx | 4 +- .../proof-of-identity-validation.tsx | 121 ----------- .../supporting-documents-files.tsx | 204 ++++++++++++++++++ ... => supporting-documents-refusal-form.tsx} | 36 ++-- .../supporting-documents-refusal-modal.tsx | 75 +++++++ .../supporting-documents-validation.tsx | 132 ++++++++++++ .../models/proof-of-identity-file.ts | 8 +- app/frontend/src/stylesheets/application.scss | 3 + .../supporting-documents-files.scss | 120 +++++++++++ .../supporting-documents-refusal-form.scss | 9 + .../supporting-documents-validation.scss | 70 ++++++ .../templates/admin/members/edit.html | 4 +- .../dashboard/proof_of_identity_files.html | 2 +- config/locales/app.admin.en.yml | 38 ++-- config/locales/app.logged.en.yml | 10 + 18 files changed, 680 insertions(+), 408 deletions(-) rename app/frontend/src/javascript/components/supporting-documents/{delete-proof-of-identity-type-modal.tsx => delete-supporting-documents-type-modal.tsx} (52%) delete mode 100644 app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx delete mode 100644 app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-modal.tsx delete mode 100644 app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx create mode 100644 app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx rename app/frontend/src/javascript/components/supporting-documents/{proof-of-identity-refusal-form.tsx => supporting-documents-refusal-form.tsx} (53%) create mode 100644 app/frontend/src/javascript/components/supporting-documents/supporting-documents-refusal-modal.tsx create mode 100644 app/frontend/src/javascript/components/supporting-documents/supporting-documents-validation.tsx create mode 100644 app/frontend/src/stylesheets/modules/supporting-documents/supporting-documents-files.scss create mode 100644 app/frontend/src/stylesheets/modules/supporting-documents/supporting-documents-refusal-form.scss create mode 100644 app/frontend/src/stylesheets/modules/supporting-documents/supporting-documents-validation.scss diff --git a/app/frontend/src/javascript/components/supporting-documents/delete-proof-of-identity-type-modal.tsx b/app/frontend/src/javascript/components/supporting-documents/delete-supporting-documents-type-modal.tsx similarity index 52% rename from app/frontend/src/javascript/components/supporting-documents/delete-proof-of-identity-type-modal.tsx rename to app/frontend/src/javascript/components/supporting-documents/delete-supporting-documents-type-modal.tsx index 8c8e9117d..f581320eb 100644 --- a/app/frontend/src/javascript/components/supporting-documents/delete-proof-of-identity-type-modal.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/delete-supporting-documents-type-modal.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { FabModal } from '../base/fab-modal'; import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; -interface DeleteProofOfIdentityTypeModalProps { +interface DeleteSupportingDocumentsTypeModalProps { isOpen: boolean, proofOfIdentityTypeId: number, toggleModal: () => void, @@ -14,7 +14,7 @@ interface DeleteProofOfIdentityTypeModalProps { /** * Modal dialog to remove a requested type of supporting documents */ -export const DeleteProofOfIdentityTypeModal: React.FC = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypeId, onError }) => { +export const DeleteSupportingDocumentsTypeModal: React.FC = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypeId, onError }) => { const { t } = useTranslation('admin'); /** @@ -23,21 +23,21 @@ export const DeleteProofOfIdentityTypeModal: React.FC => { try { await ProofOfIdentityTypeAPI.destroy(proofOfIdentityTypeId); - onSuccess(t('app.admin.settings.account.delete_proof_of_identity_type_modal.deleted')); + onSuccess(t('app.admin.settings.account.delete_supporting_documents_type_modal.deleted')); } catch (e) { - onError(t('app.admin.settings.account.delete_proof_of_identity_type_modal.unable_to_delete') + e); + onError(t('app.admin.settings.account.delete_supporting_documents_type_modal.unable_to_delete') + e); } }; return ( - -

{t('app.admin.settings.account.delete_proof_of_identity_type_modal.confirm_delete_proof_of_identity')}

+ className="delete-supporting-documents-type-modal"> +

{t('app.admin.settings.account.delete_supporting_documents_type_modal.confirm_delete_supporting_documents_type')}

); }; diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx deleted file mode 100644 index e1a030f71..000000000 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-files.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { react2angular } from 'react2angular'; -import _ from 'lodash'; -import { HtmlTranslate } from '../base/html-translate'; -import { Loader } from '../base/loader'; -import { User } from '../../models/user'; -import { IApplication } from '../../models/application'; -import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; -import { ProofOfIdentityFile } from '../../models/proof-of-identity-file'; -import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; -import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file'; -import { IFablab } from '../../models/fablab'; - -declare let Fablab: IFablab; - -declare const Application: IApplication; - -interface ProofOfIdentityFilesProps { - currentUser: User, - onSuccess: (message: string) => void, - onError: (message: string) => void, -} - -interface FilesType { - number?: File -} - -/** - * This component upload the supporting documents file of the member - */ -export const ProofOfIdentityFiles: React.FC = ({ currentUser, onSuccess, onError }) => { - const { t } = useTranslation('admin'); - - const maxProofOfIdentityFileSizeMb = (Fablab.maxProofOfIdentityFileSize / 1024 / 1024).toFixed(); - - // list of supporting documents type - const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState>([]); - const [proofOfIdentityFiles, setProofOfIdentityFiles] = useState>([]); - const [files, setFiles] = useState({}); - const [errors, setErrors] = useState>([]); - - // get supporting documents type and files - useEffect(() => { - ProofOfIdentityTypeAPI.index({ group_id: currentUser.group_id }).then(tData => { - setProofOfIdentityTypes(tData); - }); - ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => { - setProofOfIdentityFiles(fData); - }); - }, []); - - const getProofOfIdentityFileByType = (proofOfIdentityTypeId: number): ProofOfIdentityFile => { - return _.find(proofOfIdentityFiles, { proof_of_identity_type_id: proofOfIdentityTypeId }); - }; - - const hasFile = (proofOfIdentityTypeId: number): boolean => { - return files[proofOfIdentityTypeId] || getProofOfIdentityFileByType(proofOfIdentityTypeId); - }; - - /** - * Check if the current collection of supporting documents types is empty or not. - */ - const hasProofOfIdentityTypes = (): boolean => { - return proofOfIdentityTypes.length > 0; - }; - - const onFileChange = (poitId: number) => { - return (event) => { - const fileSize = event.target.files[0].size; - let _errors = errors; - if (fileSize > Fablab.maxProofOfIdentityFileSize) { - _errors = errors.concat(poitId); - setErrors(_errors); - } else { - _errors = errors.filter(e => e !== poitId); - } - setErrors(_errors); - setFiles({ - ...files, - [poitId]: event.target.files[0] - }); - }; - }; - - const onFileUpload = async () => { - try { - for (const proofOfIdentityTypeId of Object.keys(files)) { - const formData = new FormData(); - - formData.append('proof_of_identity_file[user_id]', currentUser.id.toString()); - formData.append('proof_of_identity_file[proof_of_identity_type_id]', proofOfIdentityTypeId); - formData.append('proof_of_identity_file[attachment]', files[proofOfIdentityTypeId]); - const proofOfIdentityFile = getProofOfIdentityFileByType(parseInt(proofOfIdentityTypeId, 10)); - if (proofOfIdentityFile) { - await ProofOfIdentityFileAPI.update(proofOfIdentityFile.id, formData); - } else { - await ProofOfIdentityFileAPI.create(formData); - } - } - if (Object.keys(files).length > 0) { - ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => { - setProofOfIdentityFiles(fData); - setFiles({}); - onSuccess(t('app.admin.members_edit.proof_of_identity_files_successfully_uploaded')); - }); - } - } catch (e) { - onError(t('app.admin.members_edit.proof_of_identity_files_unable_to_upload') + e); - } - }; - - const getProofOfIdentityFileUrl = (poifId: number) => { - return `/api/proof_of_identity_files/${poifId}/download`; - }; - - return ( -
-

{t('app.admin.members_edit.proof_of_identity_files')}

-

{t('app.admin.members_edit.my_documents_info')}

-
- -
-
- {proofOfIdentityTypes.map((poit: ProofOfIdentityType) => { - return ( -
- -
-
- {hasFile(poit.id) && ( -
- {files[poit.id]?.name || getProofOfIdentityFileByType(poit.id).attachment} -
- )} - {getProofOfIdentityFileByType(poit.id) && !files[poit.id] && ( - - )} -
- - {!hasFile(poit.id) && ( - Parcourir - )} - {hasFile(poit.id) && ( - Modifier - )} - - -
- {errors.includes(poit.id) && {t('app.admin.members_edit.proof_of_identity_file_size_error', { SIZE: maxProofOfIdentityFileSizeMb })}} -
- ); - })} -
- {hasProofOfIdentityTypes() && ( - - )} -
- ); -}; - -const ProofOfIdentityFilesWrapper: React.FC = ({ currentUser, onSuccess, onError }) => { - return ( - - - - ); -}; - -Application.Components.component('proofOfIdentityFiles', react2angular(ProofOfIdentityFilesWrapper, ['currentUser', 'onSuccess', 'onError'])); diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-modal.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-modal.tsx deleted file mode 100644 index 4bfb9fd9e..000000000 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-modal.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { FabModal } from '../base/fab-modal'; -import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; -import { ProofOfIdentityRefusal } from '../../models/proof-of-identity-refusal'; -import { User } from '../../models/user'; -import ProofOfIdentityRefusalAPI from '../../api/proof-of-identity-refusal'; -import { ProofOfIdentityRefusalForm } from './proof-of-identity-refusal-form'; - -interface ProofOfIdentityRefusalModalProps { - isOpen: boolean, - toggleModal: () => void, - onSuccess: (message: string) => void, - onError: (message: string) => void, - proofOfIdentityTypes: Array, - operator: User, - member: User -} - -export const ProofOfIdentityRefusalModal: React.FC = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypes, operator, member, onError }) => { - const { t } = useTranslation('admin'); - - const [data, setData] = useState({ - id: null, - operator_id: operator.id, - user_id: member.id, - proof_of_identity_type_ids: [], - message: '' - }); - - const handleProofOfIdentityRefusalChanged = (field: string, value: string | Array) => { - setData({ - ...data, - [field]: value - }); - }; - - const handleSaveProofOfIdentityRefusal = async (): Promise => { - try { - await ProofOfIdentityRefusalAPI.create(data); - onSuccess(t('app.admin.members_edit.proof_of_identity_refusal_successfully_sent')); - } catch (e) { - onError(t('app.admin.members_edit.proof_of_identity_refusal_unable_to_send') + e); - } - }; - - const isPreventSaveProofOfIdentityRefusal = (): boolean => { - return !data.message || data.proof_of_identity_type_ids.length === 0; - }; - - return ( - - - - ); -}; diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx index 9f83b75b4..372a89e0f 100644 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx @@ -8,7 +8,7 @@ import { IApplication } from '../../models/application'; import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; import { Group } from '../../models/group'; import { ProofOfIdentityTypeModal } from './proof-of-identity-type-modal'; -import { DeleteProofOfIdentityTypeModal } from './delete-proof-of-identity-type-modal'; +import { DeleteSupportingDocumentsTypeModal } from './delete-supporting-documents-type-modal'; import GroupAPI from '../../api/group'; import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; @@ -162,7 +162,7 @@ const ProofOfIdentityTypesList: React.FC = ({ onS - +
{profileCustomFieldToEdit?.id !== field.id && field.label} {profileCustomFieldToEdit?.id !== field.id && ( - + )} {profileCustomFieldToEdit?.id === field.id && (
- - - - +
)}
- - + + + - - + + +
diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx deleted file mode 100644 index 8fe7a6047..000000000 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-validation.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { react2angular } from 'react2angular'; -import _ from 'lodash'; -import { Loader } from '../base/loader'; -import { User } from '../../models/user'; -import { IApplication } from '../../models/application'; -import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; -import { ProofOfIdentityFile } from '../../models/proof-of-identity-file'; -import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; -import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file'; -import { ProofOfIdentityRefusalModal } from './proof-of-identity-refusal-modal'; - -declare const Application: IApplication; - -interface ProofOfIdentityValidationProps { - operator: User, - member: User - onSuccess: (message: string) => void, - onError: (message: string) => void, -} - -/** - * This component shows a list of supporting documents file of member, admin can download and valid - **/ -const ProofOfIdentityValidation: React.FC = ({ operator, member, onSuccess, onError }) => { - const { t } = useTranslation('admin'); - - // list of supporting documents type - const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState>([]); - const [proofOfIdentityFiles, setProofOfIdentityFiles] = useState>([]); - const [modalIsOpen, setModalIsOpen] = useState(false); - - // get groups - useEffect(() => { - ProofOfIdentityTypeAPI.index({ group_id: member.group_id }).then(tData => { - setProofOfIdentityTypes(tData); - }); - ProofOfIdentityFileAPI.index({ user_id: member.id }).then(fData => { - setProofOfIdentityFiles(fData); - }); - }, []); - - const getProofOfIdentityFileByType = (proofOfIdentityTypeId: number): ProofOfIdentityFile => { - return _.find(proofOfIdentityFiles, { proof_of_identity_type_id: proofOfIdentityTypeId }); - }; - - /** - * Check if the current collection of supporting documents types is empty or not. - */ - const hasProofOfIdentityTypes = (): boolean => { - return proofOfIdentityTypes.length > 0; - }; - - const getProofOfIdentityFileUrl = (poifId: number): string => { - return `/api/proof_of_identity_files/${poifId}/download`; - }; - - const openProofOfIdentityRefusalModal = (): void => { - setModalIsOpen(true); - }; - - const toggleModal = (): void => { - setModalIsOpen(false); - }; - - const saveProofOfIdentityRefusalOnSuccess = (message: string): void => { - setModalIsOpen(false); - onSuccess(message); - }; - - return ( -
-
-

{t('app.admin.members_edit.proof_of_identity_files')}

-

{t('app.admin.members_edit.find_below_the_proof_of_identity_files')}

- {proofOfIdentityTypes.map((poit: ProofOfIdentityType) => { - return ( -
-
{poit.name}
- {getProofOfIdentityFileByType(poit.id) && ( - - {getProofOfIdentityFileByType(poit.id).attachment} - - - )} - {!getProofOfIdentityFileByType(poit.id) && ( -
{t('app.admin.members_edit.to_complete')}
- )} -
- ); - })} -
- {hasProofOfIdentityTypes() && !member.validated_at && ( -
-

{t('app.admin.members_edit.refuse_proof_of_identity_files')}

-

{t('app.admin.members_edit.refuse_proof_of_identity_files_info')}

- - -
- )} -
- ); -}; - -const ProofOfIdentityValidationWrapper: React.FC = ({ operator, member, onSuccess, onError }) => { - return ( - - - - ); -}; - -Application.Components.component('proofOfIdentityValidation', react2angular(ProofOfIdentityValidationWrapper, ['operator', 'member', 'onSuccess', 'onError'])); diff --git a/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx new file mode 100644 index 000000000..a59725738 --- /dev/null +++ b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx @@ -0,0 +1,204 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { react2angular } from 'react2angular'; +import _ from 'lodash'; +import { HtmlTranslate } from '../base/html-translate'; +import { Loader } from '../base/loader'; +import { User } from '../../models/user'; +import { IApplication } from '../../models/application'; +import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; +import { ProofOfIdentityFile } from '../../models/proof-of-identity-file'; +import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; +import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file'; +import { IFablab } from '../../models/fablab'; +import { FabAlert } from '../base/fab-alert'; + +declare let Fablab: IFablab; + +declare const Application: IApplication; + +interface SupportingDocumentsFilesProps { + currentUser: User, + onSuccess: (message: string) => void, + onError: (message: string) => void, +} + +interface FilesType { + number?: File +} + +/** + * This component upload the supporting documents file of the member + */ +export const SupportingDocumentsFiles: React.FC = ({ currentUser, onSuccess, onError }) => { + const { t } = useTranslation('logged'); + + const maxProofOfIdentityFileSizeMb = (Fablab.maxProofOfIdentityFileSize / 1024 / 1024).toFixed(); + + // list of supporting documents type + const [supportingDocumentsTypes, setSupportingDocumentsTypes] = useState>([]); + const [supportingDocumentsFiles, setSupportingDocumentsFiles] = useState>([]); + const [files, setFiles] = useState({}); + const [errors, setErrors] = useState>([]); + + // get supporting documents type and files + useEffect(() => { + ProofOfIdentityTypeAPI.index({ group_id: currentUser.group_id }).then(tData => { + setSupportingDocumentsTypes(tData); + }); + ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => { + setSupportingDocumentsFiles(fData); + }); + }, []); + + /** + * Return the files matching the given type id + */ + const getSupportingDocumentsFileByType = (supportingDocumentsTypeId: number): ProofOfIdentityFile => { + return _.find(supportingDocumentsFiles, { + proof_of_identity_type_id: supportingDocumentsTypeId + }); + }; + + /** + * Check if the given type has any uploaded files + */ + const hasFile = (proofOfIdentityTypeId: number): boolean => { + return files[proofOfIdentityTypeId] || getSupportingDocumentsFileByType(proofOfIdentityTypeId); + }; + + /** + * Check if the current collection of supporting documents types is empty or not. + */ + const hasProofOfIdentityTypes = (): boolean => { + return supportingDocumentsTypes.length > 0; + }; + + /** + * Callback triggered when a file is selected by the member: check if the file does not exceed the maximum allowed size + */ + const onFileChange = (documentId: number) => { + return (event) => { + const fileSize = event.target.files[0].size; + let _errors: Array; + if (fileSize > Fablab.maxProofOfIdentityFileSize) { + _errors = errors.concat(documentId); + setErrors(_errors); + } else { + _errors = errors.filter(e => e !== documentId); + } + setErrors(_errors); + setFiles({ + ...files, + [documentId]: event.target.files[0] + }); + }; + }; + + /** + * Callback triggered when the user clicks on save: upload the file to the API + */ + const onFileUpload = async () => { + try { + for (const proofOfIdentityTypeId of Object.keys(files)) { + const formData = new FormData(); + + formData.append('proof_of_identity_file[user_id]', currentUser.id.toString()); + formData.append('proof_of_identity_file[proof_of_identity_type_id]', proofOfIdentityTypeId); + formData.append('proof_of_identity_file[attachment]', files[proofOfIdentityTypeId]); + const proofOfIdentityFile = getSupportingDocumentsFileByType(parseInt(proofOfIdentityTypeId, 10)); + if (proofOfIdentityFile) { + await ProofOfIdentityFileAPI.update(proofOfIdentityFile.id, formData); + } else { + await ProofOfIdentityFileAPI.create(formData); + } + } + if (Object.keys(files).length > 0) { + ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => { + setSupportingDocumentsFiles(fData); + setFiles({}); + onSuccess(t('app.logged.dashboard.supporting_documents_files.file_successfully_uploaded')); + }); + } + } catch (e) { + onError(t('app.logged.dashboard.supporting_documents_files.unable_to_upload') + e); + } + }; + + /** + * Return the download URL of the given file + */ + const getSupportingDocumentsFileUrl = (documentId: number) => { + return `/api/proof_of_identity_files/${documentId}/download`; + }; + + return ( +
+

{t('app.logged.dashboard.supporting_documents_files.supporting_documents_files')}

+

{t('app.logged.dashboard.supporting_documents_files.my_documents_info')}

+ + + +
+ {supportingDocumentsTypes.map((documentType: ProofOfIdentityType) => { + return ( +
+ +
+
+ {hasFile(documentType.id) && ( +
+ + + {files[documentType.id]?.name || getSupportingDocumentsFileByType(documentType.id).attachment} + +
+ )} + {getSupportingDocumentsFileByType(documentType.id) && !files[documentType.id] && ( + + + + )} +
+ + {!hasFile(documentType.id) && ( + {t('app.logged.dashboard.supporting_documents_files.browse')} + )} + {hasFile(documentType.id) && ( + {t('app.logged.dashboard.supporting_documents_files.edit')} + )} + + +
+ {errors.includes(documentType.id) && + {t('app.logged.dashboard.supporting_documents_files.file_size_error', { SIZE: maxProofOfIdentityFileSizeMb })} + } +
+ ); + })} +
+ {hasProofOfIdentityTypes() && ( + + )} +
+ ); +}; + +const SupportingDocumentsFilesWrapper: React.FC = ({ currentUser, onSuccess, onError }) => { + return ( + + + + ); +}; + +Application.Components.component('supportingDocumentsFiles', react2angular(SupportingDocumentsFilesWrapper, ['currentUser', 'onSuccess', 'onError'])); diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-form.tsx b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-refusal-form.tsx similarity index 53% rename from app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-form.tsx rename to app/frontend/src/javascript/components/supporting-documents/supporting-documents-refusal-form.tsx index 8f065fa90..0d7652e70 100644 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-refusal-form.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-refusal-form.tsx @@ -2,22 +2,22 @@ import React, { BaseSyntheticEvent, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; -interface ProofOfIdentityRefusalFormProps { +interface SupportingDocumentsRefusalFormProps { proofOfIdentityTypes: Array, onChange: (field: string, value: string | Array) => void, } /** - * Form to set the stripe's public and private keys + * Form to set the refuse the uploaded supporting documents */ -export const ProofOfIdentityRefusalForm: React.FC = ({ proofOfIdentityTypes, onChange }) => { +export const SupportingDocumentsRefusalForm: React.FC = ({ proofOfIdentityTypes, onChange }) => { const { t } = useTranslation('admin'); const [values, setValues] = useState>([]); const [message, setMessage] = useState(''); /** - * Callback triggered when the name has changed. + * Callback triggered when the message has changed. */ const handleMessageChange = (e: BaseSyntheticEvent): void => { const { value } = e.target; @@ -26,10 +26,9 @@ export const ProofOfIdentityRefusalForm: React.FC { + const handleTypeSelectionChange = (value: number) => { return (event: BaseSyntheticEvent) => { let newValues: Array; if (event.target.checked) { @@ -43,27 +42,32 @@ export const ProofOfIdentityRefusalForm: React.FC { - return values.includes(value); + const isChecked = (typeId: number) => { + return values.includes(typeId); }; return ( -
+
- {proofOfIdentityTypes.map(type =>
+ {proofOfIdentityTypes.map(type =>
- +
)}
-
- +
+