mirror of
https://github.com/suitenumerique/docs.git
synced 2026-05-08 16:12:26 +02:00
Compare commits
40 Commits
new-ui/new
...
v1.8.1-pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59ca5c12a3 | ||
|
|
c94f26c8b9 | ||
|
|
fc2f14b3f4 | ||
|
|
6dd1697915 | ||
|
|
79e899c301 | ||
|
|
2194301716 | ||
|
|
0348894ab8 | ||
|
|
9b17d8bea1 | ||
|
|
69d6b6f934 | ||
|
|
6c106374fa | ||
|
|
af039d045d | ||
|
|
4c9caf09ba | ||
|
|
3fd02adbec | ||
|
|
90dac3cd15 | ||
|
|
d0307ee6d9 | ||
|
|
09d02b7ced | ||
|
|
56a26d9663 | ||
|
|
42f809f6d4 | ||
|
|
7d64c82987 | ||
|
|
6252227bb6 | ||
|
|
e9ac393a8f | ||
|
|
5b1745f991 | ||
|
|
0e55bf5c43 | ||
|
|
9f66f73501 | ||
|
|
c3da28b07f | ||
|
|
b035b96dec | ||
|
|
9623ac4141 | ||
|
|
c8edbd285b | ||
|
|
016597d5a2 | ||
|
|
52dea8fa2f | ||
|
|
0a37a8ea6d | ||
|
|
c1404ef904 | ||
|
|
2c0fce61df | ||
|
|
bbe9b6b6cf | ||
|
|
23231563c9 | ||
|
|
d75c8668c5 | ||
|
|
f266232b5a | ||
|
|
a8362e8e88 | ||
|
|
e4dfae1905 | ||
|
|
a09e740648 |
6
.github/workflows/docker-hub.yml
vendored
6
.github/workflows/docker-hub.yml
vendored
@@ -8,6 +8,9 @@ on:
|
||||
- 'main'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
env:
|
||||
DOCKER_USER: 1001:127
|
||||
@@ -52,6 +55,7 @@ jobs:
|
||||
with:
|
||||
docker-build-args: '--target backend-production -f Dockerfile'
|
||||
docker-image-name: 'docker.io/lasuite/impress-backend:${{ github.sha }}'
|
||||
continue-on-error: true
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -102,6 +106,7 @@ jobs:
|
||||
with:
|
||||
docker-build-args: '-f src/frontend/Dockerfile --target frontend-production'
|
||||
docker-image-name: 'docker.io/lasuite/impress-frontend:${{ github.sha }}'
|
||||
continue-on-error: true
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -153,6 +158,7 @@ jobs:
|
||||
with:
|
||||
docker-build-args: '-f src/frontend/Dockerfile --target y-provider'
|
||||
docker-image-name: 'docker.io/lasuite/impress-frontend:${{ github.sha }}'
|
||||
continue-on-error: true
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
|
||||
22
.github/workflows/helmfile-linter.yaml
vendored
Normal file
22
.github/workflows/helmfile-linter.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Helmfile lint
|
||||
run-name: Helmfile lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
helmfile-lint:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/helmfile/helmfile:latest
|
||||
steps:
|
||||
-
|
||||
uses: numerique-gouv/action-helmfile-lint@main
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
age-key: ${{ secrets.SOPS_PRIVATE }}
|
||||
private-key: ${{ secrets.PRIVATE_KEY }}
|
||||
helmfile-src: "src/helm"
|
||||
repositories: "impress,secrets"
|
||||
4
.github/workflows/impress-frontend.yml
vendored
4
.github/workflows/impress-frontend.yml
vendored
@@ -99,7 +99,7 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: cd src/frontend/ && yarn e2e:test --project='chromium'
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-chromium-report
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: cd src/frontend/ && yarn e2e:test --project=firefox --project=webkit
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-other-report
|
||||
|
||||
6
.github/workflows/impress.yml
vendored
6
.github/workflows/impress.yml
vendored
@@ -107,7 +107,9 @@ jobs:
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.12.6"
|
||||
- name: Upgrade pip and setuptools
|
||||
run: pip install --upgrade pip setuptools
|
||||
- name: Install development dependencies
|
||||
run: pip install --user .[dev]
|
||||
- name: Check code formatting with ruff
|
||||
@@ -199,7 +201,7 @@ jobs:
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.12.6"
|
||||
|
||||
- name: Install development dependencies
|
||||
run: pip install --user .[dev]
|
||||
|
||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -9,22 +9,41 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
## [1.8.1] - 2024-11-27
|
||||
|
||||
## Fixed
|
||||
|
||||
🐛(frontend) link not clickable and flickering firefox #457
|
||||
|
||||
|
||||
## [1.8.0] - 2024-11-25
|
||||
|
||||
## Added
|
||||
|
||||
- 🌐(backend) add german translation #259
|
||||
- 🌐(frontend) Add German translation #255
|
||||
- ✨(frontend) Add a broadcast store #387
|
||||
- ✨(frontend) WIP: New ui
|
||||
- ✨(backend) whitelist pod's IP address #443
|
||||
- ✨(backend) config endpoint #425
|
||||
- ✨(frontend) config endpoint #424
|
||||
- ✨(frontend) add sentry #424
|
||||
- ✨(frontend) add crisp chatbot #450
|
||||
|
||||
## Changed
|
||||
|
||||
- 🚸(backend) improve users similarity search and sort results #391
|
||||
- 🌐(backend) add german translation #259
|
||||
- ♻️(frontend) simplify stores #402
|
||||
- ✨(frontend) update $css Box props type to add styled components RuleSet #423
|
||||
- ✅(CI) trivy continue on error #453
|
||||
|
||||
## Fixed
|
||||
|
||||
- 🔧(backend) fix logging for docker and make it configurable by envar #427
|
||||
- 🦺(backend) add comma to sub regex #408
|
||||
- 🐛(editor) collaborative user tag hidden when read only #385
|
||||
- 🐛(frontend) user have view access when revoked #387
|
||||
- 🐛(frontend) users have view access when revoked #387
|
||||
- 🐛(frontend) fix placeholder editable when double clicks #454
|
||||
|
||||
|
||||
## [1.7.0] - 2024-10-24
|
||||
@@ -256,7 +275,9 @@ and this project adheres to
|
||||
- 🚀 Impress, project to manage your documents easily and collaboratively.
|
||||
|
||||
|
||||
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.7.0...main
|
||||
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.8.1...main
|
||||
[v1.8.1]: https://github.com/numerique-gouv/impress/releases/v1.8.1
|
||||
[v1.8.0]: https://github.com/numerique-gouv/impress/releases/v1.8.0
|
||||
[v1.7.0]: https://github.com/numerique-gouv/impress/releases/v1.7.0
|
||||
[v1.6.0]: https://github.com/numerique-gouv/impress/releases/v1.6.0
|
||||
[1.5.1]: https://github.com/numerique-gouv/impress/releases/v1.5.1
|
||||
|
||||
@@ -4,6 +4,12 @@ DJANGO_SECRET_KEY=ThisIsAnExampleKeyForDevPurposeOnly
|
||||
DJANGO_SETTINGS_MODULE=impress.settings
|
||||
DJANGO_SUPERUSER_PASSWORD=admin
|
||||
|
||||
# Logging
|
||||
# Set to DEBUG level for dev only
|
||||
LOGGING_LEVEL_HANDLERS_CONSOLE=INFO
|
||||
LOGGING_LEVEL_LOGGERS_ROOT=INFO
|
||||
LOGGING_LEVEL_LOGGERS_APP=INFO
|
||||
|
||||
# Python
|
||||
PYTHONPATH=/app
|
||||
|
||||
@@ -21,6 +27,7 @@ STORAGES_STATICFILES_BACKEND=django.contrib.staticfiles.storage.StaticFilesStora
|
||||
AWS_S3_ENDPOINT_URL=http://minio:9000
|
||||
AWS_S3_ACCESS_KEY_ID=impress
|
||||
AWS_S3_SECRET_ACCESS_KEY=password
|
||||
MEDIA_BASE_URL=http://localhost:8083
|
||||
|
||||
# OIDC
|
||||
OIDC_OP_JWKS_ENDPOINT=http://nginx:8083/realms/impress/protocol/openid-connect/certs
|
||||
@@ -44,3 +51,9 @@ OIDC_AUTH_REQUEST_EXTRA_PARAMS={"acr_values": "eidas1"}
|
||||
AI_BASE_URL=https://openaiendpoint.com
|
||||
AI_API_KEY=password
|
||||
AI_MODEL=llama
|
||||
|
||||
# Collaboration
|
||||
COLLABORATION_SERVER_URL=ws://localhost:4444
|
||||
|
||||
# Frontend
|
||||
FRONTEND_THEME=dsfr
|
||||
|
||||
@@ -26,11 +26,13 @@ from rest_framework import (
|
||||
mixins,
|
||||
pagination,
|
||||
status,
|
||||
views,
|
||||
viewsets,
|
||||
)
|
||||
from rest_framework import (
|
||||
response as drf_response,
|
||||
)
|
||||
from rest_framework.permissions import AllowAny
|
||||
|
||||
from core import enums, models
|
||||
from core.services.ai_services import AIService
|
||||
@@ -886,3 +888,31 @@ class InvitationViewset(
|
||||
invitation.document.email_invitation(
|
||||
language, invitation.email, invitation.role, self.request.user
|
||||
)
|
||||
|
||||
|
||||
class ConfigView(views.APIView):
|
||||
"""API ViewSet for sharing some public settings."""
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
GET /api/v1.0/config/
|
||||
Return a dictionary of public settings.
|
||||
"""
|
||||
array_settings = [
|
||||
"COLLABORATION_SERVER_URL",
|
||||
"CRISP_WEBSITE_ID",
|
||||
"ENVIRONMENT",
|
||||
"FRONTEND_THEME",
|
||||
"MEDIA_BASE_URL",
|
||||
"LANGUAGES",
|
||||
"LANGUAGE_CODE",
|
||||
"SENTRY_DSN",
|
||||
]
|
||||
dict_settings = {}
|
||||
for setting in array_settings:
|
||||
if hasattr(settings, setting):
|
||||
dict_settings[setting] = getattr(settings, setting)
|
||||
|
||||
return drf_response.Response(dict_settings)
|
||||
|
||||
45
src/backend/core/tests/test_api_config.py
Normal file
45
src/backend/core/tests/test_api_config.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Test config API endpoints in the Impress core app.
|
||||
"""
|
||||
|
||||
from django.test import override_settings
|
||||
|
||||
import pytest
|
||||
from rest_framework.status import (
|
||||
HTTP_200_OK,
|
||||
)
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@override_settings(
|
||||
COLLABORATION_SERVER_URL="http://testcollab/",
|
||||
CRISP_WEBSITE_ID="123",
|
||||
FRONTEND_THEME="test-theme",
|
||||
MEDIA_BASE_URL="http://testserver/",
|
||||
SENTRY_DSN="https://sentry.test/123",
|
||||
)
|
||||
@pytest.mark.parametrize("is_authenticated", [False, True])
|
||||
def test_api_config(is_authenticated):
|
||||
"""Anonymous users should be allowed to get the configuration."""
|
||||
client = APIClient()
|
||||
|
||||
if is_authenticated:
|
||||
user = factories.UserFactory()
|
||||
client.force_login(user)
|
||||
|
||||
response = client.get("/api/v1.0/config/")
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response.json() == {
|
||||
"COLLABORATION_SERVER_URL": "http://testcollab/",
|
||||
"CRISP_WEBSITE_ID": "123",
|
||||
"ENVIRONMENT": "test",
|
||||
"FRONTEND_THEME": "test-theme",
|
||||
"LANGUAGES": [["en-us", "English"], ["fr-fr", "French"], ["de-de", "German"]],
|
||||
"LANGUAGE_CODE": "en-us",
|
||||
"MEDIA_BASE_URL": "http://testserver/",
|
||||
"SENTRY_DSN": "https://sentry.test/123",
|
||||
}
|
||||
@@ -55,4 +55,5 @@ urlpatterns = [
|
||||
]
|
||||
),
|
||||
),
|
||||
path(f"api/{settings.API_VERSION}/config/", viewsets.ConfigView.as_view()),
|
||||
]
|
||||
|
||||
@@ -10,8 +10,9 @@ For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import tomllib
|
||||
from socket import gethostbyname, gethostname
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@@ -27,19 +28,12 @@ DATA_DIR = os.path.join("/", "data")
|
||||
def get_release():
|
||||
"""
|
||||
Get the current release of the application
|
||||
|
||||
By release, we mean the release from the version.json file à la Mozilla [1]
|
||||
(if any). If this file has not been found, it defaults to "NA".
|
||||
|
||||
[1]
|
||||
https://github.com/mozilla-services/Dockerflow/blob/master/docs/version_object.md
|
||||
"""
|
||||
# Try to get the current release from the version.json file generated by the
|
||||
# CI during the Docker image build
|
||||
try:
|
||||
with open(os.path.join(BASE_DIR, "version.json"), encoding="utf8") as version:
|
||||
return json.load(version)["version"]
|
||||
except FileNotFoundError:
|
||||
with open(os.path.join(BASE_DIR, "pyproject.toml"), "rb") as f:
|
||||
pyproject_data = tomllib.load(f)
|
||||
return pyproject_data["project"]["version"]
|
||||
except (FileNotFoundError, KeyError):
|
||||
return "NA" # Default: not available
|
||||
|
||||
|
||||
@@ -56,7 +50,7 @@ class Base(Configuration):
|
||||
You may also want to override default configuration by setting the following environment
|
||||
variables:
|
||||
|
||||
* DJANGO_SENTRY_DSN
|
||||
* SENTRY_DSN
|
||||
* DB_NAME
|
||||
* DB_HOST
|
||||
* DB_PASSWORD
|
||||
@@ -104,6 +98,9 @@ class Base(Configuration):
|
||||
STATIC_ROOT = os.path.join(DATA_DIR, "static")
|
||||
MEDIA_URL = "/media/"
|
||||
MEDIA_ROOT = os.path.join(DATA_DIR, "media")
|
||||
MEDIA_BASE_URL = values.Value(
|
||||
None, environ_name="MEDIA_BASE_URL", environ_prefix=None
|
||||
)
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
@@ -372,7 +369,22 @@ class Base(Configuration):
|
||||
CORS_ALLOWED_ORIGIN_REGEXES = values.ListValue([])
|
||||
|
||||
# Sentry
|
||||
SENTRY_DSN = values.Value(None, environ_name="SENTRY_DSN")
|
||||
SENTRY_DSN = values.Value(None, environ_name="SENTRY_DSN", environ_prefix=None)
|
||||
|
||||
# Collaboration
|
||||
COLLABORATION_SERVER_URL = values.Value(
|
||||
None, environ_name="COLLABORATION_SERVER_URL", environ_prefix=None
|
||||
)
|
||||
|
||||
# Frontend
|
||||
FRONTEND_THEME = values.Value(
|
||||
None, environ_name="FRONTEND_THEME", environ_prefix=None
|
||||
)
|
||||
|
||||
# Crisp
|
||||
CRISP_WEBSITE_ID = values.Value(
|
||||
None, environ_name="CRISP_WEBSITE_ID", environ_prefix=None
|
||||
)
|
||||
|
||||
# Easy thumbnails
|
||||
THUMBNAIL_EXTENSION = "webp"
|
||||
@@ -482,6 +494,42 @@ class Base(Configuration):
|
||||
environ_prefix=None,
|
||||
)
|
||||
|
||||
# Logging
|
||||
# We want to make it easy to log to console but by default we log production
|
||||
# to Sentry and don't want to log to console.
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"level": values.Value(
|
||||
"ERROR",
|
||||
environ_name="LOGGING_LEVEL_HANDLERS_CONSOLE",
|
||||
environ_prefix=None,
|
||||
),
|
||||
},
|
||||
},
|
||||
# Override root logger to send it to console
|
||||
"root": {
|
||||
"handlers": ["console"],
|
||||
"level": values.Value(
|
||||
"INFO", environ_name="LOGGING_LEVEL_LOGGERS_ROOT", environ_prefix=None
|
||||
),
|
||||
},
|
||||
"loggers": {
|
||||
"core": {
|
||||
"handlers": ["console"],
|
||||
"level": values.Value(
|
||||
"INFO",
|
||||
environ_name="LOGGING_LEVEL_LOGGERS_APP",
|
||||
environ_prefix=None,
|
||||
),
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
@property
|
||||
def ENVIRONMENT(self):
|
||||
@@ -577,23 +625,6 @@ class Development(Base):
|
||||
class Test(Base):
|
||||
"""Test environment settings"""
|
||||
|
||||
LOGGING = values.DictValue(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"impress": {
|
||||
"handlers": ["console"],
|
||||
"level": "DEBUG",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
PASSWORD_HASHERS = [
|
||||
"django.contrib.auth.hashers.MD5PasswordHasher",
|
||||
]
|
||||
@@ -624,7 +655,13 @@ class Production(Base):
|
||||
"""
|
||||
|
||||
# Security
|
||||
ALLOWED_HOSTS = values.ListValue(None)
|
||||
# Add allowed host from environment variables.
|
||||
# The machine hostname is added by default,
|
||||
# it makes the application pingable by a load balancer on the same machine by example
|
||||
ALLOWED_HOSTS = [
|
||||
*values.ListValue([], environ_name="ALLOWED_HOSTS"),
|
||||
gethostbyname(gethostname()),
|
||||
]
|
||||
CSRF_TRUSTED_ORIGINS = values.ListValue([])
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "impress"
|
||||
version = "1.7.0"
|
||||
version = "1.8.1"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
@@ -17,13 +17,13 @@ classifiers = [
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Natural Language :: English",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
]
|
||||
description = "An application to print markdown to pdf from a set of managed templates."
|
||||
keywords = ["Django", "Contacts", "Templates", "RBAC"]
|
||||
license = { file = "LICENSE" }
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"boto3==1.35.44",
|
||||
"Brotli==1.1.0",
|
||||
@@ -127,6 +127,7 @@ select = [
|
||||
[tool.ruff.lint.isort]
|
||||
section-order = ["future","standard-library","django","third-party","impress","first-party","local-folder"]
|
||||
sections = { impress=["core"], django=["django"] }
|
||||
extra-standard-library = ["tomllib"]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"**/tests/*" = ["S", "SLF"]
|
||||
|
||||
@@ -61,18 +61,9 @@ FROM impress AS impress-builder
|
||||
|
||||
WORKDIR /home/frontend/apps/impress
|
||||
|
||||
ARG FRONTEND_THEME
|
||||
ENV NEXT_PUBLIC_THEME=${FRONTEND_THEME}
|
||||
|
||||
ARG Y_PROVIDER_URL
|
||||
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}
|
||||
|
||||
|
||||
160
src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts
Normal file
160
src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import path from 'path';
|
||||
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createDoc } from './common';
|
||||
|
||||
const config = {
|
||||
CRISP_WEBSITE_ID: null,
|
||||
COLLABORATION_SERVER_URL: 'ws://localhost:4444',
|
||||
ENVIRONMENT: 'development',
|
||||
FRONTEND_THEME: 'dsfr',
|
||||
MEDIA_BASE_URL: 'http://localhost:8083',
|
||||
LANGUAGES: [
|
||||
['en-us', 'English'],
|
||||
['fr-fr', 'French'],
|
||||
['de-de', 'German'],
|
||||
],
|
||||
LANGUAGE_CODE: 'en-us',
|
||||
SENTRY_DSN: null,
|
||||
};
|
||||
|
||||
test.describe('Config', () => {
|
||||
test('it checks the config api is called', async ({ page }) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/config/') && response.status() === 200,
|
||||
);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
expect(await response.json()).toStrictEqual(config);
|
||||
});
|
||||
|
||||
test('it checks that sentry is trying to init from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route('**/api/v1.0/config/', async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('GET')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
...config,
|
||||
SENTRY_DSN: 'https://sentry.io/123',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
const invalidMsg = 'Invalid Sentry Dsn: https://sentry.io/123';
|
||||
const consoleMessage = page.waitForEvent('console', {
|
||||
timeout: 5000,
|
||||
predicate: (msg) => msg.text().includes(invalidMsg),
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
expect((await consoleMessage).text()).toContain(invalidMsg);
|
||||
});
|
||||
|
||||
test('it checks that theme is configured from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/config/') && response.status() === 200,
|
||||
);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
const jsonResponse = await response.json();
|
||||
expect(jsonResponse.FRONTEND_THEME).toStrictEqual('dsfr');
|
||||
|
||||
const footer = page.locator('footer').first();
|
||||
// alt 'Gouvernement Logo' comes from the theme
|
||||
await expect(footer.getByAltText('Gouvernement Logo')).toBeVisible();
|
||||
});
|
||||
|
||||
test('it checks that media server is configured from config endpoint', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-media', browserName, 1);
|
||||
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await page.getByText('Resizable image with caption').click();
|
||||
await page.getByText('Upload image').click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(
|
||||
path.join(__dirname, 'assets/logo-suite-numerique.png'),
|
||||
);
|
||||
|
||||
const image = page.getByRole('img', { name: 'logo-suite-numerique.png' });
|
||||
|
||||
await expect(image).toBeVisible();
|
||||
|
||||
// Check src of image
|
||||
expect(await image.getAttribute('src')).toMatch(
|
||||
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/,
|
||||
);
|
||||
});
|
||||
|
||||
test('it checks that collaboration server is configured from config endpoint', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
const webSocketPromise = page.waitForEvent('websocket', (webSocket) => {
|
||||
return webSocket.url().includes('ws://localhost:4444/');
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const randomDoc = await createDoc(
|
||||
page,
|
||||
'doc-collaboration',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await expect(page.locator('h2').getByText(randomDoc[0])).toBeVisible();
|
||||
|
||||
const webSocket = await webSocketPromise;
|
||||
expect(webSocket.url()).toContain('ws://localhost:4444/');
|
||||
});
|
||||
|
||||
test('it checks that Crisp is trying to init from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route('**/api/v1.0/config/', async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('GET')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
...config,
|
||||
CRISP_WEBSITE_ID: '1234',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(
|
||||
page.locator('#crisp-chatbox').getByText('Invalid website'),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-e2e",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .ts",
|
||||
@@ -12,7 +12,7 @@
|
||||
"test:ui::chromium": "yarn test:ui --project=chromium"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.48.1",
|
||||
"@playwright/test": "1.49.0",
|
||||
"@types/node": "*",
|
||||
"@types/pdf-parse": "1.1.4",
|
||||
"eslint-config-impress": "*",
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
NEXT_PUBLIC_API_ORIGIN=
|
||||
NEXT_PUBLIC_Y_PROVIDER_URL=
|
||||
NEXT_PUBLIC_MEDIA_URL=
|
||||
NEXT_PUBLIC_THEME=dsfr
|
||||
NEXT_PUBLIC_SW_DEACTIVATED=
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
NEXT_PUBLIC_API_ORIGIN=http://localhost:8071
|
||||
NEXT_PUBLIC_Y_PROVIDER_URL=ws://localhost:4444
|
||||
NEXT_PUBLIC_MEDIA_URL=http://localhost:8083
|
||||
NEXT_PUBLIC_SW_DEACTIVATED=true
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
NEXT_PUBLIC_API_ORIGIN=http://test.jest
|
||||
NEXT_PUBLIC_THEME=test-theme
|
||||
|
||||
@@ -3,21 +3,16 @@ const config = {
|
||||
default: {
|
||||
theme: {
|
||||
colors: {
|
||||
'card-border': '#DDDDDD',
|
||||
'card-border': '#ededed',
|
||||
'primary-bg': '#FAFAFA',
|
||||
'primary-100': '#EDF5FA',
|
||||
'primary-150': '#E5EEFA',
|
||||
'info-150': '#E5EEFA',
|
||||
'grey-400': '#929292',
|
||||
'grey-800': '#2A2A2A',
|
||||
},
|
||||
font: {
|
||||
letterSpacings: {
|
||||
h5: 'normal',
|
||||
},
|
||||
sizes: {
|
||||
ml: '0.938rem',
|
||||
xl: '1.25rem',
|
||||
xl: '1.50rem',
|
||||
t: '0.6875rem',
|
||||
s: '0.75rem',
|
||||
h1: '2.2rem',
|
||||
@@ -33,224 +28,274 @@ const config = {
|
||||
black: 900,
|
||||
},
|
||||
},
|
||||
spacings: {
|
||||
'0': '0',
|
||||
none: '0',
|
||||
auto: 'auto',
|
||||
bx: '2.2rem',
|
||||
full: '100%',
|
||||
},
|
||||
breakpoints: {
|
||||
xxs: '320px',
|
||||
xs: '480px',
|
||||
},
|
||||
logo: {
|
||||
src: '',
|
||||
widthHeader: '',
|
||||
widthFooter: '',
|
||||
alt: '',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
datagrid: {
|
||||
header: {
|
||||
weight: 'var(--c--theme--font--weights--extrabold)',
|
||||
size: 'var(--c--theme--font--sizes--ml)',
|
||||
},
|
||||
cell: {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
size: 'var(--c--theme--font--sizes--ml)',
|
||||
},
|
||||
},
|
||||
'forms-checkbox': {
|
||||
'background-color': {
|
||||
hover: '#055fd214',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'font-size': 'var(--c--theme--font--sizes--ml)',
|
||||
},
|
||||
'forms-datepicker': {
|
||||
'border-color': 'var(--c--theme--colors--primary-500)',
|
||||
'value-color': 'var(--c--theme--colors--primary-500)',
|
||||
'border-radius': {
|
||||
hover: 'var(--c--components--forms-datepicker--border-radius)',
|
||||
focus: 'var(--c--components--forms-datepicker--border-radius)',
|
||||
},
|
||||
},
|
||||
'forms-field': {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'value-color': 'var(--c--theme--colors--primary-500)',
|
||||
width: 'auto',
|
||||
},
|
||||
'forms-input': {
|
||||
'value-color': 'var(--c--theme--colors--primary-500)',
|
||||
'border-color': 'var(--c--theme--colors--primary-500)',
|
||||
color: {
|
||||
error: 'var(--c--theme--colors--danger-500)',
|
||||
'error-hover': 'var(--c--theme--colors--danger-500)',
|
||||
'box-shadow-error-hover': 'var(--c--theme--colors--danger-500)',
|
||||
},
|
||||
},
|
||||
'forms-labelledbox': {
|
||||
'label-color': {
|
||||
small: 'var(--c--theme--colors--primary-500)',
|
||||
'small-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
big: {
|
||||
disabled: 'var(--c--theme--colors--greyscale-400)',
|
||||
},
|
||||
},
|
||||
},
|
||||
'forms-select': {
|
||||
'border-color': 'var(--c--theme--colors--primary-500)',
|
||||
'border-color-disabled-hover':
|
||||
'var(--c--theme--colors--greyscale-200)',
|
||||
'border-radius': {
|
||||
hover: 'var(--c--components--forms-select--border-radius)',
|
||||
focus: 'var(--c--components--forms-select--border-radius)',
|
||||
},
|
||||
'font-size': 'var(--c--theme--font--sizes--ml)',
|
||||
'menu-background-color': '#ffffff',
|
||||
'item-background-color': {
|
||||
hover: 'var(--c--theme--colors--primary-300)',
|
||||
},
|
||||
},
|
||||
'forms-switch': {
|
||||
'accent-color': 'var(--c--theme--colors--primary-400)',
|
||||
},
|
||||
'forms-textarea': {
|
||||
'border-color': 'var(--c--components--forms-textarea--border-color)',
|
||||
'border-color-hover':
|
||||
'var(--c--components--forms-textarea--border-color)',
|
||||
'border-radius': {
|
||||
hover: 'var(--c--components--forms-textarea--border-radius)',
|
||||
focus: 'var(--c--components--forms-textarea--border-radius)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
disabled: {
|
||||
'border-color-hover': 'var(--c--theme--colors--greyscale-200)',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
'background-color': '#ffffff',
|
||||
},
|
||||
button: {
|
||||
'border-radius': {
|
||||
active: 'var(--c--components--button--border-radius)',
|
||||
},
|
||||
'medium-height': 'auto',
|
||||
'small-height': 'auto',
|
||||
success: {
|
||||
color: 'white',
|
||||
'color-disabled': 'white',
|
||||
'color-hover': 'white',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--success-600)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-300)',
|
||||
'color-hover': 'var(--c--theme--colors--success-800)',
|
||||
},
|
||||
},
|
||||
danger: {
|
||||
'color-hover': 'white',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--danger-400)',
|
||||
'color-hover': 'var(--c--theme--colors--danger-500)',
|
||||
'color-disabled': 'var(--c--theme--colors--danger-100)',
|
||||
},
|
||||
},
|
||||
primary: {
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'color-active': 'var(--c--theme--colors--primary-text)',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--primary-400)',
|
||||
'color-active': 'var(--c--theme--colors--primary-500)',
|
||||
},
|
||||
border: {
|
||||
'color-active': 'transparent',
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
background: {
|
||||
color: 'white',
|
||||
'color-hover': 'var(--c--theme--colors--primary-700)',
|
||||
},
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--primary-200)',
|
||||
},
|
||||
},
|
||||
tertiary: {
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-600)',
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-200)',
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
color: 'white',
|
||||
background: {
|
||||
color: '#b3cef0',
|
||||
},
|
||||
},
|
||||
},
|
||||
'la-gauffre': {
|
||||
activated: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
dsfr: {
|
||||
theme: {
|
||||
colors: {
|
||||
// Primary
|
||||
|
||||
'primary-text': 'var(--c--theme--colors--primary-600)',
|
||||
'primary-100': '#F5F5FE',
|
||||
'primary-200': '#ECECFE',
|
||||
'primary-300': '#E3E3FD',
|
||||
'primary-400': '#CACAFB',
|
||||
'primary-500': '#6A6AF4',
|
||||
'card-border': '#ededed',
|
||||
'primary-text': '#000091',
|
||||
'primary-100': '#f5f5fe',
|
||||
'primary-150': '#F4F4FD',
|
||||
'primary-200': '#ececfe',
|
||||
'primary-300': '#e3e3fd',
|
||||
'primary-400': '#cacafb',
|
||||
'primary-500': '#6a6af4',
|
||||
'primary-600': '#000091',
|
||||
'primary-clicked-100': '#CBCBFA',
|
||||
'primary-clicked-200': '#BBBBFC',
|
||||
'primary-clicked-300': '#ADADF9',
|
||||
'primary-clicked-400': '#8B8BF6',
|
||||
'primary-clicked-500': '#AEAEF9',
|
||||
'primary-clicked-600': '#2323FF',
|
||||
'primary-hover-100': '#DCDCFC',
|
||||
'primary-hover-200': '#CECEFC',
|
||||
'primary-hover-300': '#C1C1FB',
|
||||
'primary-hover-400': '#A1A1F8',
|
||||
'primary-hover-500': '#9898F8',
|
||||
'primary-hover-600': '#1212FF',
|
||||
|
||||
// secondary
|
||||
'secondary-100': '#FEF4F4',
|
||||
'secondary-200': '#FEE9E9',
|
||||
'secondary-300': '#FDDEDE',
|
||||
'secondary-400': '#FCBFBF',
|
||||
'secondary-500': '#E1000F',
|
||||
'secondary-600': '#C9191E',
|
||||
'secondary-clicked-100': '#FAC4C4',
|
||||
'secondary-clicked-200': '#FCAFAF',
|
||||
'secondary-clicked-300': '#FA9E9E',
|
||||
'secondary-clicked-400': '#FA7474',
|
||||
'secondary-clicked-500': '#FF4347',
|
||||
'secondary-clicked-600': '#F95A5C',
|
||||
'secondary-hover-100': '#FCD7D7',
|
||||
'secondary-hover-200': '#FDC5C5',
|
||||
'secondary-hover-300': '#FBB6B6',
|
||||
'secondary-hover-400': '#FB8F8F',
|
||||
'secondary-hover-500': '#FF292F',
|
||||
'secondary-hover-600': '#F93F42',
|
||||
|
||||
// Greyscale
|
||||
'greyscale-000': '#FFFFFF',
|
||||
'greyscale-050': '#F6F6F6',
|
||||
'greyscale-100': '#EEEEEE',
|
||||
'greyscale-200': '#E5E5E5',
|
||||
'greyscale-250': '#DDDDDD',
|
||||
'greyscale-300': '#CECECE',
|
||||
'greyscale-400': '#929292',
|
||||
'greyscale-500': '#666666',
|
||||
'greyscale-700': '#3A3A3A',
|
||||
'greyscale-1000': '#161616',
|
||||
'greyscale-clicked-000': '#EDEDED',
|
||||
'greyscale-clicked-050': '#CFCFCF',
|
||||
'greyscale-clicked-100': '#C1C1C1',
|
||||
'greyscale-clicked-200': '#B2B2B2',
|
||||
'greyscale-clicked-250': '#A7A7A7',
|
||||
'greyscale-clicked-300': '#939393',
|
||||
'greyscale-clicked-400': '#CECECE',
|
||||
'greyscale-clicked-500': '#A6A6A6',
|
||||
'greyscale-clicked-700': '#777777',
|
||||
'greyscale-clicked-1000': '#474747',
|
||||
'greyscale-hover-000': '#F6F6F6',
|
||||
'greyscale-hover-050': '#DFDFDF',
|
||||
'greyscale-hover-100': '#D2D2D2',
|
||||
'greyscale-hover-200': '#C5C5C5',
|
||||
'greyscale-hover-250': '#BBBBBB',
|
||||
'greyscale-hover-300': '#A8A8A8',
|
||||
'greyscale-hover-400': '#BBBBBB',
|
||||
'greyscale-hover-500': '#919191',
|
||||
'greyscale-hover-700': '#616161',
|
||||
'greyscale-hover-1000': '#343434',
|
||||
|
||||
// 'success-text': '#1f8d49',
|
||||
'success-200': '#B8FEC9',
|
||||
'success-500': '#18753C', // Same has 600 for cunningham
|
||||
'success-600': '#18753C',
|
||||
'success-clicked-200': '#34EB7B',
|
||||
'success-clicked-600': '#2FC368',
|
||||
'success-hover-200': '#46FD89',
|
||||
'success-hover-600': '#27A959',
|
||||
|
||||
// 'info-text': '#0078f3',
|
||||
'info-200': '#E8EDFF',
|
||||
'info-500': '#0063CB', // Same has 600 for cunningham
|
||||
'info-600': '#0063CB',
|
||||
'info-clicked-200': '#A9BFFF',
|
||||
'info-clicked-600': '#6798FF',
|
||||
'info-hover-200': '#C2D1FF',
|
||||
'info-hover-600': '#3B87FF',
|
||||
|
||||
// 'warning-text': '#d64d00',
|
||||
'warning-200': '#FFE9E6',
|
||||
'warning-500': '#B34000', // Same has 600 for cunningham
|
||||
'warning-600': '#B34000',
|
||||
'warning-clicked-200': '#FFB0A2',
|
||||
'warning-clicked-600': '#FF7A55',
|
||||
'warning-hover-200': '#FFC6BD',
|
||||
'warning-hover-600': '#FF6218',
|
||||
|
||||
// Danger
|
||||
'danger-200': '#FFE9E9',
|
||||
'danger-500': '#CE0500', // Same has 600 for cunningham
|
||||
'danger-600': '#CE0500',
|
||||
'danger-clicked-200': '#FFAFAF',
|
||||
'danger-clicked-600': '#FF4140',
|
||||
'danger-hover-200': '#FFC5C5',
|
||||
'danger-hover-600': '#FF2725',
|
||||
|
||||
// Cumulus
|
||||
'cumulus-100': '#F3F6FE',
|
||||
'cumulus-200': '#E6EEFE',
|
||||
'cumulus-300': '#DAE6FD',
|
||||
'cumulus-400': '#B6CFFB',
|
||||
'cumulus-500': '#417DC4',
|
||||
'cumulus-600': '#3558A2',
|
||||
|
||||
// emeraude
|
||||
'emeraude-100': '#E3FDEB',
|
||||
'emeraude-200': '#C3FAD5',
|
||||
'emeraude-300': '#9EF9BE',
|
||||
'emeraude-400': '#6FE49D',
|
||||
'emeraude-500': '#00A95F',
|
||||
'emeraude-600': '#297254',
|
||||
|
||||
// glycine
|
||||
'glycine-100': '#FEF3FD',
|
||||
'glycine-200': '#FEE7FC',
|
||||
'glycine-300': '#FDDBFA',
|
||||
'glycine-400': '#FBB8F6',
|
||||
'glycine-500': '#A558A0',
|
||||
'glycine-600': '#6E445A',
|
||||
|
||||
// terre-battue
|
||||
'terre-battue-100': '#FEF7DA',
|
||||
'terre-battue-200': '#FCEEAC',
|
||||
'terre-battue-300': '#FBE769',
|
||||
'terre-battue-400': '#E2CF58',
|
||||
'terre-battue-500': '#B7A73F',
|
||||
'terre-battue-600': '#66673D',
|
||||
|
||||
// tilleul-verveine
|
||||
'tilleul-verveine-100': '#FEF7DA',
|
||||
'tilleul-verveine-200': '#FCEEAC',
|
||||
'tilleul-verveine-300': '#FBE769',
|
||||
'tilleul-verveine-400': '#E2CF58',
|
||||
'tilleul-verveine-500': '#B7A73F',
|
||||
'tilleul-verveine-600': '#66673D',
|
||||
|
||||
// Focus
|
||||
'focus-500': '#0A76F6',
|
||||
},
|
||||
spacings: {
|
||||
'050V': '2px',
|
||||
'100V': '4px',
|
||||
'150V': '6px',
|
||||
'100W': '8px',
|
||||
'300V': '12px',
|
||||
'200W': '16px',
|
||||
'300W': '24px',
|
||||
'400W': '32px',
|
||||
'500W': '40px',
|
||||
'600W': '48px',
|
||||
'700W': '56px',
|
||||
'800W': '64px',
|
||||
'900W': '72px',
|
||||
'1200W': '96px',
|
||||
'1500W': '120px',
|
||||
'primary-700': '#272747',
|
||||
'primary-800': '#21213f',
|
||||
'primary-900': '#1c1a36',
|
||||
'secondary-text': '#FFFFFF',
|
||||
'secondary-100': '#fee9ea',
|
||||
'secondary-200': '#fedfdf',
|
||||
'secondary-300': '#fdbfbf',
|
||||
'secondary-400': '#e1020f',
|
||||
'secondary-500': '#c91a1f',
|
||||
'secondary-600': '#5e2b2b',
|
||||
'secondary-700': '#3b2424',
|
||||
'secondary-800': '#341f1f',
|
||||
'secondary-900': '#2b1919',
|
||||
'greyscale-text': '#303C4B',
|
||||
'greyscale-000': '#f6f6f6',
|
||||
'greyscale-100': '#eeeeee',
|
||||
'greyscale-200': '#e5e5e5',
|
||||
'greyscale-300': '#e1e1e1',
|
||||
'greyscale-400': '#dddddd',
|
||||
'greyscale-500': '#cecece',
|
||||
'greyscale-600': '#7b7b7b',
|
||||
'greyscale-700': '#666666',
|
||||
'greyscale-800': '#2a2a2a',
|
||||
'greyscale-900': '#1e1e1e',
|
||||
'success-text': '#1f8d49',
|
||||
'success-100': '#dffee6',
|
||||
'success-200': '#b8fec9',
|
||||
'success-300': '#88fdaa',
|
||||
'success-400': '#3bea7e',
|
||||
'success-500': '#1f8d49',
|
||||
'success-600': '#18753c',
|
||||
'success-700': '#204129',
|
||||
'success-800': '#1e2e22',
|
||||
'success-900': '#19281d',
|
||||
'info-text': '#0078f3',
|
||||
'info-100': '#f4f6ff',
|
||||
'info-200': '#e8edff',
|
||||
'info-300': '#dde5ff',
|
||||
'info-400': '#bdcdff',
|
||||
'info-500': '#0078f3',
|
||||
'info-600': '#0063cb',
|
||||
'info-700': '#f4f6ff',
|
||||
'info-800': '#222a3f',
|
||||
'info-900': '#1d2437',
|
||||
'warning-text': '#d64d00',
|
||||
'warning-100': '#fff4f3',
|
||||
'warning-200': '#ffe9e6',
|
||||
'warning-300': '#ffded9',
|
||||
'warning-400': '#ffbeb4',
|
||||
'warning-500': '#d64d00',
|
||||
'warning-600': '#b34000',
|
||||
'warning-700': '#5e2c21',
|
||||
'warning-800': '#3e241e',
|
||||
'warning-900': '#361e19',
|
||||
'danger-text': '#e1000f',
|
||||
'danger-100': '#fef4f4',
|
||||
'danger-200': '#fee9e9',
|
||||
'danger-300': '#fddede',
|
||||
'danger-400': '#fcbfbf',
|
||||
'danger-500': '#e1000f',
|
||||
'danger-600': '#c9191e',
|
||||
'danger-700': '#642727',
|
||||
'danger-800': '#412121',
|
||||
'danger-900': '#3a1c1c',
|
||||
},
|
||||
font: {
|
||||
families: {
|
||||
accent: 'Marianne',
|
||||
base: 'Marianne',
|
||||
},
|
||||
sizes: {
|
||||
xl: '20px',
|
||||
lg: '18px',
|
||||
md: '16px',
|
||||
sm: '14px',
|
||||
xs: '12px',
|
||||
h1: '32px',
|
||||
h2: '28px',
|
||||
h3: '24px',
|
||||
h4: '22px',
|
||||
h5: '20px',
|
||||
h6: '18px',
|
||||
'title-alt-xl': '80px',
|
||||
'title-alt-lg': '72px',
|
||||
'title-alt-md': '64px',
|
||||
'title-alt-sm': '56px',
|
||||
'title-alt-xs': '48px',
|
||||
},
|
||||
},
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
widthHeader: '110px',
|
||||
widthFooter: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
alert: {
|
||||
'border-radius': '0',
|
||||
'background-color': 'var(--c--theme--colors--greyscale-000)',
|
||||
},
|
||||
modal: {
|
||||
'box-shadow': '0px 6px 18px 0px rgba(0, 0, 18, 0.16);',
|
||||
},
|
||||
button: {
|
||||
'medium-height': '48px',
|
||||
'border-radius': '4px',
|
||||
'medium-text-height': '48px',
|
||||
primary: {
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-hover-600)',
|
||||
'color-active': 'var(--c--theme--colors--primary-600)',
|
||||
'color-focus': 'var(--c--theme--colors--focus-500)',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'color-hover': '#1212ff',
|
||||
'color-active': '#2323ff',
|
||||
},
|
||||
color: '#ffffff',
|
||||
'color-hover': '#ffffff',
|
||||
@@ -258,51 +303,33 @@ const config = {
|
||||
},
|
||||
'primary-text': {
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-hover-000)',
|
||||
'color-active': 'var(--c--theme--colors--greyscale-clicked-000)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
'color-active': 'var(--c--theme--colors--primary-100)',
|
||||
},
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
secondary: {
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--greyscale-000)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-hover-000)',
|
||||
'color-hover': '#F6F6F6',
|
||||
'color-active': '#EDEDED',
|
||||
},
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--greyscale-250)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-250)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
['color-hover']: 'var(--c--theme--colors--primary-600)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
},
|
||||
tertiary: {
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--primary-300)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-hover-300)',
|
||||
'color-active': 'var(--c--theme--colors--primary-clicked-300)',
|
||||
},
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-250)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
['color-hover']: 'var(--c--theme--colors--primary-600)',
|
||||
['color-disabled']: 'var(--c--theme--colors--primary-400)',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
'tertiary-text': {
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
},
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
},
|
||||
datagrid: {
|
||||
header: {
|
||||
color: 'var(--c--theme--colors--greyscale-500)',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
size: 'var(--c--theme--font--sizes--s)',
|
||||
},
|
||||
body: {
|
||||
@@ -312,48 +339,60 @@ 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': {
|
||||
'border-radius': '0',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
text: {
|
||||
color: 'var(--c--theme--colors--greyscale-text)',
|
||||
size: 'var(--c--theme--font--sizes--t)',
|
||||
},
|
||||
},
|
||||
'forms-datepicker': {
|
||||
// 'border-radius': '0',
|
||||
'border-radius': '0',
|
||||
},
|
||||
'forms-fileuploader': {
|
||||
// 'border-radius': '0',
|
||||
'border-radius': '0',
|
||||
},
|
||||
'forms-field': {
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'footer-font-size': 'var(--c--theme--font--sizes--t)',
|
||||
'footer-color': 'var(--c--theme--colors--greyscale-text)',
|
||||
},
|
||||
'forms-input': {
|
||||
// 'background-color': 'var(--c--theme--colors--greyscale-050)',
|
||||
// 'border-radius': '5px 5px 0 0',
|
||||
// 'border-color': 'var(--c--theme--colors--greyscale-100)',
|
||||
// 'border-color--focus': 'var(--c--theme--colors--greyscale-1000)',
|
||||
// 'border-width': '0 0 2px 0',
|
||||
// 'label-color--focus':
|
||||
// 'var(--c--components--forms-labelledbox--label-color--small)',
|
||||
'border-radius': '4px',
|
||||
'background-color': '#ffffff',
|
||||
'border-color': 'var(--c--theme--colors--primary-text)',
|
||||
'box-shadow-color': 'var(--c--theme--colors--primary-text)',
|
||||
'value-color': 'var(--c--theme--colors--primary-text)',
|
||||
'font-size': '14px',
|
||||
},
|
||||
'forms-textarea': {
|
||||
// 'background-color': 'var(--c--theme--colors--greyscale-050)',
|
||||
// 'border-radius': '5px 5px 0 0',
|
||||
// 'border-color': 'var(--c--theme--colors--greyscale-100)',
|
||||
// 'border-color--focus': 'var(--c--theme--colors--greyscale-1000)',
|
||||
// 'border-width': '0 0 2px 0',
|
||||
// 'border-color--hover': 'var(--c--theme--colors--greyscale-1000)',
|
||||
// 'label-color--focus':
|
||||
// 'var(--c--components--forms-labelledbox--label-color--small)',
|
||||
'forms-labelledbox': {
|
||||
'label-color': {
|
||||
big: 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
},
|
||||
'forms-radio': {
|
||||
'accent-color': 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
'forms-select': {
|
||||
// 'background-color': 'var(--c--theme--colors--greyscale-100)',
|
||||
// 'border-radius': '0',
|
||||
// 'border-color': 'var(--c--theme--colors--greyscale-1000)',
|
||||
// 'border-width': '0 0 2px 0',
|
||||
// 'border-color--focus': '#0974F6',
|
||||
// 'border-color--hover': 'var(--c--theme--colors--greyscale-1000)',
|
||||
// 'label-color--focus':
|
||||
// 'var(--c--components--forms-labelledbox--label-color--big)',
|
||||
'item-font-size': '14px',
|
||||
'border-radius': '4px',
|
||||
'border-radius-hover': '4px',
|
||||
'background-color': '#ffffff',
|
||||
'border-color': 'var(--c--theme--colors--primary-text)',
|
||||
'border-color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
'box-shadow-color': 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
'forms-switch': {
|
||||
// 'accent-color': '#2323ff',
|
||||
'handle-border-radius': '2px',
|
||||
'rail-border-radius': '4px',
|
||||
'accent-color': 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
'forms-checkbox': {
|
||||
// 'accent-color': '#2323ff',
|
||||
'forms-textarea': {
|
||||
'border-radius': '0',
|
||||
},
|
||||
'la-gauffre': {
|
||||
activated: true,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
|
||||
const { InjectManifest } = require('workbox-webpack-plugin');
|
||||
|
||||
@@ -12,9 +11,6 @@ const nextConfig = {
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
sassOptions: {
|
||||
includePaths: [path.join(__dirname, 'styles')],
|
||||
},
|
||||
compiler: {
|
||||
// Enables the styled-components SWC transform
|
||||
styledComponents: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-impress",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -19,38 +19,39 @@
|
||||
"@blocknote/mantine": "*",
|
||||
"@blocknote/react": "*",
|
||||
"@gouvfr-lasuite/integration": "1.0.2",
|
||||
"@hocuspocus/provider": "2.13.7",
|
||||
"@hocuspocus/provider": "2.14.0",
|
||||
"@openfun/cunningham-react": "2.9.4",
|
||||
"@tanstack/react-query": "5.59.15",
|
||||
"classnames": "2.5.1",
|
||||
"i18next": "23.16.2",
|
||||
"@sentry/nextjs": "8.40.0",
|
||||
"@tanstack/react-query": "5.61.3",
|
||||
"crisp-sdk-web": "1.0.25",
|
||||
"i18next": "24.0.0",
|
||||
"i18next-browser-languagedetector": "8.0.0",
|
||||
"idb": "8.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"luxon": "3.5.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.3",
|
||||
"react": "*",
|
||||
"react-aria-components": "1.4.1",
|
||||
"react-aria-components": "1.5.0",
|
||||
"react-dom": "*",
|
||||
"react-i18next": "15.0.3",
|
||||
"react-select": "5.8.1",
|
||||
"react-i18next": "15.1.1",
|
||||
"react-select": "5.8.3",
|
||||
"styled-components": "6.1.13",
|
||||
"y-protocols": "1.0.6",
|
||||
"yjs": "*",
|
||||
"zustand": "5.0.0"
|
||||
"zustand": "5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@tanstack/react-query-devtools": "5.59.15",
|
||||
"@tanstack/react-query-devtools": "5.61.3",
|
||||
"@testing-library/dom": "10.4.0",
|
||||
"@testing-library/jest-dom": "6.6.2",
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@testing-library/user-event": "14.5.2",
|
||||
"@types/jest": "29.5.13",
|
||||
"@types/lodash": "4.17.12",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/lodash": "4.17.13",
|
||||
"@types/luxon": "3.4.2",
|
||||
"@types/node": "*",
|
||||
"@types/react": "18.3.11",
|
||||
"@types/react": "18.3.12",
|
||||
"@types/react-dom": "*",
|
||||
"cross-env": "*",
|
||||
"dotenv": "16.4.5",
|
||||
@@ -60,12 +61,11 @@
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"node-fetch": "2.7.0",
|
||||
"prettier": "3.3.3",
|
||||
"sass": "1.80.4",
|
||||
"stylelint": "16.10.0",
|
||||
"stylelint-config-standard": "36.0.1",
|
||||
"stylelint-prettier": "5.0.2",
|
||||
"typescript": "*",
|
||||
"webpack": "5.95.0",
|
||||
"webpack": "5.96.1",
|
||||
"workbox-webpack-plugin": "7.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AppWrapper } from '@/tests/utils';
|
||||
|
||||
import Page from '../pages';
|
||||
|
||||
jest.mock('next/navigation', () => ({
|
||||
jest.mock('next/router', () => ({
|
||||
useRouter() {
|
||||
return {
|
||||
push: jest.fn(),
|
||||
@@ -13,6 +13,12 @@ jest.mock('next/navigation', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@sentry/nextjs', () => ({
|
||||
captureException: jest.fn(),
|
||||
captureMessage: jest.fn(),
|
||||
setUser: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Page', () => {
|
||||
it('checks Page rendering', () => {
|
||||
render(<Page />, { wrapper: AppWrapper });
|
||||
|
||||
6
src/frontend/apps/impress/src/api/config.ts
Normal file
6
src/frontend/apps/impress/src/api/config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const backendUrl = () =>
|
||||
process.env.NEXT_PUBLIC_API_ORIGIN ||
|
||||
(typeof window !== 'undefined' ? window.location.origin : '');
|
||||
|
||||
export const baseApiUrl = (apiVersion: string = '1.0') =>
|
||||
`${backendUrl()}/api/v${apiVersion}/`;
|
||||
@@ -1,5 +1,4 @@
|
||||
import { baseApiUrl } from '@/core';
|
||||
|
||||
import { baseApiUrl } from './config';
|
||||
import { getCSRFToken } from './utils';
|
||||
|
||||
interface FetchAPIInit extends RequestInit {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './APIError';
|
||||
export * from './config';
|
||||
export * from './fetchApi';
|
||||
export * from './helpers';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ComponentPropsWithRef, ReactHTML } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { CSSProperties } from 'styled-components/dist/types';
|
||||
import { CSSProperties, RuleSet } from 'styled-components/dist/types';
|
||||
|
||||
import {
|
||||
MarginPadding,
|
||||
@@ -15,7 +15,7 @@ export interface BoxProps {
|
||||
$align?: CSSProperties['alignItems'];
|
||||
$background?: CSSProperties['background'];
|
||||
$color?: CSSProperties['color'];
|
||||
$css?: string;
|
||||
$css?: string | RuleSet<object>;
|
||||
$direction?: CSSProperties['flexDirection'];
|
||||
$display?: CSSProperties['display'];
|
||||
$effect?: 'show' | 'hide';
|
||||
@@ -73,7 +73,7 @@ export const Box = styled('div')<BoxProps>`
|
||||
${({ $transition }) => $transition && `transition: ${$transition};`}
|
||||
${({ $width }) => $width && `width: ${$width};`}
|
||||
${({ $wrap }) => $wrap && `flex-wrap: ${$wrap};`}
|
||||
${({ $css }) => $css && `${$css};`}
|
||||
${({ $css }) => $css && (typeof $css === 'string' ? `${$css};` : $css)}
|
||||
${({ $zIndex }) => $zIndex && `z-index: ${$zIndex};`}
|
||||
${({ $effect }) => {
|
||||
let effect;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ComponentPropsWithRef, forwardRef } from 'react';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box, BoxType } from './Box';
|
||||
|
||||
@@ -26,7 +27,7 @@ const BoxButton = forwardRef<HTMLDivElement, BoxType>(
|
||||
$background="none"
|
||||
$margin="none"
|
||||
$padding="none"
|
||||
$css={`
|
||||
$css={css`
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
|
||||
@@ -15,7 +16,7 @@ export const Card = ({
|
||||
<Box
|
||||
$background="white"
|
||||
$radius="4px"
|
||||
$css={`
|
||||
$css={css`
|
||||
box-shadow: 2px 2px 5px ${colorsTokens()['greyscale-300']};
|
||||
border: 1px solid ${colorsTokens()['card-border']};
|
||||
${$css}
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface LinkProps {
|
||||
|
||||
export const StyledLink = styled(Link)<LinkProps>`
|
||||
text-decoration: none;
|
||||
color: #ffffff33;
|
||||
&[aria-current='page'] {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
icon: string;
|
||||
className?: string;
|
||||
};
|
||||
export const Icon = ({ icon, className }: Props) => {
|
||||
return <span className={`material-icons ${className}`}>{icon}</span>;
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
|
||||
import style from './separator.module.scss';
|
||||
|
||||
export enum SeparatorVariant {
|
||||
LIGHT = 'light',
|
||||
DARK = 'dark',
|
||||
}
|
||||
|
||||
type Props = {
|
||||
variant?: SeparatorVariant;
|
||||
};
|
||||
|
||||
export const HorizontalSeparator = ({
|
||||
variant = SeparatorVariant.LIGHT,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(style.horizontal, {
|
||||
[style.dark]: variant === SeparatorVariant.DARK,
|
||||
[style.light]: variant === SeparatorVariant.LIGHT,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import styles from './separator.module.scss';
|
||||
|
||||
type Props = {
|
||||
showSeparator?: boolean;
|
||||
};
|
||||
|
||||
export const SeparatedSection = ({
|
||||
showSeparator = true,
|
||||
children,
|
||||
}: PropsWithChildren<Props>) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(styles.separatedContainer, {
|
||||
[styles.showSeparator]: showSeparator,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
.horizontal {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
&.light {
|
||||
background-color: var(--c--theme--colors--greyscale-100);
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background-color: #e5e5e533;
|
||||
}
|
||||
}
|
||||
|
||||
.separatedContainer {
|
||||
padding: var(--c--theme--spacings--200W) var(--c--theme--spacings--300V);
|
||||
|
||||
&.showSeparator {
|
||||
border-bottom: 1px solid var(--c--theme--colors--greyscale-200);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import '@/i18n/initI18n';
|
||||
import { useResponsiveStore } from '@/stores/';
|
||||
|
||||
import { Auth } from './auth/';
|
||||
import { ConfigProvider } from './config/';
|
||||
|
||||
/**
|
||||
* QueryClient:
|
||||
@@ -39,7 +40,9 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CunninghamProvider theme={theme}>
|
||||
<Auth>{children}</Auth>
|
||||
<ConfigProvider>
|
||||
<Auth>{children}</Auth>
|
||||
</ConfigProvider>
|
||||
</CunninghamProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Crisp } from 'crisp-sdk-web';
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import { useAuthStore } from '../useAuthStore';
|
||||
|
||||
jest.mock('crisp-sdk-web', () => ({
|
||||
...jest.requireActual('crisp-sdk-web'),
|
||||
Crisp: {
|
||||
isCrispInjected: jest.fn().mockReturnValue(true),
|
||||
setTokenId: jest.fn(),
|
||||
user: {
|
||||
setEmail: jest.fn(),
|
||||
},
|
||||
session: {
|
||||
reset: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('useAuthStore', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it('checks support session is terminated when logout', () => {
|
||||
window.$crisp = true;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
...window.location,
|
||||
replace: jest.fn(),
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
|
||||
useAuthStore.getState().logout();
|
||||
|
||||
expect(Crisp.session.reset).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { baseApiUrl } from '@/core/conf';
|
||||
import { baseApiUrl } from '@/api';
|
||||
import { terminateCrispSession } from '@/services';
|
||||
|
||||
import { User, getMe } from './api';
|
||||
import { PATH_AUTH_LOCAL_STORAGE } from './conf';
|
||||
@@ -42,6 +43,7 @@ export const useAuthStore = create<AuthStore>((set, get) => ({
|
||||
window.location.replace(`${baseApiUrl()}authenticate/`);
|
||||
},
|
||||
logout: () => {
|
||||
terminateCrispSession();
|
||||
window.location.replace(`${baseApiUrl()}logout/`);
|
||||
},
|
||||
// If we try to access a specific page and we are not authenticated
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
export const mediaUrl = () =>
|
||||
process.env.NEXT_PUBLIC_MEDIA_URL ||
|
||||
(typeof window !== 'undefined' ? window.location.origin : '');
|
||||
|
||||
export const backendUrl = () =>
|
||||
process.env.NEXT_PUBLIC_API_ORIGIN ||
|
||||
(typeof window !== 'undefined' ? window.location.origin : '');
|
||||
|
||||
export const baseApiUrl = (apiVersion: string = '1.0') =>
|
||||
`${backendUrl()}/api/v${apiVersion}/`;
|
||||
|
||||
export const providerUrl = (docId: string) => {
|
||||
const base =
|
||||
process.env.NEXT_PUBLIC_Y_PROVIDER_URL ||
|
||||
(typeof window !== 'undefined' ? `wss://${window.location.host}/ws` : '');
|
||||
|
||||
return `${base}/${docId}`;
|
||||
};
|
||||
49
src/frontend/apps/impress/src/core/config/ConfigProvider.tsx
Normal file
49
src/frontend/apps/impress/src/core/config/ConfigProvider.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Loader } from '@openfun/cunningham-react';
|
||||
import { PropsWithChildren, useEffect } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { configureCrispSession } from '@/services';
|
||||
import { useSentryStore } from '@/stores/useSentryStore';
|
||||
|
||||
import { useConfig } from './api/useConfig';
|
||||
|
||||
export const ConfigProvider = ({ children }: PropsWithChildren) => {
|
||||
const { data: conf } = useConfig();
|
||||
const { setSentry } = useSentryStore();
|
||||
const { setTheme } = useCunninghamTheme();
|
||||
|
||||
useEffect(() => {
|
||||
if (!conf?.SENTRY_DSN) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSentry(conf.SENTRY_DSN, conf.ENVIRONMENT);
|
||||
}, [conf?.SENTRY_DSN, conf?.ENVIRONMENT, setSentry]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!conf?.FRONTEND_THEME) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTheme(conf.FRONTEND_THEME);
|
||||
}, [conf?.FRONTEND_THEME, setTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!conf?.CRISP_WEBSITE_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
configureCrispSession(conf.CRISP_WEBSITE_ID);
|
||||
}, [conf?.CRISP_WEBSITE_ID]);
|
||||
|
||||
if (!conf) {
|
||||
return (
|
||||
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
|
||||
<Loader />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
1
src/frontend/apps/impress/src/core/config/api/index.ts
Normal file
1
src/frontend/apps/impress/src/core/config/api/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './useConfig';
|
||||
35
src/frontend/apps/impress/src/core/config/api/useConfig.tsx
Normal file
35
src/frontend/apps/impress/src/core/config/api/useConfig.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
import { Theme } from '@/cunningham/';
|
||||
|
||||
interface ConfigResponse {
|
||||
LANGUAGES: [string, string][];
|
||||
LANGUAGE_CODE: string;
|
||||
ENVIRONMENT: string;
|
||||
COLLABORATION_SERVER_URL?: string;
|
||||
CRISP_WEBSITE_ID?: string;
|
||||
FRONTEND_THEME?: Theme;
|
||||
MEDIA_BASE_URL?: string;
|
||||
SENTRY_DSN?: string;
|
||||
}
|
||||
|
||||
export const getConfig = async (): Promise<ConfigResponse> => {
|
||||
const response = await fetchAPI(`config/`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError('Failed to get the doc', await errorCauses(response));
|
||||
}
|
||||
|
||||
return response.json() as Promise<ConfigResponse>;
|
||||
};
|
||||
|
||||
export const KEY_CONFIG = 'config';
|
||||
|
||||
export function useConfig() {
|
||||
return useQuery<ConfigResponse, APIError, ConfigResponse>({
|
||||
queryKey: [KEY_CONFIG],
|
||||
queryFn: () => getConfig(),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
}
|
||||
2
src/frontend/apps/impress/src/core/config/hooks/index.ts
Normal file
2
src/frontend/apps/impress/src/core/config/hooks/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './useMediaUrl';
|
||||
export * from './useCollaborationUrl';
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useConfig } from '../api';
|
||||
|
||||
export const useCollaborationUrl = (room?: string) => {
|
||||
const { data: conf } = useConfig();
|
||||
|
||||
if (!room) {
|
||||
return;
|
||||
}
|
||||
|
||||
const base =
|
||||
conf?.COLLABORATION_SERVER_URL ||
|
||||
(typeof window !== 'undefined' ? `wss://${window.location.host}/ws` : '');
|
||||
|
||||
return `${base}/${room}`;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useConfig } from '../api';
|
||||
|
||||
export const useMediaUrl = () => {
|
||||
const { data: conf } = useConfig();
|
||||
|
||||
return (
|
||||
conf?.MEDIA_BASE_URL ||
|
||||
(typeof window !== 'undefined' ? window.location.origin : '')
|
||||
);
|
||||
};
|
||||
3
src/frontend/apps/impress/src/core/config/index.ts
Normal file
3
src/frontend/apps/impress/src/core/config/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './api/';
|
||||
export * from './ConfigProvider';
|
||||
export * from './hooks';
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './AppProvider';
|
||||
export * from './auth';
|
||||
export * from './conf';
|
||||
export * from './config';
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import useCunninghamTheme from '../useCunninghamTheme';
|
||||
import { useCunninghamTheme } from '../useCunninghamTheme';
|
||||
|
||||
describe('<useCunninghamTheme />', () => {
|
||||
it('has the theme from NEXT_PUBLIC_THEME', () => {
|
||||
const { theme } = useCunninghamTheme.getState();
|
||||
|
||||
expect(theme).toBe('test-theme');
|
||||
});
|
||||
|
||||
it('has the dsfr logo correctly set', () => {
|
||||
const { themeTokens, setTheme } = useCunninghamTheme.getState();
|
||||
setTheme('dsfr');
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
:root {
|
||||
/**
|
||||
* Input
|
||||
*/
|
||||
--c--components--forms-input--border-radius--hover: var(
|
||||
--c--components--forms-input--border-radius
|
||||
);
|
||||
--c--components--forms-input--border-radius--focus: var(
|
||||
--c--components--forms-input--border-radius
|
||||
);
|
||||
--c--components--forms-input--border-color--hover: var(
|
||||
--c--components--forms-input--border-color
|
||||
);
|
||||
|
||||
/**
|
||||
* Datepicker
|
||||
**/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@import url('@openfun/cunningham-react/icons');
|
||||
@import url('@openfun/cunningham-react/style');
|
||||
@import url('@openfun/cunningham-react/fonts');
|
||||
@import './cunningham-tokens';
|
||||
@import './cunningham-custom-tokens';
|
||||
@import '../assets/fonts/Marianne/Marianne-font';
|
||||
@import url('./cunningham-tokens.css');
|
||||
@import url('./cunningham-custom-tokens.css');
|
||||
@import url('../assets/fonts/Marianne/Marianne-font.css');
|
||||
|
||||
.c__input,
|
||||
.c__field,
|
||||
@@ -12,20 +12,221 @@
|
||||
font-family: var(--c--theme--font--families--base);
|
||||
}
|
||||
|
||||
.c__field {
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.c__field .c__field__footer {
|
||||
padding: 2px 0 0;
|
||||
font-size: var(--c--components--forms-field--footer-font-size);
|
||||
color: var(--c--components--forms-field--footer-color);
|
||||
}
|
||||
|
||||
.labelled-box label {
|
||||
color: var(--c--theme--colors--primary-text);
|
||||
}
|
||||
|
||||
.labelled-box--disabled label {
|
||||
color: var(--c--components--forms-labelledbox--label-color--small-disabled);
|
||||
}
|
||||
|
||||
.c__field :not(.c__textarea__wrapper, div) .labelled-box label.placeholder {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Input
|
||||
* TextArea
|
||||
*/
|
||||
.c__input__wrapper,
|
||||
.c__textarea__wrapper {
|
||||
transition: all var(--c--theme--transitions--duration)
|
||||
var(--c--theme--transitions--ease-out);
|
||||
}
|
||||
|
||||
.c__input__wrapper:has(input[readonly]),
|
||||
.c__input__wrapper:has(input[readonly]) * {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.c__textarea__wrapper:has(input.border-none),
|
||||
.c__textarea__wrapper:has(input.border-none) *,
|
||||
.c__input__wrapper:has(input.border-none),
|
||||
.c__input__wrapper:has(input.border-none) * {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.c__input__wrapper:hover,
|
||||
.c__textarea__wrapper:hover {
|
||||
box-shadow: var(--c--components--forms-input--box-shadow-color) 0 0 0 2px;
|
||||
}
|
||||
|
||||
.c__textarea__wrapper--disabled:hover,
|
||||
.c__input__wrapper--disabled:hover,
|
||||
.c__input__wrapper:hover:has(input[readonly]) {
|
||||
box-shadow: var(--c--theme--colors--primary-500) 0 0 0 0;
|
||||
}
|
||||
|
||||
.c__input__wrapper--disabled {
|
||||
color: var(--c--components--forms-input--value-color--disabled);
|
||||
}
|
||||
|
||||
.c__input__wrapper .labelled-box__label.placeholder {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.c__input__wrapper .c__input,
|
||||
.c__textarea__wrapper .c__textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.c__input__wrapper--disabled .c__input {
|
||||
color: var(--c--components--forms-input--value-color--disabled);
|
||||
}
|
||||
|
||||
.c__input__wrapper--error .c__input {
|
||||
color: var(--c--components--forms-input--color--error);
|
||||
}
|
||||
|
||||
.c__input__wrapper--error:not(.c__input__wrapper--disabled):hover {
|
||||
border-color: var(--c--components--forms-input--border--color-error-hover);
|
||||
color: var(--c--components--forms-input--color--error-hover);
|
||||
}
|
||||
|
||||
.c__input__wrapper--error:hover {
|
||||
box-shadow: var(--c--components--forms-input--color--box-shadow-error-hover) 0
|
||||
0 0 2px;
|
||||
}
|
||||
|
||||
.c__input__wrapper--error:not(.c__input__wrapper--disabled):hover label {
|
||||
color: var(--c--components--forms-input--border--color-error-hover);
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:focus {
|
||||
transition:
|
||||
background-color 0s 600000s,
|
||||
color 0s 600000s;
|
||||
}
|
||||
|
||||
.c__textarea__wrapper .c__textarea {
|
||||
color: var(--c--components--forms-textarea--color);
|
||||
}
|
||||
|
||||
.c__textarea__wrapper:hover {
|
||||
border-color: var(--c--components--forms-textarea--border-color-hover);
|
||||
}
|
||||
|
||||
.c__textarea__wrapper--disabled:hover {
|
||||
border-color: var(
|
||||
--c--components--forms-textarea--disabled--border-color-hover
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select
|
||||
*/
|
||||
.c_select__no_border .c__select .c__select__wrapper,
|
||||
.c_select__no_border .c__select .c__select__wrapper:hover,
|
||||
.c_select__no_border
|
||||
.c__select:not(.c__select--disabled)
|
||||
.c__select__wrapper:hover {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.c__select__wrapper {
|
||||
transition: all var(--c--theme--transitions--duration)
|
||||
var(--c--theme--transitions--ease-out);
|
||||
min-height: var(--c--components--forms-select--height);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.c__select:not(.c__select--disabled) .c__select__wrapper:hover {
|
||||
box-shadow: var(--c--components--forms-input--box-shadow-color) 0 0 0 2px;
|
||||
}
|
||||
|
||||
.c__select__wrapper:hover {
|
||||
border-radius: var(--c--components--forms-select--border-radius-hover);
|
||||
border-color: var(--c--components--forms-select--border-color-hover);
|
||||
}
|
||||
|
||||
.c__select--disabled .c__select__wrapper:hover {
|
||||
border-color: var(--c--components--forms-select--border-color-disabled-hover);
|
||||
}
|
||||
|
||||
.c__select--disabled .c__select__wrapper label,
|
||||
.c__select--disabled .c__select__wrapper input,
|
||||
.c__select--disabled .c__select__wrapper {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c__select__menu__item {
|
||||
transition: all var(--c--theme--transitions--duration)
|
||||
var(--c--theme--transitions--ease-out);
|
||||
}
|
||||
|
||||
.c__select--disabled .c__select__wrapper label,
|
||||
.c__select--disabled .c__select__wrapper input,
|
||||
.c_select__no_bg .c__select__wrapper {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.c__select__wrapper:focus-within .labelled-box--disabled label {
|
||||
color: var(--c--components--forms-labelledbox--label-color--small-disabled);
|
||||
}
|
||||
|
||||
.c__select__wrapper .labelled-box {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.c__select__wrapper .labelled-box .labelled-box__children {
|
||||
padding: unset;
|
||||
padding-right: 5rem;
|
||||
}
|
||||
|
||||
.c__select__wrapper .labelled-box .c__select__inner__actions {
|
||||
right: 0;
|
||||
top: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.c__select__wrapper label {
|
||||
position: relative;
|
||||
padding-right: 5rem;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.c__select__wrapper .c__select__inner__actions__open:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.c__select__wrapper .labelled-box__label.c__offscreen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* DataGrid
|
||||
*/
|
||||
.c__datagrid__table__container {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table th .c__datagrid__header {
|
||||
color: var(--c--components--datagrid--header--color);
|
||||
font-weight: var(--c--theme--font--sizes--sm);
|
||||
font-weight: var(--c--components--datagrid--header--weight);
|
||||
font-size: var(--c--components--datagrid--header--size);
|
||||
padding-block: 2rem;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table tbody tr {
|
||||
/*border: none;*/
|
||||
/*border-top: 1px var(--c--theme--colors--greyscale-300) solid;*/
|
||||
/*border-bottom: 1px var(--c--theme--colors--greyscale-300) solid;*/
|
||||
border: none;
|
||||
border-top: 1px var(--c--theme--colors--greyscale-100) solid;
|
||||
border-bottom: 1px var(--c--theme--colors--greyscale-100) solid;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table tbody {
|
||||
@@ -38,6 +239,19 @@
|
||||
);
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table {
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table td {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table th:first-child,
|
||||
.c__datagrid__table__container > table td:first-child {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.c__datagrid > .c__pagination {
|
||||
padding-inline: 1rem;
|
||||
justify-content: flex-end;
|
||||
@@ -47,6 +261,7 @@
|
||||
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 {
|
||||
@@ -106,6 +321,23 @@
|
||||
transition: all 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
.c__checkbox .c__field__text {
|
||||
color: var(--c--components--forms-checkbox--text--color);
|
||||
font-size: var(--c--components--forms-checkbox--text--size);
|
||||
}
|
||||
|
||||
.c__checkbox.c__checkbox--disabled .c__field__text {
|
||||
color: var(--c--theme--colors--greyscale-600);
|
||||
}
|
||||
|
||||
.c__switch.c__checkbox--disabled .c__switch__rail {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.c__checkbox.c__checkbox--disabled .c__checkbox__label {
|
||||
color: var(--c--theme--colors--greyscale-400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Button
|
||||
*/
|
||||
@@ -119,12 +351,8 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.c__button--nano.c__button--with-icon--left {
|
||||
padding: 0 var(--c--theme--spacings--100W);
|
||||
}
|
||||
|
||||
.c__button--medium {
|
||||
padding: var(--c--theme--spacings--100W);
|
||||
padding: 0.9rem var(--c--theme--spacings--s);
|
||||
}
|
||||
|
||||
.c__button--small {
|
||||
@@ -136,12 +364,6 @@
|
||||
var(--c--theme--spacings--s);
|
||||
}
|
||||
|
||||
.c__button:focus-visible {
|
||||
box-shadow: 0 0 0 2px
|
||||
var(--c--components--button--primary--background--color-focus);
|
||||
}
|
||||
|
||||
/* Button Primary */
|
||||
.c__button--primary {
|
||||
background-color: var(--c--components--button--primary--background--color);
|
||||
color: var(--c--components--button--primary--color);
|
||||
@@ -156,69 +378,34 @@
|
||||
|
||||
.c__button--primary:active,
|
||||
.c__button--primary.c__button--active {
|
||||
background-color: var(--c--components--button--primary--background--color);
|
||||
color: var(--c--components--button--primary--color);
|
||||
background-color: var(
|
||||
--c--components--button--primary--background--color-active
|
||||
);
|
||||
color: var(--c--components--button--primary--color-active);
|
||||
border-color: var(--c--components--button--primary--border--color-active);
|
||||
}
|
||||
|
||||
/* Button Primary-text */
|
||||
|
||||
.c__button--primary-text {
|
||||
.c__button--primary-text:active,
|
||||
.c__button--primary-text.c__button--active {
|
||||
border: none;
|
||||
color: var(--c--components--button--primary-text--color);
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-active
|
||||
);
|
||||
}
|
||||
|
||||
.c__button--primary-text:hover,
|
||||
.c__button--primary-text.c__button--active {
|
||||
.c__button--primary-text:focus-visible {
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-hover
|
||||
);
|
||||
color: var(--c--components--button--primary-text--color-hover);
|
||||
}
|
||||
|
||||
.c__button--primary-text:active,
|
||||
.c__button--primary-text.c__button--active {
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-active
|
||||
);
|
||||
.c__button:disabled {
|
||||
background-color: var(--c--components--button--disabled--background--color);
|
||||
color: var(--c--components--button--disabled--color);
|
||||
}
|
||||
|
||||
.c__button--primary-text:disabled {
|
||||
background-color: transparent;
|
||||
color: var(--c--components--button--primary-text--color-disabled);
|
||||
}
|
||||
|
||||
/* Button tertiary */
|
||||
.c__button--tertiary {
|
||||
background-color: var(--c--components--button--tertiary--background--color);
|
||||
color: var(--c--components--button--tertiary--color);
|
||||
}
|
||||
.c__button--tertiary:disabled {
|
||||
background-color: var(--c--components--button--tertiary--background--color);
|
||||
color: var(--c--components--button--tertiary--color-disabled);
|
||||
}
|
||||
|
||||
.c__button--tertiary:hover,
|
||||
.c__button--tertiary.c__button--active {
|
||||
background-color: var(
|
||||
--c--components--button--tertiary--background--color-hover
|
||||
);
|
||||
color: var(--c--components--button--tertiary--color);
|
||||
}
|
||||
|
||||
.c__button--tertiary:active,
|
||||
.c__button--tertiary.c__button--active {
|
||||
background-color: var(
|
||||
--c--components--button--tertiary--background--color-active
|
||||
);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* Button tertiary-text */
|
||||
.c__button--tertiary-text {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Button success */
|
||||
.c__button--success {
|
||||
background-color: var(--c--components--button--success--background--color);
|
||||
color: var(--c--components--button--success--color);
|
||||
@@ -239,18 +426,12 @@
|
||||
color: var(--c--components--button--success--color-disabled);
|
||||
}
|
||||
|
||||
/* Button secondary */
|
||||
.c__button--secondary {
|
||||
background-color: var(--c--components--button--secondary--background--color);
|
||||
color: var(--c--components--button--secondary--color);
|
||||
border: 1px solid var(--c--components--button--secondary--border--color);
|
||||
}
|
||||
|
||||
.c__button--secondary:disabled {
|
||||
background-color: var(--c--theme--colors--greyscale-000);
|
||||
color: var(--c--components--button--secondary--color-disabled);
|
||||
}
|
||||
|
||||
.c__button--secondary:hover,
|
||||
.c__button--secondary:focus-visible {
|
||||
background-color: var(
|
||||
@@ -260,60 +441,111 @@
|
||||
border: 1px solid var(--c--components--button--secondary--border--color-hover);
|
||||
}
|
||||
|
||||
.c__button--secondary:active,
|
||||
.c__button--secondary.c__button--active {
|
||||
background-color: var(
|
||||
--c--components--button--secondary--background--color-active
|
||||
);
|
||||
.c__button--tertiary {
|
||||
color: var(--c--components--button--tertiary--color);
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Button danger */
|
||||
.c__button--tertiary:hover,
|
||||
.c__button--tertiary:focus-visible {
|
||||
background-color: var(
|
||||
--c--components--button--tertiary--background--color-hover
|
||||
);
|
||||
color: var(--c--components--button--tertiary--color);
|
||||
}
|
||||
|
||||
.c__button--tertiary:disabled {
|
||||
background-color: var(
|
||||
--c--components--button--tertiary--background--color-disabled
|
||||
);
|
||||
color: var(--c--components--button--tertiary--color-disabled);
|
||||
}
|
||||
|
||||
.c__button--tertiary-text {
|
||||
border: none;
|
||||
color: var(--c--components--button--tertiary-text--color);
|
||||
}
|
||||
|
||||
.c__button--tertiary-text:hover,
|
||||
.c__button--tertiary-text:focus-visible {
|
||||
background-color: var(
|
||||
--c--components--button--tertiary-text--background--color-hover
|
||||
);
|
||||
color: var(--c--components--button--tertiary-text--color-hover);
|
||||
}
|
||||
|
||||
.c__button--tertiary-text:disabled {
|
||||
background-color: var(
|
||||
--c--components--button--tertiary-text--background--color-disabled
|
||||
);
|
||||
color: var(--c--components--button--tertiary-text--color-disabled);
|
||||
}
|
||||
|
||||
.c__button--danger {
|
||||
background-color: var(--c--components--button--danger--background--color);
|
||||
}
|
||||
|
||||
.c__button--danger:hover,
|
||||
.c__button--danger:focus-visible {
|
||||
background-color: var(
|
||||
--c--components--button--danger--background--color-hover
|
||||
);
|
||||
color: var(--c--components--button--danger--color-hover);
|
||||
}
|
||||
|
||||
.c__button--danger:disabled {
|
||||
background-color: var(
|
||||
--c--components--button--danger--background--color-disabled
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal
|
||||
*/
|
||||
|
||||
.c__modal__title {
|
||||
text-align: start;
|
||||
font-size: var(--c--theme--font--sizes--h6);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.c__modal__backdrop {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/*.c__modal__close .c__button--tertiary-text:hover,*/
|
||||
/*.c__modal__close .c__button--tertiary-text:focus-visible {*/
|
||||
/* box-shadow: none;*/
|
||||
/*}*/
|
||||
.c__modal__close .c__button--tertiary-text:hover,
|
||||
.c__modal__close .c__button--tertiary-text:focus-visible {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*.c__modal__close .c__button {*/
|
||||
/* right: 5px;*/
|
||||
/* top: 5px;*/
|
||||
/* padding: 1.5rem 1rem;*/
|
||||
/*}*/
|
||||
.c__modal__close button {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
/*!***/
|
||||
/* * Toast*/
|
||||
/**!*/
|
||||
/*.c__toast__container {*/
|
||||
/* z-index: 10000;*/
|
||||
/*}*/
|
||||
.c__modal--full .c__modal__content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@media screen and (width <= 420px) {
|
||||
.c__modal__scroller {
|
||||
padding: 0.7rem;
|
||||
}
|
||||
|
||||
.c__modal__title h2 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 576px) {
|
||||
.c__modal__footer--sided {
|
||||
gap: 0.5rem;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert
|
||||
*/
|
||||
|
||||
* Toast
|
||||
*/
|
||||
.c__toast__container {
|
||||
z-index: 1001;
|
||||
z-index: 10000;
|
||||
}
|
||||
.c__alert--success {
|
||||
border-color: var(--c--theme--colors--success-600);
|
||||
}
|
||||
.c__alert--info {
|
||||
border-color: var(--c--theme--colors--info-600);
|
||||
}
|
||||
.c__alert--error {
|
||||
border-color: var(--c--theme--colors--danger-600);
|
||||
|
||||
/**
|
||||
* Tooltip
|
||||
*/
|
||||
.c__tooltip {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -73,12 +73,10 @@ export const tokens = {
|
||||
'success-text': '#FFFFFF',
|
||||
'warning-text': '#FFFFFF',
|
||||
'danger-text': '#FFFFFF',
|
||||
'card-border': '#DDDDDD',
|
||||
'card-border': '#ededed',
|
||||
'primary-bg': '#FAFAFA',
|
||||
'primary-150': '#E5EEFA',
|
||||
'info-150': '#E5EEFA',
|
||||
'grey-400': '#929292',
|
||||
'grey-800': '#2A2A2A',
|
||||
},
|
||||
font: {
|
||||
sizes: {
|
||||
@@ -92,7 +90,7 @@ export const tokens = {
|
||||
m: '0.8125rem',
|
||||
s: '0.75rem',
|
||||
ml: '0.938rem',
|
||||
xl: '1.25rem',
|
||||
xl: '1.50rem',
|
||||
t: '0.6875rem',
|
||||
},
|
||||
weights: {
|
||||
@@ -113,7 +111,7 @@ export const tokens = {
|
||||
h2: 'normal',
|
||||
h3: 'normal',
|
||||
h4: 'normal',
|
||||
h5: 'normal',
|
||||
h5: '1px',
|
||||
h6: 'normal',
|
||||
l: 'normal',
|
||||
m: 'normal',
|
||||
@@ -121,12 +119,17 @@ export const tokens = {
|
||||
},
|
||||
},
|
||||
spacings: {
|
||||
'0': '0',
|
||||
xl: '4rem',
|
||||
l: '3rem',
|
||||
b: '1.625rem',
|
||||
s: '1rem',
|
||||
t: '0.5rem',
|
||||
st: '0.25rem',
|
||||
none: '0',
|
||||
auto: 'auto',
|
||||
bx: '2.2rem',
|
||||
full: '100%',
|
||||
},
|
||||
transitions: {
|
||||
'ease-in': 'cubic-bezier(0.32, 0, 0.67, 0)',
|
||||
@@ -135,13 +138,145 @@ export const tokens = {
|
||||
duration: '250ms',
|
||||
},
|
||||
breakpoints: {
|
||||
xs: 0,
|
||||
xs: '480px',
|
||||
sm: '576px',
|
||||
md: '768px',
|
||||
lg: '992px',
|
||||
xl: '1200px',
|
||||
xxl: '1400px',
|
||||
xxs: '320px',
|
||||
},
|
||||
logo: { src: '', widthHeader: '', widthFooter: '', alt: '' },
|
||||
},
|
||||
components: {
|
||||
datagrid: {
|
||||
header: {
|
||||
weight: 'var(--c--theme--font--weights--extrabold)',
|
||||
size: 'var(--c--theme--font--sizes--ml)',
|
||||
},
|
||||
cell: {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
size: 'var(--c--theme--font--sizes--ml)',
|
||||
},
|
||||
},
|
||||
'forms-checkbox': {
|
||||
'background-color': { hover: '#055fd214' },
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'font-size': 'var(--c--theme--font--sizes--ml)',
|
||||
},
|
||||
'forms-datepicker': {
|
||||
'border-color': 'var(--c--theme--colors--primary-500)',
|
||||
'value-color': 'var(--c--theme--colors--primary-500)',
|
||||
'border-radius': {
|
||||
hover: 'var(--c--components--forms-datepicker--border-radius)',
|
||||
focus: 'var(--c--components--forms-datepicker--border-radius)',
|
||||
},
|
||||
},
|
||||
'forms-field': {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'value-color': 'var(--c--theme--colors--primary-500)',
|
||||
width: 'auto',
|
||||
},
|
||||
'forms-input': {
|
||||
'value-color': 'var(--c--theme--colors--primary-500)',
|
||||
'border-color': 'var(--c--theme--colors--primary-500)',
|
||||
color: {
|
||||
error: 'var(--c--theme--colors--danger-500)',
|
||||
'error-hover': 'var(--c--theme--colors--danger-500)',
|
||||
'box-shadow-error-hover': 'var(--c--theme--colors--danger-500)',
|
||||
},
|
||||
},
|
||||
'forms-labelledbox': {
|
||||
'label-color': {
|
||||
small: 'var(--c--theme--colors--primary-500)',
|
||||
'small-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
big: { disabled: 'var(--c--theme--colors--greyscale-400)' },
|
||||
},
|
||||
},
|
||||
'forms-select': {
|
||||
'border-color': 'var(--c--theme--colors--primary-500)',
|
||||
'border-color-disabled-hover':
|
||||
'var(--c--theme--colors--greyscale-200)',
|
||||
'border-radius': {
|
||||
hover: 'var(--c--components--forms-select--border-radius)',
|
||||
focus: 'var(--c--components--forms-select--border-radius)',
|
||||
},
|
||||
'font-size': 'var(--c--theme--font--sizes--ml)',
|
||||
'menu-background-color': '#ffffff',
|
||||
'item-background-color': {
|
||||
hover: 'var(--c--theme--colors--primary-300)',
|
||||
},
|
||||
},
|
||||
'forms-switch': {
|
||||
'accent-color': 'var(--c--theme--colors--primary-400)',
|
||||
},
|
||||
'forms-textarea': {
|
||||
'border-color': 'var(--c--components--forms-textarea--border-color)',
|
||||
'border-color-hover':
|
||||
'var(--c--components--forms-textarea--border-color)',
|
||||
'border-radius': {
|
||||
hover: 'var(--c--components--forms-textarea--border-radius)',
|
||||
focus: 'var(--c--components--forms-textarea--border-radius)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
disabled: {
|
||||
'border-color-hover': 'var(--c--theme--colors--greyscale-200)',
|
||||
},
|
||||
},
|
||||
modal: { 'background-color': '#ffffff' },
|
||||
button: {
|
||||
'border-radius': {
|
||||
active: 'var(--c--components--button--border-radius)',
|
||||
},
|
||||
'medium-height': 'auto',
|
||||
'small-height': 'auto',
|
||||
success: {
|
||||
color: 'white',
|
||||
'color-disabled': 'white',
|
||||
'color-hover': 'white',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--success-600)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-300)',
|
||||
'color-hover': 'var(--c--theme--colors--success-800)',
|
||||
},
|
||||
},
|
||||
danger: {
|
||||
'color-hover': 'white',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--danger-400)',
|
||||
'color-hover': 'var(--c--theme--colors--danger-500)',
|
||||
'color-disabled': 'var(--c--theme--colors--danger-100)',
|
||||
},
|
||||
},
|
||||
primary: {
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'color-active': 'var(--c--theme--colors--primary-text)',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--primary-400)',
|
||||
'color-active': 'var(--c--theme--colors--primary-500)',
|
||||
},
|
||||
border: { 'color-active': 'transparent' },
|
||||
},
|
||||
secondary: {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
background: {
|
||||
color: 'white',
|
||||
'color-hover': 'var(--c--theme--colors--primary-700)',
|
||||
},
|
||||
border: { color: 'var(--c--theme--colors--primary-200)' },
|
||||
},
|
||||
tertiary: {
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-600)',
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-200)',
|
||||
},
|
||||
},
|
||||
disabled: { color: 'white', background: { color: '#b3cef0' } },
|
||||
},
|
||||
'la-gauffre': { activated: false },
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
@@ -199,188 +334,98 @@ export const tokens = {
|
||||
dsfr: {
|
||||
theme: {
|
||||
colors: {
|
||||
'primary-text': 'var(--c--theme--colors--primary-600)',
|
||||
'primary-100': '#F5F5FE',
|
||||
'primary-200': '#ECECFE',
|
||||
'primary-300': '#E3E3FD',
|
||||
'primary-400': '#CACAFB',
|
||||
'primary-500': '#6A6AF4',
|
||||
'card-border': '#ededed',
|
||||
'primary-text': '#000091',
|
||||
'primary-100': '#f5f5fe',
|
||||
'primary-150': '#F4F4FD',
|
||||
'primary-200': '#ececfe',
|
||||
'primary-300': '#e3e3fd',
|
||||
'primary-400': '#cacafb',
|
||||
'primary-500': '#6a6af4',
|
||||
'primary-600': '#000091',
|
||||
'primary-clicked-100': '#CBCBFA',
|
||||
'primary-clicked-200': '#BBBBFC',
|
||||
'primary-clicked-300': '#ADADF9',
|
||||
'primary-clicked-400': '#8B8BF6',
|
||||
'primary-clicked-500': '#AEAEF9',
|
||||
'primary-clicked-600': '#2323FF',
|
||||
'primary-hover-100': '#DCDCFC',
|
||||
'primary-hover-200': '#CECEFC',
|
||||
'primary-hover-300': '#C1C1FB',
|
||||
'primary-hover-400': '#A1A1F8',
|
||||
'primary-hover-500': '#9898F8',
|
||||
'primary-hover-600': '#1212FF',
|
||||
'secondary-100': '#FEF4F4',
|
||||
'secondary-200': '#FEE9E9',
|
||||
'secondary-300': '#FDDEDE',
|
||||
'secondary-400': '#FCBFBF',
|
||||
'secondary-500': '#E1000F',
|
||||
'secondary-600': '#C9191E',
|
||||
'secondary-clicked-100': '#FAC4C4',
|
||||
'secondary-clicked-200': '#FCAFAF',
|
||||
'secondary-clicked-300': '#FA9E9E',
|
||||
'secondary-clicked-400': '#FA7474',
|
||||
'secondary-clicked-500': '#FF4347',
|
||||
'secondary-clicked-600': '#F95A5C',
|
||||
'secondary-hover-100': '#FCD7D7',
|
||||
'secondary-hover-200': '#FDC5C5',
|
||||
'secondary-hover-300': '#FBB6B6',
|
||||
'secondary-hover-400': '#FB8F8F',
|
||||
'secondary-hover-500': '#FF292F',
|
||||
'secondary-hover-600': '#F93F42',
|
||||
'greyscale-000': '#FFFFFF',
|
||||
'greyscale-050': '#F6F6F6',
|
||||
'greyscale-100': '#EEEEEE',
|
||||
'greyscale-200': '#E5E5E5',
|
||||
'greyscale-250': '#DDDDDD',
|
||||
'greyscale-300': '#CECECE',
|
||||
'greyscale-400': '#929292',
|
||||
'greyscale-500': '#666666',
|
||||
'greyscale-700': '#3A3A3A',
|
||||
'greyscale-1000': '#161616',
|
||||
'greyscale-clicked-000': '#EDEDED',
|
||||
'greyscale-clicked-050': '#CFCFCF',
|
||||
'greyscale-clicked-100': '#C1C1C1',
|
||||
'greyscale-clicked-200': '#B2B2B2',
|
||||
'greyscale-clicked-250': '#A7A7A7',
|
||||
'greyscale-clicked-300': '#939393',
|
||||
'greyscale-clicked-400': '#CECECE',
|
||||
'greyscale-clicked-500': '#A6A6A6',
|
||||
'greyscale-clicked-700': '#777777',
|
||||
'greyscale-clicked-1000': '#474747',
|
||||
'greyscale-hover-000': '#F6F6F6',
|
||||
'greyscale-hover-050': '#DFDFDF',
|
||||
'greyscale-hover-100': '#D2D2D2',
|
||||
'greyscale-hover-200': '#C5C5C5',
|
||||
'greyscale-hover-250': '#BBBBBB',
|
||||
'greyscale-hover-300': '#A8A8A8',
|
||||
'greyscale-hover-400': '#BBBBBB',
|
||||
'greyscale-hover-500': '#919191',
|
||||
'greyscale-hover-700': '#616161',
|
||||
'greyscale-hover-1000': '#343434',
|
||||
'success-200': '#B8FEC9',
|
||||
'success-500': '#18753C',
|
||||
'success-600': '#18753C',
|
||||
'success-clicked-200': '#34EB7B',
|
||||
'success-clicked-600': '#2FC368',
|
||||
'success-hover-200': '#46FD89',
|
||||
'success-hover-600': '#27A959',
|
||||
'info-200': '#E8EDFF',
|
||||
'info-500': '#0063CB',
|
||||
'info-600': '#0063CB',
|
||||
'info-clicked-200': '#A9BFFF',
|
||||
'info-clicked-600': '#6798FF',
|
||||
'info-hover-200': '#C2D1FF',
|
||||
'info-hover-600': '#3B87FF',
|
||||
'warning-200': '#FFE9E6',
|
||||
'warning-500': '#B34000',
|
||||
'warning-600': '#B34000',
|
||||
'warning-clicked-200': '#FFB0A2',
|
||||
'warning-clicked-600': '#FF7A55',
|
||||
'warning-hover-200': '#FFC6BD',
|
||||
'warning-hover-600': '#FF6218',
|
||||
'danger-200': '#FFE9E9',
|
||||
'danger-500': '#CE0500',
|
||||
'danger-600': '#CE0500',
|
||||
'danger-clicked-200': '#FFAFAF',
|
||||
'danger-clicked-600': '#FF4140',
|
||||
'danger-hover-200': '#FFC5C5',
|
||||
'danger-hover-600': '#FF2725',
|
||||
'cumulus-100': '#F3F6FE',
|
||||
'cumulus-200': '#E6EEFE',
|
||||
'cumulus-300': '#DAE6FD',
|
||||
'cumulus-400': '#B6CFFB',
|
||||
'cumulus-500': '#417DC4',
|
||||
'cumulus-600': '#3558A2',
|
||||
'emeraude-100': '#E3FDEB',
|
||||
'emeraude-200': '#C3FAD5',
|
||||
'emeraude-300': '#9EF9BE',
|
||||
'emeraude-400': '#6FE49D',
|
||||
'emeraude-500': '#00A95F',
|
||||
'emeraude-600': '#297254',
|
||||
'glycine-100': '#FEF3FD',
|
||||
'glycine-200': '#FEE7FC',
|
||||
'glycine-300': '#FDDBFA',
|
||||
'glycine-400': '#FBB8F6',
|
||||
'glycine-500': '#A558A0',
|
||||
'glycine-600': '#6E445A',
|
||||
'terre-battue-100': '#FEF7DA',
|
||||
'terre-battue-200': '#FCEEAC',
|
||||
'terre-battue-300': '#FBE769',
|
||||
'terre-battue-400': '#E2CF58',
|
||||
'terre-battue-500': '#B7A73F',
|
||||
'terre-battue-600': '#66673D',
|
||||
'tilleul-verveine-100': '#FEF7DA',
|
||||
'tilleul-verveine-200': '#FCEEAC',
|
||||
'tilleul-verveine-300': '#FBE769',
|
||||
'tilleul-verveine-400': '#E2CF58',
|
||||
'tilleul-verveine-500': '#B7A73F',
|
||||
'tilleul-verveine-600': '#66673D',
|
||||
'focus-500': '#0A76F6',
|
||||
'primary-700': '#272747',
|
||||
'primary-800': '#21213f',
|
||||
'primary-900': '#1c1a36',
|
||||
'secondary-text': '#FFFFFF',
|
||||
'secondary-100': '#fee9ea',
|
||||
'secondary-200': '#fedfdf',
|
||||
'secondary-300': '#fdbfbf',
|
||||
'secondary-400': '#e1020f',
|
||||
'secondary-500': '#c91a1f',
|
||||
'secondary-600': '#5e2b2b',
|
||||
'secondary-700': '#3b2424',
|
||||
'secondary-800': '#341f1f',
|
||||
'secondary-900': '#2b1919',
|
||||
'greyscale-text': '#303C4B',
|
||||
'greyscale-000': '#f6f6f6',
|
||||
'greyscale-100': '#eeeeee',
|
||||
'greyscale-200': '#e5e5e5',
|
||||
'greyscale-300': '#e1e1e1',
|
||||
'greyscale-400': '#dddddd',
|
||||
'greyscale-500': '#cecece',
|
||||
'greyscale-600': '#7b7b7b',
|
||||
'greyscale-700': '#666666',
|
||||
'greyscale-800': '#2a2a2a',
|
||||
'greyscale-900': '#1e1e1e',
|
||||
'success-text': '#1f8d49',
|
||||
'success-100': '#dffee6',
|
||||
'success-200': '#b8fec9',
|
||||
'success-300': '#88fdaa',
|
||||
'success-400': '#3bea7e',
|
||||
'success-500': '#1f8d49',
|
||||
'success-600': '#18753c',
|
||||
'success-700': '#204129',
|
||||
'success-800': '#1e2e22',
|
||||
'success-900': '#19281d',
|
||||
'info-text': '#0078f3',
|
||||
'info-100': '#f4f6ff',
|
||||
'info-200': '#e8edff',
|
||||
'info-300': '#dde5ff',
|
||||
'info-400': '#bdcdff',
|
||||
'info-500': '#0078f3',
|
||||
'info-600': '#0063cb',
|
||||
'info-700': '#f4f6ff',
|
||||
'info-800': '#222a3f',
|
||||
'info-900': '#1d2437',
|
||||
'warning-text': '#d64d00',
|
||||
'warning-100': '#fff4f3',
|
||||
'warning-200': '#ffe9e6',
|
||||
'warning-300': '#ffded9',
|
||||
'warning-400': '#ffbeb4',
|
||||
'warning-500': '#d64d00',
|
||||
'warning-600': '#b34000',
|
||||
'warning-700': '#5e2c21',
|
||||
'warning-800': '#3e241e',
|
||||
'warning-900': '#361e19',
|
||||
'danger-text': '#e1000f',
|
||||
'danger-100': '#fef4f4',
|
||||
'danger-200': '#fee9e9',
|
||||
'danger-300': '#fddede',
|
||||
'danger-400': '#fcbfbf',
|
||||
'danger-500': '#e1000f',
|
||||
'danger-600': '#c9191e',
|
||||
'danger-700': '#642727',
|
||||
'danger-800': '#412121',
|
||||
'danger-900': '#3a1c1c',
|
||||
},
|
||||
spacings: {
|
||||
'050V': '2px',
|
||||
'100V': '4px',
|
||||
'150V': '6px',
|
||||
'100W': '8px',
|
||||
'300V': '12px',
|
||||
'200W': '16px',
|
||||
'300W': '24px',
|
||||
'400W': '32px',
|
||||
'500W': '40px',
|
||||
'600W': '48px',
|
||||
'700W': '56px',
|
||||
'800W': '64px',
|
||||
'900W': '72px',
|
||||
'1200W': '96px',
|
||||
'1500W': '120px',
|
||||
},
|
||||
font: {
|
||||
families: { accent: 'Marianne', base: 'Marianne' },
|
||||
sizes: {
|
||||
xl: '20px',
|
||||
lg: '18px',
|
||||
md: '16px',
|
||||
sm: '14px',
|
||||
xs: '12px',
|
||||
h1: '32px',
|
||||
h2: '28px',
|
||||
h3: '24px',
|
||||
h4: '22px',
|
||||
h5: '20px',
|
||||
h6: '18px',
|
||||
'title-alt-xl': '80px',
|
||||
'title-alt-lg': '72px',
|
||||
'title-alt-md': '64px',
|
||||
'title-alt-sm': '56px',
|
||||
'title-alt-xs': '48px',
|
||||
},
|
||||
font: { families: { accent: 'Marianne', base: 'Marianne' } },
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
widthHeader: '110px',
|
||||
widthFooter: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
alert: {
|
||||
'border-radius': '0',
|
||||
'background-color': 'var(--c--theme--colors--greyscale-000)',
|
||||
},
|
||||
modal: { 'box-shadow': '0px 6px 18px 0px rgba(0, 0, 18, 0.16);' },
|
||||
alert: { 'border-radius': '0' },
|
||||
button: {
|
||||
'medium-height': '48px',
|
||||
'border-radius': '4px',
|
||||
'medium-text-height': '48px',
|
||||
primary: {
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-hover-600)',
|
||||
'color-active': 'var(--c--theme--colors--primary-600)',
|
||||
'color-focus': 'var(--c--theme--colors--focus-500)',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'color-hover': '#1212ff',
|
||||
'color-active': '#2323ff',
|
||||
},
|
||||
color: '#ffffff',
|
||||
'color-hover': '#ffffff',
|
||||
@@ -388,51 +433,30 @@ export const tokens = {
|
||||
},
|
||||
'primary-text': {
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-hover-000)',
|
||||
'color-active': 'var(--c--theme--colors--greyscale-clicked-000)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
'color-active': 'var(--c--theme--colors--primary-100)',
|
||||
},
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
secondary: {
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--greyscale-000)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-hover-000)',
|
||||
'color-active': '#EDEDED',
|
||||
},
|
||||
background: { 'color-hover': '#F6F6F6', 'color-active': '#EDEDED' },
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--greyscale-250)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-250)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
},
|
||||
tertiary: {
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--primary-300)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-hover-300)',
|
||||
'color-active': 'var(--c--theme--colors--primary-clicked-300)',
|
||||
},
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--primary-500)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-250)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
'color-disabled': 'var(--c--theme--colors--primary-400)',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
'tertiary-text': {
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
},
|
||||
'color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
},
|
||||
datagrid: {
|
||||
header: {
|
||||
color: 'var(--c--theme--colors--greyscale-500)',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
size: 'var(--c--theme--font--sizes--s)',
|
||||
},
|
||||
body: {
|
||||
@@ -442,15 +466,53 @@ 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-datepicker': {},
|
||||
'forms-fileuploader': {},
|
||||
'forms-input': {},
|
||||
'forms-textarea': {},
|
||||
'forms-select': {},
|
||||
'forms-switch': {},
|
||||
'forms-checkbox': {},
|
||||
'forms-checkbox': {
|
||||
'border-radius': '0',
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
text: {
|
||||
color: 'var(--c--theme--colors--greyscale-text)',
|
||||
size: 'var(--c--theme--font--sizes--t)',
|
||||
},
|
||||
},
|
||||
'forms-datepicker': { 'border-radius': '0' },
|
||||
'forms-fileuploader': { 'border-radius': '0' },
|
||||
'forms-field': {
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
'footer-font-size': 'var(--c--theme--font--sizes--t)',
|
||||
'footer-color': 'var(--c--theme--colors--greyscale-text)',
|
||||
},
|
||||
'forms-input': {
|
||||
'border-radius': '4px',
|
||||
'background-color': '#ffffff',
|
||||
'border-color': 'var(--c--theme--colors--primary-text)',
|
||||
'box-shadow-color': 'var(--c--theme--colors--primary-text)',
|
||||
'value-color': 'var(--c--theme--colors--primary-text)',
|
||||
'font-size': '14px',
|
||||
},
|
||||
'forms-labelledbox': {
|
||||
'label-color': { big: 'var(--c--theme--colors--primary-text)' },
|
||||
},
|
||||
'forms-radio': {
|
||||
'accent-color': 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
'forms-select': {
|
||||
'item-font-size': '14px',
|
||||
'border-radius': '4px',
|
||||
'border-radius-hover': '4px',
|
||||
'background-color': '#ffffff',
|
||||
'border-color': 'var(--c--theme--colors--primary-text)',
|
||||
'border-color-hover': 'var(--c--theme--colors--primary-text)',
|
||||
'box-shadow-color': 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
'forms-switch': {
|
||||
'handle-border-radius': '2px',
|
||||
'rail-border-radius': '4px',
|
||||
'accent-color': 'var(--c--theme--colors--primary-text)',
|
||||
},
|
||||
'forms-textarea': { 'border-radius': '0' },
|
||||
'la-gauffre': { activated: true },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
import { tokens } from './cunningham-tokens';
|
||||
import useCunninghamTheme from './useCunninghamTheme';
|
||||
|
||||
export { tokens, useCunninghamTheme };
|
||||
export * from './cunningham-tokens';
|
||||
export * from './useCunninghamTheme';
|
||||
|
||||
@@ -6,22 +6,25 @@ import { tokens } from './cunningham-tokens';
|
||||
type Tokens = typeof tokens.themes.default & Partial<typeof tokens.themes.dsfr>;
|
||||
type ColorsTokens = Tokens['theme']['colors'];
|
||||
type ComponentTokens = Tokens['components'];
|
||||
type Theme = 'default' | 'dsfr';
|
||||
export type Theme = keyof typeof tokens.themes;
|
||||
|
||||
interface AuthStore {
|
||||
theme: Theme;
|
||||
theme: string;
|
||||
setTheme: (theme: Theme) => void;
|
||||
themeTokens: () => Partial<Tokens['theme']>;
|
||||
colorsTokens: () => Partial<ColorsTokens>;
|
||||
componentTokens: () => ComponentTokens;
|
||||
}
|
||||
|
||||
const useCunninghamTheme = create<AuthStore>((set, get) => {
|
||||
export const useCunninghamTheme = create<AuthStore>((set, get) => {
|
||||
const currentTheme = () =>
|
||||
merge(tokens.themes['default'], tokens.themes[get().theme]) as Tokens;
|
||||
merge(
|
||||
tokens.themes['default'],
|
||||
tokens.themes[get().theme as keyof typeof tokens.themes],
|
||||
) as Tokens;
|
||||
|
||||
return {
|
||||
theme: (process.env.NEXT_PUBLIC_THEME as Theme) || 'dsfr',
|
||||
theme: 'dsfr',
|
||||
themeTokens: () => currentTheme().theme,
|
||||
colorsTokens: () => currentTheme().theme.colors,
|
||||
componentTokens: () => currentTheme().components,
|
||||
@@ -30,5 +33,3 @@ const useCunninghamTheme = create<AuthStore>((set, get) => {
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default useCunninghamTheme;
|
||||
|
||||
@@ -20,9 +20,6 @@ declare module '*.svg?url' {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
NEXT_PUBLIC_API_ORIGIN?: string;
|
||||
NEXT_PUBLIC_MEDIA_URL?: string;
|
||||
NEXT_PUBLIC_Y_PROVIDER_URL?: string;
|
||||
NEXT_PUBLIC_SW_DEACTIVATED?: string;
|
||||
NEXT_PUBLIC_THEME?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { locales } from '@blocknote/core';
|
||||
import { Dictionary, locales } from '@blocknote/core';
|
||||
import '@blocknote/core/fonts/inter.css';
|
||||
import { BlockNoteView } from '@blocknote/mantine';
|
||||
import '@blocknote/mantine/style.css';
|
||||
import { useCreateBlockNote } from '@blocknote/react';
|
||||
import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||
import { t } from 'i18next';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, TextErrors } from '@/components';
|
||||
import { mediaUrl } from '@/core';
|
||||
import { useAuthStore } from '@/core/auth';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
|
||||
import { useCreateDocAttachment } from '../api/useCreateDocUpload';
|
||||
import { useUploadFile } from '../hook';
|
||||
import useSaveDoc from '../hook/useSaveDoc';
|
||||
import { useHeadingStore } from '../stores';
|
||||
import { useEditorStore, useHeadingStore } from '../stores';
|
||||
import { randomColor } from '../utils';
|
||||
|
||||
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
||||
@@ -26,17 +24,8 @@ const cssEditor = (readonly: boolean) => `
|
||||
};
|
||||
& .bn-editor {
|
||||
padding-right: 30px;
|
||||
${
|
||||
readonly &&
|
||||
`
|
||||
padding-left: 30px;
|
||||
pointer-events: none;
|
||||
`
|
||||
}
|
||||
${readonly && `padding-left: 30px;`}
|
||||
};
|
||||
& .collaboration-cursor__caret.ProseMirror-widget{
|
||||
word-wrap: initial;
|
||||
}
|
||||
& .bn-inline-content code {
|
||||
background-color: gainsboro;
|
||||
padding: 2px;
|
||||
@@ -86,34 +75,20 @@ export const BlockNoteEditor = ({
|
||||
}: BlockNoteEditorProps) => {
|
||||
const isVersion = doc.id !== storeId;
|
||||
const { userData } = useAuthStore();
|
||||
const { setStore, docsStore } = useDocStore();
|
||||
|
||||
const { setEditor } = useEditorStore();
|
||||
const { t } = useTranslation();
|
||||
const readOnly = !doc.abilities.partial_update || isVersion;
|
||||
useSaveDoc(doc.id, provider.document, !readOnly);
|
||||
const storedEditor = docsStore?.[storeId]?.editor;
|
||||
const {
|
||||
mutateAsync: createDocAttachment,
|
||||
isError: isErrorAttachment,
|
||||
error: errorAttachment,
|
||||
} = useCreateDocAttachment();
|
||||
const { setHeadings, resetHeadings } = useHeadingStore();
|
||||
const { i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
|
||||
const uploadFile = useCallback(
|
||||
async (file: File) => {
|
||||
const body = new FormData();
|
||||
body.append('file', file);
|
||||
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
|
||||
|
||||
const ret = await createDocAttachment({
|
||||
docId: doc.id,
|
||||
body,
|
||||
});
|
||||
|
||||
return `${mediaUrl()}${ret.file}`;
|
||||
},
|
||||
[createDocAttachment, doc.id],
|
||||
);
|
||||
const collabName =
|
||||
userData?.full_name ||
|
||||
userData?.email ||
|
||||
(readOnly ? 'Reader' : t('Anonymous'));
|
||||
|
||||
const editor = useCreateBlockNote(
|
||||
{
|
||||
@@ -121,19 +96,48 @@ export const BlockNoteEditor = ({
|
||||
provider,
|
||||
fragment: provider.document.getXmlFragment('document-store'),
|
||||
user: {
|
||||
name: userData?.full_name || userData?.email || t('Anonymous'),
|
||||
name: collabName,
|
||||
color: randomColor(),
|
||||
},
|
||||
/**
|
||||
* We re-use the blocknote code to render the cursor but we:
|
||||
* - fix rendering issue with Firefox
|
||||
* - We don't want to show the cursor when anonymous users
|
||||
*/
|
||||
renderCursor: (user: { color: string; name: string }) => {
|
||||
const cursor = document.createElement('span');
|
||||
|
||||
if (user.name === 'Reader') {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
cursor.classList.add('collaboration-cursor__caret');
|
||||
cursor.setAttribute('style', `border-color: ${user.color}`);
|
||||
|
||||
const label = document.createElement('span');
|
||||
|
||||
label.classList.add('collaboration-cursor__label');
|
||||
label.setAttribute('style', `background-color: ${user.color}`);
|
||||
label.insertBefore(document.createTextNode(user.name), null);
|
||||
|
||||
cursor.insertBefore(label, null);
|
||||
|
||||
return cursor;
|
||||
},
|
||||
},
|
||||
dictionary: locales[lang as keyof typeof locales],
|
||||
dictionary: locales[lang as keyof typeof locales] as Dictionary,
|
||||
uploadFile,
|
||||
},
|
||||
[lang, provider, uploadFile, userData?.email, userData?.full_name],
|
||||
[collabName, lang, provider, uploadFile],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setStore(storeId, { editor });
|
||||
}, [setStore, storeId, editor]);
|
||||
setEditor(editor);
|
||||
|
||||
return () => {
|
||||
setEditor(undefined);
|
||||
};
|
||||
}, [setEditor, editor]);
|
||||
|
||||
useEffect(() => {
|
||||
setHeadings(editor);
|
||||
@@ -149,7 +153,7 @@ export const BlockNoteEditor = ({
|
||||
|
||||
return (
|
||||
<Box $css={cssEditor(readOnly)}>
|
||||
{isErrorAttachment && (
|
||||
{errorAttachment && (
|
||||
<Box $margin={{ bottom: 'big' }}>
|
||||
<TextErrors
|
||||
causes={errorAttachment.cause}
|
||||
@@ -160,7 +164,7 @@ export const BlockNoteEditor = ({
|
||||
)}
|
||||
|
||||
<BlockNoteView
|
||||
editor={storedEditor ?? editor}
|
||||
editor={editor}
|
||||
formattingToolbar={false}
|
||||
editable={!readOnly}
|
||||
theme="light"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Alert, Loader, VariantType } from '@openfun/cunningham-react';
|
||||
import { useRouter as useNavigate } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, Card, Text, TextErrors } from '@/components';
|
||||
import { useCollaborationUrl } from '@/core';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { DocHeader } from '@/features/docs/doc-header';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
@@ -32,8 +32,8 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
|
||||
const { docsStore } = useDocStore();
|
||||
const provider = docsStore?.[doc.id]?.provider;
|
||||
const { providers } = useDocStore();
|
||||
const provider = providers?.[doc.id];
|
||||
|
||||
if (!provider) {
|
||||
return null;
|
||||
@@ -98,24 +98,25 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
||||
docId: doc.id,
|
||||
versionId,
|
||||
});
|
||||
const { createProvider, docsStore } = useDocStore();
|
||||
const { createProvider, providers } = useDocStore();
|
||||
const collaborationUrl = useCollaborationUrl(versionId);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { replace } = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!version?.id) {
|
||||
if (!version?.id || !collaborationUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = docsStore?.[version.id]?.provider;
|
||||
const provider = providers?.[version.id];
|
||||
if (!provider || provider.document.guid !== version.id) {
|
||||
createProvider(version.id, version.content);
|
||||
createProvider(collaborationUrl, version.id, version.content);
|
||||
}
|
||||
}, [createProvider, docsStore, version]);
|
||||
}, [createProvider, providers, version, collaborationUrl]);
|
||||
|
||||
if (isError && error) {
|
||||
if (error.status === 404) {
|
||||
navigate.replace(`/404`);
|
||||
void replace(`/404`);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
const provider = docsStore?.[version.id]?.provider;
|
||||
const provider = providers?.[version.id];
|
||||
|
||||
if (!provider) {
|
||||
return null;
|
||||
|
||||
@@ -127,9 +127,7 @@ export const PanelEditor = ({
|
||||
</BoxButton>
|
||||
)}
|
||||
</Box>
|
||||
{isPanelTableContentOpen && (
|
||||
<TableContent doc={doc} headings={headings} />
|
||||
)}
|
||||
{isPanelTableContentOpen && <TableContent headings={headings} />}
|
||||
{!isPanelTableContentOpen && doc.abilities.versions_list && (
|
||||
<VersionList doc={doc} />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './useSaveDoc';
|
||||
export * from './useUploadFile';
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useMediaUrl } from '@/core/config';
|
||||
|
||||
import { useCreateDocAttachment } from '../api';
|
||||
|
||||
export const useUploadFile = (docId: string) => {
|
||||
const mediaUrl = useMediaUrl();
|
||||
const {
|
||||
mutateAsync: createDocAttachment,
|
||||
isError: isErrorAttachment,
|
||||
error: errorAttachment,
|
||||
} = useCreateDocAttachment();
|
||||
|
||||
const uploadFile = useCallback(
|
||||
async (file: File) => {
|
||||
const body = new FormData();
|
||||
body.append('file', file);
|
||||
|
||||
const ret = await createDocAttachment({
|
||||
docId,
|
||||
body,
|
||||
});
|
||||
|
||||
return `${mediaUrl}${ret.file}`;
|
||||
},
|
||||
[createDocAttachment, docId, mediaUrl],
|
||||
);
|
||||
|
||||
return {
|
||||
uploadFile,
|
||||
isErrorAttachment,
|
||||
errorAttachment,
|
||||
};
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './useEditorStore';
|
||||
export * from './useHeadingStore';
|
||||
export * from './usePanelEditorStore';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { BlockNoteEditor } from '@blocknote/core';
|
||||
import { create } from 'zustand';
|
||||
|
||||
export interface UseEditorstore {
|
||||
editor?: BlockNoteEditor;
|
||||
setEditor: (editor: BlockNoteEditor | undefined) => void;
|
||||
}
|
||||
|
||||
export const useEditorStore = create<UseEditorstore>((set) => ({
|
||||
editor: undefined,
|
||||
setEditor: (editor) => {
|
||||
set({ editor });
|
||||
},
|
||||
}));
|
||||
@@ -23,6 +23,5 @@ function hslToHex(h: number, s: number, l: number) {
|
||||
return `#${f(0)}${f(8)}${f(4)}`;
|
||||
}
|
||||
|
||||
export const toBase64 = (
|
||||
str: WithImplicitCoercion<ArrayBuffer | SharedArrayBuffer>,
|
||||
) => Buffer.from(str).toString('base64');
|
||||
export const toBase64 = (str: Uint8Array) =>
|
||||
Buffer.from(str).toString('base64');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box, Card, StyledLink, Text } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
@@ -46,7 +47,11 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
|
||||
$theme="primary"
|
||||
$variation="600"
|
||||
$size="2rem"
|
||||
$css={`&:hover {background-color: ${colorsTokens()['primary-100']}; };`}
|
||||
$css={css`
|
||||
&:hover {
|
||||
background-color: ${colorsTokens()['primary-100']};
|
||||
}
|
||||
`}
|
||||
$hasTransition
|
||||
$radius="5px"
|
||||
$padding="tiny"
|
||||
|
||||
@@ -57,7 +57,7 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
const { broadcast } = useBroadcastStore();
|
||||
|
||||
const { mutate: updateDoc } = useUpdateDoc({
|
||||
listInvalideQueries: [KEY_DOC, KEY_LIST_DOC],
|
||||
listInvalideQueries: [KEY_LIST_DOC],
|
||||
onSuccess(data) {
|
||||
if (data.title !== untitledDocument) {
|
||||
toast(t('Document title updated successfully'), VariantType.SUCCESS);
|
||||
@@ -104,6 +104,10 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTitleDisplay(doc.title);
|
||||
}, [doc.title]);
|
||||
|
||||
useEffect(() => {
|
||||
if ((!debounceRef.current && !isUntitled) || !headingText) {
|
||||
return;
|
||||
@@ -129,6 +133,7 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
$radius="4px"
|
||||
$padding={{ horizontal: 'tiny', vertical: '4px' }}
|
||||
$margin="none"
|
||||
$minWidth="200px"
|
||||
contentEditable={isFirefox() ? 'true' : 'plaintext-only'}
|
||||
onClick={handleOnClick}
|
||||
onBlurCapture={(e) =>
|
||||
|
||||
@@ -8,17 +8,18 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, DropButton, IconOptions } from '@/components';
|
||||
import { useAuthStore } from '@/core';
|
||||
import { usePanelEditorStore } from '@/features/docs/doc-editor/';
|
||||
import {
|
||||
useEditorStore,
|
||||
usePanelEditorStore,
|
||||
} from '@/features/docs/doc-editor/';
|
||||
import {
|
||||
Doc,
|
||||
ModalRemoveDoc,
|
||||
ModalShare,
|
||||
useDocStore,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { ModalVersion, Versions } from '@/features/docs/doc-versioning';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { ModalVersion, Versions } from '../../doc-versioning';
|
||||
|
||||
import { ModalPDF } from './ModalExport';
|
||||
|
||||
interface DocToolBoxProps {
|
||||
@@ -36,13 +37,12 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
|
||||
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
|
||||
const { isSmallMobile } = useResponsiveStore();
|
||||
const { authenticated } = useAuthStore();
|
||||
const { docsStore } = useDocStore();
|
||||
const { editor } = useEditorStore();
|
||||
const { toast } = useToastProvider();
|
||||
|
||||
const copyCurrentEditorToClipboard = async (
|
||||
asFormat: 'html' | 'markdown',
|
||||
) => {
|
||||
const editor = docsStore[doc.id]?.editor;
|
||||
if (!editor) {
|
||||
toast(t('Editor unavailable'), VariantType.ERROR, { duration: 3000 });
|
||||
return;
|
||||
|
||||
@@ -10,11 +10,12 @@ import {
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { useEditorStore } from '@/features/docs/doc-editor';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
|
||||
import { useExport } from '../api/useExport';
|
||||
import { TemplatesOrdering, useTemplates } from '../api/useTemplates';
|
||||
@@ -26,11 +27,12 @@ interface ModalPDFProps {
|
||||
}
|
||||
|
||||
export const ModalPDF = ({ onClose, doc }: ModalPDFProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { data: templates } = useTemplates({
|
||||
ordering: TemplatesOrdering.BY_CREATED_ON_DESC,
|
||||
});
|
||||
const { toast } = useToastProvider();
|
||||
const { docsStore } = useDocStore();
|
||||
const { editor } = useEditorStore();
|
||||
const {
|
||||
mutate: createExport,
|
||||
data: documentGenerated,
|
||||
@@ -103,8 +105,6 @@ export const ModalPDF = ({ onClose, doc }: ModalPDFProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = docsStore[doc.id].editor;
|
||||
|
||||
if (!editor) {
|
||||
toast(t('No editor found'), VariantType.ERROR);
|
||||
return;
|
||||
|
||||
@@ -6,11 +6,11 @@ import {
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, Text, TextErrors } from '@/components';
|
||||
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
||||
import { useCunninghamTheme } from '@/cunningham/';
|
||||
|
||||
import { useRemoveDoc } from '../api/useRemoveDoc';
|
||||
import IconDoc from '../assets/icon-doc.svg';
|
||||
@@ -22,9 +22,10 @@ interface ModalRemoveDocProps {
|
||||
}
|
||||
|
||||
export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const { toast } = useToastProvider();
|
||||
const router = useRouter();
|
||||
const { push } = useRouter();
|
||||
|
||||
const {
|
||||
mutate: removeDoc,
|
||||
@@ -35,7 +36,7 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
|
||||
toast(t('The document has been deleted.'), VariantType.SUCCESS, {
|
||||
duration: 4000,
|
||||
});
|
||||
router.push('/');
|
||||
void push('/');
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
import { Box, Card, IconBG, SideModal, Text } from '@/components';
|
||||
@@ -44,6 +44,7 @@ interface ModalShareProps {
|
||||
}
|
||||
|
||||
export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { isMobile, isSmallMobile } = useResponsiveStore();
|
||||
const width = isSmallMobile ? '100vw' : isMobile ? '90vw' : '70vw';
|
||||
const { toast } = useToastProvider();
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
import { BlockNoteEditor } from '@blocknote/core';
|
||||
import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||
import * as Y from 'yjs';
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { providerUrl } from '@/core';
|
||||
import { Base64, Doc, blocksToYDoc } from '@/features/docs/doc-management';
|
||||
|
||||
interface DocStore {
|
||||
provider: HocuspocusProvider;
|
||||
editor?: BlockNoteEditor;
|
||||
}
|
||||
|
||||
export interface UseDocStore {
|
||||
currentDoc?: Doc;
|
||||
docsStore: {
|
||||
[storeId: string]: DocStore;
|
||||
providers: {
|
||||
[storeId: string]: HocuspocusProvider;
|
||||
};
|
||||
createProvider: (storeId: string, initialDoc: Base64) => HocuspocusProvider;
|
||||
setStore: (storeId: string, props: Partial<DocStore>) => void;
|
||||
createProvider: (
|
||||
providerUrl: string,
|
||||
storeId: string,
|
||||
initialDoc: Base64,
|
||||
) => HocuspocusProvider;
|
||||
setProviders: (storeId: string, providers: HocuspocusProvider) => void;
|
||||
setCurrentDoc: (doc: Doc | undefined) => void;
|
||||
}
|
||||
|
||||
export const useDocStore = create<UseDocStore>((set, get) => ({
|
||||
currentDoc: undefined,
|
||||
docsStore: {},
|
||||
createProvider: (storeId: string, initialDoc: Base64) => {
|
||||
providers: {},
|
||||
createProvider: (providerUrl, storeId, initialDoc) => {
|
||||
const doc = new Y.Doc({
|
||||
guid: storeId,
|
||||
});
|
||||
@@ -43,28 +40,22 @@ export const useDocStore = create<UseDocStore>((set, get) => ({
|
||||
}
|
||||
|
||||
const provider = new HocuspocusProvider({
|
||||
url: providerUrl(storeId),
|
||||
url: providerUrl,
|
||||
name: storeId,
|
||||
document: doc,
|
||||
});
|
||||
|
||||
get().setStore(storeId, { provider });
|
||||
get().setProviders(storeId, provider);
|
||||
|
||||
return provider;
|
||||
},
|
||||
setStore: (storeId, props) => {
|
||||
set(({ docsStore }, ...store) => {
|
||||
return {
|
||||
...store,
|
||||
docsStore: {
|
||||
...docsStore,
|
||||
[storeId]: {
|
||||
...docsStore[storeId],
|
||||
...props,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
setProviders: (storeId, provider) => {
|
||||
set(({ providers }) => ({
|
||||
providers: {
|
||||
...providers,
|
||||
[storeId]: provider,
|
||||
},
|
||||
}));
|
||||
},
|
||||
setCurrentDoc: (doc) => {
|
||||
set({ currentDoc: doc });
|
||||
|
||||
@@ -2,22 +2,19 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, BoxButton, Text } from '@/components';
|
||||
import { HeadingBlock } from '@/features/docs/doc-editor';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { HeadingBlock, useEditorStore } from '@/features/docs/doc-editor';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { Heading } from './Heading';
|
||||
|
||||
interface TableContentProps {
|
||||
doc: Doc;
|
||||
headings: HeadingBlock[];
|
||||
}
|
||||
|
||||
export const TableContent = ({ doc, headings }: TableContentProps) => {
|
||||
const { docsStore } = useDocStore();
|
||||
export const TableContent = ({ headings }: TableContentProps) => {
|
||||
const { editor } = useEditorStore();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
const { t } = useTranslation();
|
||||
const editor = docsStore?.[doc.id]?.editor;
|
||||
const [headingIdHighlight, setHeadingIdHighlight] = useState<string>();
|
||||
|
||||
// To highlight the first heading in the viewport
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
@@ -30,30 +30,27 @@ export const ModalVersion = ({
|
||||
docId,
|
||||
versionId,
|
||||
}: ModalVersionProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToastProvider();
|
||||
const router = useRouter();
|
||||
const { docsStore, setStore } = useDocStore();
|
||||
const { push } = useRouter();
|
||||
const { providers } = useDocStore();
|
||||
const { mutate: updateDoc } = useUpdateDoc({
|
||||
listInvalideQueries: [KEY_LIST_DOC_VERSIONS],
|
||||
onSuccess: () => {
|
||||
const onDisplaySuccess = () => {
|
||||
toast(t('Version restored successfully'), VariantType.SUCCESS);
|
||||
router.push(`/docs/${docId}`);
|
||||
void push(`/docs/${docId}`);
|
||||
};
|
||||
|
||||
if (!docsStore?.[docId]?.provider || !docsStore?.[versionId]?.provider) {
|
||||
if (!providers?.[docId] || !providers?.[versionId]) {
|
||||
onDisplaySuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
setStore(docId, {
|
||||
editor: undefined,
|
||||
});
|
||||
|
||||
revertUpdate(
|
||||
docsStore[docId].provider.document,
|
||||
docsStore[docId].provider.document,
|
||||
docsStore[versionId].provider.document,
|
||||
providers[docId].document,
|
||||
providers[docId].document,
|
||||
providers[versionId].document,
|
||||
);
|
||||
|
||||
onDisplaySuccess();
|
||||
@@ -83,7 +80,7 @@ export const ModalVersion = ({
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
const newDoc = toBase64(
|
||||
Y.encodeStateAsUpdate(docsStore?.[versionId]?.provider.document),
|
||||
Y.encodeStateAsUpdate(providers?.[versionId].document),
|
||||
);
|
||||
|
||||
updateDoc({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import React, { PropsWithChildren, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, DropButton, IconOptions, StyledLink, Text } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
@@ -25,6 +25,7 @@ export const VersionItem = ({
|
||||
link,
|
||||
isActive,
|
||||
}: VersionItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const [isDropOpen, setIsDropOpen] = useState(false);
|
||||
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
SortModel,
|
||||
usePagination,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
@@ -118,7 +118,7 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
<Text $weight="bold" $theme="primary">
|
||||
{row.title}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
@@ -131,9 +131,7 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{formatDate(row.created_at)}
|
||||
</Text>
|
||||
<Text $weight="bold">{formatDate(row.created_at)}</Text>
|
||||
</StyledLink>
|
||||
);
|
||||
},
|
||||
@@ -144,9 +142,7 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{formatDate(row.updated_at)}
|
||||
</Text>
|
||||
<Text $weight="bold">{formatDate(row.updated_at)}</Text>
|
||||
</StyledLink>
|
||||
);
|
||||
},
|
||||
@@ -157,7 +153,7 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
<Text $weight="bold">
|
||||
{transRole(currentDocRole(row.abilities))}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
@@ -170,9 +166,7 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{row.accesses.length}
|
||||
</Text>
|
||||
<Text $weight="bold">{row.accesses.length}</Text>
|
||||
</StyledLink>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -12,12 +12,12 @@ import { DocsGrid } from './DocsGrid';
|
||||
export const DocsGridContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { untitledDocument } = useTrans();
|
||||
const router = useRouter();
|
||||
const { push } = useRouter();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
|
||||
const { mutate: createDoc } = useCreateDoc({
|
||||
onSuccess: (doc) => {
|
||||
router.push(`/docs/${doc.id}`);
|
||||
void push(`/docs/${doc.id}`);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -35,7 +35,7 @@ export const MemberItem = ({
|
||||
const { isSmallMobile, screenWidth } = useResponsiveStore();
|
||||
const [localRole, setLocalRole] = useState(role);
|
||||
const { toast } = useToastProvider();
|
||||
const router = useRouter();
|
||||
const { push } = useRouter();
|
||||
const { mutate: updateDocAccess, error: errorUpdate } = useUpdateDocAccess({
|
||||
onSuccess: () => {
|
||||
toast(t('The role has been updated'), VariantType.SUCCESS, {
|
||||
@@ -55,7 +55,7 @@ export const MemberItem = ({
|
||||
);
|
||||
|
||||
if (isMyself) {
|
||||
router.push('/');
|
||||
void push('/');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Image from 'next/image';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ export type RequestData = {
|
||||
url: string;
|
||||
method?: string;
|
||||
headers: Record<string, string>;
|
||||
body?: ArrayBuffer;
|
||||
body?: ArrayBufferLike;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
};
|
||||
@@ -48,12 +48,12 @@ export class RequestSerializer {
|
||||
return new RequestSerializer(requestData);
|
||||
}
|
||||
|
||||
public static arrayBufferToString(buffer: ArrayBuffer) {
|
||||
public static arrayBufferToString(buffer: ArrayBufferLike) {
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(buffer);
|
||||
return decoder.decode(buffer as ArrayBuffer);
|
||||
}
|
||||
|
||||
public static arrayBufferToJson<T>(buffer: ArrayBuffer) {
|
||||
public static arrayBufferToJson<T>(buffer: ArrayBufferLike) {
|
||||
const jsonString = RequestSerializer.arrayBufferToString(buffer);
|
||||
return JSON.parse(jsonString) as T;
|
||||
}
|
||||
@@ -64,7 +64,9 @@ export class RequestSerializer {
|
||||
}
|
||||
|
||||
public static objectToArrayBuffer(ob: Record<string, unknown>) {
|
||||
return RequestSerializer.stringToArrayBuffer(JSON.stringify(ob));
|
||||
return RequestSerializer.stringToArrayBuffer(
|
||||
JSON.stringify(ob),
|
||||
) as ArrayBuffer;
|
||||
}
|
||||
|
||||
constructor(requestData: RequestData) {
|
||||
@@ -85,7 +87,7 @@ export class RequestSerializer {
|
||||
|
||||
toRequest(): Request {
|
||||
const { url, ...rest } = this._requestData;
|
||||
return new Request(url, rest);
|
||||
return new Request(url, { ...rest, body: rest.body as BodyInit });
|
||||
}
|
||||
|
||||
clone(): RequestSerializer {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
|
||||
import { StyledLink } from '@/components';
|
||||
import { Icon } from '@/components/icons/Icon';
|
||||
import { ButtonLogin } from '@/core';
|
||||
import { LaGaufre } from '@/features/header/components/LaGaufre';
|
||||
import { LanguagePicker } from '@/features/language';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { default as IconDocs } from './assets/logo-docs.svg';
|
||||
import styles from './docs-layout.module.scss';
|
||||
|
||||
export const DocsHeaderLayout = () => {
|
||||
const { isResponsive, toggleMobileMenu } = useResponsiveStore();
|
||||
|
||||
return (
|
||||
<div className={styles.header}>
|
||||
{isResponsive && (
|
||||
<Button
|
||||
size="medium"
|
||||
onClick={toggleMobileMenu}
|
||||
aria-label="Button with only an icon"
|
||||
color="primary-text"
|
||||
icon={<Icon icon="menu" />}
|
||||
/>
|
||||
)}
|
||||
<StyledLink href="/">
|
||||
<div className={styles.iconContainer}>
|
||||
<IconDocs />
|
||||
<div>Docs</div>
|
||||
</div>
|
||||
</StyledLink>
|
||||
<div className={styles.rightContainer}>
|
||||
{!isResponsive && (
|
||||
<>
|
||||
<ButtonLogin />
|
||||
<LanguagePicker />
|
||||
<LaGaufre />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
import { DocsHeaderLayout } from './DocsHeaderLayout';
|
||||
import { DocsLeftLayout } from './DocsLeftLayout';
|
||||
import styles from './docs-layout.module.scss';
|
||||
|
||||
type Props = {
|
||||
leftContent?: ReactNode;
|
||||
backgroundStyle?: 'white' | 'grey';
|
||||
};
|
||||
export const DocsLayout = ({
|
||||
children,
|
||||
backgroundStyle = 'grey',
|
||||
leftContent,
|
||||
}: PropsWithChildren<Props>) => {
|
||||
return (
|
||||
<div>
|
||||
<DocsHeaderLayout />
|
||||
<Box $direction="row" $width="100%">
|
||||
<DocsLeftLayout>{leftContent}</DocsLeftLayout>
|
||||
<div
|
||||
id="mainContent"
|
||||
className={[styles.mainContent, styles[backgroundStyle]].join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import classNames from 'classnames';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Icon } from '@/components/icons/Icon';
|
||||
import { HorizontalSeparator } from '@/components/separator/HorizontalSeparator';
|
||||
import { SeparatedSection } from '@/components/separator/SeparatedSection';
|
||||
import { ButtonLogin } from '@/core';
|
||||
import { useCreateDoc } from '@/features/docs';
|
||||
import { LanguagePicker } from '@/features/language';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import styles from './docs-layout.module.scss';
|
||||
|
||||
export const DocsLeftLayout = ({ children }: PropsWithChildren) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { isMobileMenuOpen, ...responsiveStore } = useResponsiveStore();
|
||||
|
||||
const { mutate: createDoc } = useCreateDoc({
|
||||
onSuccess: (doc) => {
|
||||
router.push(`/docs/${doc.id}`);
|
||||
responsiveStore.toggleMobileMenu();
|
||||
},
|
||||
});
|
||||
|
||||
const goToHome = () => {
|
||||
router.push('/');
|
||||
responsiveStore.toggleMobileMenu();
|
||||
};
|
||||
|
||||
const createNewDoc = () => {
|
||||
createDoc({ title: t('Untitled document') });
|
||||
};
|
||||
|
||||
const getContent = (): ReactNode => {
|
||||
return (
|
||||
<div>
|
||||
<SeparatedSection>
|
||||
<div className={styles.leftNavigationBarHeader}>
|
||||
<div>
|
||||
<Button
|
||||
onClick={goToHome}
|
||||
size="medium"
|
||||
color="primary-text"
|
||||
icon={<Icon icon="house" />}
|
||||
/>
|
||||
|
||||
<Button
|
||||
size="medium"
|
||||
color="primary-text"
|
||||
icon={<Icon icon="search" />}
|
||||
/>
|
||||
</div>
|
||||
<Button onClick={createNewDoc}>{t('New document')}</Button>
|
||||
</div>
|
||||
</SeparatedSection>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.leftNavigationBar}>{getContent()}</div>
|
||||
<div
|
||||
className={classNames(styles.leftNavigationBarResponsive, {
|
||||
[styles.openNavigationMenu]: isMobileMenuOpen,
|
||||
})}
|
||||
>
|
||||
{getContent()}
|
||||
|
||||
<div className={styles.responsiveMenuActions}>
|
||||
<HorizontalSeparator />
|
||||
<ButtonLogin />
|
||||
<LanguagePicker />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.6305 28.8312C22.7983 28.5038 23.9166 27.9062 24.6505 26.8503C25.3749 25.8163 25.5789 24.5047 25.5789 23.2425V4.75099C25.5789 4.42358 25.5611 4.09557 25.5216 3.77148C26.1016 3.99961 26.5486 4.37658 26.8626 4.90239C27.2331 5.50024 27.4184 6.28757 27.4184 7.26435V26.0464C27.4184 27.3684 27.0942 28.3578 26.4458 29.0146C25.7974 29.6714 24.8207 29.9998 23.5155 29.9998H16.4209C16.5889 29.9704 16.7574 29.9401 16.9262 29.909C18.4067 29.6444 19.9713 29.2854 21.6185 28.8346L21.6305 28.8312Z" fill="#C9191E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.58203 25.655V6.8477C4.58203 5.70251 4.88938 4.83519 5.50408 4.24575C6.1272 3.65631 6.95242 3.33212 7.97972 3.27318C9.49542 3.18055 10.9311 3.05425 12.2868 2.89425C13.6425 2.72584 14.9393 2.53217 16.1771 2.31324C17.4234 2.0943 18.6359 1.85011 19.8148 1.58065C21.0274 1.29435 21.9578 1.4375 22.6062 2.0101C23.2546 2.58269 23.5788 3.49632 23.5788 4.75099V23.2425C23.5788 24.3456 23.3893 25.1666 23.0104 25.7055C22.6315 26.2529 21.9915 26.6528 21.0905 26.9054C19.4906 27.3433 17.9833 27.6886 16.5687 27.9412C15.154 28.2022 13.7731 28.4001 12.4258 28.5348C11.0785 28.6696 9.69751 28.7748 8.28286 28.8506C7.11241 28.918 6.20299 28.6738 5.5546 28.118C4.90622 27.5707 4.58203 26.7497 4.58203 25.655ZM9.20865 10.2624C11.0635 10.1444 12.7632 9.96305 14.3075 9.71831C14.6822 9.65722 15.0564 9.5936 15.4291 9.52759C15.8192 9.45851 16.1013 9.11859 16.1013 8.72337C16.1013 8.21154 15.638 7.82609 15.135 7.91189C14.846 7.96118 14.5555 8.00909 14.2635 8.05562C12.7346 8.29923 11.0452 8.47998 9.19523 8.5977C8.91819 8.61558 8.69776 8.70188 8.55608 8.87391C8.42209 9.03661 8.35645 9.23229 8.35645 9.45535C8.35645 9.68212 8.43296 9.87951 8.58568 10.0418L8.58783 10.0439C8.75336 10.2095 8.96369 10.2811 9.20865 10.2624ZM9.20801 14.456C11.0631 14.338 12.763 14.1566 14.3075 13.9119C15.8588 13.6589 17.3936 13.3638 18.9112 13.0266C19.2191 12.9581 19.4498 12.8503 19.5652 12.683C19.6786 12.5221 19.7347 12.3376 19.7347 12.1332C19.7347 11.9026 19.6469 11.704 19.476 11.5426C19.2921 11.3689 19.0348 11.3284 18.7304 11.3911L18.7285 11.3915C17.2823 11.7194 15.794 12.0053 14.2635 12.2492C12.7346 12.4928 11.0452 12.6735 9.19523 12.7913C8.91819 12.8091 8.69776 12.8954 8.55608 13.0675C8.42276 13.2294 8.35645 13.4205 8.35645 13.6363C8.35645 13.8703 8.43209 14.0723 8.58558 14.2354L8.59 14.2396C8.75499 14.3949 8.96316 14.4655 9.20551 14.4562L9.20801 14.456ZM9.20847 18.6494C11.0634 18.5229 12.7631 18.3374 14.3075 18.0927C15.8589 17.8482 17.3934 17.5573 18.9112 17.22C19.2199 17.1514 19.4508 17.0391 19.566 16.8627C19.6783 16.7029 19.7347 16.5233 19.7347 16.3266C19.7347 16.0961 19.6469 15.8974 19.476 15.7361C19.2921 15.5623 19.0348 15.5218 18.7304 15.5845L18.729 15.5848C17.2827 15.9043 15.7942 16.1861 14.2635 16.43C12.7345 16.6736 11.045 16.8586 9.19495 16.9847C8.91804 17.0026 8.69771 17.0889 8.55608 17.2609C8.42276 17.4228 8.35645 17.6139 8.35645 17.8297C8.35645 18.0637 8.43209 18.2658 8.58558 18.4289L8.59 18.433C8.75499 18.5883 8.96316 18.6589 9.20551 18.6496L9.20847 18.6494ZM14.3075 22.257C12.7632 22.5018 11.0635 22.6831 9.20867 22.8012C8.9637 22.8198 8.75337 22.7482 8.58783 22.5826L8.58572 22.5805C8.433 22.4182 8.35645 22.2208 8.35645 21.9941C8.35645 21.771 8.42209 21.5753 8.55608 21.4126C8.69776 21.2406 8.91827 21.1543 9.19531 21.1364C11.0453 21.0187 12.7346 20.838 14.2635 20.5943C14.5555 20.5478 14.846 20.4999 15.135 20.4506C15.638 20.3648 16.1013 20.7503 16.1013 21.2621C16.1013 21.6573 15.8192 21.9972 15.4291 22.0663C15.0564 22.1323 14.6822 22.1959 14.3075 22.257Z" fill="#000091"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,90 +0,0 @@
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 var(--c--theme--spacings--200W);
|
||||
height: 52px;
|
||||
background-color: var(--c--theme--colors--greyscale-000);
|
||||
border-bottom: 1px solid var(--c--theme--colors--greyscale-200, #e5e5e5);
|
||||
}
|
||||
|
||||
.iconContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--c--theme--spacings--150V);
|
||||
color: var(--c--theme--colors--primary-600);
|
||||
font-weight: var(--c--theme--font--weights--bold);
|
||||
}
|
||||
|
||||
.rightContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--c--theme--spacings--300V);
|
||||
}
|
||||
|
||||
|
||||
.mainContent {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: var(--c--theme--spacings--200W) var(--c--theme--spacings--200W);
|
||||
overflow-y: scroll;
|
||||
height: calc(100dvh - 52px);
|
||||
|
||||
&.grey {
|
||||
background-color: var(--c--theme--colors--greyscale-050);
|
||||
}
|
||||
&.white {
|
||||
background-color: var(--c--theme--colors--greyscale-000);
|
||||
}
|
||||
}
|
||||
|
||||
.leftNavigationBar {
|
||||
min-width: 300px;
|
||||
width: 300px;
|
||||
max-width: 300px;
|
||||
height: calc(100dvh - 52px);
|
||||
border-right: 1px solid var(--c--theme--colors--greyscale-200);
|
||||
}
|
||||
|
||||
.leftNavigationBarHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.leftNavigationBarResponsive {
|
||||
z-index: 999;
|
||||
width: 100dvw;
|
||||
height: calc(100dvh - 52px);
|
||||
border-right: 1px solid var(--c--theme--colors--greyscale-200);
|
||||
position: fixed;
|
||||
left: -100vw;
|
||||
background-color: var(--c--theme--colors--greyscale-000);
|
||||
transition: 0.15s;
|
||||
|
||||
&.openNavigationMenu {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.responsiveMenuActions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--c--theme--spacings--200W);
|
||||
}
|
||||
|
||||
@media (min-width: 1025px) {
|
||||
.leftNavigationBarResponsive {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.leftNavigationBar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Loader } from '@openfun/cunningham-react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import Head from 'next/head';
|
||||
import { useRouter as useNavigate } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { TextErrors } from '@/components/TextErrors';
|
||||
import { useCollaborationUrl } from '@/core';
|
||||
import { useAuthStore } from '@/core/auth';
|
||||
import { DocEditor } from '@/features/docs/doc-editor';
|
||||
import { KEY_DOC, useDoc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { DocsLayout } from '@/layouts/docs/DocsLayout';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { useBroadcastStore } from '@/stores';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
@@ -28,11 +28,9 @@ export function DocLayout() {
|
||||
<Head>
|
||||
<meta name="robots" content="noindex" />
|
||||
</Head>
|
||||
<DocsLayout>
|
||||
<Box $width="100%">
|
||||
<DocPage id={id} />
|
||||
</Box>
|
||||
</DocsLayout>
|
||||
<MainLayout withoutFooter>
|
||||
<DocPage id={id} />
|
||||
</MainLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -45,11 +43,12 @@ const DocPage = ({ id }: DocProps) => {
|
||||
const { login } = useAuthStore();
|
||||
const { data: docQuery, isError, error } = useDoc({ id });
|
||||
const [doc, setDoc] = useState(docQuery);
|
||||
const { setCurrentDoc, createProvider, docsStore } = useDocStore();
|
||||
const { setCurrentDoc, createProvider, providers } = useDocStore();
|
||||
const { setBroadcastProvider, addTask } = useBroadcastStore();
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const provider = docsStore?.[id]?.provider;
|
||||
const { replace } = useRouter();
|
||||
const provider = providers?.[id];
|
||||
const collaborationUrl = useCollaborationUrl(doc?.id);
|
||||
|
||||
useEffect(() => {
|
||||
if (doc?.title) {
|
||||
@@ -73,17 +72,17 @@ const DocPage = ({ id }: DocProps) => {
|
||||
}, [docQuery, setCurrentDoc]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!doc?.id) {
|
||||
if (!doc?.id || !collaborationUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newProvider = provider;
|
||||
if (!provider || provider.document.guid !== doc.id) {
|
||||
newProvider = createProvider(doc.id, doc.content);
|
||||
newProvider = createProvider(collaborationUrl, doc.id, doc.content);
|
||||
}
|
||||
|
||||
setBroadcastProvider(newProvider);
|
||||
}, [createProvider, doc, provider, setBroadcastProvider]);
|
||||
}, [createProvider, doc, provider, setBroadcastProvider, collaborationUrl]);
|
||||
|
||||
/**
|
||||
* We add a broadcast task to reset the query cache
|
||||
@@ -103,7 +102,7 @@ const DocPage = ({ id }: DocProps) => {
|
||||
|
||||
if (isError && error) {
|
||||
if (error.status === 404) {
|
||||
navigate.replace(`/404`);
|
||||
void replace(`/404`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { DocsGrid } from '@/features/docs/docs-grid/components/DocsGrid';
|
||||
import { DocsLayout } from '@/layouts/docs/DocsLayout';
|
||||
import { DocsGridContainer } from '@/features/docs/docs-grid';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
return (
|
||||
<Box $overflow="auto" $width="100%">
|
||||
<DocsGrid />
|
||||
</Box>
|
||||
);
|
||||
return <DocsGridContainer />;
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <DocsLayout>{page}</DocsLayout>;
|
||||
return <MainLayout>{page}</MainLayout>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
31
src/frontend/apps/impress/src/services/Crisp.tsx
Normal file
31
src/frontend/apps/impress/src/services/Crisp.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Configure Crisp chat for real-time support across all pages.
|
||||
*/
|
||||
|
||||
import { Crisp } from 'crisp-sdk-web';
|
||||
|
||||
import { User } from '@/core';
|
||||
|
||||
export const initializeCrispSession = (user: User) => {
|
||||
if (!Crisp.isCrispInjected()) {
|
||||
return;
|
||||
}
|
||||
Crisp.setTokenId(`impress-${user.id}`);
|
||||
Crisp.user.setEmail(user.email);
|
||||
};
|
||||
|
||||
export const configureCrispSession = (websiteId: string) => {
|
||||
if (Crisp.isCrispInjected()) {
|
||||
return;
|
||||
}
|
||||
Crisp.configure(websiteId);
|
||||
Crisp.setSafeMode(true);
|
||||
};
|
||||
|
||||
export const terminateCrispSession = () => {
|
||||
if (!Crisp.isCrispInjected()) {
|
||||
return;
|
||||
}
|
||||
Crisp.setTokenId();
|
||||
Crisp.session.reset();
|
||||
};
|
||||
1
src/frontend/apps/impress/src/services/index.ts
Normal file
1
src/frontend/apps/impress/src/services/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Crisp';
|
||||
@@ -4,74 +4,41 @@ export type ScreenSize = 'small-mobile' | 'mobile' | 'tablet' | 'desktop';
|
||||
|
||||
export interface UseResponsiveStore {
|
||||
isMobile: boolean;
|
||||
isTablet: boolean;
|
||||
isMobileMenuOpen: boolean;
|
||||
isSmallMobile: boolean;
|
||||
screenSize: ScreenSize;
|
||||
screenWidth: number;
|
||||
setScreenSize: (size: ScreenSize) => void;
|
||||
toggleMobileMenu: () => void;
|
||||
isResponsive: boolean;
|
||||
initializeResizeListener: () => () => void;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
isMobile: false,
|
||||
isTablet: false,
|
||||
isSmallMobile: false,
|
||||
isResponsive: false,
|
||||
isMobileMenuOpen: false,
|
||||
screenSize: 'desktop' as ScreenSize,
|
||||
screenWidth: 0,
|
||||
};
|
||||
|
||||
export const useResponsiveStore = create<UseResponsiveStore>((set, get) => ({
|
||||
export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
|
||||
isMobile: initialState.isMobile,
|
||||
isTablet: initialState.isTablet,
|
||||
isSmallMobile: initialState.isSmallMobile,
|
||||
screenSize: initialState.screenSize,
|
||||
isMobileMenuOpen: initialState.isMobileMenuOpen,
|
||||
screenWidth: initialState.screenWidth,
|
||||
setScreenSize: (size: ScreenSize) => set(() => ({ screenSize: size })),
|
||||
isResponsive: initialState.isResponsive,
|
||||
toggleMobileMenu: () => {
|
||||
set((old) => ({ isMobileMenuOpen: !old.isMobileMenuOpen }));
|
||||
},
|
||||
initializeResizeListener: () => {
|
||||
const resizeHandler = () => {
|
||||
const width = window.innerWidth;
|
||||
if (width < 560) {
|
||||
set({
|
||||
isResponsive: true,
|
||||
screenSize: 'small-mobile',
|
||||
isMobile: true,
|
||||
isTablet: false,
|
||||
isSmallMobile: true,
|
||||
});
|
||||
} else if (width < 768) {
|
||||
set({
|
||||
isResponsive: true,
|
||||
screenSize: 'mobile',
|
||||
isTablet: false,
|
||||
isMobile: true,
|
||||
isSmallMobile: false,
|
||||
});
|
||||
set({ screenSize: 'mobile', isMobile: true, isSmallMobile: false });
|
||||
} else if (width >= 768 && width < 1024) {
|
||||
set({
|
||||
isResponsive: true,
|
||||
screenSize: 'tablet',
|
||||
isTablet: true,
|
||||
isMobile: false,
|
||||
isSmallMobile: false,
|
||||
});
|
||||
set({ screenSize: 'tablet', isMobile: false, isSmallMobile: false });
|
||||
} else {
|
||||
set({
|
||||
isResponsive: false,
|
||||
screenSize: 'desktop',
|
||||
isTablet: false,
|
||||
isMobile: false,
|
||||
isSmallMobile: false,
|
||||
});
|
||||
set({ screenSize: 'desktop', isMobile: false, isSmallMobile: false });
|
||||
}
|
||||
|
||||
set({ screenWidth: width });
|
||||
|
||||
32
src/frontend/apps/impress/src/stores/useSentryStore.tsx
Normal file
32
src/frontend/apps/impress/src/stores/useSentryStore.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import type { Client } from '@sentry/types';
|
||||
import { create } from 'zustand';
|
||||
|
||||
import packageJson from '../../package.json';
|
||||
|
||||
interface SentryState {
|
||||
sentry?: Client;
|
||||
setSentry: (dsn?: string, environment?: string) => void;
|
||||
}
|
||||
|
||||
export const useSentryStore = create<SentryState>((set, get) => ({
|
||||
sentry: undefined,
|
||||
setSentry: (dsn, environment) => {
|
||||
const sentry = get().sentry;
|
||||
if (sentry) {
|
||||
return;
|
||||
}
|
||||
|
||||
set({
|
||||
sentry: Sentry.init({
|
||||
dsn,
|
||||
environment,
|
||||
integrations: [Sentry.replayIntegration()],
|
||||
release: packageJson.version,
|
||||
replaysSessionSampleRate: 0.1,
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
tracesSampleRate: 1.0,
|
||||
}),
|
||||
});
|
||||
},
|
||||
}));
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "impress",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
@@ -25,18 +25,18 @@
|
||||
"i18n:test": "yarn I18N run test"
|
||||
},
|
||||
"resolutions": {
|
||||
"@blocknote/core": "0.17.1",
|
||||
"@blocknote/mantine": "0.17.1",
|
||||
"@blocknote/react": "0.17.1",
|
||||
"@types/node": "20.16.13",
|
||||
"@blocknote/core": "0.19.2",
|
||||
"@blocknote/mantine": "0.19.2",
|
||||
"@blocknote/react": "0.19.2",
|
||||
"@types/node": "22.9.3",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.10.0",
|
||||
"@typescript-eslint/parser": "8.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.15.0",
|
||||
"@typescript-eslint/parser": "8.15.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.57.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"typescript": "5.6.3",
|
||||
"typescript": "5.7.2",
|
||||
"yjs": "13.6.20"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"name": "eslint-config-impress",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "14.2.15",
|
||||
"@tanstack/eslint-plugin-query": "5.59.7",
|
||||
"@next/eslint-plugin-next": "15.0.3",
|
||||
"@tanstack/eslint-plugin-query": "5.61.3",
|
||||
"@typescript-eslint/eslint-plugin": "*",
|
||||
"@typescript-eslint/parser": "*",
|
||||
"eslint": "*",
|
||||
"eslint-config-next": "14.2.15",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-jest": "28.8.3",
|
||||
"eslint-plugin-jsx-a11y": "6.10.1",
|
||||
"eslint-plugin-playwright": "1.8.0",
|
||||
"eslint-plugin-jest": "28.9.0",
|
||||
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||
"eslint-plugin-playwright": "2.1.0",
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"eslint-plugin-react": "7.37.1",
|
||||
"eslint-plugin-testing-library": "6.4.0",
|
||||
"eslint-plugin-react": "7.37.2",
|
||||
"eslint-plugin-testing-library": "7.0.0",
|
||||
"prettier": "3.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "packages-i18n",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"extract-translation": "yarn extract-translation:impress",
|
||||
@@ -12,7 +12,7 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/jest": "29.5.13",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/node": "*",
|
||||
"eslint-config-impress": "*",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "server-y-provider",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"description": "Y.js provider for docs",
|
||||
"repository": "https://github.com/numerique-gouv/impress",
|
||||
"license": "MIT",
|
||||
@@ -15,7 +15,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hocuspocus/server": "2.13.7",
|
||||
"@hocuspocus/server": "2.14.0",
|
||||
"y-protocols": "1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
62
src/helm/env.d/dev/secrets.enc.yaml
Normal file
62
src/helm/env.d/dev/secrets.enc.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
djangoSuperUserEmail: ENC[AES256_GCM,data:7b1xfYmr1g0RlBmsHBRA39ZPV/6+1DrtHQ==,iv:/GW7oLxPTZYmRWVPvyAQMoZl1owHM4Fo0XAOtyEh2rA=,tag:DaqoW+dglyAOXMm5+mrDfA==,type:str]
|
||||
djangoSuperUserPass: ENC[AES256_GCM,data:RQgX,iv:q3CdfmwGfHSTjLXTimDk/1MyoFLviRuwmZa2E7GUzhY=,tag:HCtdtqgSxdJIHFhI8xpegQ==,type:str]
|
||||
djangoSecretKey: ENC[AES256_GCM,data:9fr7VwwXN6+9+rdDtgeDuEbq6R2Gb0JhifUgxTPVbd4usFQv1AUVkxF40fu5nYBmM8vk,iv:X44837MB7NQZ1J0o0JPDK+2g5eqbCzo9mDPJTz/bKSk=,tag:Ju4l5Pi8ccNASdiwFVFKgg==,type:str]
|
||||
oidc:
|
||||
clientId: ENC[AES256_GCM,data:wndPCbysbWDybdHglcG+wkMWk1rrD40hKqFxct9T3TLEGOk/,iv:RH1OdBX1GYIT90sSq0AGz49fFi6dL0m49Pegs6Ko9tQ=,tag:/tKytQwoZkBX1Tf96gAjIA==,type:str]
|
||||
clientSecret: ENC[AES256_GCM,data:MUJ0wsg+LC2QZ1jZ0Twd3FS3dQevmJq9/97qVI3ARHuJIVlQz0Qah4vE7/iR+sn7ME2o1s1AzV4c1Yx/F3nHBg==,iv:LvinICSzF/8EvrHZD4Jp6lt7g3yxSOEgVHPrc3SShjo=,tag:yvkyyBXmhEkmGL7jZevUCA==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMMjFCeWhkUmRWTnlIM1JM
|
||||
dVFock1DWmtXQnpQZWZMWW1YdndhSS93MlVFCmxKVDUwOUt0NjJIZiswSm5aRi9U
|
||||
VEllelBZVmFKdVFzcVJPUm50VHo5RTgKLS0tIDlkU3htTEdSREFOSUxlTGVtUm1n
|
||||
RzJZbzhFcDNZKzdxMWFHTWx6Uy9GVFkKTw8LbhzAACp0NUHDfNcXpZyr2pJyNxxw
|
||||
C7j/UB0cAejlSJHaUUiZ6TEcslXRpqnNagwUw4z/uzo7m4temay22A==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age16hnlml8yv4ynwy0seer57g8qww075crd0g7nsundz3pj4wk7m3vqftszg7
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQQjBNMnVlNURQVWdjSyty
|
||||
RGozcmN5eTUwRHJIWnhhc1E3U1NXQ3AwTWxBCnFjbmJNZnFiRVJ6VHhmQmt1Vk5n
|
||||
OTVXWVh3RzhoMWNrbUl6OHphTjFLQVUKLS0tIGJjUlNhK0dHQ2R3SCtrbTRnaFJT
|
||||
Q1pyRXhSVm8xQWk2NG1MK0srVU1pL2sKkoxGCM00UM2leTNCn5H8499uwJw1NIXs
|
||||
PoRNgplehrHFptrAwGEpSYMXbxu88N7EWa/rtOp+sHWK5zpxscMkjA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1plkp8td6zzfcavjusmsfrlk54t9vn8jjxm8zaz7cmnr7kzl2nfnsd54hwg
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzYnpkYnJnYnJjVFRHRzRa
|
||||
N09JOXVnQkVrcVcwdk9kR1k1azNib2lkMVZFCmhvOHlpVnJ0RlRpYWZ1TkVoaklV
|
||||
NmNzY3BEeWN1MUtKWmZFT2RaMUxBRW8KLS0tIG92ZmhsZ29LSkRSREhiaG9kWXhH
|
||||
akREb0ttYVpNWTJHb1pjaWRFbWpxUjgKgZp3cN2rZw4ktbpb5cUnDEtsT/KWszGi
|
||||
pmpJHgsMADigyUc+Pjw+1pwpn0FtXVEXGedbf8bBuJavvbS2PuJBsg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age12g6f5fse25tgrwweleh4jls3qs52hey2edh759smulwmk5lnzadslu2cp3
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxaHZJeStiVnBzTGNTNzdo
|
||||
UDFVTU51ZWp0WWorUnBlSzVBSU9IU2JnbUNNCkpMZGdNV3FUYkZOcWNLK0JWci81
|
||||
WGNwYi9Jb0QrV0lkUzNJWTcrUjIzUmMKLS0tIHlTKzNsVzNsSGFuYjJ0RFp0Y1Nr
|
||||
a1VOcDBPTTYvNjkxN092N1UrYk1CM2cKNifC3ZLOrFTFKA9iKg8nPpZb+3DxnTwq
|
||||
grsrxQa40b/Vv/aPoiPBMeSENDcH48X/EhMFNKX7dvl+7HEaY+QPlA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1hnhuzj96ktkhpyygvmz0x9h8mfvssz7ss6emmukags644mdhf4msajk93r
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoZ2ZlcllJeGlKUDNxUk1w
|
||||
ekZ3TSttaXREV1FBRWwzNW54cjlYbHpLdWpRCnhSL2hEVVBEWEJKQWF0YTk1YzhJ
|
||||
RTBGN25sT0hBM3V4QndiTVkveDBwQ2cKLS0tIEdoZGRLRXdCME1wcUJHQXhtSHBQ
|
||||
UVEyNUVIanF6Z3ZSUjU1aTk0NFRBR0EKGuH5vzOV9lP/qRew0maECapKtLILaf/4
|
||||
XoSgPnjh8pIbJG7i9VKnFORlzkNJ6OPhZlX3ax15hd1qQv0PSCMBDA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2024-11-02T06:36:16Z"
|
||||
mac: ENC[AES256_GCM,data:CFU67noumihiYd0zSQex6Bgs5e/w3v3a9Ywd2XX53mx6W16w8DGyMykjaBzwX+wKC9oTqEmBXmmixf8NpQRuG9owcf9GIsFy1cK+69y+ISQINxBqxMvYouaC7UQeywpC1b9gHw7sVU1GCAiY6Ha+lPHvEavelbGWn/MSVyaBB2k=,iv:m1ShIjNGFjcC0N5mjvhbgxnVN7PcpSkBxMquUlsROCk=,tag:XTNxFRMQslbpvbL9gzMxHA==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.0
|
||||
123
src/helm/env.d/dev/values.impress.yaml.gotmpl
Normal file
123
src/helm/env.d/dev/values.impress.yaml.gotmpl
Normal file
@@ -0,0 +1,123 @@
|
||||
image:
|
||||
repository: localhost:5001/impress-backend
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
backend:
|
||||
replicas: 1
|
||||
envVars:
|
||||
DJANGO_CSRF_TRUSTED_ORIGINS: https://impress.127.0.0.1.nip.io,http://impress.127.0.0.1.nip.io
|
||||
DJANGO_CONFIGURATION: Production
|
||||
DJANGO_ALLOWED_HOSTS: "*"
|
||||
DJANGO_SECRET_KEY: {{ .Values.djangoSecretKey }}
|
||||
DJANGO_SETTINGS_MODULE: impress.settings
|
||||
DJANGO_SUPERUSER_PASSWORD: admin
|
||||
DJANGO_EMAIL_HOST: "mailcatcher"
|
||||
DJANGO_EMAIL_PORT: 1025
|
||||
DJANGO_EMAIL_USE_SSL: False
|
||||
LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR
|
||||
LOGGING_LEVEL_LOGGERS_ROOT: INFO
|
||||
LOGGING_LEVEL_LOGGERS_APP: INFO
|
||||
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
|
||||
OIDC_OP_USER_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/session/end
|
||||
OIDC_RP_CLIENT_ID: {{ .Values.oidc.clientId }}
|
||||
OIDC_RP_CLIENT_SECRET: {{ .Values.oidc.clientSecret }}
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email"
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://impress.127.0.0.1.nip.io
|
||||
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
||||
LOGIN_REDIRECT_URL: https://impress.127.0.0.1.nip.io
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://impress.127.0.0.1.nip.io
|
||||
LOGOUT_REDIRECT_URL: https://impress.127.0.0.1.nip.io
|
||||
DB_HOST: postgres-postgresql
|
||||
DB_NAME: impress
|
||||
DB_USER: dinum
|
||||
DB_PASSWORD: pass
|
||||
DB_PORT: 5432
|
||||
POSTGRES_DB: impress
|
||||
POSTGRES_USER: dinum
|
||||
POSTGRES_PASSWORD: pass
|
||||
REDIS_URL: redis://default:pass@redis-master:6379/1
|
||||
AWS_S3_ENDPOINT_URL: http://minio.impress.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: impress
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
AWS_STORAGE_BUCKET_NAME: impress-media-storage
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
|
||||
migrate:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py migrate --no-input &&
|
||||
python manage.py create_demo --force
|
||||
restartPolicy: Never
|
||||
|
||||
command:
|
||||
- "gunicorn"
|
||||
- "-c"
|
||||
- "/usr/local/etc/gunicorn/impress.py"
|
||||
- "impress.wsgi:application"
|
||||
- "--reload"
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py createsuperuser --email admin@example.com --password admin
|
||||
restartPolicy: Never
|
||||
|
||||
frontend:
|
||||
envVars:
|
||||
PORT: 8080
|
||||
NEXT_PUBLIC_API_ORIGIN: https://impress.127.0.0.1.nip.io
|
||||
NEXT_PUBLIC_Y_PROVIDER_URL: wss://impress.127.0.0.1.nip.io/ws
|
||||
NEXT_PUBLIC_MEDIA_URL: https://impress.127.0.0.1.nip.io
|
||||
|
||||
replicas: 1
|
||||
command:
|
||||
- yarn
|
||||
- dev
|
||||
|
||||
image:
|
||||
repository: localhost:5001/impress-frontend
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
yProvider:
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: localhost:5001/impress-y-provider
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: impress.127.0.0.1.nip.io
|
||||
|
||||
ingressWS:
|
||||
enabled: true
|
||||
host: impress.127.0.0.1.nip.io
|
||||
|
||||
ingressAdmin:
|
||||
enabled: true
|
||||
host: impress.127.0.0.1.nip.io
|
||||
|
||||
ingressMedia:
|
||||
enabled: true
|
||||
host: impress.127.0.0.1.nip.io
|
||||
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-url: https://impress.127.0.0.1.nip.io/api/v1.0/documents/retrieve-auth/
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: minio.impress.svc.cluster.local:9000
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /impress-media-storage/$1
|
||||
|
||||
serviceMedia:
|
||||
host: minio.impress.svc.cluster.local
|
||||
port: 9000
|
||||
5
src/helm/extra/Chart.yaml
Normal file
5
src/helm/extra/Chart.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
apiVersion: v2
|
||||
name: extra
|
||||
description: A Helm chart to add some manifests to impress
|
||||
type: application
|
||||
version: 1.8.1
|
||||
7
src/helm/extra/templates/keydb.yaml
Normal file
7
src/helm/extra/templates/keydb.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Redis
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
disableAuth: false
|
||||
7
src/helm/extra/templates/postgresql.yaml
Normal file
7
src/helm/extra/templates/postgresql.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Postgres
|
||||
metadata:
|
||||
name: postgresql
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
database: impress
|
||||
8
src/helm/extra/templates/s3.yaml
Normal file
8
src/helm/extra/templates/s3.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Bucket
|
||||
metadata:
|
||||
name: impress-media-storage
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
provider: data
|
||||
versioned: true
|
||||
67
src/helm/helmfile.yaml
Normal file
67
src/helm/helmfile.yaml
Normal file
@@ -0,0 +1,67 @@
|
||||
repositories:
|
||||
- name: bitnami
|
||||
url: registry-1.docker.io/bitnamicharts
|
||||
oci: true
|
||||
|
||||
releases:
|
||||
- name: postgres
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
namespace: {{ .Namespace }}
|
||||
chart: bitnami/postgresql
|
||||
version: 13.1.5
|
||||
values:
|
||||
- auth:
|
||||
username: dinum
|
||||
password: pass
|
||||
database: impress
|
||||
- tls:
|
||||
enabled: true
|
||||
autoGenerated: true
|
||||
|
||||
- name: minio
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
namespace: {{ .Namespace }}
|
||||
chart: bitnami/minio
|
||||
version: 12.10.10
|
||||
values:
|
||||
- auth:
|
||||
rootUser: impress
|
||||
rootPassword: password
|
||||
- provisioning:
|
||||
enabled: true
|
||||
buckets:
|
||||
- name: impress-media-storage
|
||||
versioning: true
|
||||
|
||||
- name: redis
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
namespace: {{ .Namespace }}
|
||||
chart: bitnami/redis
|
||||
version: 18.19.2
|
||||
values:
|
||||
- auth:
|
||||
password: pass
|
||||
architecture: standalone
|
||||
|
||||
- name: extra
|
||||
installed: {{ ne .Environment.Name "dev" | toYaml }}
|
||||
namespace: {{ .Namespace }}
|
||||
chart: ./extra
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
|
||||
- name: impress
|
||||
version: {{ .Values.version }}
|
||||
namespace: {{ .Namespace }}
|
||||
chart: ./impress
|
||||
values:
|
||||
- env.d/{{ .Environment.Name }}/values.impress.yaml.gotmpl
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
|
||||
environments:
|
||||
dev:
|
||||
values:
|
||||
- version: 1.8.1
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
4
src/helm/impress/Chart.yaml
Normal file
4
src/helm/impress/Chart.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v2
|
||||
type: application
|
||||
name: impress
|
||||
version: 1.8.1
|
||||
128
src/helm/impress/README.md
Normal file
128
src/helm/impress/README.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Impress helm chart
|
||||
|
||||
## Parameters
|
||||
|
||||
### General configuration
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------------ | ---------------------------------------------------- | ------------------------ |
|
||||
| `image.repository` | Repository to use to pull impress's container image | `lasuite/impress-backend` |
|
||||
| `image.tag` | impress's container tag | `latest` |
|
||||
| `image.pullPolicy` | Container image pull policy | `IfNotPresent` |
|
||||
| `image.credentials.username` | Username for container registry authentication | |
|
||||
| `image.credentials.password` | Password for container registry authentication | |
|
||||
| `image.credentials.registry` | Registry url for which the credentials are specified | |
|
||||
| `image.credentials.name` | Name of the generated secret for imagePullSecrets | |
|
||||
| `nameOverride` | Override the chart name | `""` |
|
||||
| `fullnameOverride` | Override the full application name | `""` |
|
||||
| `ingress.enabled` | whether to enable the Ingress or not | `false` |
|
||||
| `ingress.className` | IngressClass to use for the Ingress | `nil` |
|
||||
| `ingress.host` | Host for the Ingress | `impress.example.com` |
|
||||
| `ingress.path` | Path to use for the Ingress | `/` |
|
||||
| `ingress.hosts` | Additional host to configure for the Ingress | `[]` |
|
||||
| `ingress.tls.enabled` | Weather to enable TLS for the Ingress | `true` |
|
||||
| `ingress.tls.additional[].secretName` | Secret name for additional TLS config | |
|
||||
| `ingress.tls.additional[].hosts[]` | Hosts for additional TLS config | |
|
||||
| `ingress.customBackends` | Add custom backends to ingress | `[]` |
|
||||
| `ingressAdmin.enabled` | whether to enable the Ingress or not | `false` |
|
||||
| `ingressAdmin.className` | IngressClass to use for the Ingress | `nil` |
|
||||
| `ingressAdmin.host` | Host for the Ingress | `impress.example.com` |
|
||||
| `ingressAdmin.path` | Path to use for the Ingress | `/admin` |
|
||||
| `ingressAdmin.hosts` | Additional host to configure for the Ingress | `[]` |
|
||||
| `ingressAdmin.tls.enabled` | Weather to enable TLS for the Ingress | `true` |
|
||||
| `ingressAdmin.tls.additional[].secretName` | Secret name for additional TLS config | |
|
||||
| `ingressAdmin.tls.additional[].hosts[]` | Hosts for additional TLS config | |
|
||||
|
||||
### backend
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| `backend.command` | Override the backend container command | `[]` |
|
||||
| `backend.args` | Override the backend container args | `[]` |
|
||||
| `backend.replicas` | Amount of backend replicas | `3` |
|
||||
| `backend.shareProcessNamespace` | Enable share process namespace between containers | `false` |
|
||||
| `backend.sidecars` | Add sidecars containers to backend deployment | `[]` |
|
||||
| `backend.securityContext` | Configure backend Pod security context | `nil` |
|
||||
| `backend.envVars` | Configure backend container environment variables | `undefined` |
|
||||
| `backend.envVars.BY_VALUE` | Example environment variable by setting value directly | |
|
||||
| `backend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
|
||||
| `backend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
|
||||
| `backend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
|
||||
| `backend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
|
||||
| `backend.podAnnotations` | Annotations to add to the backend Pod | `{}` |
|
||||
| `backend.service.type` | backend Service type | `ClusterIP` |
|
||||
| `backend.service.port` | backend Service listening port | `80` |
|
||||
| `backend.service.targetPort` | backend container listening port | `8000` |
|
||||
| `backend.service.annotations` | Annotations to add to the backend Service | `{}` |
|
||||
| `backend.migrate.command` | backend migrate command | `["python","manage.py","migrate","--no-input"]` |
|
||||
| `backend.migrate.restartPolicy` | backend migrate job restart policy | `Never` |
|
||||
| `backend.probes.liveness.path` | Configure path for backend HTTP liveness probe | `/__heartbeat__` |
|
||||
| `backend.probes.liveness.targetPort` | Configure port for backend HTTP liveness probe | `undefined` |
|
||||
| `backend.probes.liveness.initialDelaySeconds` | Configure initial delay for backend liveness probe | `10` |
|
||||
| `backend.probes.liveness.initialDelaySeconds` | Configure timeout for backend liveness probe | `10` |
|
||||
| `backend.probes.startup.path` | Configure path for backend HTTP startup probe | `undefined` |
|
||||
| `backend.probes.startup.targetPort` | Configure port for backend HTTP startup probe | `undefined` |
|
||||
| `backend.probes.startup.initialDelaySeconds` | Configure initial delay for backend startup probe | `undefined` |
|
||||
| `backend.probes.startup.initialDelaySeconds` | Configure timeout for backend startup probe | `undefined` |
|
||||
| `backend.probes.readiness.path` | Configure path for backend HTTP readiness probe | `/__lbheartbeat__` |
|
||||
| `backend.probes.readiness.targetPort` | Configure port for backend HTTP readiness probe | `undefined` |
|
||||
| `backend.probes.readiness.initialDelaySeconds` | Configure initial delay for backend readiness probe | `10` |
|
||||
| `backend.probes.readiness.initialDelaySeconds` | Configure timeout for backend readiness probe | `10` |
|
||||
| `backend.resources` | Resource requirements for the backend container | `{}` |
|
||||
| `backend.nodeSelector` | Node selector for the backend Pod | `{}` |
|
||||
| `backend.tolerations` | Tolerations for the backend Pod | `[]` |
|
||||
| `backend.affinity` | Affinity for the backend Pod | `{}` |
|
||||
| `backend.persistence` | Additional volumes to create and mount on the backend. Used for debugging purposes | `{}` |
|
||||
| `backend.persistence.volume-name.size` | Size of the additional volume | |
|
||||
| `backend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
|
||||
| `backend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
|
||||
| `backend.extraVolumeMounts` | Additional volumes to mount on the backend. | `[]` |
|
||||
| `backend.extraVolumes` | Additional volumes to mount on the backend. | `[]` |
|
||||
|
||||
### frontend
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- | ------------------------- |
|
||||
| `frontend.image.repository` | Repository to use to pull impress's frontend container image | `lasuite/impress-frontend` |
|
||||
| `frontend.image.tag` | impress's frontend container tag | `latest` |
|
||||
| `frontend.image.pullPolicy` | frontend container image pull policy | `IfNotPresent` |
|
||||
| `frontend.command` | Override the frontend container command | `[]` |
|
||||
| `frontend.args` | Override the frontend container args | `[]` |
|
||||
| `frontend.replicas` | Amount of frontend replicas | `3` |
|
||||
| `frontend.shareProcessNamespace` | Enable share process namefrontend between containers | `false` |
|
||||
| `frontend.sidecars` | Add sidecars containers to frontend deployment | `[]` |
|
||||
| `frontend.securityContext` | Configure frontend Pod security context | `nil` |
|
||||
| `frontend.envVars` | Configure frontend container environment variables | `undefined` |
|
||||
| `frontend.envVars.BY_VALUE` | Example environment variable by setting value directly | |
|
||||
| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
|
||||
| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
|
||||
| `frontend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
|
||||
| `frontend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
|
||||
| `frontend.podAnnotations` | Annotations to add to the frontend Pod | `{}` |
|
||||
| `frontend.service.type` | frontend Service type | `ClusterIP` |
|
||||
| `frontend.service.port` | frontend Service listening port | `80` |
|
||||
| `frontend.service.targetPort` | frontend container listening port | `8080` |
|
||||
| `frontend.service.annotations` | Annotations to add to the frontend Service | `{}` |
|
||||
| `frontend.probes` | Configure probe for frontend | `{}` |
|
||||
| `frontend.probes.liveness.path` | Configure path for frontend HTTP liveness probe | |
|
||||
| `frontend.probes.liveness.targetPort` | Configure port for frontend HTTP liveness probe | |
|
||||
| `frontend.probes.liveness.initialDelaySeconds` | Configure initial delay for frontend liveness probe | |
|
||||
| `frontend.probes.liveness.initialDelaySeconds` | Configure timeout for frontend liveness probe | |
|
||||
| `frontend.probes.startup.path` | Configure path for frontend HTTP startup probe | |
|
||||
| `frontend.probes.startup.targetPort` | Configure port for frontend HTTP startup probe | |
|
||||
| `frontend.probes.startup.initialDelaySeconds` | Configure initial delay for frontend startup probe | |
|
||||
| `frontend.probes.startup.initialDelaySeconds` | Configure timeout for frontend startup probe | |
|
||||
| `frontend.probes.readiness.path` | Configure path for frontend HTTP readiness probe | |
|
||||
| `frontend.probes.readiness.targetPort` | Configure port for frontend HTTP readiness probe | |
|
||||
| `frontend.probes.readiness.initialDelaySeconds` | Configure initial delay for frontend readiness probe | |
|
||||
| `frontend.probes.readiness.initialDelaySeconds` | Configure timeout for frontend readiness probe | |
|
||||
| `frontend.resources` | Resource requirements for the frontend container | `{}` |
|
||||
| `frontend.nodeSelector` | Node selector for the frontend Pod | `{}` |
|
||||
| `frontend.tolerations` | Tolerations for the frontend Pod | `[]` |
|
||||
| `frontend.affinity` | Affinity for the frontend Pod | `{}` |
|
||||
| `frontend.persistence` | Additional volumes to create and mount on the frontend. Used for debugging purposes | `{}` |
|
||||
| `frontend.persistence.volume-name.size` | Size of the additional volume | |
|
||||
| `frontend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
|
||||
| `frontend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
|
||||
| `frontend.extraVolumeMounts` | Additional volumes to mount on the frontend. | `[]` |
|
||||
| `frontend.extraVolumes` | Additional volumes to mount on the frontend. | `[]` |
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user