Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b502b51ad | |||
| 9b33a1f464 | |||
| 5ee6a43f08 | |||
| 8bd83cbfcd | |||
| bc14d1d0f8 | |||
| 526e649f06 | |||
| ac40eb8f7c | |||
| c750cf10a8 | |||
| 4f4951cdcd | |||
| 50891afd05 | |||
| cbb6fc740a | |||
| 31c3dd6119 | |||
| 15700ddd8d |
@@ -9,6 +9,24 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Added
|
||||
|
||||
- 🌐(frontend) Add German translation #255
|
||||
- ✨(frontend) Add a broadcast store #387
|
||||
- ✨(frontend) WIP: New ui
|
||||
|
||||
## Changed
|
||||
|
||||
- 🚸(backend) improve users similarity search and sort results #391
|
||||
- 🌐(backend) add german translation #259
|
||||
|
||||
## Fixed
|
||||
|
||||
- 🦺(backend) add comma to sub regex #408
|
||||
- 🐛(editor) collaborative user tag hidden when read only #385
|
||||
- 🐛(frontend) user have view access when revoked #387
|
||||
|
||||
|
||||
## [1.7.0] - 2024-10-24
|
||||
|
||||
## Added
|
||||
@@ -17,10 +35,13 @@ and this project adheres to
|
||||
- 🌐(frontend) add localization to editor #368
|
||||
- ✨Public and restricted doc editable #357
|
||||
- ✨(frontend) Add full name if available #380
|
||||
- ✨(backend) Add view accesses ability #376
|
||||
|
||||
## Changed
|
||||
|
||||
- ♻️(frontend) list accesses if user has abilities #376
|
||||
- ♻️(frontend) avoid documents indexing in search engine #372
|
||||
- 👔(backend) doc restricted by default #388
|
||||
|
||||
## Fixed
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from urllib.parse import urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.postgres.aggregates import ArrayAgg
|
||||
from django.contrib.postgres.search import TrigramSimilarity
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import (
|
||||
@@ -156,8 +157,21 @@ class UserViewSet(
|
||||
|
||||
# Filter users by email similarity
|
||||
if query := self.request.GET.get("q", ""):
|
||||
# For performance reasons we filter first by similarity, which relies on an index,
|
||||
# then only calculate precise similarity scores for sorting purposes
|
||||
queryset = queryset.filter(email__trigram_word_similar=query)
|
||||
|
||||
queryset = queryset.annotate(
|
||||
similarity=TrigramSimilarity("email", query)
|
||||
)
|
||||
# When the query only is on the name part, we should try to make many proposals
|
||||
# But when the query looks like an email we should only propose serious matches
|
||||
threshold = 0.6 if "@" in query else 0.1
|
||||
|
||||
queryset = queryset.filter(similarity__gt=threshold).order_by(
|
||||
"-similarity"
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
@decorators.action(
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-25 11:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0007_fix_users_duplicate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='document',
|
||||
name='link_reach',
|
||||
field=models.CharField(choices=[('restricted', 'Restricted'), ('authenticated', 'Authenticated'), ('public', 'Public')], default='restricted', max_length=20),
|
||||
),
|
||||
]
|
||||
@@ -130,17 +130,17 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
||||
"""User model to work with OIDC only authentication."""
|
||||
|
||||
sub_validator = validators.RegexValidator(
|
||||
regex=r"^[\w.@+-]+\Z",
|
||||
regex=r"^[\w.@+-:]+\Z",
|
||||
message=_(
|
||||
"Enter a valid sub. This value may contain only letters, "
|
||||
"numbers, and @/./+/-/_ characters."
|
||||
"numbers, and @/./+/-/_/: characters."
|
||||
),
|
||||
)
|
||||
|
||||
sub = models.CharField(
|
||||
_("sub"),
|
||||
help_text=_(
|
||||
"Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ characters only."
|
||||
"Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
||||
),
|
||||
max_length=255,
|
||||
unique=True,
|
||||
@@ -336,7 +336,7 @@ class Document(BaseModel):
|
||||
link_reach = models.CharField(
|
||||
max_length=20,
|
||||
choices=LinkReachChoices.choices,
|
||||
default=LinkReachChoices.AUTHENTICATED,
|
||||
default=LinkReachChoices.RESTRICTED,
|
||||
)
|
||||
link_role = models.CharField(
|
||||
max_length=20, choices=LinkRoleChoices.choices, default=LinkRoleChoices.READER
|
||||
@@ -496,7 +496,8 @@ class Document(BaseModel):
|
||||
# Compute version roles before adding link roles because we don't
|
||||
# want anonymous users to access versions (we wouldn't know from
|
||||
# which date to allow them anyway)
|
||||
can_get_versions = bool(roles)
|
||||
# Anonymous users should also not see document accesses
|
||||
has_role = bool(roles)
|
||||
|
||||
# Add role provided by the document link
|
||||
if self.link_reach == LinkReachChoices.PUBLIC or (
|
||||
@@ -511,19 +512,20 @@ class Document(BaseModel):
|
||||
can_get = bool(roles)
|
||||
|
||||
return {
|
||||
"accesses_manage": is_owner_or_admin,
|
||||
"accesses_view": has_role,
|
||||
"ai_transform": is_owner_or_admin or is_editor,
|
||||
"ai_translate": is_owner_or_admin or is_editor,
|
||||
"attachment_upload": is_owner_or_admin or is_editor,
|
||||
"destroy": RoleChoices.OWNER in roles,
|
||||
"link_configuration": is_owner_or_admin,
|
||||
"manage_accesses": is_owner_or_admin,
|
||||
"invite_owner": RoleChoices.OWNER in roles,
|
||||
"partial_update": is_owner_or_admin or is_editor,
|
||||
"retrieve": can_get,
|
||||
"update": is_owner_or_admin or is_editor,
|
||||
"versions_destroy": is_owner_or_admin,
|
||||
"versions_list": can_get_versions,
|
||||
"versions_retrieve": can_get_versions,
|
||||
"versions_list": has_role,
|
||||
"versions_retrieve": has_role,
|
||||
}
|
||||
|
||||
def email_invitation(self, language, email, role, sender):
|
||||
@@ -679,7 +681,7 @@ class Template(BaseModel):
|
||||
return {
|
||||
"destroy": RoleChoices.OWNER in roles,
|
||||
"generate_document": can_get,
|
||||
"manage_accesses": is_owner_or_admin,
|
||||
"accesses_manage": is_owner_or_admin,
|
||||
"update": is_owner_or_admin or is_editor,
|
||||
"partial_update": is_owner_or_admin or is_editor,
|
||||
"retrieve": can_get,
|
||||
|
||||
@@ -47,6 +47,7 @@ def test_api_documents_create_authenticated_success():
|
||||
assert response.status_code == 201
|
||||
document = Document.objects.get()
|
||||
assert document.title == "my document"
|
||||
assert document.link_reach == "restricted"
|
||||
assert document.accesses.filter(role="owner", user=user).exists()
|
||||
|
||||
|
||||
|
||||
@@ -21,13 +21,14 @@ def test_api_documents_retrieve_anonymous_public():
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_transform": document.link_role == "editor",
|
||||
"ai_translate": document.link_role == "editor",
|
||||
"attachment_upload": document.link_role == "editor",
|
||||
"destroy": False,
|
||||
"invite_owner": False,
|
||||
"link_configuration": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": document.link_role == "editor",
|
||||
"retrieve": True,
|
||||
"update": document.link_role == "editor",
|
||||
@@ -78,13 +79,14 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_transform": document.link_role == "editor",
|
||||
"ai_translate": document.link_role == "editor",
|
||||
"attachment_upload": document.link_role == "editor",
|
||||
"link_configuration": False,
|
||||
"destroy": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": document.link_role == "editor",
|
||||
"retrieve": True,
|
||||
"update": document.link_role == "editor",
|
||||
|
||||
@@ -22,7 +22,7 @@ def test_api_templates_retrieve_anonymous_public():
|
||||
"abilities": {
|
||||
"destroy": False,
|
||||
"generate_document": True,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
@@ -68,7 +68,7 @@ def test_api_templates_retrieve_authenticated_unrelated_public():
|
||||
"abilities": {
|
||||
"destroy": False,
|
||||
"generate_document": True,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
|
||||
@@ -69,6 +69,48 @@ def test_api_users_list_query_email():
|
||||
assert user_ids == [str(nicole.id), str(frank.id)]
|
||||
|
||||
|
||||
def test_api_users_list_query_email_matching():
|
||||
"""While filtering by email, results should be filtered and sorted by similarity"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
alice = factories.UserFactory(email="alice.johnson@example.gouv.fr")
|
||||
factories.UserFactory(email="jane.smith@example.gouv.fr")
|
||||
michael_wilson = factories.UserFactory(email="michael.wilson@example.gouv.fr")
|
||||
factories.UserFactory(email="david.jones@example.gouv.fr")
|
||||
michael_brown = factories.UserFactory(email="michael.brown@example.gouv.fr")
|
||||
factories.UserFactory(email="sophia.taylor@example.gouv.fr")
|
||||
|
||||
response = client.get(
|
||||
"/api/v1.0/users/?q=michael.johnson@example.gouv.f",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
user_ids = [user["id"] for user in response.json()["results"]]
|
||||
assert user_ids == [str(michael_wilson.id)]
|
||||
|
||||
response = client.get("/api/v1.0/users/?q=michael.johnson@example.gouv.fr")
|
||||
|
||||
assert response.status_code == 200
|
||||
user_ids = [user["id"] for user in response.json()["results"]]
|
||||
assert user_ids == [str(michael_wilson.id), str(alice.id), str(michael_brown.id)]
|
||||
|
||||
response = client.get(
|
||||
"/api/v1.0/users/?q=ajohnson@example.gouv.f",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
user_ids = [user["id"] for user in response.json()["results"]]
|
||||
assert user_ids == [str(alice.id)]
|
||||
|
||||
response = client.get(
|
||||
"/api/v1.0/users/?q=michael.wilson@example.gouv.f",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
user_ids = [user["id"] for user in response.json()["results"]]
|
||||
assert user_ids == [str(michael_wilson.id)]
|
||||
|
||||
|
||||
def test_api_users_list_query_email_exclude_doc_user():
|
||||
"""
|
||||
Authenticated users should be able to list users
|
||||
|
||||
@@ -83,13 +83,14 @@ def test_models_documents_get_abilities_forbidden(is_authenticated, reach, role)
|
||||
user = factories.UserFactory() if is_authenticated else AnonymousUser()
|
||||
abilities = document.get_abilities(user)
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
"link_configuration": False,
|
||||
"destroy": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
@@ -116,13 +117,14 @@ def test_models_documents_get_abilities_reader(is_authenticated, reach):
|
||||
user = factories.UserFactory() if is_authenticated else AnonymousUser()
|
||||
abilities = document.get_abilities(user)
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
"destroy": False,
|
||||
"link_configuration": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
@@ -149,13 +151,14 @@ def test_models_documents_get_abilities_editor(is_authenticated, reach):
|
||||
user = factories.UserFactory() if is_authenticated else AnonymousUser()
|
||||
abilities = document.get_abilities(user)
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
"destroy": False,
|
||||
"link_configuration": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
@@ -171,13 +174,14 @@ def test_models_documents_get_abilities_owner():
|
||||
access = factories.UserDocumentAccessFactory(role="owner", user=user)
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
assert abilities == {
|
||||
"accesses_manage": True,
|
||||
"accesses_view": True,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
"destroy": True,
|
||||
"link_configuration": True,
|
||||
"invite_owner": True,
|
||||
"manage_accesses": True,
|
||||
"partial_update": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
@@ -192,13 +196,14 @@ def test_models_documents_get_abilities_administrator():
|
||||
access = factories.UserDocumentAccessFactory(role="administrator")
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
assert abilities == {
|
||||
"accesses_manage": True,
|
||||
"accesses_view": True,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
"destroy": False,
|
||||
"link_configuration": True,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": True,
|
||||
"partial_update": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
@@ -216,13 +221,14 @@ def test_models_documents_get_abilities_editor_user(django_assert_num_queries):
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": True,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
"destroy": False,
|
||||
"link_configuration": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
@@ -242,13 +248,14 @@ def test_models_documents_get_abilities_reader_user(django_assert_num_queries):
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": True,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
"destroy": False,
|
||||
"link_configuration": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
@@ -269,13 +276,14 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": True,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
"destroy": False,
|
||||
"link_configuration": False,
|
||||
"invite_owner": False,
|
||||
"manage_accesses": False,
|
||||
"partial_update": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
|
||||
@@ -62,7 +62,7 @@ def test_models_templates_get_abilities_anonymous_public():
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"generate_document": True,
|
||||
}
|
||||
@@ -76,7 +76,7 @@ def test_models_templates_get_abilities_anonymous_not_public():
|
||||
"destroy": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"generate_document": False,
|
||||
}
|
||||
@@ -90,7 +90,7 @@ def test_models_templates_get_abilities_authenticated_public():
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"generate_document": True,
|
||||
}
|
||||
@@ -104,7 +104,7 @@ def test_models_templates_get_abilities_authenticated_not_public():
|
||||
"destroy": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"generate_document": False,
|
||||
}
|
||||
@@ -119,7 +119,7 @@ def test_models_templates_get_abilities_owner():
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"manage_accesses": True,
|
||||
"accesses_manage": True,
|
||||
"partial_update": True,
|
||||
"generate_document": True,
|
||||
}
|
||||
@@ -133,7 +133,7 @@ def test_models_templates_get_abilities_administrator():
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"manage_accesses": True,
|
||||
"accesses_manage": True,
|
||||
"partial_update": True,
|
||||
"generate_document": True,
|
||||
}
|
||||
@@ -150,7 +150,7 @@ def test_models_templates_get_abilities_editor_user(django_assert_num_queries):
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": True,
|
||||
"generate_document": True,
|
||||
}
|
||||
@@ -167,7 +167,7 @@ def test_models_templates_get_abilities_reader_user(django_assert_num_queries):
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"generate_document": True,
|
||||
}
|
||||
@@ -185,7 +185,7 @@ def test_models_templates_get_abilities_preset_role(django_assert_num_queries):
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
"accesses_manage": False,
|
||||
"partial_update": False,
|
||||
"generate_document": True,
|
||||
}
|
||||
|
||||
@@ -237,6 +237,7 @@ class Base(Configuration):
|
||||
(
|
||||
("en-us", _("English")),
|
||||
("fr-fr", _("French")),
|
||||
("de-de", _("German")),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,349 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-people\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-09-25 10:15+0000\n"
|
||||
"PO-Revision-Date: 2024-09-25 10:21\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Crowdin-Project: lasuite-people\n"
|
||||
"X-Crowdin-Project-ID: 637934\n"
|
||||
"X-Crowdin-Language: de\n"
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 8\n"
|
||||
|
||||
#: core/admin.py:32
|
||||
msgid "Personal info"
|
||||
msgstr "Persönliche Angaben"
|
||||
|
||||
#: core/admin.py:34
|
||||
msgid "Permissions"
|
||||
msgstr "Berechtigungen"
|
||||
|
||||
#: core/admin.py:46
|
||||
msgid "Important dates"
|
||||
msgstr "Wichtige Termine"
|
||||
|
||||
#: core/api/serializers.py:253
|
||||
msgid "Body"
|
||||
msgstr ""
|
||||
|
||||
#: core/api/serializers.py:256
|
||||
msgid "Body type"
|
||||
msgstr ""
|
||||
|
||||
#: core/api/serializers.py:262
|
||||
msgid "Format"
|
||||
msgstr ""
|
||||
|
||||
#: core/authentication/backends.py:56
|
||||
msgid "Invalid response format or token verification failed"
|
||||
msgstr ""
|
||||
|
||||
#: core/authentication/backends.py:81
|
||||
msgid "User info contained no recognizable user identification"
|
||||
msgstr ""
|
||||
|
||||
#: core/authentication/backends.py:101
|
||||
msgid "Claims contained no recognizable user identification"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:62 core/models.py:69
|
||||
msgid "Reader"
|
||||
msgstr "Leser"
|
||||
|
||||
#: core/models.py:63 core/models.py:70
|
||||
msgid "Editor"
|
||||
msgstr "Bearbeiter"
|
||||
|
||||
#: core/models.py:71
|
||||
msgid "Administrator"
|
||||
msgstr "Administrator"
|
||||
|
||||
#: core/models.py:72
|
||||
msgid "Owner"
|
||||
msgstr "Eigentümer"
|
||||
|
||||
#: core/models.py:80
|
||||
msgid "Restricted"
|
||||
msgstr "Eingeschränkt"
|
||||
|
||||
#: core/models.py:84
|
||||
msgid "Authenticated"
|
||||
msgstr "Authentifiziert"
|
||||
|
||||
#: core/models.py:86
|
||||
msgid "Public"
|
||||
msgstr "Öffentlich"
|
||||
|
||||
#: core/models.py:98
|
||||
msgid "id"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:99
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:105
|
||||
msgid "created on"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:106
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:111
|
||||
msgid "updated on"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:112
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:132
|
||||
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_ characters."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:138
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:140
|
||||
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ characters only."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:148
|
||||
msgid "identity email address"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:153
|
||||
msgid "admin email address"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:160
|
||||
msgid "language"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:161
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:167
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:170
|
||||
msgid "device"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:172
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:175
|
||||
msgid "staff status"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:177
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:180
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:183
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:195
|
||||
msgid "user"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:196
|
||||
msgid "users"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:328 core/models.py:644
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:343
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:344
|
||||
msgid "Documents"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:347
|
||||
msgid "Untitled Document"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:537
|
||||
#, python-format
|
||||
msgid "%(username)s shared a document with you: %(document)s"
|
||||
msgstr "%(username)s hat ein Dokument mit Ihnen geteilt: %(document)s"
|
||||
|
||||
#: core/models.py:580
|
||||
msgid "Document/user link trace"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:581
|
||||
msgid "Document/user link traces"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:587
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:608
|
||||
msgid "Document/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:609
|
||||
msgid "Document/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:615
|
||||
msgid "This user is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:621
|
||||
msgid "This team is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:627 core/models.py:816
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:645
|
||||
msgid "description"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:646
|
||||
msgid "code"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:647
|
||||
msgid "css"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:649
|
||||
msgid "public"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:651
|
||||
msgid "Whether this template is public for anyone to use."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:657
|
||||
msgid "Template"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:658
|
||||
msgid "Templates"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:797
|
||||
msgid "Template/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:798
|
||||
msgid "Template/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:804
|
||||
msgid "This user is already in this template."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:810
|
||||
msgid "This team is already in this template."
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:833
|
||||
msgid "email address"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:850
|
||||
msgid "Document invitation"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:851
|
||||
msgid "Document invitations"
|
||||
msgstr ""
|
||||
|
||||
#: core/models.py:868
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/invitation.html:160
|
||||
#: core/templates/mail/html/invitation2.html:160
|
||||
#: core/templates/mail/text/invitation.txt:3
|
||||
#: core/templates/mail/text/invitation2.txt:3
|
||||
msgid "La Suite Numérique"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/invitation.html:190
|
||||
#: core/templates/mail/text/invitation.txt:6
|
||||
#, python-format
|
||||
msgid " %(username)s shared a document with you ! "
|
||||
msgstr " %(username)s hat ein Dokument mit Ihnen geteilt! "
|
||||
|
||||
#: core/templates/mail/html/invitation.html:197
|
||||
#: core/templates/mail/text/invitation.txt:8
|
||||
#, python-format
|
||||
msgid " %(username)s invited you as an %(role)s on the following document : "
|
||||
msgstr " %(username)s hat Sie als %(role)s zum folgenden Dokument eingeladen: "
|
||||
|
||||
#: core/templates/mail/html/invitation.html:206
|
||||
#: core/templates/mail/html/invitation2.html:211
|
||||
#: core/templates/mail/text/invitation.txt:10
|
||||
#: core/templates/mail/text/invitation2.txt:11
|
||||
msgid "Open"
|
||||
msgstr "Öffnen"
|
||||
|
||||
#: core/templates/mail/html/invitation.html:223
|
||||
#: core/templates/mail/text/invitation.txt:14
|
||||
msgid " Docs, your new essential tool for organizing, sharing and collaborate on your documents as a team. "
|
||||
msgstr " Docs, Ihr neues unverzichtbares Werkzeug zum Organisieren, Teilen und Zusammenarbeiten an Dokumenten im Team. "
|
||||
|
||||
#: core/templates/mail/html/invitation.html:230
|
||||
#: core/templates/mail/html/invitation2.html:235
|
||||
#: core/templates/mail/text/invitation.txt:16
|
||||
#: core/templates/mail/text/invitation2.txt:17
|
||||
msgid "Brought to you by La Suite Numérique"
|
||||
msgstr "Bereitgestellt von La Suite Numérique"
|
||||
|
||||
#: core/templates/mail/html/invitation2.html:190
|
||||
#, python-format
|
||||
msgid "%(username)s shared a document with you"
|
||||
msgstr "%(username)s hat ein Dokument mit Ihnen geteilt"
|
||||
|
||||
#: core/templates/mail/html/invitation2.html:197
|
||||
#: core/templates/mail/text/invitation2.txt:8
|
||||
#, python-format
|
||||
msgid "%(username)s invited you as an %(role)s on the following document :"
|
||||
msgstr "%(username)s hat Sie als %(role)s zum folgenden Dokument eingeladen:"
|
||||
|
||||
#: core/templates/mail/html/invitation2.html:228
|
||||
#: core/templates/mail/text/invitation2.txt:15
|
||||
msgid "Docs, your new essential tool for organizing, sharing and collaborate on your document as a team."
|
||||
msgstr "Docs, Ihr neues unverzichtbares Werkzeug zum Organisieren, Teilen und gemeinsamen Arbeiten an Dokumenten im Team."
|
||||
|
||||
#: impress/settings.py:177
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:178
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:176
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
@@ -345,11 +345,14 @@ msgstr ""
|
||||
msgid "This mail has been sent to %(email)s by %(name)s [%(href)s]"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:176
|
||||
#: impress/settings.py:177
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:177
|
||||
#: impress/settings.py:178
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:176
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
@@ -345,11 +345,14 @@ msgstr "Proposé par La Suite Numérique"
|
||||
msgid "This mail has been sent to %(email)s by %(name)s [%(href)s]"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:176
|
||||
#: impress/settings.py:177
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:177
|
||||
#: impress/settings.py:178
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: impress/settings.py:176
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
@@ -144,7 +144,7 @@ export const mockedDocument = async (page: Page, json: object) => {
|
||||
versions_destroy: false,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: false, // Means not admin
|
||||
accesses_manage: false, // Means not admin
|
||||
update: false,
|
||||
partial_update: false, // Means not editor
|
||||
retrieve: true,
|
||||
|
||||
@@ -215,7 +215,7 @@ test.describe('Doc Editor', () => {
|
||||
versions_destroy: false,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: false, // Means not admin
|
||||
accesses_manage: false, // Means not admin
|
||||
update: false,
|
||||
partial_update: false, // Means not editor
|
||||
retrieve: true,
|
||||
|
||||
@@ -303,7 +303,7 @@ test.describe('Documents Grid mobile', () => {
|
||||
attachment_upload: true,
|
||||
destroy: true,
|
||||
link_configuration: true,
|
||||
manage_accesses: true,
|
||||
accesses_manage: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
update: true,
|
||||
|
||||
@@ -45,7 +45,7 @@ test.describe('Doc Header', () => {
|
||||
versions_destroy: true,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: true,
|
||||
accesses_manage: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
@@ -177,12 +177,13 @@ test.describe('Doc Header', () => {
|
||||
test('it checks the options available if administrator', async ({ page }) => {
|
||||
await mockedDocument(page, {
|
||||
abilities: {
|
||||
accesses_manage: true, // Means admin
|
||||
accesses_view: true,
|
||||
destroy: false, // Means not owner
|
||||
link_configuration: true,
|
||||
versions_destroy: true,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: true, // Means admin
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
@@ -247,12 +248,13 @@ test.describe('Doc Header', () => {
|
||||
test('it checks the options available if editor', async ({ page }) => {
|
||||
await mockedDocument(page, {
|
||||
abilities: {
|
||||
accesses_manage: false, // Means not admin
|
||||
accesses_view: true,
|
||||
destroy: false, // Means not owner
|
||||
link_configuration: false,
|
||||
versions_destroy: true,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: false, // Means not admin
|
||||
update: true,
|
||||
partial_update: true, // Means editor
|
||||
retrieve: true,
|
||||
@@ -324,12 +326,13 @@ test.describe('Doc Header', () => {
|
||||
test('it checks the options available if reader', async ({ page }) => {
|
||||
await mockedDocument(page, {
|
||||
abilities: {
|
||||
accesses_manage: false, // Means not admin
|
||||
accesses_view: true,
|
||||
destroy: false, // Means not owner
|
||||
link_configuration: false,
|
||||
versions_destroy: false,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: false, // Means not admin
|
||||
update: false,
|
||||
partial_update: false, // Means not editor
|
||||
retrieve: true,
|
||||
@@ -489,7 +492,7 @@ test.describe('Documents Header mobile', () => {
|
||||
versions_destroy: true,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: true,
|
||||
accesses_manage: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
|
||||
@@ -40,20 +40,20 @@ test.describe('Doc Visibility', () => {
|
||||
name: 'Visibility',
|
||||
});
|
||||
|
||||
await expect(selectVisibility.getByText('Authenticated')).toBeVisible();
|
||||
await expect(selectVisibility.getByText('Restricted')).toBeVisible();
|
||||
|
||||
await expect(page.getByLabel('Read only')).toBeVisible();
|
||||
await expect(page.getByLabel('Can read and edit')).toBeVisible();
|
||||
await expect(page.getByLabel('Read only')).toBeHidden();
|
||||
await expect(page.getByLabel('Can read and edit')).toBeHidden();
|
||||
|
||||
await selectVisibility.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Restricted',
|
||||
name: 'Authenticated',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(page.getByLabel('Read only')).toBeHidden();
|
||||
await expect(page.getByLabel('Can read and edit')).toBeHidden();
|
||||
await expect(page.getByLabel('Read only')).toBeVisible();
|
||||
await expect(page.getByLabel('Can read and edit')).toBeVisible();
|
||||
|
||||
await selectVisibility.click();
|
||||
|
||||
@@ -87,26 +87,6 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
|
||||
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page
|
||||
.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
})
|
||||
.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Restricted',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('.c__modal__backdrop').click({
|
||||
position: { x: 0, y: 0 },
|
||||
});
|
||||
|
||||
const urlDoc = page.url();
|
||||
|
||||
await page
|
||||
@@ -133,26 +113,6 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
|
||||
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page
|
||||
.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
})
|
||||
.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Restricted',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('.c__modal__backdrop').click({
|
||||
position: { x: 0, y: 0 },
|
||||
});
|
||||
|
||||
const urlDoc = page.url();
|
||||
|
||||
await page
|
||||
@@ -182,20 +142,6 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page
|
||||
.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
})
|
||||
.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Restricted',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
const inputSearch = page.getByLabel(/Find a member to add to the document/);
|
||||
|
||||
@@ -389,6 +335,26 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
|
||||
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page
|
||||
.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
})
|
||||
.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Authenticated',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('.c__modal__backdrop').click({
|
||||
position: { x: 0, y: 0 },
|
||||
});
|
||||
|
||||
const urlDoc = page.url();
|
||||
|
||||
await page
|
||||
@@ -421,6 +387,26 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
|
||||
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page
|
||||
.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
})
|
||||
.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Authenticated',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('.c__modal__backdrop').click({
|
||||
position: { x: 0, y: 0 },
|
||||
});
|
||||
|
||||
const urlDoc = page.url();
|
||||
|
||||
await page
|
||||
@@ -435,10 +421,20 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
await page.goto(urlDoc);
|
||||
|
||||
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await expect(
|
||||
page.getByText('Read only, you cannot edit this document'),
|
||||
).toBeVisible();
|
||||
|
||||
const shareModal = page.getByLabel('Share modal');
|
||||
|
||||
await expect(
|
||||
shareModal.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
}),
|
||||
).toHaveAttribute('disabled');
|
||||
await expect(shareModal.getByText('Search by email')).toBeHidden();
|
||||
await expect(shareModal.getByLabel('List members card')).toBeHidden();
|
||||
});
|
||||
|
||||
test('It checks a authenticated doc in editable mode', async ({
|
||||
@@ -457,6 +453,26 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
|
||||
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page
|
||||
.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
})
|
||||
.click();
|
||||
await page
|
||||
.getByRole('option', {
|
||||
name: 'Authenticated',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('.c__modal__backdrop').click({
|
||||
position: { x: 0, y: 0 },
|
||||
});
|
||||
|
||||
const urlDoc = page.url();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
@@ -483,9 +499,19 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
await page.goto(urlDoc);
|
||||
|
||||
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await expect(
|
||||
page.getByText('Read only, you cannot edit this document'),
|
||||
).toBeHidden();
|
||||
|
||||
const shareModal = page.getByLabel('Share modal');
|
||||
|
||||
await expect(
|
||||
shareModal.getByRole('combobox', {
|
||||
name: 'Visibility',
|
||||
}),
|
||||
).toHaveAttribute('disabled');
|
||||
await expect(shareModal.getByText('Search by email')).toBeHidden();
|
||||
await expect(shareModal.getByLabel('List members card')).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,18 @@ test.describe('Language', () => {
|
||||
name: 'Créer un nouveau document',
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await header.getByRole('combobox').getByText('Français').click();
|
||||
await header.getByRole('option', { name: 'Deutsch' }).click();
|
||||
await expect(
|
||||
header.getByRole('combobox').getByText('Deutsch'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', {
|
||||
name: 'Neues Dokument erstellen',
|
||||
}),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks that backend uses the same language as the frontend', async ({
|
||||
|
||||
@@ -3,16 +3,21 @@ const config = {
|
||||
default: {
|
||||
theme: {
|
||||
colors: {
|
||||
'card-border': '#ededed',
|
||||
'card-border': '#DDDDDD',
|
||||
'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.50rem',
|
||||
xl: '1.25rem',
|
||||
t: '0.6875rem',
|
||||
s: '0.75rem',
|
||||
h1: '2.2rem',
|
||||
@@ -28,274 +33,224 @@ 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: {
|
||||
'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
|
||||
|
||||
'primary-text': 'var(--c--theme--colors--primary-600)',
|
||||
'primary-100': '#F5F5FE',
|
||||
'primary-200': '#ECECFE',
|
||||
'primary-300': '#E3E3FD',
|
||||
'primary-400': '#CACAFB',
|
||||
'primary-500': '#6A6AF4',
|
||||
'primary-600': '#000091',
|
||||
'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',
|
||||
'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',
|
||||
},
|
||||
font: {
|
||||
families: {
|
||||
accent: 'Marianne',
|
||||
base: 'Marianne',
|
||||
},
|
||||
},
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
widthHeader: '110px',
|
||||
widthFooter: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
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-text)',
|
||||
'color-hover': '#1212ff',
|
||||
'color-active': '#2323ff',
|
||||
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: '#ffffff',
|
||||
'color-hover': '#ffffff',
|
||||
@@ -303,33 +258,51 @@ const config = {
|
||||
},
|
||||
'primary-text': {
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
'color-active': 'var(--c--theme--colors--primary-100)',
|
||||
'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-text)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
secondary: {
|
||||
background: {
|
||||
'color-hover': '#F6F6F6',
|
||||
color: 'var(--c--theme--colors--greyscale-000)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-hover-000)',
|
||||
'color-active': '#EDEDED',
|
||||
},
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
color: 'var(--c--theme--colors--greyscale-250)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-250)',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
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)',
|
||||
},
|
||||
'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--primary-text)',
|
||||
color: 'var(--c--theme--colors--greyscale-500)',
|
||||
size: 'var(--c--theme--font--sizes--s)',
|
||||
},
|
||||
body: {
|
||||
@@ -339,60 +312,48 @@ 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',
|
||||
},
|
||||
'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)',
|
||||
// 'border-radius': '0',
|
||||
},
|
||||
'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)',
|
||||
// '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)',
|
||||
},
|
||||
'forms-textarea': {
|
||||
'border-radius': '0',
|
||||
// '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-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)',
|
||||
},
|
||||
'forms-switch': {
|
||||
// 'accent-color': '#2323ff',
|
||||
},
|
||||
'forms-checkbox': {
|
||||
// 'accent-color': '#2323ff',
|
||||
},
|
||||
'la-gauffre': {
|
||||
activated: true,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
|
||||
const { InjectManifest } = require('workbox-webpack-plugin');
|
||||
|
||||
@@ -11,6 +12,9 @@ const nextConfig = {
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
sassOptions: {
|
||||
includePaths: [path.join(__dirname, 'styles')],
|
||||
},
|
||||
compiler: {
|
||||
// Enables the styled-components SWC transform
|
||||
styledComponents: true,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"@hocuspocus/provider": "2.13.7",
|
||||
"@openfun/cunningham-react": "2.9.4",
|
||||
"@tanstack/react-query": "5.59.15",
|
||||
"classnames": "2.5.1",
|
||||
"i18next": "23.16.2",
|
||||
"i18next-browser-languagedetector": "8.0.0",
|
||||
"idb": "8.0.0",
|
||||
@@ -59,6 +60,7 @@
|
||||
"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",
|
||||
|
||||
@@ -7,7 +7,6 @@ export interface LinkProps {
|
||||
|
||||
export const StyledLink = styled(Link)<LinkProps>`
|
||||
text-decoration: none;
|
||||
color: #ffffff33;
|
||||
&[aria-current='page'] {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
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>;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
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,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,4 @@
|
||||
: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 url('./cunningham-tokens.css');
|
||||
@import url('./cunningham-custom-tokens.css');
|
||||
@import url('../assets/fonts/Marianne/Marianne-font.css');
|
||||
@import './cunningham-tokens';
|
||||
@import './cunningham-custom-tokens';
|
||||
@import '../assets/fonts/Marianne/Marianne-font';
|
||||
|
||||
.c__input,
|
||||
.c__field,
|
||||
@@ -12,221 +12,20 @@
|
||||
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--components--datagrid--header--weight);
|
||||
font-weight: var(--c--theme--font--sizes--sm);
|
||||
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-100) solid;
|
||||
border-bottom: 1px var(--c--theme--colors--greyscale-100) solid;
|
||||
/*border: none;*/
|
||||
/*border-top: 1px var(--c--theme--colors--greyscale-300) solid;*/
|
||||
/*border-bottom: 1px var(--c--theme--colors--greyscale-300) solid;*/
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table tbody {
|
||||
@@ -239,19 +38,6 @@ input:-webkit-autofill:focus {
|
||||
);
|
||||
}
|
||||
|
||||
.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;
|
||||
@@ -261,7 +47,6 @@ input:-webkit-autofill:focus {
|
||||
gap: 3px;
|
||||
border-radius: 4px;
|
||||
background: var(--c--components--datagrid--pagination--background-color);
|
||||
border-color: var(--c--components--datagrid--pagination--border-color);
|
||||
}
|
||||
|
||||
.c__pagination__list .c__button--tertiary-text.c__button--active {
|
||||
@@ -321,23 +106,6 @@ input:-webkit-autofill:focus {
|
||||
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
|
||||
*/
|
||||
@@ -351,8 +119,12 @@ input:-webkit-autofill:focus {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.c__button--nano.c__button--with-icon--left {
|
||||
padding: 0 var(--c--theme--spacings--100W);
|
||||
}
|
||||
|
||||
.c__button--medium {
|
||||
padding: 0.9rem var(--c--theme--spacings--s);
|
||||
padding: var(--c--theme--spacings--100W);
|
||||
}
|
||||
|
||||
.c__button--small {
|
||||
@@ -364,6 +136,12 @@ input:-webkit-autofill:focus {
|
||||
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);
|
||||
@@ -378,34 +156,69 @@ input:-webkit-autofill:focus {
|
||||
|
||||
.c__button--primary:active,
|
||||
.c__button--primary.c__button--active {
|
||||
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);
|
||||
background-color: var(--c--components--button--primary--background--color);
|
||||
color: var(--c--components--button--primary--color);
|
||||
}
|
||||
|
||||
.c__button--primary-text:active,
|
||||
.c__button--primary-text.c__button--active {
|
||||
/* Button Primary-text */
|
||||
|
||||
.c__button--primary-text {
|
||||
border: none;
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-active
|
||||
);
|
||||
color: var(--c--components--button--primary-text--color);
|
||||
}
|
||||
|
||||
.c__button--primary-text:hover,
|
||||
.c__button--primary-text:focus-visible {
|
||||
.c__button--primary-text.c__button--active {
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-hover
|
||||
);
|
||||
color: var(--c--components--button--primary-text--color-hover);
|
||||
}
|
||||
|
||||
.c__button:disabled {
|
||||
background-color: var(--c--components--button--disabled--background--color);
|
||||
color: var(--c--components--button--disabled--color);
|
||||
.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--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);
|
||||
@@ -426,12 +239,18 @@ input:-webkit-autofill:focus {
|
||||
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(
|
||||
@@ -441,111 +260,60 @@ input:-webkit-autofill:focus {
|
||||
border: 1px solid var(--c--components--button--secondary--border--color-hover);
|
||||
}
|
||||
|
||||
.c__button--tertiary {
|
||||
color: var(--c--components--button--tertiary--color);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.c__button--tertiary:hover,
|
||||
.c__button--tertiary:focus-visible {
|
||||
.c__button--secondary:active,
|
||||
.c__button--secondary.c__button--active {
|
||||
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
|
||||
--c--components--button--secondary--background--color-active
|
||||
);
|
||||
}
|
||||
|
||||
/* Button danger */
|
||||
|
||||
/**
|
||||
* 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 button {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
/*.c__modal__close .c__button {*/
|
||||
/* right: 5px;*/
|
||||
/* top: 5px;*/
|
||||
/* padding: 1.5rem 1rem;*/
|
||||
/*}*/
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
/*!***/
|
||||
/* * Toast*/
|
||||
/**!*/
|
||||
/*.c__toast__container {*/
|
||||
/* z-index: 10000;*/
|
||||
/*}*/
|
||||
|
||||
/**
|
||||
* Toast
|
||||
*/
|
||||
* Alert
|
||||
*/
|
||||
|
||||
.c__toast__container {
|
||||
z-index: 10000;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tooltip
|
||||
*/
|
||||
.c__tooltip {
|
||||
padding: 4px 6px;
|
||||
.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);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -73,10 +73,12 @@ export const tokens = {
|
||||
'success-text': '#FFFFFF',
|
||||
'warning-text': '#FFFFFF',
|
||||
'danger-text': '#FFFFFF',
|
||||
'card-border': '#ededed',
|
||||
'card-border': '#DDDDDD',
|
||||
'primary-bg': '#FAFAFA',
|
||||
'primary-150': '#E5EEFA',
|
||||
'info-150': '#E5EEFA',
|
||||
'grey-400': '#929292',
|
||||
'grey-800': '#2A2A2A',
|
||||
},
|
||||
font: {
|
||||
sizes: {
|
||||
@@ -90,7 +92,7 @@ export const tokens = {
|
||||
m: '0.8125rem',
|
||||
s: '0.75rem',
|
||||
ml: '0.938rem',
|
||||
xl: '1.50rem',
|
||||
xl: '1.25rem',
|
||||
t: '0.6875rem',
|
||||
},
|
||||
weights: {
|
||||
@@ -111,7 +113,7 @@ export const tokens = {
|
||||
h2: 'normal',
|
||||
h3: 'normal',
|
||||
h4: 'normal',
|
||||
h5: '1px',
|
||||
h5: 'normal',
|
||||
h6: 'normal',
|
||||
l: 'normal',
|
||||
m: 'normal',
|
||||
@@ -119,17 +121,12 @@ 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)',
|
||||
@@ -138,145 +135,13 @@ export const tokens = {
|
||||
duration: '250ms',
|
||||
},
|
||||
breakpoints: {
|
||||
xs: '480px',
|
||||
xs: 0,
|
||||
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: {
|
||||
@@ -334,98 +199,188 @@ export const tokens = {
|
||||
dsfr: {
|
||||
theme: {
|
||||
colors: {
|
||||
'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-text': 'var(--c--theme--colors--primary-600)',
|
||||
'primary-100': '#F5F5FE',
|
||||
'primary-200': '#ECECFE',
|
||||
'primary-300': '#E3E3FD',
|
||||
'primary-400': '#CACAFB',
|
||||
'primary-500': '#6A6AF4',
|
||||
'primary-600': '#000091',
|
||||
'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',
|
||||
'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',
|
||||
},
|
||||
font: { families: { accent: 'Marianne', base: 'Marianne' } },
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
widthHeader: '110px',
|
||||
widthFooter: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
alert: { 'border-radius': '0' },
|
||||
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-text)',
|
||||
'color-hover': '#1212ff',
|
||||
'color-active': '#2323ff',
|
||||
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: '#ffffff',
|
||||
'color-hover': '#ffffff',
|
||||
@@ -433,30 +388,51 @@ export const tokens = {
|
||||
},
|
||||
'primary-text': {
|
||||
background: {
|
||||
'color-hover': 'var(--c--theme--colors--primary-100)',
|
||||
'color-active': 'var(--c--theme--colors--primary-100)',
|
||||
'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-text)',
|
||||
'color-disabled': 'var(--c--theme--colors--greyscale-400)',
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
},
|
||||
secondary: {
|
||||
background: { 'color-hover': '#F6F6F6', 'color-active': '#EDEDED' },
|
||||
border: {
|
||||
color: 'var(--c--theme--colors--primary-600)',
|
||||
'color-hover': 'var(--c--theme--colors--primary-600)',
|
||||
background: {
|
||||
color: 'var(--c--theme--colors--greyscale-000)',
|
||||
'color-hover': 'var(--c--theme--colors--greyscale-hover-000)',
|
||||
'color-active': '#EDEDED',
|
||||
},
|
||||
color: 'var(--c--theme--colors--primary-text)',
|
||||
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-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)',
|
||||
},
|
||||
'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--primary-text)',
|
||||
color: 'var(--c--theme--colors--greyscale-500)',
|
||||
size: 'var(--c--theme--font--sizes--s)',
|
||||
},
|
||||
body: {
|
||||
@@ -466,53 +442,15 @@ export const tokens = {
|
||||
pagination: {
|
||||
'background-color': 'transparent',
|
||||
'background-color-active': 'var(--c--theme--colors--primary-300)',
|
||||
'border-color': 'var(--c--theme--colors--primary-400)',
|
||||
},
|
||||
},
|
||||
'forms-checkbox': {
|
||||
'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' },
|
||||
'forms-datepicker': {},
|
||||
'forms-fileuploader': {},
|
||||
'forms-input': {},
|
||||
'forms-textarea': {},
|
||||
'forms-select': {},
|
||||
'forms-switch': {},
|
||||
'forms-checkbox': {},
|
||||
'la-gauffre': { activated: true },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,14 +14,13 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { isAPIError } from '@/api';
|
||||
import { Box, Text } from '@/components';
|
||||
import { useDocOptions } from '@/features/docs/doc-management/';
|
||||
import { useDocOptions, useDocStore } from '@/features/docs/doc-management/';
|
||||
|
||||
import {
|
||||
AITransformActions,
|
||||
useDocAITransform,
|
||||
useDocAITranslate,
|
||||
} from '../api/';
|
||||
import { useDocStore } from '../stores';
|
||||
|
||||
type LanguageTranslate = {
|
||||
value: string;
|
||||
|
||||
+14
-32
@@ -4,18 +4,18 @@ 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 { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, TextErrors } from '@/components';
|
||||
import { mediaUrl } from '@/core';
|
||||
import { useAuthStore } from '@/core/auth';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
import { Version } from '@/features/docs/doc-versioning/';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
|
||||
import { useCreateDocAttachment } from '../api/useCreateDocUpload';
|
||||
import useSaveDoc from '../hook/useSaveDoc';
|
||||
import { useDocStore, useHeadingStore } from '../stores';
|
||||
import { useHeadingStore } from '../stores';
|
||||
import { randomColor } from '../utils';
|
||||
|
||||
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
||||
@@ -26,7 +26,13 @@ const cssEditor = (readonly: boolean) => `
|
||||
};
|
||||
& .bn-editor {
|
||||
padding-right: 30px;
|
||||
${readonly && `padding-left: 30px;`}
|
||||
${
|
||||
readonly &&
|
||||
`
|
||||
padding-left: 30px;
|
||||
pointer-events: none;
|
||||
`
|
||||
}
|
||||
};
|
||||
& .collaboration-cursor__caret.ProseMirror-widget{
|
||||
word-wrap: initial;
|
||||
@@ -68,40 +74,16 @@ const cssEditor = (readonly: boolean) => `
|
||||
`;
|
||||
|
||||
interface BlockNoteEditorProps {
|
||||
doc: Doc;
|
||||
version?: Version;
|
||||
}
|
||||
|
||||
export const BlockNoteEditor = ({ doc, version }: BlockNoteEditorProps) => {
|
||||
const { createProvider, docsStore } = useDocStore();
|
||||
const storeId = version?.id || doc.id;
|
||||
const initialContent = version?.content || doc.content;
|
||||
const provider = docsStore?.[storeId]?.provider;
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider || provider.document.guid !== storeId) {
|
||||
createProvider(storeId, initialContent);
|
||||
}
|
||||
}, [createProvider, initialContent, provider, storeId]);
|
||||
|
||||
if (!provider) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <BlockNoteContent doc={doc} provider={provider} storeId={storeId} />;
|
||||
};
|
||||
|
||||
interface BlockNoteContentProps {
|
||||
doc: Doc;
|
||||
provider: HocuspocusProvider;
|
||||
storeId: string;
|
||||
}
|
||||
|
||||
export const BlockNoteContent = ({
|
||||
export const BlockNoteEditor = ({
|
||||
doc,
|
||||
provider,
|
||||
storeId,
|
||||
}: BlockNoteContentProps) => {
|
||||
}: BlockNoteEditorProps) => {
|
||||
const isVersion = doc.id !== storeId;
|
||||
const { userData } = useAuthStore();
|
||||
const { setStore, docsStore } = useDocStore();
|
||||
@@ -139,14 +121,14 @@ export const BlockNoteContent = ({
|
||||
provider,
|
||||
fragment: provider.document.getXmlFragment('document-store'),
|
||||
user: {
|
||||
name: userData?.email || 'Anonymous',
|
||||
name: userData?.full_name || userData?.email || t('Anonymous'),
|
||||
color: randomColor(),
|
||||
},
|
||||
},
|
||||
dictionary: locales[lang as keyof typeof locales],
|
||||
uploadFile,
|
||||
},
|
||||
[provider, uploadFile, userData?.email, lang],
|
||||
[lang, provider, uploadFile, userData?.email, userData?.full_name],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Alert, Loader, VariantType } from '@openfun/cunningham-react';
|
||||
import { useRouter as useNavigate } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, Card, Text, TextErrors } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { DocHeader } from '@/features/docs/doc-header';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
@@ -32,6 +32,13 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
|
||||
const { docsStore } = useDocStore();
|
||||
const provider = docsStore?.[doc.id]?.provider;
|
||||
|
||||
if (!provider) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocHeader doc={doc} versionId={versionId as Versions['version_id']} />
|
||||
@@ -66,7 +73,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
{isVersion ? (
|
||||
<DocVersionEditor doc={doc} versionId={versionId} />
|
||||
) : (
|
||||
<BlockNoteEditor doc={doc} />
|
||||
<BlockNoteEditor doc={doc} storeId={doc.id} provider={provider} />
|
||||
)}
|
||||
{!isMobile && <IconOpenPanelEditor headings={headings} />}
|
||||
</Card>
|
||||
@@ -91,9 +98,21 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
||||
docId: doc.id,
|
||||
versionId,
|
||||
});
|
||||
const { createProvider, docsStore } = useDocStore();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!version?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = docsStore?.[version.id]?.provider;
|
||||
if (!provider || provider.document.guid !== version.id) {
|
||||
createProvider(version.id, version.content);
|
||||
}
|
||||
}, [createProvider, docsStore, version]);
|
||||
|
||||
if (isError && error) {
|
||||
if (error.status === 404) {
|
||||
navigate.replace(`/404`);
|
||||
@@ -124,5 +143,11 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
return <BlockNoteEditor doc={doc} version={version} />;
|
||||
const provider = docsStore?.[version.id]?.provider;
|
||||
|
||||
if (!provider) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <BlockNoteEditor doc={doc} storeId={version.id} provider={provider} />;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './useDocStore';
|
||||
export * from './useHeadingStore';
|
||||
export * from './usePanelEditorStore';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import * as Y from 'yjs';
|
||||
|
||||
export const randomColor = () => {
|
||||
const randomInt = (min: number, max: number) => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
@@ -28,20 +26,3 @@ function hslToHex(h: number, s: number, l: number) {
|
||||
export const toBase64 = (
|
||||
str: WithImplicitCoercion<ArrayBuffer | SharedArrayBuffer>,
|
||||
) => Buffer.from(str).toString('base64');
|
||||
|
||||
type BasicBlock = {
|
||||
type: string;
|
||||
content: string;
|
||||
};
|
||||
export const blocksToYDoc = (blocks: BasicBlock[], doc: Y.Doc) => {
|
||||
const xmlFragment = doc.getXmlFragment('document-store');
|
||||
|
||||
blocks.forEach((block) => {
|
||||
const xmlElement = new Y.XmlElement(block.type);
|
||||
if (block.content) {
|
||||
xmlElement.insert(0, [new Y.XmlText(block.content)]);
|
||||
}
|
||||
|
||||
xmlFragment.push([xmlElement]);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
useTrans,
|
||||
useUpdateDoc,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
import { useBroadcastStore, useResponsiveStore } from '@/stores';
|
||||
import { isFirefox } from '@/utils/userAgent';
|
||||
|
||||
interface DocTitleProps {
|
||||
@@ -54,6 +54,7 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
const headingText = headings?.[0]?.contentText;
|
||||
const debounceRef = useRef<NodeJS.Timeout>();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
const { broadcast } = useBroadcastStore();
|
||||
|
||||
const { mutate: updateDoc } = useUpdateDoc({
|
||||
listInvalideQueries: [KEY_DOC, KEY_LIST_DOC],
|
||||
@@ -61,6 +62,9 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
if (data.title !== untitledDocument) {
|
||||
toast(t('Document title updated successfully'), VariantType.SUCCESS);
|
||||
}
|
||||
|
||||
// Broadcast to every user connected to the document
|
||||
broadcast(`${KEY_DOC}-${data.id}`);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -8,11 +8,12 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, DropButton, IconOptions } from '@/components';
|
||||
import { useAuthStore } from '@/core';
|
||||
import { useDocStore, usePanelEditorStore } from '@/features/docs/doc-editor/';
|
||||
import { usePanelEditorStore } from '@/features/docs/doc-editor/';
|
||||
import {
|
||||
Doc,
|
||||
ModalRemoveDoc,
|
||||
ModalShare,
|
||||
useDocStore,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ import { t } from 'i18next';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { useDocStore } from '@/features/docs/doc-editor/';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
|
||||
import { useExport } from '../api/useExport';
|
||||
import { TemplatesOrdering, useTemplates } from '../api/useTemplates';
|
||||
|
||||
@@ -5,7 +5,7 @@ export interface Template {
|
||||
abilities: {
|
||||
destroy: boolean;
|
||||
generate_document: boolean;
|
||||
manage_accesses: boolean;
|
||||
accesses_manage: boolean;
|
||||
retrieve: boolean;
|
||||
update: boolean;
|
||||
partial_update: boolean;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
import { Doc } from '@/features/docs';
|
||||
import { Doc, KEY_DOC } from '@/features/docs/doc-management';
|
||||
import { useBroadcastStore } from '@/stores';
|
||||
|
||||
export type UpdateDocLinkParams = Pick<Doc, 'id'> &
|
||||
Partial<Pick<Doc, 'link_role' | 'link_reach'>>;
|
||||
@@ -37,14 +38,20 @@ export function useUpdateDocLink({
|
||||
listInvalideQueries,
|
||||
}: UpdateDocLinkProps = {}) {
|
||||
const queryClient = useQueryClient();
|
||||
const { broadcast } = useBroadcastStore();
|
||||
|
||||
return useMutation<Doc, APIError, UpdateDocLinkParams>({
|
||||
mutationFn: updateDocLink,
|
||||
onSuccess: (data) => {
|
||||
onSuccess: (data, variable) => {
|
||||
listInvalideQueries?.forEach((queryKey) => {
|
||||
void queryClient.resetQueries({
|
||||
queryKey: [queryKey],
|
||||
});
|
||||
});
|
||||
|
||||
// Broadcast to every user connected to the document
|
||||
broadcast(`${KEY_DOC}-${variable.id}`);
|
||||
|
||||
onSuccess?.(data);
|
||||
},
|
||||
});
|
||||
|
||||
+7
-3
@@ -115,7 +115,7 @@ export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
|
||||
</Box>
|
||||
</Card>
|
||||
<DocVisibility doc={doc} />
|
||||
{doc.abilities.manage_accesses && (
|
||||
{doc.abilities.accesses_manage && (
|
||||
<AddMembers
|
||||
doc={doc}
|
||||
currentRole={currentDocRole(doc.abilities)}
|
||||
@@ -123,8 +123,12 @@ export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
|
||||
)}
|
||||
</Box>
|
||||
<Box $minHeight="0">
|
||||
<InvitationList doc={doc} />
|
||||
<MemberList doc={doc} />
|
||||
{doc.abilities.accesses_view && (
|
||||
<>
|
||||
<InvitationList doc={doc} />
|
||||
<MemberList doc={doc} />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</SideModal>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './api';
|
||||
export * from './components';
|
||||
export * from './hooks';
|
||||
export * from './stores';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './useDocStore';
|
||||
+1
-3
@@ -4,9 +4,7 @@ import * as Y from 'yjs';
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { providerUrl } from '@/core';
|
||||
import { Base64, Doc } from '@/features/docs/doc-management';
|
||||
|
||||
import { blocksToYDoc } from '../utils';
|
||||
import { Base64, Doc, blocksToYDoc } from '@/features/docs/doc-management';
|
||||
|
||||
interface DocStore {
|
||||
provider: HocuspocusProvider;
|
||||
@@ -44,10 +44,11 @@ export interface Doc {
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
abilities: {
|
||||
accesses_manage: boolean;
|
||||
accesses_view: boolean;
|
||||
attachment_upload: true;
|
||||
destroy: boolean;
|
||||
link_configuration: boolean;
|
||||
manage_accesses: boolean;
|
||||
partial_update: boolean;
|
||||
retrieve: boolean;
|
||||
update: boolean;
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { Doc, Role } from './types';
|
||||
|
||||
export const currentDocRole = (abilities: Doc['abilities']): Role => {
|
||||
return abilities.destroy
|
||||
? Role.OWNER
|
||||
: abilities.manage_accesses
|
||||
: abilities.accesses_manage
|
||||
? Role.ADMIN
|
||||
: abilities.partial_update
|
||||
? Role.EDITOR
|
||||
: Role.READER;
|
||||
};
|
||||
|
||||
type BasicBlock = {
|
||||
type: string;
|
||||
content: string;
|
||||
};
|
||||
export const blocksToYDoc = (blocks: BasicBlock[], doc: Y.Doc) => {
|
||||
const xmlFragment = doc.getXmlFragment('document-store');
|
||||
|
||||
blocks.forEach((block) => {
|
||||
const xmlElement = new Y.XmlElement(block.type);
|
||||
if (block.content) {
|
||||
xmlElement.insert(0, [new Y.XmlText(block.content)]);
|
||||
}
|
||||
|
||||
xmlFragment.push([xmlElement]);
|
||||
});
|
||||
};
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, BoxButton, Text } from '@/components';
|
||||
import { HeadingBlock, useDocStore } from '@/features/docs/doc-editor';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
import { HeadingBlock } from '@/features/docs/doc-editor';
|
||||
import { Doc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { Heading } from './Heading';
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ import { useRouter } from 'next/navigation';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { toBase64, useDocStore } from '@/features/docs/doc-editor';
|
||||
import { Doc, useUpdateDoc } from '@/features/docs/doc-management';
|
||||
import { toBase64 } from '@/features/docs/doc-editor';
|
||||
import { Doc, useDocStore, useUpdateDoc } from '@/features/docs/doc-management';
|
||||
|
||||
import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
|
||||
import { Versions } from '../types';
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
SortModel,
|
||||
usePagination,
|
||||
} from '@openfun/cunningham-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { 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 $weight="bold" $theme="primary">
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{row.title}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
@@ -131,7 +131,9 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $weight="bold">{formatDate(row.created_at)}</Text>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{formatDate(row.created_at)}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
);
|
||||
},
|
||||
@@ -142,7 +144,9 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $weight="bold">{formatDate(row.updated_at)}</Text>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{formatDate(row.updated_at)}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
);
|
||||
},
|
||||
@@ -153,7 +157,7 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $weight="bold">
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{transRole(currentDocRole(row.abilities))}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
@@ -166,7 +170,9 @@ export const DocsGrid = () => {
|
||||
renderCell: ({ row }) => {
|
||||
return (
|
||||
<StyledLink href={`/docs/${row.id}`}>
|
||||
<Text $weight="bold">{row.accesses.length}</Text>
|
||||
<Text $theme="primary" $weight="bold">
|
||||
{row.accesses.length}
|
||||
</Text>
|
||||
</StyledLink>
|
||||
);
|
||||
},
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ export const InvitationItem = ({
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{doc.abilities.manage_accesses && (
|
||||
{doc.abilities.accesses_manage && (
|
||||
<Box $margin={isSmallMobile ? 'auto' : ''}>
|
||||
<Button
|
||||
color="tertiary-text"
|
||||
|
||||
+8
-1
@@ -5,11 +5,13 @@ import { User } from '@/core/auth';
|
||||
import {
|
||||
Access,
|
||||
Doc,
|
||||
KEY_DOC,
|
||||
KEY_LIST_DOC,
|
||||
Role,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { KEY_LIST_DOC_ACCESSES } from '@/features/docs/members/members-list';
|
||||
import { ContentLanguage } from '@/i18n/types';
|
||||
import { useBroadcastStore } from '@/stores';
|
||||
|
||||
import { OptionType } from '../types';
|
||||
|
||||
@@ -53,9 +55,11 @@ export const createDocAccess = async ({
|
||||
|
||||
export function useCreateDocAccess() {
|
||||
const queryClient = useQueryClient();
|
||||
const { broadcast } = useBroadcastStore();
|
||||
|
||||
return useMutation<Access, APIError, CreateDocAccessParams>({
|
||||
mutationFn: createDocAccess,
|
||||
onSuccess: () => {
|
||||
onSuccess: (_data, variable) => {
|
||||
void queryClient.resetQueries({
|
||||
queryKey: [KEY_LIST_DOC],
|
||||
});
|
||||
@@ -65,6 +69,9 @@ export function useCreateDocAccess() {
|
||||
void queryClient.resetQueries({
|
||||
queryKey: [KEY_LIST_DOC_ACCESSES],
|
||||
});
|
||||
|
||||
// Broadcast to every user connected to the document
|
||||
broadcast(`${KEY_DOC}-${variable.docId}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+3
-3
@@ -170,14 +170,14 @@ export const AddMembers = ({ currentRole, doc }: ModalAddMembersProps) => {
|
||||
doc={doc}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
selectedUsers={selectedUsers}
|
||||
disabled={isPending || !doc.abilities.manage_accesses}
|
||||
disabled={isPending || !doc.abilities.accesses_manage}
|
||||
/>
|
||||
</Box>
|
||||
<Box $css="flex: auto;">
|
||||
<ChooseRole
|
||||
key={resetKey}
|
||||
currentRole={currentRole}
|
||||
disabled={isPending || !doc.abilities.manage_accesses}
|
||||
disabled={isPending || !doc.abilities.accesses_manage}
|
||||
setRole={setSelectedRole}
|
||||
/>
|
||||
</Box>
|
||||
@@ -189,7 +189,7 @@ export const AddMembers = ({ currentRole, doc }: ModalAddMembersProps) => {
|
||||
!selectedUsers.length ||
|
||||
isPending ||
|
||||
!selectedRole ||
|
||||
!doc.abilities.manage_accesses
|
||||
!doc.abilities.accesses_manage
|
||||
}
|
||||
onClick={() => void handleValidate()}
|
||||
style={{ height: '100%', maxHeight: '55px' }}
|
||||
|
||||
+7
@@ -7,6 +7,7 @@ import {
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
import { KEY_DOC, KEY_LIST_DOC } from '@/features/docs/doc-management';
|
||||
import { KEY_LIST_USER } from '@/features/docs/members/members-add';
|
||||
import { useBroadcastStore } from '@/stores';
|
||||
|
||||
import { KEY_LIST_DOC_ACCESSES } from './useDocAccesses';
|
||||
|
||||
@@ -39,6 +40,8 @@ type UseDeleteDocAccessOptions = UseMutationOptions<
|
||||
|
||||
export const useDeleteDocAccess = (options?: UseDeleteDocAccessOptions) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { broadcast } = useBroadcastStore();
|
||||
|
||||
return useMutation<void, APIError, DeleteDocAccessProps>({
|
||||
mutationFn: deleteDocAccess,
|
||||
...options,
|
||||
@@ -49,6 +52,10 @@ export const useDeleteDocAccess = (options?: UseDeleteDocAccessOptions) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_DOC],
|
||||
});
|
||||
|
||||
// Broadcast to every user connected to the document
|
||||
broadcast(`${KEY_DOC}-${variables.docId}`);
|
||||
|
||||
void queryClient.resetQueries({
|
||||
queryKey: [KEY_LIST_DOC],
|
||||
});
|
||||
|
||||
+7
@@ -11,6 +11,7 @@ import {
|
||||
KEY_LIST_DOC,
|
||||
Role,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { useBroadcastStore } from '@/stores';
|
||||
|
||||
import { KEY_LIST_DOC_ACCESSES } from './useDocAccesses';
|
||||
|
||||
@@ -49,6 +50,8 @@ type UseUpdateDocAccessOptions = UseMutationOptions<
|
||||
|
||||
export const useUpdateDocAccess = (options?: UseUpdateDocAccessOptions) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { broadcast } = useBroadcastStore();
|
||||
|
||||
return useMutation<Access, APIError, UpdateDocAccessProps>({
|
||||
mutationFn: updateDocAccess,
|
||||
...options,
|
||||
@@ -59,6 +62,10 @@ export const useUpdateDocAccess = (options?: UseUpdateDocAccessOptions) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_DOC],
|
||||
});
|
||||
|
||||
// Broadcast to every user connected to the document
|
||||
broadcast(`${KEY_DOC}-${variables.docId}`);
|
||||
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_LIST_DOC],
|
||||
});
|
||||
|
||||
+3
-3
@@ -61,7 +61,7 @@ export const MemberItem = ({
|
||||
});
|
||||
|
||||
const isNotAllowed =
|
||||
isOtherOwner || isLastOwner || !doc.abilities.manage_accesses;
|
||||
isOtherOwner || isLastOwner || !doc.abilities.accesses_manage;
|
||||
|
||||
if (!access.user) {
|
||||
return (
|
||||
@@ -112,7 +112,7 @@ export const MemberItem = ({
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{doc.abilities.manage_accesses && (
|
||||
{doc.abilities.accesses_manage && (
|
||||
<Box $margin={isSmallMobile ? 'auto' : ''}>
|
||||
<Button
|
||||
color="tertiary-text"
|
||||
@@ -136,7 +136,7 @@ export const MemberItem = ({
|
||||
<TextErrors causes={errorUpdate?.cause || errorDelete?.cause} />
|
||||
</Box>
|
||||
)}
|
||||
{(isLastOwner || isOtherOwner) && doc.abilities.manage_accesses && (
|
||||
{(isLastOwner || isOtherOwner) && doc.abilities.accesses_manage && (
|
||||
<Box $margin={{ top: 'tiny' }}>
|
||||
<Alert
|
||||
canClose={false}
|
||||
|
||||
@@ -194,16 +194,17 @@ export class ApiPlugin implements WorkboxPlugin {
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
abilities: {
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
attachment_upload: true,
|
||||
destroy: true,
|
||||
link_configuration: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
update: true,
|
||||
versions_destroy: true,
|
||||
versions_list: true,
|
||||
versions_retrieve: true,
|
||||
manage_accesses: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
attachment_upload: true,
|
||||
},
|
||||
accesses: [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export const LANGUAGES_ALLOWED: { [key: string]: string } = {
|
||||
en: 'English',
|
||||
fr: 'Français',
|
||||
de: 'Deutsch',
|
||||
};
|
||||
export const LANGUAGE_COOKIE_NAME = 'docs_language';
|
||||
export const BASE_LANGUAGE = 'en';
|
||||
|
||||
@@ -1,4 +1,118 @@
|
||||
{
|
||||
"de": {
|
||||
"translation": {
|
||||
"\"{{email}}\" is already invited to the document.": "\"{{email}}\" ist bereits zum Dokument eingeladen.",
|
||||
"Accessibility": "Barrierefreiheit",
|
||||
"Accessibility statement": "Erklärung zur Barrierefreiheit",
|
||||
"Address:": "Anschrift:",
|
||||
"Administrator": "Administrator",
|
||||
"Anyone on the internet with the link can view": "Für jeden im Internet mit diesem Link sichtbar",
|
||||
"Are you sure you want to delete the document \"{{title}}\"?": "Sind Sie sicher, dass Sie das Dokument \"{{title}}\" löschen möchten?",
|
||||
"Back to home page": "Zurück zur Startseite",
|
||||
"Back to top": "Zurück nach oben",
|
||||
"Can't load this page, please check your internet connection.": "Diese Seite kann nicht geladen werden. Bitte überprüfen Sie Ihre Internetverbindung.",
|
||||
"Cancel": "Abbrechen",
|
||||
"Choose a role": "Wählen Sie eine Rolle",
|
||||
"Close the modal": "Pop up schliessen",
|
||||
"Close the panel": "Fenster schließen",
|
||||
"Compliance status": "Konformitätsstatus",
|
||||
"Confirm deletion": "Löschung bestätigen",
|
||||
"Content modal to delete document": "Inhalts-Modal zum Löschen des Dokuments",
|
||||
"Content modal to export the document": "Inhalte zum Exportieren des Dokuments",
|
||||
"Copy link": "Link kopieren",
|
||||
"Create a new document": "Neues Dokument erstellen",
|
||||
"Created at": "Erstellt am",
|
||||
"Current version": "Aktuelle Version",
|
||||
"Delete document": "Dokument löschen",
|
||||
"Delete the document": "Dokument löschen",
|
||||
"Deleting the document \"{{title}}\"": "Lösche das Dokument \"{{title}}\"",
|
||||
"Doc visibility card": "Dokumenten-Sichtbarkeitskarte",
|
||||
"Docs": "Docs",
|
||||
"Docs: Your new companion to collaborate on documents efficiently, intuitively, and securely.": "Pages: Ihr neuer Begleiter für eine effiziente, intuitive und sichere Zusammenarbeit bei Dokumenten.",
|
||||
"Document icon": "Dokumentensymbol",
|
||||
"Document name": "Dokumentenname",
|
||||
"Document panel": "Dokumenten-Panel",
|
||||
"Document title updated successfully": "Titel des Dokuments erfolgreich aktualisiert",
|
||||
"Documents": "Dokumente",
|
||||
"Docx": "Docx",
|
||||
"Download": "Herunterladen",
|
||||
"E-mail:": "E-Mail:",
|
||||
"Editor": "Editor",
|
||||
"Export": "Exportieren",
|
||||
"Export your document, it will be inserted in the selected template.": "Exportieren Sie Ihr Dokument, es wird in die gewählte Vorlage eingefügt.",
|
||||
"Failed to add the member in the document.": "Fehler beim Hinzufügen des Mitglieds zum Dokument.",
|
||||
"Failed to copy link": "Link konnte nicht kopiert werden",
|
||||
"Failed to create the invitation for {{email}}.": "Fehler beim Erstellen der Einladung für {{email}}.",
|
||||
"Find a member to add to the document": "Suchen Sie ein Mitglied, das dem Dokument hinzugefügt werden soll",
|
||||
"Go to bottom": "Gehe nach unten",
|
||||
"If a member is editing, his works can be lost.": "Wenn ein Mitglied editiert, können seine Änderungen verloren gehen.",
|
||||
"Improvement and contact": "Verbesserungen und Kontakt",
|
||||
"Invitation sent to {{email}}.": "Einladung an {{email}} gesendet.",
|
||||
"Invite new members to {{title}}": "Neue Mitglieder zu {{title}} einladen",
|
||||
"Invited": "Eingeladen",
|
||||
"It is the card information about the document.": "Es handelt sich um die Karteninformationen zum Dokument.",
|
||||
"It seems that the page you are looking for does not exist or cannot be displayed correctly.": "Es scheint, dass die von Ihnen gesuchte Seite nicht existiert oder nicht korrekt angezeigt werden kann.",
|
||||
"Language": "Sprache",
|
||||
"Legal Notice": "Impressum",
|
||||
"Legal notice": "Impressum",
|
||||
"Link Copied !": "Link kopiert!",
|
||||
"Login": "Anmelden",
|
||||
"Logout": "Abmelden",
|
||||
"Members": "Mitglieder",
|
||||
"No editor found": "Kein Editor gefunden",
|
||||
"Offline ?!": "Offline?!",
|
||||
"Only for people with access": "Nur für Personen mit Zugriff",
|
||||
"Open the document options": "Öffnen Sie die Dokumentoptionen",
|
||||
"Open the panel": "Panel öffnen",
|
||||
"Open the version options": "Öffnen Sie die Versionsoptionen",
|
||||
"Ouch !": "Autsch!",
|
||||
"Owner": "Besitzer",
|
||||
"Owners:": "Besitzer:",
|
||||
"PDF": "PDF",
|
||||
"Personal data and cookies": "Personenbezogene Daten und Cookies",
|
||||
"Public": "Öffentlich",
|
||||
"Read only, you cannot edit document versions.": "Nur lesen: Sie können Dokumentenversionen nicht bearbeiten.",
|
||||
"Read only, you cannot edit this document.": "Nur lesen: Sie können dieses Dokument nicht bearbeiten.",
|
||||
"Reader": "Leser",
|
||||
"Rename": "Umbenennen",
|
||||
"Restore": "Wiederherstellen",
|
||||
"Restore the version": "Version wiederherstellen",
|
||||
"Restore this version": "Version wiederherstellen",
|
||||
"Restore this version?": "Diese Version wiederherstellen?",
|
||||
"Role": "Rolle",
|
||||
"Search by email": "Nach E-Mail suchen",
|
||||
"Share": "Teilen",
|
||||
"Share modal": "Teilen-Modal",
|
||||
"Something bad happens, please retry.": "Etwas ist schiefgelaufen, bitte versuchen Sie es erneut.",
|
||||
"Table of content": "Inhaltsverzeichnis",
|
||||
"Table of contents": "Inhaltsverzeichnis",
|
||||
"Template": "Vorlage",
|
||||
"The document has been deleted.": "Das Dokument wurde gelöscht.",
|
||||
"The invitation has been removed.": "Die Einladung wurde zurückgenommen.",
|
||||
"The member has been removed from the document": "Das Mitglied wurde aus dem Dokument entfernt",
|
||||
"The role has been updated": "Die Rolle wurde aktualisiert",
|
||||
"The role has been updated.": "Die Rolle wurde aktualisiert.",
|
||||
"This accessibility statement applies to the site hosted on": "Diese Erklärung zur Barrierefreiheit gilt für die gehostete Seite",
|
||||
"This site does not display a cookie consent banner, why?": "",
|
||||
"Unless otherwise stated, all content on this site is under": "Sofern nicht anders angegeben, steht der gesamte Inhalt dieser Website unter",
|
||||
"Untitled document": "Unbenanntes Dokument",
|
||||
"Updated at": "Aktualisiert am",
|
||||
"User {{email}} added to the document.": "Benutzer {{email}} wurde dem Dokument hinzugefügt.",
|
||||
"Validate": "Bestätigen",
|
||||
"Version history": "Versionsverlauf",
|
||||
"Version restored successfully": "Version erfolgreich wiederhergestellt",
|
||||
"Versions": "Versionen",
|
||||
"We didn't find a mail matching, try to be more accurate": "Wir haben keine übereinstimmende E-Mail gefunden, versuchen Sie genauer zu sein",
|
||||
"We try to respond within 2 working days.": "Wir versuchen, innerhalb von 2 Arbeitstagen zu antworten.",
|
||||
"You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "Sie sind der einzige Besitzer dieser Gruppe. Machen Sie ein anderes Mitglied zum Gruppenbesitzer, bevor Sie Ihre eigene Rolle ändern oder aus Ihrem Dokument entfernen können.",
|
||||
"You cannot update the role or remove other owner.": "Sie können die Rolle nicht aktualisieren oder einen anderen Besitzer entfernen.",
|
||||
"You don't have any document yet.": "Sie haben noch kein Dokument.",
|
||||
"Your current document will revert to this version.": "Ihr aktuelles Dokument wird auf diese Version zurückgesetzt.",
|
||||
"Your role": "Ihre Rolle",
|
||||
"Your role:": "Ihre Rolle:",
|
||||
"Your {{format}} was downloaded succesfully": "Ihr {{format}} wurde erfolgreich heruntergeladen"
|
||||
}
|
||||
},
|
||||
"en": { "translation": {} },
|
||||
"fr": {
|
||||
"translation": {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,90 @@
|
||||
.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,4 +1,5 @@
|
||||
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';
|
||||
@@ -7,9 +8,10 @@ import { useEffect, useState } from 'react';
|
||||
import { Box, Text } from '@/components';
|
||||
import { TextErrors } from '@/components/TextErrors';
|
||||
import { useAuthStore } from '@/core/auth';
|
||||
import { DocEditor, useDocStore } from '@/features/docs';
|
||||
import { useDoc } from '@/features/docs/doc-management';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { DocEditor } from '@/features/docs/doc-editor';
|
||||
import { KEY_DOC, useDoc, useDocStore } from '@/features/docs/doc-management';
|
||||
import { DocsLayout } from '@/layouts/docs/DocsLayout';
|
||||
import { useBroadcastStore } from '@/stores';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
export function DocLayout() {
|
||||
@@ -26,9 +28,11 @@ export function DocLayout() {
|
||||
<Head>
|
||||
<meta name="robots" content="noindex" />
|
||||
</Head>
|
||||
<MainLayout withoutFooter>
|
||||
<DocPage id={id} />
|
||||
</MainLayout>
|
||||
<DocsLayout>
|
||||
<Box $width="100%">
|
||||
<DocPage id={id} />
|
||||
</Box>
|
||||
</DocsLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -41,9 +45,11 @@ const DocPage = ({ id }: DocProps) => {
|
||||
const { login } = useAuthStore();
|
||||
const { data: docQuery, isError, error } = useDoc({ id });
|
||||
const [doc, setDoc] = useState(docQuery);
|
||||
const { setCurrentDoc } = useDocStore();
|
||||
|
||||
const { setCurrentDoc, createProvider, docsStore } = useDocStore();
|
||||
const { setBroadcastProvider, addTask } = useBroadcastStore();
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const provider = docsStore?.[id]?.provider;
|
||||
|
||||
useEffect(() => {
|
||||
if (doc?.title) {
|
||||
@@ -66,6 +72,35 @@ const DocPage = ({ id }: DocProps) => {
|
||||
};
|
||||
}, [docQuery, setCurrentDoc]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!doc?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newProvider = provider;
|
||||
if (!provider || provider.document.guid !== doc.id) {
|
||||
newProvider = createProvider(doc.id, doc.content);
|
||||
}
|
||||
|
||||
setBroadcastProvider(newProvider);
|
||||
}, [createProvider, doc, provider, setBroadcastProvider]);
|
||||
|
||||
/**
|
||||
* We add a broadcast task to reset the query cache
|
||||
* when the document visibility changes.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!doc?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
addTask(`${KEY_DOC}-${doc.id}`, () => {
|
||||
void queryClient.resetQueries({
|
||||
queryKey: [KEY_DOC, { id: doc.id }],
|
||||
});
|
||||
});
|
||||
}, [addTask, doc?.id, queryClient]);
|
||||
|
||||
if (isError && error) {
|
||||
if (error.status === 404) {
|
||||
navigate.replace(`/404`);
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
import { DocsGridContainer } from '@/features/docs/docs-grid';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { Box } from '@/components';
|
||||
import { DocsGrid } from '@/features/docs/docs-grid/components/DocsGrid';
|
||||
import { DocsLayout } from '@/layouts/docs/DocsLayout';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
return <DocsGridContainer />;
|
||||
return (
|
||||
<Box $overflow="auto" $width="100%">
|
||||
<DocsGrid />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <MainLayout>{page}</MainLayout>;
|
||||
return <DocsLayout>{page}</DocsLayout>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './useBroadcastStore';
|
||||
export * from './useResponsiveStore';
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||
import * as Y from 'yjs';
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface BroadcastState {
|
||||
addTask: (taskLabel: string, action: () => void) => void;
|
||||
broadcast: (taskLabel: string) => void;
|
||||
getBroadcastProvider: () => HocuspocusProvider | undefined;
|
||||
provider?: HocuspocusProvider;
|
||||
setBroadcastProvider: (provider: HocuspocusProvider) => void;
|
||||
tasks: { [taskLabel: string]: Y.Array<string> };
|
||||
}
|
||||
|
||||
export const useBroadcastStore = create<BroadcastState>((set, get) => ({
|
||||
provider: undefined,
|
||||
tasks: {},
|
||||
setBroadcastProvider: (provider) => set({ provider }),
|
||||
getBroadcastProvider: () => {
|
||||
const provider = get().provider;
|
||||
if (!provider) {
|
||||
console.warn('Provider is not defined');
|
||||
return;
|
||||
}
|
||||
|
||||
return provider;
|
||||
},
|
||||
addTask: (taskLabel, action) => {
|
||||
const taskExistAlready = get().tasks[taskLabel];
|
||||
const provider = get().getBroadcastProvider();
|
||||
if (taskExistAlready || !provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const task = provider.document.getArray<string>(taskLabel);
|
||||
task.observe(() => {
|
||||
action();
|
||||
});
|
||||
|
||||
set((state) => ({
|
||||
tasks: {
|
||||
...state.tasks,
|
||||
[taskLabel]: task,
|
||||
},
|
||||
}));
|
||||
},
|
||||
broadcast: (taskLabel) => {
|
||||
const task = get().tasks[taskLabel];
|
||||
if (!task) {
|
||||
console.warn(`Task ${taskLabel} is not defined`);
|
||||
return;
|
||||
}
|
||||
task.push([`broadcast: ${taskLabel}`]);
|
||||
},
|
||||
}));
|
||||
@@ -4,41 +4,74 @@ 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) => ({
|
||||
export const useResponsiveStore = create<UseResponsiveStore>((set, get) => ({
|
||||
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({ screenSize: 'mobile', isMobile: true, isSmallMobile: false });
|
||||
set({
|
||||
isResponsive: true,
|
||||
screenSize: 'mobile',
|
||||
isTablet: false,
|
||||
isMobile: true,
|
||||
isSmallMobile: false,
|
||||
});
|
||||
} else if (width >= 768 && width < 1024) {
|
||||
set({ screenSize: 'tablet', isMobile: false, isSmallMobile: false });
|
||||
set({
|
||||
isResponsive: true,
|
||||
screenSize: 'tablet',
|
||||
isTablet: true,
|
||||
isMobile: false,
|
||||
isSmallMobile: false,
|
||||
});
|
||||
} else {
|
||||
set({ screenSize: 'desktop', isMobile: false, isSmallMobile: false });
|
||||
set({
|
||||
isResponsive: false,
|
||||
screenSize: 'desktop',
|
||||
isTablet: false,
|
||||
isMobile: false,
|
||||
isSmallMobile: false,
|
||||
});
|
||||
}
|
||||
|
||||
set({ screenWidth: width });
|
||||
|
||||
Reference in New Issue
Block a user