Compare commits

..

11 Commits

Author SHA1 Message Date
Jacques ROUSSEL ae1254dc68 wip fix dockerfile 2024-10-16 14:50:53 +02:00
lebaudantoine 5342eb49c7 wip install y-py 2024-10-16 14:36:20 +02:00
Anthony LC 97d00b678f 🚨(docker) fix docker warning about casing
When we build the docker image, we get a warning
about the casing in the Dockerfile. This commit
fixes the casing in the Dockerfile.
2024-10-14 22:20:54 +02:00
Anthony LC 52eb973164 (CI) refecto test-e2e
With the new container available, we can simplify
the workflow by removing the build step
and using the container directly.
2024-10-14 22:20:54 +02:00
Anthony LC 789879a9cc 🧑‍💻(project) improve frontend bootstrap
We were providing a frontend development container
to the developers, but it was not working properly.
Problem of hot reload was present for Windows and
Linux users.
We stop to provide this development container and
we will provide a container connected to the build
of the frontend.
You can still access the frontend after bootstrap
on the "localhost:3000", but if you want to develop
you will have to install the frontend dependencies
localy and run the frontend in development mode.
This will be more efficient and will avoid the
problem of hot reload, and right on folder access.
2024-10-14 22:20:54 +02:00
Anthony LC 52c52d53b7 🔧(docker) add missing frontent env
The env MEDIA_URL was missing in the frontend
Dockerfile. It is not necessary in our
running environment (staging / preprod ...) but it
is necessary if we want to run the frontend with
a different media url.
SW_DEACTIVATED was missing as well, we need to
deactivate the service worker in the frontend when
we test with Playwright.
2024-10-14 22:20:54 +02:00
Anthony LC 54fe6a2319 🐛(frontend) invalidate queries after removing user
When we remove a user from the list of members,
we need to invalidate the user query for the
user to be found again.
We improve the error message when a user is
already a member of the document.
2024-10-14 19:58:41 +02:00
Anthony LC bc5dcb0ed5 ️(frontend) use Marianne woff2 if compatible
Woff2 is a more modern format for web fonts,
and it is supported by all modern browsers.
We still keep the woff format for
compatibility with older browsers.
2024-10-11 15:26:18 +02:00
Anthony LC 6c3f3f6a77 💄(frontend) components more multi theme friendly
We adapt a bit the tokens of some components to be
more multi theme friendly.
When we will add another theme, it will be
easier to adapt to the new theme.
2024-10-11 15:26:18 +02:00
Anthony LC 6e64bad1e2 🔖(patch) release 1.5.1
Fixed:
- 🐛(db) fix users duplicate
2024-10-10 16:46:27 +02:00
Anthony LC 0d5b2382ab 🐛(db) fix users duplicate
Some OIDC identity providers provide a random
value in the "sub" field instead of an
identifying ID.
It created duplicate users in the database.
This migration fixes the issue by removing the
duplicate users after having updated all
the references to the old users.
2024-10-10 16:23:46 +02:00
55 changed files with 422 additions and 271 deletions
+7 -119
View File
@@ -39,29 +39,6 @@ jobs:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
build-front:
runs-on: ubuntu-latest
needs: install-front
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Build CI App
run: cd src/frontend/ && yarn ci:build
- name: Cache build frontend
uses: actions/cache@v4
with:
path: src/frontend/apps/impress/out/
key: build-front-${{ github.run_id }}
test-front:
runs-on: ubuntu-latest
needs: install-front
@@ -98,25 +75,11 @@ jobs:
test-e2e-chromium:
runs-on: ubuntu-latest
needs: build-front
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set services env variables
run: |
make data/media
make create-env-files
cat env.d/development/common.e2e.dist >> env.d/development/common
- name: Restore the mail templates
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
@@ -124,42 +87,11 @@ jobs:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Restore the build cache
uses: actions/cache@v4
id: cache-build
with:
path: src/frontend/apps/impress/out/
key: build-front-${{ github.run_id }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build the Docker images
uses: docker/bake-action@v4
with:
targets: |
app-dev
y-provider
load: true
set: |
*.cache-from=type=gha,scope=cached-stage
*.cache-to=type=gha,scope=cached-stage,mode=max
- name: Set e2e env variables
run: cat env.d/development/common.e2e.dist >> env.d/development/common.dist
- name: Start Docker services
run: |
make run
- name: Start Nginx for the frontend
run: |
docker compose up --force-recreate -d nginx-front
- name: Apply DRF migrations
run: |
make migrate
- name: Add dummy data
run: |
make demo FLUSH_ARGS='--no-input'
run: make bootstrap FLUSH_ARGS='--no-input' cache=
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install-playwright chromium
@@ -176,25 +108,12 @@ jobs:
test-e2e-other-browser:
runs-on: ubuntu-latest
needs: build-front
needs: test-e2e-chromium
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set services env variables
run: |
make data/media
make create-env-files
cat env.d/development/common.e2e.dist >> env.d/development/common
- name: Restore the mail templates
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
@@ -202,42 +121,11 @@ jobs:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Restore the build cache
uses: actions/cache@v4
id: cache-build
with:
path: src/frontend/apps/impress/out/
key: build-front-${{ github.run_id }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build the Docker images
uses: docker/bake-action@v4
with:
targets: |
app-dev
y-provider
load: true
set: |
*.cache-from=type=gha,scope=cached-stage
*.cache-to=type=gha,scope=cached-stage,mode=max
- name: Set e2e env variables
run: cat env.d/development/common.e2e.dist >> env.d/development/common.dist
- name: Start Docker services
run: |
make run
- name: Start Nginx for the frontend
run: |
docker compose up --force-recreate -d nginx-front
- name: Apply DRF migrations
run: |
make migrate
- name: Add dummy data
run: |
make demo FLUSH_ARGS='--no-input'
run: make bootstrap FLUSH_ARGS='--no-input' cache=
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install-playwright firefox webkit chromium
+18 -1
View File
@@ -9,6 +9,22 @@ and this project adheres to
## [Unreleased]
## Changed
- ♻️(frontend) More multi theme friendly #325
- ♻️ Bootstrap frontend #257
## Fixed
🐛(frontend) invalidate queries after removing user #336
## [1.5.1] - 2024-10-10
## Fixed
- 🐛(db) fix users duplicate #316
## [1.5.0] - 2024-10-09
@@ -182,7 +198,8 @@ and this project adheres to
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.5.0...main
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.5.1...main
[1.5.1]: https://github.com/numerique-gouv/impress/releases/v1.5.1
[1.5.0]: https://github.com/numerique-gouv/impress/releases/v1.5.0
[1.4.0]: https://github.com/numerique-gouv/impress/releases/v1.4.0
[1.3.0]: https://github.com/numerique-gouv/impress/releases/v1.3.0
+10 -7
View File
@@ -1,7 +1,7 @@
# Django impress
# ---- base image to inherit from ----
FROM python:3.12.6-alpine3.20 as base
FROM python:3.12.6-alpine3.20 AS base
# Upgrade pip to its latest release to speed up dependencies installation
RUN python -m pip install --upgrade pip setuptools
@@ -11,7 +11,10 @@ RUN apk update && \
apk upgrade
# ---- Back-end builder image ----
FROM base as back-builder
FROM base AS back-builder
RUN apk add \
cargo
WORKDIR /builder
@@ -23,7 +26,7 @@ RUN mkdir /install && \
# ---- mails ----
FROM node:20 as mail-builder
FROM node:20 AS mail-builder
COPY ./src/mail /mail/app
@@ -34,7 +37,7 @@ RUN yarn install --frozen-lockfile && \
# ---- static link collector ----
FROM base as link-collector
FROM base AS link-collector
ARG IMPRESS_STATIC_ROOT=/data/static
# Install pango & rdfind
@@ -59,7 +62,7 @@ RUN DJANGO_CONFIGURATION=Build DJANGO_JWT_PRIVATE_SIGNING_KEY=Dummy \
RUN rdfind -makesymlinks true -followsymlinks true -makeresultsfile false ${IMPRESS_STATIC_ROOT}
# ---- Core application image ----
FROM base as core
FROM base AS core
ENV PYTHONUNBUFFERED=1
@@ -97,7 +100,7 @@ WORKDIR /app
ENTRYPOINT [ "/usr/local/bin/entrypoint" ]
# ---- Development image ----
FROM core as backend-development
FROM core AS backend-development
# Switch back to the root user to install development dependencies
USER root:root
@@ -123,7 +126,7 @@ ENV DB_HOST=postgresql \
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
# ---- Production image ----
FROM core as backend-production
FROM core AS backend-production
ARG IMPRESS_STATIC_ROOT=/data/static
+36 -9
View File
@@ -81,7 +81,7 @@ bootstrap: \
data/static \
create-env-files \
build \
run-frontend-dev \
run-with-frontend \
migrate \
demo \
back-i18n-compile \
@@ -90,11 +90,28 @@ bootstrap: \
.PHONY: bootstrap
# -- Docker/compose
build: ## build the app-dev container
@$(COMPOSE) build app-dev --no-cache
@$(COMPOSE) build frontend-dev --no-cache
build: cache ?= --no-cache
build: ## build the project containers
@$(MAKE) build-backend cache=$(cache)
@$(MAKE) build-yjs-provider cache=$(cache)
@$(MAKE) build-frontend cache=$(cache)
.PHONY: build
build-backend: cache ?=
build-backend: ## build the app-dev container
@$(COMPOSE) build app-dev $(cache)
.PHONY: build-backend
build-yjs-provider: cache ?=
build-yjs-provider: ## build the y-provider container
@$(COMPOSE) build y-provider $(cache)
.PHONY: build-yjs-provider
build-frontend: cache ?=
build-frontend: ## build the frontend container
@$(COMPOSE) build frontend-dev $(cache)
.PHONY: build-frontend
down: ## stop and remove containers, networks, images, and volumes
@$(COMPOSE) down
.PHONY: down
@@ -110,6 +127,11 @@ run: ## start the wsgi (production) and development server
@$(WAIT_DB)
.PHONY: run
run-with-frontend: ## Start all the containers needed (backend to frontend)
@$(MAKE) run
@$(COMPOSE) up --force-recreate -d frontend-dev
.PHONY: run-with-frontend
status: ## an alias for "docker compose ps"
@$(COMPOSE) ps
.PHONY: status
@@ -286,10 +308,15 @@ help:
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-30s$(RESET) %s\n", $$1, $$2}'
.PHONY: help
# Front
run-frontend-dev: ## Install and run the frontend dev
@$(COMPOSE) up --force-recreate -d frontend-dev
.PHONY: run-frontend-dev
# Front
frontend-install: ## install the frontend locally
cd $(PATH_FRONT_IMPRESS) && yarn
.PHONY: frontend-install
run-frontend-development: ## Run the frontend in development mode
@$(COMPOSE) stop frontend-dev
cd $(PATH_FRONT_IMPRESS) && yarn dev
.PHONY: run-frontend-development
frontend-i18n-extract: ## Extract the frontend translation inside a json to be used for crowdin
cd $(PATH_FRONT) && yarn i18n:extract
@@ -314,7 +341,7 @@ start-tilt: ## start the kubernetes cluster using kind
tilt up -f ./bin/Tiltfile
.PHONY: build-k8s-cluster
VERSION_TYPE ?= minor
bump-packages-version: VERSION_TYPE ?= minor
bump-packages-version: ## bump the version of the project - VERSION_TYPE can be "major", "minor", "patch"
cd ./src/mail && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
+31 -10
View File
@@ -37,14 +37,6 @@ The easiest way to start working on the project is to use GNU Make:
$ make bootstrap FLUSH_ARGS='--no-input'
```
Then you can access to the project in development mode by going to http://localhost:3000.
You will be prompted to log in, the default credentials are:
```bash
username: impress
password: impress
```
---
This command builds the `app` container, installs dependencies, performs
database migrations and compile translations. It's a good idea to use this
command each time you are pulling code from the project repository to avoid
@@ -52,12 +44,41 @@ dependency-releated or migration-releated issues.
Your Docker services should now be up and running 🎉
Note that if you need to run them afterwards, you can use the eponym Make rule:
You can access to the project by going to http://localhost:3000.
You will be prompted to log in, the default credentials are:
```bash
username: impress
password: impress
```
📝 Note that if you need to run them afterwards, you can use the eponym Make rule:
```bash
$ make run-frontend-dev
$ make run-with-frontend
```
---
⚠️ For the frontend developper, it is often better to run the frontend in development mode locally.
To do so, install the frontend dependencies with the following command:
```bash
$ make frontend-install
```
And run the frontend locally in development mode with the following command:
```bash
$ make run-frontend-development
```
To start all the services, except the frontend container, you can use the following command:
```bash
$ make run
```
---
### Adding content
You can create a basic demo site by running:
+12 -20
View File
@@ -119,13 +119,20 @@ services:
depends_on:
- keycloak
nginx-front:
image: nginx:1.25
frontend-dev:
user: "${DOCKER_USER:-1000}"
build:
context: .
dockerfile: ./src/frontend/Dockerfile
target: frontend-production
args:
API_ORIGIN: "http://localhost:8071"
Y_PROVIDER_URL: "ws://localhost:4444"
MEDIA_URL: "http://localhost:8083"
SW_DEACTIVATED: "true"
image: impress:frontend-development
ports:
- "3000:3000"
volumes:
- ./src/frontend/apps/impress/conf/default.conf:/etc/nginx/conf.d/default.conf
- ./src/frontend/apps/impress/out:/usr/share/nginx/html
dockerize:
image: jwilder/dockerize
@@ -161,21 +168,6 @@ services:
- /home/frontend/servers/y-provider/node_modules/
- /home/frontend/servers/y-provider/dist/
frontend-dev:
user: "${DOCKER_USER:-1000}"
build:
context: .
dockerfile: ./src/frontend/Dockerfile
target: impress-dev
ports:
- "3000:3000"
volumes:
- ./src/frontend/apps/impress:/home/frontend/apps/impress
- /home/frontend/node_modules/
depends_on:
- y-provider
- celery-dev
kc_postgresql:
image: postgres:14.3
ports:
@@ -0,0 +1,128 @@
# Generated by Django 5.1.1 on 2024-10-10 11:45
from django.db import migrations
procedure = """
DO $$
DECLARE
user_email TEXT;
BEGIN
-- Step 1: Create a temporary table (without the unique constraint)
-- impress_document_access
DROP TABLE IF EXISTS impress_document_access_tmp;
CREATE TEMP TABLE impress_document_access_tmp AS
SELECT * FROM impress_document_access;
-- impress_link_trace
DROP TABLE IF EXISTS impress_link_trace_tmp;
CREATE TEMP TABLE impress_link_trace_tmp AS
SELECT * FROM impress_link_trace;
-- Step 2: Loop through each email that appears more than once
FOR user_email IN
SELECT email
FROM impress_user
GROUP BY email
HAVING COUNT(email) > 1
LOOP
-- Step 3: Update user_id in the temporary table based on email
-- For impress_document_access
UPDATE impress_document_access_tmp
SET user_id = (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
)
WHERE user_id IN (
SELECT id
FROM impress_user
WHERE email = user_email
);
-- For impress_link_trace
UPDATE impress_link_trace_tmp
SET user_id = (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
)
WHERE user_id IN (
SELECT id
FROM impress_user
WHERE email = user_email
);
-- update impress_invitation
UPDATE impress_invitation
SET issuer_id = (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
)
WHERE issuer_id IN (
SELECT id
FROM impress_user
WHERE email = user_email
);
DELETE FROM impress_user
WHERE id IN (
SELECT id
FROM impress_user
WHERE email = user_email
)
AND id != (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
);
RAISE NOTICE 'Processed updates for email: %', user_email;
END LOOP;
-- Step 4: Remove duplicate rows from the temporary table, keeping only one row per (document_id, user_id)
-- For impress_document_access
DELETE FROM impress_document_access_tmp a
USING impress_document_access_tmp b
WHERE a.ctid < b.ctid -- Keep one row
AND a.document_id = b.document_id
AND a.user_id = b.user_id;
-- Step 5: Replace the original table with the cleaned-up temporary table
TRUNCATE TABLE impress_document_access;
-- Insert cleaned-up data back into the original table
INSERT INTO impress_document_access
SELECT * FROM impress_document_access_tmp;
-- For impress_link_trace
DELETE FROM impress_link_trace_tmp a
USING impress_link_trace_tmp b
WHERE a.ctid < b.ctid -- Keep one row
AND a.document_id = b.document_id
AND a.user_id = b.user_id;
-- Step 5: Replace the original table with the cleaned-up temporary table
TRUNCATE TABLE impress_link_trace;
-- Insert cleaned-up data back into the original table
INSERT INTO impress_link_trace
SELECT * FROM impress_link_trace_tmp;
RAISE NOTICE 'Update and deduplication process completed.';
END $$;
"""
class Migration(migrations.Migration):
dependencies = [
('core', '0006_add_user_full_name_and_short_name'),
]
operations = [
migrations.RunSQL(procedure),
]
+2 -1
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "1.5.0"
version = "1.5.1"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -57,6 +57,7 @@ dependencies = [
"WeasyPrint>=60.2",
"whitenoise==6.7.0",
"mozilla-django-oidc==4.0.1",
"y-py==0.6.2"
]
[project.urls]
+13 -7
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine as frontend-deps-y-provider
FROM node:20-alpine AS frontend-deps-y-provider
WORKDIR /home/frontend/
@@ -15,7 +15,7 @@ COPY ./src/frontend/ .
COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
# ---- y-provider ----
FROM frontend-deps-y-provider as y-provider
FROM frontend-deps-y-provider AS y-provider
WORKDIR /home/frontend/servers/y-provider
RUN yarn build
@@ -28,7 +28,7 @@ ENTRYPOINT [ "/usr/local/bin/entrypoint" ]
CMD ["yarn", "start"]
FROM node:20-alpine as frontend-deps
FROM node:20-alpine AS frontend-deps
WORKDIR /home/frontend/
@@ -43,11 +43,11 @@ COPY .dockerignore ./.dockerignore
COPY ./src/frontend/ .
### ---- Front-end builder image ----
FROM frontend-deps as impress
FROM frontend-deps AS impress
WORKDIR /home/frontend/apps/impress
FROM frontend-deps as impress-dev
FROM frontend-deps AS impress-dev
WORKDIR /home/frontend/apps/impress
@@ -57,7 +57,7 @@ CMD [ "yarn", "dev"]
# Tilt will rebuild impress target so, we dissociate impress and impress-builder
# to avoid rebuilding the app at every changes.
FROM impress as impress-builder
FROM impress AS impress-builder
WORKDIR /home/frontend/apps/impress
@@ -70,10 +70,16 @@ ENV NEXT_PUBLIC_Y_PROVIDER_URL=${Y_PROVIDER_URL}
ARG API_ORIGIN
ENV NEXT_PUBLIC_API_ORIGIN=${API_ORIGIN}
ARG MEDIA_URL
ENV NEXT_PUBLIC_MEDIA_URL=${MEDIA_URL}
ARG SW_DEACTIVATED
ENV NEXT_PUBLIC_SW_DEACTIVATED=${SW_DEACTIVATED}
RUN yarn build
# ---- Front-end image ----
FROM nginxinc/nginx-unprivileged:1.26-alpine as frontend-production
FROM nginxinc/nginx-unprivileged:1.26-alpine AS frontend-production
# Un-privileged user running the application
ARG DOCKER_USER
@@ -26,7 +26,7 @@ test.describe('Header', () => {
}),
).toBeVisible();
await expect(header.getByAltText('Language Icon')).toBeVisible();
await expect(header.getByText('English')).toBeVisible();
await expect(
header.getByRole('button', {
@@ -87,7 +87,7 @@ test.describe('Header mobile', () => {
}),
).toBeHidden();
await expect(page.getByAltText('Language Icon')).toBeHidden();
await expect(page.getByText('English')).toBeHidden();
await header.getByLabel('Open the header menu').click();
@@ -97,7 +97,7 @@ test.describe('Header mobile', () => {
}),
).toBeVisible();
await expect(page.getByAltText('Language Icon')).toBeVisible();
await expect(page.getByText('English')).toBeVisible();
});
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "app-e2e",
"version": "1.5.0",
"version": "1.5.1",
"private": true,
"scripts": {
"lint": "eslint . --ext .ts",
+2 -1
View File
@@ -1,4 +1,5 @@
NEXT_PUBLIC_API_ORIGIN=
NEXT_PUBLIC_Y_PROVIDER_URL=
NEXT_PUBLIC_MEDIA_URL=
NEXT_PUBLIC_THEME=dsfr
NEXT_PUBLIC_THEME=dsfr
NEXT_PUBLIC_SW_DEACTIVATED=
+8
View File
@@ -189,6 +189,9 @@ const config = {
},
},
},
'la-gauffre': {
activated: false,
},
},
},
dsfr: {
@@ -321,6 +324,7 @@ const config = {
'color-hover': 'var(--c--theme--colors--primary-100)',
},
'color-hover': 'var(--c--theme--colors--primary-text)',
color: 'var(--c--theme--colors--primary-600)',
},
},
datagrid: {
@@ -335,6 +339,7 @@ const config = {
pagination: {
'background-color': 'transparent',
'background-color-active': 'var(--c--theme--colors--primary-300)',
'border-color': 'var(--c--theme--colors--primary-400)',
},
},
'forms-checkbox': {
@@ -384,6 +389,9 @@ const config = {
'forms-textarea': {
'border-radius': '0',
},
'la-gauffre': {
activated: true,
},
},
},
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "app-impress",
"version": "1.5.0",
"version": "1.5.1",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,77 +1,101 @@
@font-face {
font-family: Marianne;
src: url('Marianne-Thin.woff') format('truetype');
src:
url('Marianne-Thin.woff2') format('woff2'),
url('Marianne-Thin.woff') format('woff');
font-weight: 100;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Thin_Italic.woff') format('truetype');
src:
url('Marianne-Thin_Italic.woff2') format('woff2'),
url('Marianne-Thin_Italic.woff') format('woff');
font-weight: 100;
font-style: italic;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Light.woff') format('truetype');
src:
url('Marianne-Light.woff2') format('woff2'),
url('Marianne-Light.woff') format('woff');
font-weight: 300;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Light_Italic.woff') format('truetype');
src:
url('Marianne-Light_Italic.woff2') format('woff2'),
url('Marianne-Light_Italic.woff') format('woff');
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Regular.woff') format('truetype');
src:
url('Marianne-Regular.woff2') format('woff2'),
url('Marianne-Regular.woff') format('woff');
font-weight: 400;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Regular_Italic.woff') format('truetype');
src:
url('Marianne-Regular_Italic.woff2') format('woff2'),
url('Marianne-Regular_Italic.woff') format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Medium.woff') format('truetype');
src:
url('Marianne-Medium.woff2') format('woff2'),
url('Marianne-Medium.woff') format('woff');
font-weight: 500;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Medium_Italic.woff') format('truetype');
src:
url('Marianne-Medium_Italic.woff2') format('woff2'),
url('Marianne-Medium_Italic.woff') format('woff');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Bold.woff') format('truetype');
src:
url('Marianne-Bold.woff2') format('woff2'),
url('Marianne-Bold.woff') format('woff');
font-weight: 700;
}
@font-face {
font-family: Marianne;
src: url('Marianne-Bold_Italic.woff') format('truetype');
src:
url('Marianne-Bold_Italic.woff2') format('woff2'),
url('Marianne-Bold_Italic.woff') format('woff');
font-weight: 700;
font-style: italic;
}
@font-face {
font-family: Marianne;
src: url('Marianne-ExtraBold.woff') format('truetype');
src:
url('Marianne-ExtraBold.woff2') format('woff2'),
url('Marianne-ExtraBold.woff') format('woff');
font-weight: 800;
}
@font-face {
font-family: Marianne;
src: url('Marianne-ExtraBold_Italic.woff') format('truetype');
src:
url('Marianne-ExtraBold_Italic.woff2') format('woff2'),
url('Marianne-ExtraBold_Italic.woff') format('woff');
font-weight: 800;
font-style: italic;
}
@@ -13,6 +13,7 @@ export const IconBG = ({ iconName, ...textProps }: IconBGProps) => {
$isMaterialIcon
$size="36px"
$theme="primary"
$variation="600"
$background={colorsTokens()['primary-bg']}
$css={`
border: 1px solid ${colorsTokens()['primary-200']};
@@ -255,6 +255,7 @@ input:-webkit-autofill:focus {
gap: 3px;
border-radius: 4px;
background: var(--c--components--datagrid--pagination--background-color);
border-color: var(--c--components--datagrid--pagination--border-color);
}
.c__pagination__list .c__button--tertiary-text.c__button--active {
@@ -452,6 +453,7 @@ input:-webkit-autofill:focus {
.c__button--tertiary-text {
border: none;
color: var(--c--components--button--tertiary-text--color);
}
.c__button--tertiary-text:hover,
@@ -283,6 +283,7 @@
);
--c--components--button--disabled--color: white;
--c--components--button--disabled--background--color: #b3cef0;
--c--components--la-gauffre--activated: false;
}
.cunningham-theme--dark {
@@ -451,6 +452,9 @@
--c--components--button--tertiary-text--color-hover: var(
--c--theme--colors--primary-text
);
--c--components--button--tertiary-text--color: var(
--c--theme--colors--primary-600
);
--c--components--datagrid--header--color: var(
--c--theme--colors--primary-text
);
@@ -461,6 +465,9 @@
--c--components--datagrid--pagination--background-color-active: var(
--c--theme--colors--primary-300
);
--c--components--datagrid--pagination--border-color: var(
--c--theme--colors--primary-400
);
--c--components--forms-checkbox--border-radius: 0;
--c--components--forms-checkbox--color: var(--c--theme--colors--primary-text);
--c--components--forms-checkbox--text--color: var(
@@ -504,6 +511,7 @@
--c--theme--colors--primary-text
);
--c--components--forms-textarea--border-radius: 0;
--c--components--la-gauffre--activated: true;
}
.clr-secondary-text {
@@ -276,6 +276,7 @@ export const tokens = {
},
disabled: { color: 'white', background: { color: '#b3cef0' } },
},
'la-gauffre': { activated: false },
},
},
dark: {
@@ -450,6 +451,7 @@ export const tokens = {
'color-hover': 'var(--c--theme--colors--primary-100)',
},
'color-hover': 'var(--c--theme--colors--primary-text)',
color: 'var(--c--theme--colors--primary-600)',
},
},
datagrid: {
@@ -464,6 +466,7 @@ export const tokens = {
pagination: {
'background-color': 'transparent',
'background-color-active': 'var(--c--theme--colors--primary-300)',
'border-color': 'var(--c--theme--colors--primary-400)',
},
},
'forms-checkbox': {
@@ -503,6 +506,7 @@ export const tokens = {
'accent-color': 'var(--c--theme--colors--primary-text)',
},
'forms-textarea': { 'border-radius': '0' },
'la-gauffre': { activated: true },
},
},
},
@@ -102,6 +102,7 @@ export const PanelEditor = ({
$weight="bold"
$size="m"
$theme="primary"
$variation="600"
$padding={{ vertical: 'small', horizontal: 'small' }}
>
{t('Table of content')}
@@ -118,6 +119,7 @@ export const PanelEditor = ({
$weight="bold"
$size="m"
$theme="primary"
$variation="600"
$padding={{ vertical: 'small', horizontal: 'small' }}
>
{t('Versions')}
@@ -44,6 +44,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
<Text
$isMaterialIcon
$theme="primary"
$variation="600"
$size="2rem"
$css={`&:hover {background-color: ${colorsTokens()['primary-100']}; };`}
$hasTransition
@@ -2,7 +2,7 @@ import { Button } from '@openfun/cunningham-react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, DropButton, IconOptions, Text } from '@/components';
import { Box, DropButton, IconOptions } from '@/components';
import { useAuthStore } from '@/core';
import { usePanelEditorStore } from '@/features/docs/doc-editor/';
import {
@@ -86,7 +86,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
icon={<span className="material-icons">history</span>}
size="small"
>
<Text $theme="primary">{t('Version history')}</Text>
{t('Version history')}
</Button>
)}
<Button
@@ -99,7 +99,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
icon={<span className="material-icons">summarize</span>}
size="small"
>
<Text $theme="primary">{t('Table of contents')}</Text>
{t('Table of contents')}
</Button>
<Button
onClick={() => {
@@ -110,7 +110,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
icon={<span className="material-icons">file_download</span>}
size="small"
>
<Text $theme="primary">{t('Export')}</Text>
{t('Export')}
</Button>
{doc.abilities.destroy && (
<Button
@@ -122,7 +122,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
icon={<span className="material-icons">delete</span>}
size="small"
>
<Text $theme="primary">{t('Delete document')}</Text>
{t('Delete document')}
</Button>
)}
</Box>
@@ -152,7 +152,12 @@ export const ModalPDF = ({ onClose, doc }: ModalPDFProps) => {
size={ModalSize.MEDIUM}
title={
<Box $align="center" $gap="1rem">
<Text className="material-icons" $size="3.5rem" $theme="primary">
<Text
className="material-icons"
$size="3.5rem"
$theme="primary"
$variation="600"
>
picture_as_pdf
</Text>
<Text as="h2" $size="h3" $margin="none" $theme="primary">
@@ -1,6 +0,0 @@
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 38C12 40.2 13.8 42 16 42H32C34.2 42 36 40.2 36 38V14H12V38ZM18.34 25.18C17.56 24.4 17.56 23.14 18.34 22.36C19.12 21.58 20.38 21.58 21.16 22.36L24 25.18L26.82 22.36C27.6 21.58 28.86 21.58 29.64 22.36C30.42 23.14 30.42 24.4 29.64 25.18L26.82 28L29.64 30.82C30.42 31.6 30.42 32.86 29.64 33.64C28.86 34.42 27.6 34.42 26.82 33.64L24 30.82L21.18 33.64C20.4 34.42 19.14 34.42 18.36 33.64C17.58 32.86 17.58 31.6 18.36 30.82L21.18 28L18.34 25.18ZM36 8H31L29.58 6.58C29.22 6.22 28.7 6 28.18 6H19.82C19.3 6 18.78 6.22 18.42 6.58L17 8H12C10.9 8 10 8.9 10 10C10 11.1 10.9 12 12 12H36C37.1 12 38 11.1 38 10C38 8.9 37.1 8 36 8Z"
fill="currentColor"
/>
</svg>

Before

Width:  |  Height:  |  Size: 742 B

@@ -14,7 +14,6 @@ import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
import { useRemoveDoc } from '../api/useRemoveDoc';
import IconDoc from '../assets/icon-doc.svg';
import IconRemove from '../assets/icon-trash.svg';
import { Doc } from '../types';
interface ModalRemoveDocProps {
@@ -73,7 +72,9 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
size={ModalSize.MEDIUM}
title={
<Box $align="center" $gap="1rem">
<IconRemove width={48} color={colorsTokens()['primary-text']} />
<Text $isMaterialIcon $size="48px" $theme="primary" $variation="600">
delete_forever
</Text>
<Text as="h2" $size="h3" $margin="none">
{t('Deleting the document "{{title}}"', { title: doc.title })}
</Text>
@@ -1,7 +1,7 @@
import { t } from 'i18next';
import { createGlobalStyle } from 'styled-components';
import { Box, Card, SideModal, Text } from '@/components';
import { Box, Card, IconBG, SideModal, Text } from '@/components';
import { InvitationList } from '@/features/docs/members/invitation-list';
import { AddMembers } from '@/features/docs/members/members-add';
import { MemberList } from '@/features/docs/members/members-list';
@@ -62,9 +62,12 @@ export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
$padding="tiny"
$gap="1rem"
>
<Text $isMaterialIcon $size="48px" $theme="primary">
share
</Text>
<IconBG
$isMaterialIcon
$size="48px"
iconName="share"
$margin="none"
/>
<Box $align="flex-start">
<Text as="h3" $size="26px" $margin="none">
{t('Share')}
@@ -35,13 +35,13 @@ export const VersionItem = ({
as="li"
$background={isActive ? colorsTokens()['primary-300'] : 'transparent'}
$css={`
border-left: 4px solid transparent;
border-bottom: 1px solid ${colorsTokens()['primary-100']};
&:hover{
border-left: 4px solid ${colorsTokens()['primary-400']};
background: ${colorsTokens()['primary-300']};
}
`}
border-left: 4px solid transparent;
border-bottom: 1px solid ${colorsTokens()['primary-100']};
&:hover{
border-left: 4px solid ${colorsTokens()['primary-400']};
background: ${colorsTokens()['primary-300']};
}
`}
$hasTransition
$minWidth="13rem"
>
@@ -54,10 +54,15 @@ export const VersionItem = ({
$width="100%"
>
<Box $direction="row" $gap="0.5rem" $align="center">
<Text $isMaterialIcon $size="24px" $theme="primary">
<Text
$isMaterialIcon
$size="24px"
$theme="primary"
$variation="600"
>
description
</Text>
<Text $weight="bold" $theme="primary" $size="m">
<Text $weight="bold" $theme="primary" $size="m" $variation="600">
{text}
</Text>
</Box>
@@ -81,7 +86,7 @@ export const VersionItem = ({
icon={<span className="material-icons">save</span>}
size="small"
>
<Text $theme="primary">{t('Restore the version')}</Text>
{t('Restore the version')}
</Button>
</Box>
</DropButton>
@@ -94,6 +94,15 @@ export const AddMembers = ({ currentRole, doc }: ModalAddMembersProps) => {
});
}
if (
dataError.cause?.[0] ===
'This email is already associated to a registered user.'
) {
messageError = t('"{{email}}" is already member of the document.', {
email: dataError['data']?.value,
});
}
toast(messageError, VariantType.ERROR, toastOptions);
};
@@ -123,6 +132,7 @@ export const AddMembers = ({ currentRole, doc }: ModalAddMembersProps) => {
setIsPending(false);
setResetKey(resetKey + 1);
setSelectedUsers([]);
setSelectedRole(undefined);
settledPromises.forEach((settledPromise) => {
switch (settledPromise.status) {
@@ -156,7 +166,7 @@ export const AddMembers = ({ currentRole, doc }: ModalAddMembersProps) => {
<Box $gap="0.7rem" $direction="row" $wrap="wrap" $css="flex: 80%;">
<Box $css="flex: auto;" $width="15rem">
<SearchUsers
key={resetKey + 1}
key={resetKey}
doc={doc}
setSelectedUsers={setSelectedUsers}
selectedUsers={selectedUsers}
@@ -70,6 +70,7 @@ export const SearchUsers = ({
if (!isFoundUser && !isFoundEmail) {
users = [
...users,
{
value: { email: userQuery },
label: userQuery,
@@ -83,8 +84,7 @@ export const SearchUsers = ({
resolveOptionsRef.current = null;
return users;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [options, selectedUsers]);
}, [options, selectedUsers, userQuery]);
const loadOptions = (): Promise<OptionsSelect> => {
return new Promise<OptionsSelect>((resolve) => {
@@ -1 +1,2 @@
export * from './api';
export * from './components';
@@ -6,6 +6,7 @@ import {
import { APIError, errorCauses, fetchAPI } from '@/api';
import { KEY_DOC, KEY_LIST_DOC } from '@/features/docs/doc-management';
import { KEY_LIST_USER } from '@/features/docs/members/members-add';
import { KEY_LIST_DOC_ACCESSES } from './useDocAccesses';
@@ -51,6 +52,9 @@ export const useDeleteDocAccess = (options?: UseDeleteDocAccessOptions) => {
void queryClient.resetQueries({
queryKey: [KEY_LIST_DOC],
});
void queryClient.invalidateQueries({
queryKey: [KEY_LIST_USER],
});
if (options?.onSuccess) {
options.onSuccess(data, variables, context);
}
@@ -116,11 +116,7 @@ export const MemberItem = ({
<Button
color="tertiary-text"
icon={
<Text
$isMaterialIcon
$theme={isNotAllowed ? 'greyscale' : 'primary'}
$variation={isNotAllowed ? '500' : 'text'}
>
<Text $isMaterialIcon $color="inherit">
delete
</Text>
}
@@ -62,9 +62,10 @@ export const Header = () => {
<Text
$margin="none"
as="h2"
$theme="primary"
$color="#000091"
$zIndex={1}
$size="1.30rem"
$css="font-family: 'Marianne'"
>
{t('Docs')}
</Text>
@@ -4,20 +4,30 @@ import Script from 'next/script';
import React from 'react';
import { createGlobalStyle } from 'styled-components';
import { useCunninghamTheme } from '@/cunningham';
const GaufreStyle = createGlobalStyle`
.lasuite-gaufre-btn{
box-shadow: inset 0 0 0 0 !important;
}
`;
export const LaGaufre = () => (
<>
<Script
src="https://integration.lasuite.numerique.gouv.fr/api/v1/gaufre.js"
strategy="lazyOnload"
id="lasuite-gaufre-script"
/>
<GaufreStyle />
<Gaufre variant="small" />
</>
);
export const LaGaufre = () => {
const { componentTokens } = useCunninghamTheme();
if (!componentTokens()['la-gauffre'].activated) {
return null;
}
return (
<>
<Script
src="https://integration.lasuite.numerique.gouv.fr/api/v1/gaufre.js"
strategy="lazyOnload"
id="lasuite-gaufre-script"
/>
<GaufreStyle />
<Gaufre variant="small" />
</>
);
};
@@ -1,5 +1,4 @@
import { Select } from '@openfun/cunningham-react';
import Image from 'next/image';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
@@ -7,8 +6,6 @@ import styled from 'styled-components';
import { Box, Text } from '@/components/';
import { LANGUAGES_ALLOWED } from '@/i18n/conf';
import IconLanguage from './assets/icon-language.svg?url';
const SelectStyled = styled(Select)<{ $isSmall?: boolean }>`
flex-shrink: 0;
width: auto;
@@ -49,12 +46,16 @@ export const LanguagePicker = () => {
$gap="0.7rem"
$align="center"
>
<Image priority src={IconLanguage} alt={t('Language Icon')} />
<Text $theme="primary">{LANGUAGES_ALLOWED[lang]}</Text>
<Text $isMaterialIcon $size="1rem" $theme="primary" $variation="600">
translate
</Text>
<Text $theme="primary" $variation="600">
{LANGUAGES_ALLOWED[lang]}
</Text>
</Box>
),
}));
}, [languages, t]);
}, [languages]);
return (
<SelectStyled
@@ -1,14 +0,0 @@
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12.334 6.66683L15.2673 14.0002H13.8307L13.03 12.0002H10.3033L9.50398 14.0002H8.06798L11.0007 6.66683H12.334ZM6.66732 1.3335V2.66683H10.6673V4.00016H9.35532C8.84106 5.54821 8.0203 6.97684 6.94198 8.20083C7.42285 8.6299 7.94444 9.01104 8.49932 9.33883L7.99865 10.5908C7.28233 10.1846 6.61238 9.70149 6.00065 9.15016C4.80971 10.228 3.39934 11.035 1.86665 11.5155L1.50932 10.2295C2.82254 9.81077 4.03266 9.11972 5.06065 8.2015C4.29978 7.34012 3.66603 6.37434 3.17865 5.3335H4.67198C5.04355 6.0194 5.4891 6.66257 6.00065 7.2515C6.83406 6.2909 7.49085 5.19037 7.94065 4.00083L1.33398 4.00016V2.66683H5.33398V1.3335H6.66732ZM11.6673 8.59016L10.836 10.6668H12.4973L11.6673 8.59016Z"
fill="#000091"
/>
</svg>

Before

Width:  |  Height:  |  Size: 877 B

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "1.5.0",
"version": "1.5.1",
"private": true,
"workspaces": {
"packages": [
@@ -1,6 +1,6 @@
{
"name": "eslint-config-impress",
"version": "1.5.0",
"version": "1.5.1",
"license": "MIT",
"scripts": {
"lint": "eslint --ext .js ."
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "packages-i18n",
"version": "1.5.0",
"version": "1.5.1",
"private": true,
"scripts": {
"extract-translation": "yarn extract-translation:impress",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "server-y-provider",
"version": "1.5.0",
"version": "1.5.1",
"description": "Y.js provider for docs",
"repository": "https://github.com/numerique-gouv/impress",
"license": "MIT",
@@ -1,7 +1,7 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v1.5.0-preprod"
tag: "v1.5.1-preprod"
backend:
migrateJobAnnotations:
@@ -124,13 +124,13 @@ frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v1.5.0-preprod"
tag: "v1.5.1-preprod"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v1.5.0-preprod"
tag: "v1.5.1-preprod"
ingress:
enabled: true
@@ -1,7 +1,7 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v1.5.0"
tag: "v1.5.1"
backend:
migrateJobAnnotations:
@@ -124,13 +124,13 @@ frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v1.5.0"
tag: "v1.5.1"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v1.5.0"
tag: "v1.5.1"
ingress:
enabled: true
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.5.0",
"version": "1.5.1",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {