Compare commits

..

4 Commits

Author SHA1 Message Date
Nathan Panchout 225366e92d (e2e) update test suite to use '/docs/' route
- Modify all test files to navigate to '/docs/' instead of '/'
- Add explicit login button click for authentication tests
- Update routing and navigation expectations in test specs
- Introduce new home page test spec
- Ensure consistent authentication flow across test scenarios
2025-01-29 17:21:02 +01:00
Nathan Panchout 2d0a50984b (frontend) implement home page and authentication flow
- Add home page components with responsive design
- Update authentication logic to handle direct login and redirects
- Modify header, layout, and routing to support new home page
- Introduce ProConnectButton for simplified login experience
- Refactor auth store and login mechanisms
2025-01-29 17:19:21 +01:00
Nathan Panchout 7bb6924292 (frontend) add new color and font size tokens for Cunningham
- Add 'primary-action' color token
- Introduce alternative font size tokens (xl-alt, lg-alt, md-alt,
sm-alt, xs-alt)
- Update button border colors to use greyscale-300
2025-01-29 17:19:21 +01:00
Nathan Panchout 3f420fcffc (frontend) add home page assets and update changelog
Add various assets for the home page feature, including logos,
screenshots, and banner images. Update CHANGELOG.md to reflect the new
home page feature.
2025-01-29 17:19:21 +01:00
86 changed files with 3121 additions and 3305 deletions
+4 -14
View File
@@ -9,13 +9,6 @@ and this project adheres to
## [Unreleased]
## Added
- 📝(doc) Add security.md and codeofconduct.md #604
- ✨(frontend) add Alert, Quote, and Divider blocks to the editor #566
## [2.1.0] - 2025-01-29
## Added
- ✨(backend) add soft delete and restore API endpoints to documents #516
@@ -30,18 +23,15 @@ and this project adheres to
- 💄(frontend) add abilities on doc row #581
- 💄(frontend) improve DocsGridItem responsive padding #582
- 🔧(backend) Bump maximum page size to 200 #516
- 📝(doc) Improve Read me #558
## Fixed
## Changed
- 🐛Fix invitations #575
- 🔧(backend) Bump page size to 200 #516
## Removed
- 🔥(backend) remove "content" field from list serializer # 516
## [2.0.1] - 2025-01-17
## Fixed
@@ -61,6 +51,7 @@ and this project adheres to
- ✨(frontend) add favorite feature #515
- 📝(documentation) Documentation about self-hosted installation #530
- ✨(helm) helm versioning #530
- ✨(frontend) add home page #553
## Changed
@@ -396,8 +387,7 @@ and this project adheres to
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/numerique-gouv/impress/compare/v2.1.0...main
[v2.1.0]: https://github.com/numerique-gouv/impress/releases/v2.1.0
[unreleased]: https://github.com/numerique-gouv/impress/compare/v2.0.1...main
[v2.0.1]: https://github.com/numerique-gouv/impress/releases/v2.0.1
[v2.0.0]: https://github.com/numerique-gouv/impress/releases/v2.0.0
[v1.10.0]: https://github.com/numerique-gouv/impress/releases/v1.10.0
-75
View File
@@ -1,75 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
## Scope
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at docs@numerique.gouv.fr.
- All complaints will be reviewed and investigated promptly and fairly.
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this
## Code of Conduct:
1. Correction
Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
2. Warning
Community Impact: A violation through a single incident or series of actions.
Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
3. Temporary Ban
Community Impact: A serious violation of community standards, including sustained inappropriate behavior.
Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
4. Permanent Ban
Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
Consequence: A permanent ban from any sort of public interaction within the community.
Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.
For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
+7 -8
View File
@@ -13,10 +13,8 @@ Welcome to Docs! The open source document editor where your notes can become kno
Chat on Matrix
</a> - <a href="/docs/">
Documentation
</a> - <a href="#getting-started-">
</a> - <a href="#getting-started">
Getting started
</a> - <a href="mailto:docs@numerique.gouv.fr">
Reach out
</a>
</p>
@@ -62,7 +60,7 @@ $ docker compose -v
docker compose version 1.27.4, build 40524192
```
> ⚠️ You may need to run the following commands with sudo but this can be avoided by adding your user to the `docker` group.
> ⚠️ You may need to run the following commands with sudo but this can be avoided by assigning your user to the `docker` group.
**Project bootstrap**
The easiest way to start working on the project is to use GNU Make:
@@ -73,7 +71,7 @@ $ make bootstrap FLUSH_ARGS='--no-input'
This command builds the `app` container, installs dependencies, performs database migrations and compile translations. It's a good idea to use this
command each time you are pulling code from the project repository to avoid dependency-related or migration-related issues.
command each time you are pulling code from the project repository to avoid dependency-releated or migration-releated issues.
Your Docker services should now be up and running 🎉
@@ -81,8 +79,9 @@ You can access to the project by going to <http://localhost:3000>.
You will be prompted to log in, the default credentials are:
```
```shellscript
username: impress
password: impress
```
@@ -173,11 +172,11 @@ docs
Docs is built on top of [Django Rest Framework](https://www.django-rest-framework.org/), [Next.js](https://nextjs.org/), [MinIO](https://min.io/), [BlockNote.js](https://www.blocknotejs.org/), [HocusPocus](https://tiptap.dev/docs/hocuspocus/introduction) and [Yjs](https://yjs.dev/)
### Gov ❤️ open source
Docs is the result of a joint effort led by the French 🇫🇷🥖 ([DINUM](https://www.numerique.gouv.fr/dinum/)) and German 🇩🇪🥨 governments ([ZenDiS](https://zendis.de/)).
Docs is the result of a joint effort lead by the French 🇫🇷🥖 ([DINUM](https://www.numerique.gouv.fr/dinum/)) and German 🇩🇪🥨 governments ([ZenDiS](https://zendis.de/)).
We are proud sponsors of [BlockNotejs](https://www.blocknotejs.org/) and [Yjs](https://yjs.dev/).
We are always looking for new public partners (we are currently onboarding the Netherlands 🇳🇱🧀), feel free to [reach out](mailto:docs@numerique.gouv.fr) if you are interested in using or contributing to Docs.
We are always looking for new public partners (we are currently onboarding the Netherlands 🇳🇱🧀), feel free to [reach out](https://matrix.to/#/#docs-official:matrix.org) if you are interested in using or contributing to Docs.
<p align="center">
<img src="/docs/assets/europe_opensource.png" width="50%"/>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

-23
View File
@@ -1,23 +0,0 @@
# Security Policy
## Reporting a Vulnerability
Security is very important to us.
If you have any issue regarding security, please disclose the information responsibly submiting [this form](https://vdp.numerique.gouv.fr/p/Send-a-report?lang=en) and not by creating an issue on the repository. You can also email us at docs@numerique.gouv.fr
We appreciate your effort to make Docs more secure.
## Vulnerability disclosure policy
Working with security issues in an open source project can be challenging, as we are required to disclose potential problems that could be exploited by attackers. With this in mind, our security fix policy is as follows:
1. The Maintainers team will handle the fix as usual (Pull Request,
release).
2. In the release notes, we will include the identification numbers from the
GitHub Advisory Database (GHSA) and, if applicable, the Common Vulnerabilities
and Exposures (CVE) identifier for the vulnerability.
3. Once this grace period has passed, we will publish the vulnerability.
By adhering to this security policy, we aim to address security concerns
effectively and responsibly in our open source software project.
+1 -6
View File
@@ -14,15 +14,10 @@
"groupName": "ignored js dependencies",
"matchManagers": ["npm"],
"matchPackageNames": [
"@openfun/cunningham-react",
"@types/react",
"@types/react-dom",
"eslint",
"fetch-mock",
"node",
"node-fetch",
"react",
"react-dom",
"eslint",
"workbox-webpack-plugin"
]
}
+79 -120
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-29 13:43+0000\n"
"PO-Revision-Date: 2025-01-30 10:24\n"
"POT-Creation-Date: 2025-01-15 21:00+0000\n"
"PO-Revision-Date: 2025-01-27 09:27\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -17,23 +17,18 @@ msgstr ""
"X-Crowdin-File: backend-impress.pot\n"
"X-Crowdin-File-ID: 18\n"
#: build/lib/core/admin.py:37 core/admin.py:37
#: build/lib/core/admin.py:33 core/admin.py:33
msgid "Personal info"
msgstr "Persönliche Daten"
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
#: core/admin.py:138
#: build/lib/core/admin.py:46 core/admin.py:46
msgid "Permissions"
msgstr "Berechtigungen"
#: build/lib/core/admin.py:62 core/admin.py:62
#: build/lib/core/admin.py:58 core/admin.py:58
msgid "Important dates"
msgstr "Wichtige Daten"
#: build/lib/core/admin.py:148 core/admin.py:148
msgid "Tree structure"
msgstr ""
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
msgid "Creator is me"
msgstr "Ersteller bin ich"
@@ -46,23 +41,23 @@ msgstr "Favorit"
msgid "Title"
msgstr "Titel"
#: build/lib/core/api/serializers.py:346 core/api/serializers.py:346
#: build/lib/core/api/serializers.py:317 core/api/serializers.py:317
msgid "A new document was created on your behalf!"
msgstr "Ein neues Dokument wurde in Ihrem Namen erstellt!"
#: build/lib/core/api/serializers.py:350 core/api/serializers.py:350
#: build/lib/core/api/serializers.py:321 core/api/serializers.py:321
msgid "You have been granted ownership of a new document:"
msgstr "Sie sind Besitzer eines neuen Dokuments:"
#: build/lib/core/api/serializers.py:453 core/api/serializers.py:453
#: build/lib/core/api/serializers.py:422 core/api/serializers.py:422
msgid "Body"
msgstr "Inhalt"
#: build/lib/core/api/serializers.py:456 core/api/serializers.py:456
#: build/lib/core/api/serializers.py:425 core/api/serializers.py:425
msgid "Body type"
msgstr "Typ"
#: build/lib/core/api/serializers.py:462 core/api/serializers.py:462
#: build/lib/core/api/serializers.py:431 core/api/serializers.py:431
msgid "Format"
msgstr ""
@@ -76,306 +71,270 @@ msgstr "Ungültiges Antwortformat oder Token-Verifizierung fehlgeschlagen"
msgid "User account is disabled"
msgstr "Benutzerkonto ist deaktiviert"
#: build/lib/core/enums.py:19 core/enums.py:19
msgid "First child"
msgstr ""
#: build/lib/core/enums.py:20 core/enums.py:20
msgid "Last child"
msgstr ""
#: build/lib/core/enums.py:21 core/enums.py:21
msgid "First sibling"
msgstr ""
#: build/lib/core/enums.py:22 core/enums.py:22
msgid "Last sibling"
msgstr ""
#: build/lib/core/enums.py:23 core/enums.py:23
msgid "Left"
msgstr ""
#: build/lib/core/enums.py:24 core/enums.py:24
msgid "Right"
msgstr ""
#: build/lib/core/models.py:54 build/lib/core/models.py:61 core/models.py:54
#: core/models.py:61
#: build/lib/core/models.py:63 build/lib/core/models.py:70 core/models.py:63
#: core/models.py:70
msgid "Reader"
msgstr "Lesen"
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
#: core/models.py:62
#: build/lib/core/models.py:64 build/lib/core/models.py:71 core/models.py:64
#: core/models.py:71
msgid "Editor"
msgstr "Bearbeiten"
#: build/lib/core/models.py:63 core/models.py:63
#: build/lib/core/models.py:72 core/models.py:72
msgid "Administrator"
msgstr ""
#: build/lib/core/models.py:64 core/models.py:64
#: build/lib/core/models.py:73 core/models.py:73
msgid "Owner"
msgstr "Besitzer"
#: build/lib/core/models.py:75 core/models.py:75
#: build/lib/core/models.py:84 core/models.py:84
msgid "Restricted"
msgstr "Beschränkt"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:88 core/models.py:88
msgid "Authenticated"
msgstr "Authentifiziert"
#: build/lib/core/models.py:81 core/models.py:81
#: build/lib/core/models.py:90 core/models.py:90
msgid "Public"
msgstr "Öffentlich"
#: build/lib/core/models.py:103 core/models.py:103
#: build/lib/core/models.py:112 core/models.py:112
msgid "id"
msgstr ""
#: build/lib/core/models.py:104 core/models.py:104
#: build/lib/core/models.py:113 core/models.py:113
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:110 core/models.py:110
#: build/lib/core/models.py:119 core/models.py:119
msgid "created on"
msgstr "Erstellt"
#: build/lib/core/models.py:111 core/models.py:111
#: build/lib/core/models.py:120 core/models.py:120
msgid "date and time at which a record was created"
msgstr "Datum und Uhrzeit, an dem ein Datensatz erstellt wurde"
#: build/lib/core/models.py:116 core/models.py:116
#: build/lib/core/models.py:125 core/models.py:125
msgid "updated on"
msgstr "Aktualisiert"
#: build/lib/core/models.py:117 core/models.py:117
#: build/lib/core/models.py:126 core/models.py:126
msgid "date and time at which a record was last updated"
msgstr "Datum und Uhrzeit, an dem zuletzt aktualisiert wurde"
#: build/lib/core/models.py:153 core/models.py:153
#: build/lib/core/models.py:162 core/models.py:162
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:166 core/models.py:166
#: build/lib/core/models.py:175 core/models.py:175
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr "Geben Sie eine gültige Unterseite ein. Dieser Wert darf nur Buchstaben, Zahlen und die @/./+/-/_/: Zeichen enthalten."
#: build/lib/core/models.py:172 core/models.py:172
#: build/lib/core/models.py:181 core/models.py:181
msgid "sub"
msgstr "unter"
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:183 core/models.py:183
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr "Erforderlich. 255 Zeichen oder weniger. Buchstaben, Zahlen und die Zeichen @/./+/-/_/:"
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:192 core/models.py:192
msgid "full name"
msgstr "Name"
#: build/lib/core/models.py:184 core/models.py:184
#: build/lib/core/models.py:193 core/models.py:193
msgid "short name"
msgstr "Kurzbezeichnung"
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:195 core/models.py:195
msgid "identity email address"
msgstr "Identitäts-E-Mail-Adresse"
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:200 core/models.py:200
msgid "admin email address"
msgstr "Admin E-Mail-Adresse"
#: build/lib/core/models.py:198 core/models.py:198
#: build/lib/core/models.py:207 core/models.py:207
msgid "language"
msgstr "Sprache"
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:208 core/models.py:208
msgid "The language in which the user wants to see the interface."
msgstr "Die Sprache, in der der Benutzer die Benutzeroberfläche sehen möchte."
#: build/lib/core/models.py:205 core/models.py:205
#: build/lib/core/models.py:214 core/models.py:214
msgid "The timezone in which the user wants to see times."
msgstr "Die Zeitzone, in der der Nutzer Zeiten sehen möchte."
#: build/lib/core/models.py:208 core/models.py:208
#: build/lib/core/models.py:217 core/models.py:217
msgid "device"
msgstr "Gerät"
#: build/lib/core/models.py:210 core/models.py:210
#: build/lib/core/models.py:219 core/models.py:219
msgid "Whether the user is a device or a real user."
msgstr "Ob der Benutzer ein Gerät oder ein echter Benutzer ist."
#: build/lib/core/models.py:213 core/models.py:213
#: build/lib/core/models.py:222 core/models.py:222
msgid "staff status"
msgstr "Status des Teammitgliedes"
#: build/lib/core/models.py:215 core/models.py:215
#: build/lib/core/models.py:224 core/models.py:224
msgid "Whether the user can log into this admin site."
msgstr "Gibt an, ob der Benutzer sich in diese Admin-Seite einloggen kann."
#: build/lib/core/models.py:218 core/models.py:218
#: build/lib/core/models.py:227 core/models.py:227
msgid "active"
msgstr "aktiviert"
#: build/lib/core/models.py:221 core/models.py:221
#: build/lib/core/models.py:230 core/models.py:230
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr "Ob dieser Benutzer als aktiviert behandelt werden soll. Deaktivieren Sie diese Option, anstatt Konten zu löschen."
#: build/lib/core/models.py:233 core/models.py:233
#: build/lib/core/models.py:242 core/models.py:242
msgid "user"
msgstr "Benutzer"
#: build/lib/core/models.py:234 core/models.py:234
#: build/lib/core/models.py:243 core/models.py:243
msgid "users"
msgstr "Benutzer"
#: build/lib/core/models.py:373 build/lib/core/models.py:925 core/models.py:373
#: core/models.py:925
#: build/lib/core/models.py:382 build/lib/core/models.py:758 core/models.py:382
#: core/models.py:758
msgid "title"
msgstr "Titel"
#: build/lib/core/models.py:374 core/models.py:374
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:405 core/models.py:405
#: build/lib/core/models.py:404 core/models.py:404
msgid "Document"
msgstr "Dokument"
#: build/lib/core/models.py:406 core/models.py:406
#: build/lib/core/models.py:405 core/models.py:405
msgid "Documents"
msgstr "Dokumente"
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:408 core/models.py:408
msgid "Untitled Document"
msgstr "Unbenanntes Dokument"
#: build/lib/core/models.py:719 core/models.py:719
#: build/lib/core/models.py:633 core/models.py:633
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} hat ein Dokument mit Ihnen geteilt!"
#: build/lib/core/models.py:723 core/models.py:723
#: build/lib/core/models.py:637 core/models.py:637
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} hat Sie mit der Rolle \"{role}\" zu folgendem Dokument eingeladen:"
#: build/lib/core/models.py:726 core/models.py:726
#: build/lib/core/models.py:640 core/models.py:640
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} hat ein Dokument mit Ihnen geteilt: {title}"
#: build/lib/core/models.py:762 core/models.py:762
msgid "This document is not deleted."
msgstr ""
#: build/lib/core/models.py:769 core/models.py:769
msgid "This document was permanently deleted and cannot be restored."
msgstr ""
#: build/lib/core/models.py:820 core/models.py:820
#: build/lib/core/models.py:663 core/models.py:663
msgid "Document/user link trace"
msgstr "Dokument/Benutzer Linkverfolgung"
#: build/lib/core/models.py:821 core/models.py:821
#: build/lib/core/models.py:664 core/models.py:664
msgid "Document/user link traces"
msgstr "Dokument/Benutzer Linkverfolgung"
#: build/lib/core/models.py:827 core/models.py:827
#: build/lib/core/models.py:670 core/models.py:670
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:850 core/models.py:850
#: build/lib/core/models.py:693 core/models.py:693
msgid "Document favorite"
msgstr "Dokumentenfavorit"
#: build/lib/core/models.py:851 core/models.py:851
#: build/lib/core/models.py:694 core/models.py:694
msgid "Document favorites"
msgstr "Dokumentfavoriten"
#: build/lib/core/models.py:857 core/models.py:857
#: build/lib/core/models.py:700 core/models.py:700
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr "Dieses Dokument ist bereits durch den gleichen Benutzer favorisiert worden."
#: build/lib/core/models.py:879 core/models.py:879
#: build/lib/core/models.py:722 core/models.py:722
msgid "Document/user relation"
msgstr "Dokument/Benutzerbeziehung"
#: build/lib/core/models.py:880 core/models.py:880
#: build/lib/core/models.py:723 core/models.py:723
msgid "Document/user relations"
msgstr "Dokument/Benutzerbeziehungen"
#: build/lib/core/models.py:886 core/models.py:886
#: build/lib/core/models.py:729 core/models.py:729
msgid "This user is already in this document."
msgstr "Dieser Benutzer befindet sich bereits in diesem Dokument."
#: build/lib/core/models.py:892 core/models.py:892
#: build/lib/core/models.py:735 core/models.py:735
msgid "This team is already in this document."
msgstr "Dieses Team befindet sich bereits in diesem Dokument."
#: build/lib/core/models.py:898 build/lib/core/models.py:1012
#: core/models.py:898 core/models.py:1012
#: build/lib/core/models.py:741 build/lib/core/models.py:930 core/models.py:741
#: core/models.py:930
msgid "Either user or team must be set, not both."
msgstr "Benutzer oder Team müssen gesetzt werden, nicht beides."
#: build/lib/core/models.py:926 core/models.py:926
#: build/lib/core/models.py:759 core/models.py:759
msgid "description"
msgstr "Beschreibung"
#: build/lib/core/models.py:927 core/models.py:927
#: build/lib/core/models.py:760 core/models.py:760
msgid "code"
msgstr "Code"
#: build/lib/core/models.py:928 core/models.py:928
#: build/lib/core/models.py:761 core/models.py:761
msgid "css"
msgstr "CSS"
#: build/lib/core/models.py:930 core/models.py:930
#: build/lib/core/models.py:763 core/models.py:763
msgid "public"
msgstr "öffentlich"
#: build/lib/core/models.py:932 core/models.py:932
#: build/lib/core/models.py:765 core/models.py:765
msgid "Whether this template is public for anyone to use."
msgstr "Ob diese Vorlage für jedermann öffentlich ist."
#: build/lib/core/models.py:938 core/models.py:938
#: build/lib/core/models.py:771 core/models.py:771
msgid "Template"
msgstr "Vorlage"
#: build/lib/core/models.py:939 core/models.py:939
#: build/lib/core/models.py:772 core/models.py:772
msgid "Templates"
msgstr "Vorlagen"
#: build/lib/core/models.py:993 core/models.py:993
#: build/lib/core/models.py:911 core/models.py:911
msgid "Template/user relation"
msgstr "Vorlage/Benutzer-Beziehung"
#: build/lib/core/models.py:994 core/models.py:994
#: build/lib/core/models.py:912 core/models.py:912
msgid "Template/user relations"
msgstr "Vorlage/Benutzerbeziehungen"
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:918 core/models.py:918
msgid "This user is already in this template."
msgstr "Dieser Benutzer ist bereits in dieser Vorlage."
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:924 core/models.py:924
msgid "This team is already in this template."
msgstr "Dieses Team ist bereits in diesem Template."
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:947 core/models.py:947
msgid "email address"
msgstr "E-Mail-Adresse"
#: build/lib/core/models.py:1048 core/models.py:1048
#: build/lib/core/models.py:966 core/models.py:966
msgid "Document invitation"
msgstr "Einladung zum Dokument"
#: build/lib/core/models.py:1049 core/models.py:1049
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document invitations"
msgstr "Dokumenteinladungen"
#: build/lib/core/models.py:1069 core/models.py:1069
#: build/lib/core/models.py:987 core/models.py:987
msgid "This email is already associated to a registered user."
msgstr "Diese E-Mail ist bereits einem registrierten Benutzer zugeordnet."
+79 -120
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-29 13:43+0000\n"
"PO-Revision-Date: 2025-01-30 10:24\n"
"POT-Creation-Date: 2025-01-15 21:00+0000\n"
"PO-Revision-Date: 2025-01-27 09:27\n"
"Last-Translator: \n"
"Language-Team: English\n"
"Language: en_US\n"
@@ -17,23 +17,18 @@ msgstr ""
"X-Crowdin-File: backend-impress.pot\n"
"X-Crowdin-File-ID: 18\n"
#: build/lib/core/admin.py:37 core/admin.py:37
#: build/lib/core/admin.py:33 core/admin.py:33
msgid "Personal info"
msgstr ""
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
#: core/admin.py:138
#: build/lib/core/admin.py:46 core/admin.py:46
msgid "Permissions"
msgstr ""
#: build/lib/core/admin.py:62 core/admin.py:62
#: build/lib/core/admin.py:58 core/admin.py:58
msgid "Important dates"
msgstr ""
#: build/lib/core/admin.py:148 core/admin.py:148
msgid "Tree structure"
msgstr ""
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
msgid "Creator is me"
msgstr ""
@@ -46,23 +41,23 @@ msgstr ""
msgid "Title"
msgstr ""
#: build/lib/core/api/serializers.py:346 core/api/serializers.py:346
#: build/lib/core/api/serializers.py:317 core/api/serializers.py:317
msgid "A new document was created on your behalf!"
msgstr ""
#: build/lib/core/api/serializers.py:350 core/api/serializers.py:350
#: build/lib/core/api/serializers.py:321 core/api/serializers.py:321
msgid "You have been granted ownership of a new document:"
msgstr ""
#: build/lib/core/api/serializers.py:453 core/api/serializers.py:453
#: build/lib/core/api/serializers.py:422 core/api/serializers.py:422
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:456 core/api/serializers.py:456
#: build/lib/core/api/serializers.py:425 core/api/serializers.py:425
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:462 core/api/serializers.py:462
#: build/lib/core/api/serializers.py:431 core/api/serializers.py:431
msgid "Format"
msgstr ""
@@ -76,306 +71,270 @@ msgstr ""
msgid "User account is disabled"
msgstr ""
#: build/lib/core/enums.py:19 core/enums.py:19
msgid "First child"
msgstr ""
#: build/lib/core/enums.py:20 core/enums.py:20
msgid "Last child"
msgstr ""
#: build/lib/core/enums.py:21 core/enums.py:21
msgid "First sibling"
msgstr ""
#: build/lib/core/enums.py:22 core/enums.py:22
msgid "Last sibling"
msgstr ""
#: build/lib/core/enums.py:23 core/enums.py:23
msgid "Left"
msgstr ""
#: build/lib/core/enums.py:24 core/enums.py:24
msgid "Right"
msgstr ""
#: build/lib/core/models.py:54 build/lib/core/models.py:61 core/models.py:54
#: core/models.py:61
#: build/lib/core/models.py:63 build/lib/core/models.py:70 core/models.py:63
#: core/models.py:70
msgid "Reader"
msgstr ""
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
#: core/models.py:62
#: build/lib/core/models.py:64 build/lib/core/models.py:71 core/models.py:64
#: core/models.py:71
msgid "Editor"
msgstr ""
#: build/lib/core/models.py:63 core/models.py:63
#: build/lib/core/models.py:72 core/models.py:72
msgid "Administrator"
msgstr ""
#: build/lib/core/models.py:64 core/models.py:64
#: build/lib/core/models.py:73 core/models.py:73
msgid "Owner"
msgstr ""
#: build/lib/core/models.py:75 core/models.py:75
#: build/lib/core/models.py:84 core/models.py:84
msgid "Restricted"
msgstr ""
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:88 core/models.py:88
msgid "Authenticated"
msgstr ""
#: build/lib/core/models.py:81 core/models.py:81
#: build/lib/core/models.py:90 core/models.py:90
msgid "Public"
msgstr ""
#: build/lib/core/models.py:103 core/models.py:103
#: build/lib/core/models.py:112 core/models.py:112
msgid "id"
msgstr ""
#: build/lib/core/models.py:104 core/models.py:104
#: build/lib/core/models.py:113 core/models.py:113
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:110 core/models.py:110
#: build/lib/core/models.py:119 core/models.py:119
msgid "created on"
msgstr ""
#: build/lib/core/models.py:111 core/models.py:111
#: build/lib/core/models.py:120 core/models.py:120
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:116 core/models.py:116
#: build/lib/core/models.py:125 core/models.py:125
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:117 core/models.py:117
#: build/lib/core/models.py:126 core/models.py:126
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:153 core/models.py:153
#: build/lib/core/models.py:162 core/models.py:162
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:166 core/models.py:166
#: build/lib/core/models.py:175 core/models.py:175
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:172 core/models.py:172
#: build/lib/core/models.py:181 core/models.py:181
msgid "sub"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:183 core/models.py:183
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:192 core/models.py:192
msgid "full name"
msgstr ""
#: build/lib/core/models.py:184 core/models.py:184
#: build/lib/core/models.py:193 core/models.py:193
msgid "short name"
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:195 core/models.py:195
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:200 core/models.py:200
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:198 core/models.py:198
#: build/lib/core/models.py:207 core/models.py:207
msgid "language"
msgstr ""
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:208 core/models.py:208
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:205 core/models.py:205
#: build/lib/core/models.py:214 core/models.py:214
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:208 core/models.py:208
#: build/lib/core/models.py:217 core/models.py:217
msgid "device"
msgstr ""
#: build/lib/core/models.py:210 core/models.py:210
#: build/lib/core/models.py:219 core/models.py:219
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:213 core/models.py:213
#: build/lib/core/models.py:222 core/models.py:222
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:215 core/models.py:215
#: build/lib/core/models.py:224 core/models.py:224
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:218 core/models.py:218
#: build/lib/core/models.py:227 core/models.py:227
msgid "active"
msgstr ""
#: build/lib/core/models.py:221 core/models.py:221
#: build/lib/core/models.py:230 core/models.py:230
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:233 core/models.py:233
#: build/lib/core/models.py:242 core/models.py:242
msgid "user"
msgstr ""
#: build/lib/core/models.py:234 core/models.py:234
#: build/lib/core/models.py:243 core/models.py:243
msgid "users"
msgstr ""
#: build/lib/core/models.py:373 build/lib/core/models.py:925 core/models.py:373
#: core/models.py:925
#: build/lib/core/models.py:382 build/lib/core/models.py:758 core/models.py:382
#: core/models.py:758
msgid "title"
msgstr ""
#: build/lib/core/models.py:374 core/models.py:374
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:405 core/models.py:405
#: build/lib/core/models.py:404 core/models.py:404
msgid "Document"
msgstr ""
#: build/lib/core/models.py:406 core/models.py:406
#: build/lib/core/models.py:405 core/models.py:405
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:408 core/models.py:408
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:719 core/models.py:719
#: build/lib/core/models.py:633 core/models.py:633
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:723 core/models.py:723
#: build/lib/core/models.py:637 core/models.py:637
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:726 core/models.py:726
#: build/lib/core/models.py:640 core/models.py:640
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:762 core/models.py:762
msgid "This document is not deleted."
msgstr ""
#: build/lib/core/models.py:769 core/models.py:769
msgid "This document was permanently deleted and cannot be restored."
msgstr ""
#: build/lib/core/models.py:820 core/models.py:820
#: build/lib/core/models.py:663 core/models.py:663
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:821 core/models.py:821
#: build/lib/core/models.py:664 core/models.py:664
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:827 core/models.py:827
#: build/lib/core/models.py:670 core/models.py:670
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:850 core/models.py:850
#: build/lib/core/models.py:693 core/models.py:693
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:851 core/models.py:851
#: build/lib/core/models.py:694 core/models.py:694
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:857 core/models.py:857
#: build/lib/core/models.py:700 core/models.py:700
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:879 core/models.py:879
#: build/lib/core/models.py:722 core/models.py:722
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:880 core/models.py:880
#: build/lib/core/models.py:723 core/models.py:723
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:886 core/models.py:886
#: build/lib/core/models.py:729 core/models.py:729
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:892 core/models.py:892
#: build/lib/core/models.py:735 core/models.py:735
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:898 build/lib/core/models.py:1012
#: core/models.py:898 core/models.py:1012
#: build/lib/core/models.py:741 build/lib/core/models.py:930 core/models.py:741
#: core/models.py:930
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:926 core/models.py:926
#: build/lib/core/models.py:759 core/models.py:759
msgid "description"
msgstr ""
#: build/lib/core/models.py:927 core/models.py:927
#: build/lib/core/models.py:760 core/models.py:760
msgid "code"
msgstr ""
#: build/lib/core/models.py:928 core/models.py:928
#: build/lib/core/models.py:761 core/models.py:761
msgid "css"
msgstr ""
#: build/lib/core/models.py:930 core/models.py:930
#: build/lib/core/models.py:763 core/models.py:763
msgid "public"
msgstr ""
#: build/lib/core/models.py:932 core/models.py:932
#: build/lib/core/models.py:765 core/models.py:765
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:938 core/models.py:938
#: build/lib/core/models.py:771 core/models.py:771
msgid "Template"
msgstr ""
#: build/lib/core/models.py:939 core/models.py:939
#: build/lib/core/models.py:772 core/models.py:772
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:993 core/models.py:993
#: build/lib/core/models.py:911 core/models.py:911
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:994 core/models.py:994
#: build/lib/core/models.py:912 core/models.py:912
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:918 core/models.py:918
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:924 core/models.py:924
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:947 core/models.py:947
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1048 core/models.py:1048
#: build/lib/core/models.py:966 core/models.py:966
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1049 core/models.py:1049
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1069 core/models.py:1069
#: build/lib/core/models.py:987 core/models.py:987
msgid "This email is already associated to a registered user."
msgstr ""
+79 -120
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-29 13:43+0000\n"
"PO-Revision-Date: 2025-01-30 10:24\n"
"POT-Creation-Date: 2025-01-15 21:00+0000\n"
"PO-Revision-Date: 2025-01-27 09:27\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -17,23 +17,18 @@ msgstr ""
"X-Crowdin-File: backend-impress.pot\n"
"X-Crowdin-File-ID: 18\n"
#: build/lib/core/admin.py:37 core/admin.py:37
#: build/lib/core/admin.py:33 core/admin.py:33
msgid "Personal info"
msgstr "Infos Personnelles"
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
#: core/admin.py:138
#: build/lib/core/admin.py:46 core/admin.py:46
msgid "Permissions"
msgstr ""
#: build/lib/core/admin.py:62 core/admin.py:62
#: build/lib/core/admin.py:58 core/admin.py:58
msgid "Important dates"
msgstr "Dates importantes"
#: build/lib/core/admin.py:148 core/admin.py:148
msgid "Tree structure"
msgstr ""
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
msgid "Creator is me"
msgstr ""
@@ -46,23 +41,23 @@ msgstr ""
msgid "Title"
msgstr ""
#: build/lib/core/api/serializers.py:346 core/api/serializers.py:346
#: build/lib/core/api/serializers.py:317 core/api/serializers.py:317
msgid "A new document was created on your behalf!"
msgstr "Un nouveau document a été créé pour vous !"
#: build/lib/core/api/serializers.py:350 core/api/serializers.py:350
#: build/lib/core/api/serializers.py:321 core/api/serializers.py:321
msgid "You have been granted ownership of a new document:"
msgstr "Vous avez été déclaré propriétaire d'un nouveau document :"
#: build/lib/core/api/serializers.py:453 core/api/serializers.py:453
#: build/lib/core/api/serializers.py:422 core/api/serializers.py:422
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:456 core/api/serializers.py:456
#: build/lib/core/api/serializers.py:425 core/api/serializers.py:425
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:462 core/api/serializers.py:462
#: build/lib/core/api/serializers.py:431 core/api/serializers.py:431
msgid "Format"
msgstr ""
@@ -76,306 +71,270 @@ msgstr ""
msgid "User account is disabled"
msgstr ""
#: build/lib/core/enums.py:19 core/enums.py:19
msgid "First child"
msgstr ""
#: build/lib/core/enums.py:20 core/enums.py:20
msgid "Last child"
msgstr ""
#: build/lib/core/enums.py:21 core/enums.py:21
msgid "First sibling"
msgstr ""
#: build/lib/core/enums.py:22 core/enums.py:22
msgid "Last sibling"
msgstr ""
#: build/lib/core/enums.py:23 core/enums.py:23
msgid "Left"
msgstr ""
#: build/lib/core/enums.py:24 core/enums.py:24
msgid "Right"
msgstr ""
#: build/lib/core/models.py:54 build/lib/core/models.py:61 core/models.py:54
#: core/models.py:61
#: build/lib/core/models.py:63 build/lib/core/models.py:70 core/models.py:63
#: core/models.py:70
msgid "Reader"
msgstr "Lecteur"
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
#: core/models.py:62
#: build/lib/core/models.py:64 build/lib/core/models.py:71 core/models.py:64
#: core/models.py:71
msgid "Editor"
msgstr "Éditeur"
#: build/lib/core/models.py:63 core/models.py:63
#: build/lib/core/models.py:72 core/models.py:72
msgid "Administrator"
msgstr "Administrateur"
#: build/lib/core/models.py:64 core/models.py:64
#: build/lib/core/models.py:73 core/models.py:73
msgid "Owner"
msgstr "Propriétaire"
#: build/lib/core/models.py:75 core/models.py:75
#: build/lib/core/models.py:84 core/models.py:84
msgid "Restricted"
msgstr "Restreint"
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:88 core/models.py:88
msgid "Authenticated"
msgstr "Authentifié"
#: build/lib/core/models.py:81 core/models.py:81
#: build/lib/core/models.py:90 core/models.py:90
msgid "Public"
msgstr ""
#: build/lib/core/models.py:103 core/models.py:103
#: build/lib/core/models.py:112 core/models.py:112
msgid "id"
msgstr ""
#: build/lib/core/models.py:104 core/models.py:104
#: build/lib/core/models.py:113 core/models.py:113
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:110 core/models.py:110
#: build/lib/core/models.py:119 core/models.py:119
msgid "created on"
msgstr ""
#: build/lib/core/models.py:111 core/models.py:111
#: build/lib/core/models.py:120 core/models.py:120
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:116 core/models.py:116
#: build/lib/core/models.py:125 core/models.py:125
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:117 core/models.py:117
#: build/lib/core/models.py:126 core/models.py:126
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:153 core/models.py:153
#: build/lib/core/models.py:162 core/models.py:162
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:166 core/models.py:166
#: build/lib/core/models.py:175 core/models.py:175
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:172 core/models.py:172
#: build/lib/core/models.py:181 core/models.py:181
msgid "sub"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:183 core/models.py:183
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:192 core/models.py:192
msgid "full name"
msgstr ""
#: build/lib/core/models.py:184 core/models.py:184
#: build/lib/core/models.py:193 core/models.py:193
msgid "short name"
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:195 core/models.py:195
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:200 core/models.py:200
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:198 core/models.py:198
#: build/lib/core/models.py:207 core/models.py:207
msgid "language"
msgstr ""
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:208 core/models.py:208
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:205 core/models.py:205
#: build/lib/core/models.py:214 core/models.py:214
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:208 core/models.py:208
#: build/lib/core/models.py:217 core/models.py:217
msgid "device"
msgstr ""
#: build/lib/core/models.py:210 core/models.py:210
#: build/lib/core/models.py:219 core/models.py:219
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:213 core/models.py:213
#: build/lib/core/models.py:222 core/models.py:222
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:215 core/models.py:215
#: build/lib/core/models.py:224 core/models.py:224
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:218 core/models.py:218
#: build/lib/core/models.py:227 core/models.py:227
msgid "active"
msgstr ""
#: build/lib/core/models.py:221 core/models.py:221
#: build/lib/core/models.py:230 core/models.py:230
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:233 core/models.py:233
#: build/lib/core/models.py:242 core/models.py:242
msgid "user"
msgstr ""
#: build/lib/core/models.py:234 core/models.py:234
#: build/lib/core/models.py:243 core/models.py:243
msgid "users"
msgstr ""
#: build/lib/core/models.py:373 build/lib/core/models.py:925 core/models.py:373
#: core/models.py:925
#: build/lib/core/models.py:382 build/lib/core/models.py:758 core/models.py:382
#: core/models.py:758
msgid "title"
msgstr ""
#: build/lib/core/models.py:374 core/models.py:374
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:405 core/models.py:405
#: build/lib/core/models.py:404 core/models.py:404
msgid "Document"
msgstr ""
#: build/lib/core/models.py:406 core/models.py:406
#: build/lib/core/models.py:405 core/models.py:405
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:408 core/models.py:408
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:719 core/models.py:719
#: build/lib/core/models.py:633 core/models.py:633
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr "{name} a partagé un document avec vous!"
#: build/lib/core/models.py:723 core/models.py:723
#: build/lib/core/models.py:637 core/models.py:637
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr "{name} vous a invité avec le rôle \"{role}\" sur le document suivant:"
#: build/lib/core/models.py:726 core/models.py:726
#: build/lib/core/models.py:640 core/models.py:640
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr "{name} a partagé un document avec vous: {title}"
#: build/lib/core/models.py:762 core/models.py:762
msgid "This document is not deleted."
msgstr ""
#: build/lib/core/models.py:769 core/models.py:769
msgid "This document was permanently deleted and cannot be restored."
msgstr ""
#: build/lib/core/models.py:820 core/models.py:820
#: build/lib/core/models.py:663 core/models.py:663
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:821 core/models.py:821
#: build/lib/core/models.py:664 core/models.py:664
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:827 core/models.py:827
#: build/lib/core/models.py:670 core/models.py:670
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:850 core/models.py:850
#: build/lib/core/models.py:693 core/models.py:693
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:851 core/models.py:851
#: build/lib/core/models.py:694 core/models.py:694
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:857 core/models.py:857
#: build/lib/core/models.py:700 core/models.py:700
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:879 core/models.py:879
#: build/lib/core/models.py:722 core/models.py:722
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:880 core/models.py:880
#: build/lib/core/models.py:723 core/models.py:723
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:886 core/models.py:886
#: build/lib/core/models.py:729 core/models.py:729
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:892 core/models.py:892
#: build/lib/core/models.py:735 core/models.py:735
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:898 build/lib/core/models.py:1012
#: core/models.py:898 core/models.py:1012
#: build/lib/core/models.py:741 build/lib/core/models.py:930 core/models.py:741
#: core/models.py:930
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:926 core/models.py:926
#: build/lib/core/models.py:759 core/models.py:759
msgid "description"
msgstr ""
#: build/lib/core/models.py:927 core/models.py:927
#: build/lib/core/models.py:760 core/models.py:760
msgid "code"
msgstr ""
#: build/lib/core/models.py:928 core/models.py:928
#: build/lib/core/models.py:761 core/models.py:761
msgid "css"
msgstr ""
#: build/lib/core/models.py:930 core/models.py:930
#: build/lib/core/models.py:763 core/models.py:763
msgid "public"
msgstr ""
#: build/lib/core/models.py:932 core/models.py:932
#: build/lib/core/models.py:765 core/models.py:765
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:938 core/models.py:938
#: build/lib/core/models.py:771 core/models.py:771
msgid "Template"
msgstr ""
#: build/lib/core/models.py:939 core/models.py:939
#: build/lib/core/models.py:772 core/models.py:772
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:993 core/models.py:993
#: build/lib/core/models.py:911 core/models.py:911
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:994 core/models.py:994
#: build/lib/core/models.py:912 core/models.py:912
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:918 core/models.py:918
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:924 core/models.py:924
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:947 core/models.py:947
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1048 core/models.py:1048
#: build/lib/core/models.py:966 core/models.py:966
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1049 core/models.py:1049
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1069 core/models.py:1069
#: build/lib/core/models.py:987 core/models.py:987
msgid "This email is already associated to a registered user."
msgstr ""
+79 -120
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: lasuite-docs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-29 13:43+0000\n"
"PO-Revision-Date: 2025-01-30 10:24\n"
"POT-Creation-Date: 2025-01-15 21:00+0000\n"
"PO-Revision-Date: 2025-01-27 09:27\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -17,23 +17,18 @@ msgstr ""
"X-Crowdin-File: backend-impress.pot\n"
"X-Crowdin-File-ID: 18\n"
#: build/lib/core/admin.py:37 core/admin.py:37
#: build/lib/core/admin.py:33 core/admin.py:33
msgid "Personal info"
msgstr ""
#: build/lib/core/admin.py:50 build/lib/core/admin.py:138 core/admin.py:50
#: core/admin.py:138
#: build/lib/core/admin.py:46 core/admin.py:46
msgid "Permissions"
msgstr ""
#: build/lib/core/admin.py:62 core/admin.py:62
#: build/lib/core/admin.py:58 core/admin.py:58
msgid "Important dates"
msgstr ""
#: build/lib/core/admin.py:148 core/admin.py:148
msgid "Tree structure"
msgstr ""
#: build/lib/core/api/filters.py:16 core/api/filters.py:16
msgid "Creator is me"
msgstr ""
@@ -46,23 +41,23 @@ msgstr ""
msgid "Title"
msgstr ""
#: build/lib/core/api/serializers.py:346 core/api/serializers.py:346
#: build/lib/core/api/serializers.py:317 core/api/serializers.py:317
msgid "A new document was created on your behalf!"
msgstr ""
#: build/lib/core/api/serializers.py:350 core/api/serializers.py:350
#: build/lib/core/api/serializers.py:321 core/api/serializers.py:321
msgid "You have been granted ownership of a new document:"
msgstr ""
#: build/lib/core/api/serializers.py:453 core/api/serializers.py:453
#: build/lib/core/api/serializers.py:422 core/api/serializers.py:422
msgid "Body"
msgstr ""
#: build/lib/core/api/serializers.py:456 core/api/serializers.py:456
#: build/lib/core/api/serializers.py:425 core/api/serializers.py:425
msgid "Body type"
msgstr ""
#: build/lib/core/api/serializers.py:462 core/api/serializers.py:462
#: build/lib/core/api/serializers.py:431 core/api/serializers.py:431
msgid "Format"
msgstr ""
@@ -76,306 +71,270 @@ msgstr ""
msgid "User account is disabled"
msgstr ""
#: build/lib/core/enums.py:19 core/enums.py:19
msgid "First child"
msgstr ""
#: build/lib/core/enums.py:20 core/enums.py:20
msgid "Last child"
msgstr ""
#: build/lib/core/enums.py:21 core/enums.py:21
msgid "First sibling"
msgstr ""
#: build/lib/core/enums.py:22 core/enums.py:22
msgid "Last sibling"
msgstr ""
#: build/lib/core/enums.py:23 core/enums.py:23
msgid "Left"
msgstr ""
#: build/lib/core/enums.py:24 core/enums.py:24
msgid "Right"
msgstr ""
#: build/lib/core/models.py:54 build/lib/core/models.py:61 core/models.py:54
#: core/models.py:61
#: build/lib/core/models.py:63 build/lib/core/models.py:70 core/models.py:63
#: core/models.py:70
msgid "Reader"
msgstr ""
#: build/lib/core/models.py:55 build/lib/core/models.py:62 core/models.py:55
#: core/models.py:62
#: build/lib/core/models.py:64 build/lib/core/models.py:71 core/models.py:64
#: core/models.py:71
msgid "Editor"
msgstr ""
#: build/lib/core/models.py:63 core/models.py:63
#: build/lib/core/models.py:72 core/models.py:72
msgid "Administrator"
msgstr ""
#: build/lib/core/models.py:64 core/models.py:64
#: build/lib/core/models.py:73 core/models.py:73
msgid "Owner"
msgstr ""
#: build/lib/core/models.py:75 core/models.py:75
#: build/lib/core/models.py:84 core/models.py:84
msgid "Restricted"
msgstr ""
#: build/lib/core/models.py:79 core/models.py:79
#: build/lib/core/models.py:88 core/models.py:88
msgid "Authenticated"
msgstr ""
#: build/lib/core/models.py:81 core/models.py:81
#: build/lib/core/models.py:90 core/models.py:90
msgid "Public"
msgstr ""
#: build/lib/core/models.py:103 core/models.py:103
#: build/lib/core/models.py:112 core/models.py:112
msgid "id"
msgstr ""
#: build/lib/core/models.py:104 core/models.py:104
#: build/lib/core/models.py:113 core/models.py:113
msgid "primary key for the record as UUID"
msgstr ""
#: build/lib/core/models.py:110 core/models.py:110
#: build/lib/core/models.py:119 core/models.py:119
msgid "created on"
msgstr ""
#: build/lib/core/models.py:111 core/models.py:111
#: build/lib/core/models.py:120 core/models.py:120
msgid "date and time at which a record was created"
msgstr ""
#: build/lib/core/models.py:116 core/models.py:116
#: build/lib/core/models.py:125 core/models.py:125
msgid "updated on"
msgstr ""
#: build/lib/core/models.py:117 core/models.py:117
#: build/lib/core/models.py:126 core/models.py:126
msgid "date and time at which a record was last updated"
msgstr ""
#: build/lib/core/models.py:153 core/models.py:153
#: build/lib/core/models.py:162 core/models.py:162
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
msgstr ""
#: build/lib/core/models.py:166 core/models.py:166
#: build/lib/core/models.py:175 core/models.py:175
msgid "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters."
msgstr ""
#: build/lib/core/models.py:172 core/models.py:172
#: build/lib/core/models.py:181 core/models.py:181
msgid "sub"
msgstr ""
#: build/lib/core/models.py:174 core/models.py:174
#: build/lib/core/models.py:183 core/models.py:183
msgid "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
msgstr ""
#: build/lib/core/models.py:183 core/models.py:183
#: build/lib/core/models.py:192 core/models.py:192
msgid "full name"
msgstr ""
#: build/lib/core/models.py:184 core/models.py:184
#: build/lib/core/models.py:193 core/models.py:193
msgid "short name"
msgstr ""
#: build/lib/core/models.py:186 core/models.py:186
#: build/lib/core/models.py:195 core/models.py:195
msgid "identity email address"
msgstr ""
#: build/lib/core/models.py:191 core/models.py:191
#: build/lib/core/models.py:200 core/models.py:200
msgid "admin email address"
msgstr ""
#: build/lib/core/models.py:198 core/models.py:198
#: build/lib/core/models.py:207 core/models.py:207
msgid "language"
msgstr ""
#: build/lib/core/models.py:199 core/models.py:199
#: build/lib/core/models.py:208 core/models.py:208
msgid "The language in which the user wants to see the interface."
msgstr ""
#: build/lib/core/models.py:205 core/models.py:205
#: build/lib/core/models.py:214 core/models.py:214
msgid "The timezone in which the user wants to see times."
msgstr ""
#: build/lib/core/models.py:208 core/models.py:208
#: build/lib/core/models.py:217 core/models.py:217
msgid "device"
msgstr ""
#: build/lib/core/models.py:210 core/models.py:210
#: build/lib/core/models.py:219 core/models.py:219
msgid "Whether the user is a device or a real user."
msgstr ""
#: build/lib/core/models.py:213 core/models.py:213
#: build/lib/core/models.py:222 core/models.py:222
msgid "staff status"
msgstr ""
#: build/lib/core/models.py:215 core/models.py:215
#: build/lib/core/models.py:224 core/models.py:224
msgid "Whether the user can log into this admin site."
msgstr ""
#: build/lib/core/models.py:218 core/models.py:218
#: build/lib/core/models.py:227 core/models.py:227
msgid "active"
msgstr ""
#: build/lib/core/models.py:221 core/models.py:221
#: build/lib/core/models.py:230 core/models.py:230
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
msgstr ""
#: build/lib/core/models.py:233 core/models.py:233
#: build/lib/core/models.py:242 core/models.py:242
msgid "user"
msgstr ""
#: build/lib/core/models.py:234 core/models.py:234
#: build/lib/core/models.py:243 core/models.py:243
msgid "users"
msgstr ""
#: build/lib/core/models.py:373 build/lib/core/models.py:925 core/models.py:373
#: core/models.py:925
#: build/lib/core/models.py:382 build/lib/core/models.py:758 core/models.py:382
#: core/models.py:758
msgid "title"
msgstr ""
#: build/lib/core/models.py:374 core/models.py:374
msgid "excerpt"
msgstr ""
#: build/lib/core/models.py:405 core/models.py:405
#: build/lib/core/models.py:404 core/models.py:404
msgid "Document"
msgstr ""
#: build/lib/core/models.py:406 core/models.py:406
#: build/lib/core/models.py:405 core/models.py:405
msgid "Documents"
msgstr ""
#: build/lib/core/models.py:418 core/models.py:418
#: build/lib/core/models.py:408 core/models.py:408
msgid "Untitled Document"
msgstr ""
#: build/lib/core/models.py:719 core/models.py:719
#: build/lib/core/models.py:633 core/models.py:633
#, python-brace-format
msgid "{name} shared a document with you!"
msgstr ""
#: build/lib/core/models.py:723 core/models.py:723
#: build/lib/core/models.py:637 core/models.py:637
#, python-brace-format
msgid "{name} invited you with the role \"{role}\" on the following document:"
msgstr ""
#: build/lib/core/models.py:726 core/models.py:726
#: build/lib/core/models.py:640 core/models.py:640
#, python-brace-format
msgid "{name} shared a document with you: {title}"
msgstr ""
#: build/lib/core/models.py:762 core/models.py:762
msgid "This document is not deleted."
msgstr ""
#: build/lib/core/models.py:769 core/models.py:769
msgid "This document was permanently deleted and cannot be restored."
msgstr ""
#: build/lib/core/models.py:820 core/models.py:820
#: build/lib/core/models.py:663 core/models.py:663
msgid "Document/user link trace"
msgstr ""
#: build/lib/core/models.py:821 core/models.py:821
#: build/lib/core/models.py:664 core/models.py:664
msgid "Document/user link traces"
msgstr ""
#: build/lib/core/models.py:827 core/models.py:827
#: build/lib/core/models.py:670 core/models.py:670
msgid "A link trace already exists for this document/user."
msgstr ""
#: build/lib/core/models.py:850 core/models.py:850
#: build/lib/core/models.py:693 core/models.py:693
msgid "Document favorite"
msgstr ""
#: build/lib/core/models.py:851 core/models.py:851
#: build/lib/core/models.py:694 core/models.py:694
msgid "Document favorites"
msgstr ""
#: build/lib/core/models.py:857 core/models.py:857
#: build/lib/core/models.py:700 core/models.py:700
msgid "This document is already targeted by a favorite relation instance for the same user."
msgstr ""
#: build/lib/core/models.py:879 core/models.py:879
#: build/lib/core/models.py:722 core/models.py:722
msgid "Document/user relation"
msgstr ""
#: build/lib/core/models.py:880 core/models.py:880
#: build/lib/core/models.py:723 core/models.py:723
msgid "Document/user relations"
msgstr ""
#: build/lib/core/models.py:886 core/models.py:886
#: build/lib/core/models.py:729 core/models.py:729
msgid "This user is already in this document."
msgstr ""
#: build/lib/core/models.py:892 core/models.py:892
#: build/lib/core/models.py:735 core/models.py:735
msgid "This team is already in this document."
msgstr ""
#: build/lib/core/models.py:898 build/lib/core/models.py:1012
#: core/models.py:898 core/models.py:1012
#: build/lib/core/models.py:741 build/lib/core/models.py:930 core/models.py:741
#: core/models.py:930
msgid "Either user or team must be set, not both."
msgstr ""
#: build/lib/core/models.py:926 core/models.py:926
#: build/lib/core/models.py:759 core/models.py:759
msgid "description"
msgstr ""
#: build/lib/core/models.py:927 core/models.py:927
#: build/lib/core/models.py:760 core/models.py:760
msgid "code"
msgstr ""
#: build/lib/core/models.py:928 core/models.py:928
#: build/lib/core/models.py:761 core/models.py:761
msgid "css"
msgstr ""
#: build/lib/core/models.py:930 core/models.py:930
#: build/lib/core/models.py:763 core/models.py:763
msgid "public"
msgstr ""
#: build/lib/core/models.py:932 core/models.py:932
#: build/lib/core/models.py:765 core/models.py:765
msgid "Whether this template is public for anyone to use."
msgstr ""
#: build/lib/core/models.py:938 core/models.py:938
#: build/lib/core/models.py:771 core/models.py:771
msgid "Template"
msgstr ""
#: build/lib/core/models.py:939 core/models.py:939
#: build/lib/core/models.py:772 core/models.py:772
msgid "Templates"
msgstr ""
#: build/lib/core/models.py:993 core/models.py:993
#: build/lib/core/models.py:911 core/models.py:911
msgid "Template/user relation"
msgstr ""
#: build/lib/core/models.py:994 core/models.py:994
#: build/lib/core/models.py:912 core/models.py:912
msgid "Template/user relations"
msgstr ""
#: build/lib/core/models.py:1000 core/models.py:1000
#: build/lib/core/models.py:918 core/models.py:918
msgid "This user is already in this template."
msgstr ""
#: build/lib/core/models.py:1006 core/models.py:1006
#: build/lib/core/models.py:924 core/models.py:924
msgid "This team is already in this template."
msgstr ""
#: build/lib/core/models.py:1029 core/models.py:1029
#: build/lib/core/models.py:947 core/models.py:947
msgid "email address"
msgstr ""
#: build/lib/core/models.py:1048 core/models.py:1048
#: build/lib/core/models.py:966 core/models.py:966
msgid "Document invitation"
msgstr ""
#: build/lib/core/models.py:1049 core/models.py:1049
#: build/lib/core/models.py:967 core/models.py:967
msgid "Document invitations"
msgstr ""
#: build/lib/core/models.py:1069 core/models.py:1069
#: build/lib/core/models.py:987 core/models.py:987
msgid "This email is already associated to a registered user."
msgstr ""
+1 -1
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "2.1.0"
version = "2.0.1"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
await expect(
page.locator('header').first().locator('h2').getByText('Docs'),
).toBeVisible();
@@ -24,6 +24,6 @@ test.describe('404', () => {
page,
}) => {
await page.getByText('Back to home page').click();
await expect(page).toHaveURL('/');
await expect(page).toHaveURL('/docs/');
});
});
@@ -3,7 +3,8 @@ import { test as setup } from '@playwright/test';
import { keyCloakSignIn } from './common';
setup('authenticate-chromium', async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, 'chromium');
await page
.context()
@@ -11,7 +12,8 @@ setup('authenticate-chromium', async ({ page }) => {
});
setup('authenticate-webkit', async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, 'webkit');
await page
.context()
@@ -19,7 +21,8 @@ setup('authenticate-webkit', async ({ page }) => {
});
setup('authenticate-firefox', async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, 'firefox');
await page
.context()
@@ -21,13 +21,43 @@ const config = {
};
test.describe('Config', () => {
test('it checks that media server is configured from config endpoint', async ({
page,
browserName,
}) => {
await page.goto('/docs/');
await createDoc(page, 'doc-media', browserName, 1);
const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('.bn-block-outer').last().fill('Anything');
await page.locator('.bn-block-outer').last().fill('/');
await page.getByText('Resizable image with caption').click();
await page.getByText('Upload image').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(
path.join(__dirname, 'assets/logo-suite-numerique.png'),
);
const image = page.getByRole('img', { name: 'logo-suite-numerique.png' });
await expect(image).toBeVisible();
// Check src of image
expect(await image.getAttribute('src')).toMatch(
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/,
);
});
test('it checks the config api is called', async ({ page }) => {
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/config/') && response.status() === 200,
);
await page.goto('/');
await page.goto('/docs/');
const response = await responsePromise;
expect(response.ok()).toBeTruthy();
@@ -58,62 +88,11 @@ test.describe('Config', () => {
predicate: (msg) => msg.text().includes(invalidMsg),
});
await page.goto('/');
await page.goto('/docs/');
expect((await consoleMessage).text()).toContain(invalidMsg);
});
test('it checks that theme is configured from config endpoint', async ({
page,
}) => {
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/config/') && response.status() === 200,
);
await page.goto('/');
const response = await responsePromise;
expect(response.ok()).toBeTruthy();
const jsonResponse = await response.json();
expect(jsonResponse.FRONTEND_THEME).toStrictEqual('dsfr');
const footer = page.locator('footer').first();
// alt 'Gouvernement Logo' comes from the theme
await expect(footer.getByAltText('Gouvernement Logo')).toBeVisible();
});
test('it checks that media server is configured from config endpoint', async ({
page,
browserName,
}) => {
await page.goto('/');
await createDoc(page, 'doc-media', browserName, 1);
const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('.bn-block-outer').last().fill('Anything');
await page.locator('.bn-block-outer').last().fill('/');
await page.getByText('Resizable image with caption').click();
await page.getByText('Upload image').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(
path.join(__dirname, 'assets/logo-suite-numerique.png'),
);
const image = page.getByRole('img', { name: 'logo-suite-numerique.png' });
await expect(image).toBeVisible();
// Check src of image
expect(await image.getAttribute('src')).toMatch(
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/,
);
});
test('it checks that collaboration server is configured from config endpoint', async ({
page,
browserName,
@@ -122,7 +101,7 @@ test.describe('Config', () => {
return webSocket.url().includes('ws://localhost:8083/collaboration/ws/');
});
await page.goto('/');
await page.goto('/docs');
const randomDoc = await createDoc(
page,
@@ -154,10 +133,29 @@ test.describe('Config', () => {
}
});
await page.goto('/');
await page.goto('/docs/');
await expect(
page.locator('#crisp-chatbox').getByText('Invalid website'),
).toBeVisible();
});
});
test.describe('Config footer logo', () => {
test('it checks that theme is configured from config endpoint', async ({
page,
}) => {
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/config/') && response.status() === 200,
);
await page.goto('/docs/');
const response = await responsePromise;
expect(response.ok()).toBeTruthy();
const jsonResponse = await response.json();
expect(jsonResponse.FRONTEND_THEME).toStrictEqual('dsfr');
});
});
@@ -9,7 +9,7 @@ import {
} from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Doc Create', () => {
@@ -62,7 +62,7 @@ test.describe('Doc Create: Not loggued', () => {
);
expect(newDoc.ok()).toBeTruthy();
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
await goToGridDoc(page, { title });
@@ -10,7 +10,7 @@ import {
} from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Doc Editor', () => {
@@ -257,7 +257,7 @@ test.describe('Doc Editor', () => {
await editor.fill('Hello World Doc persisted 2');
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
await page.goto('/');
await page.goto('/docs/');
await goToGridDoc(page, {
title: doc,
@@ -368,84 +368,4 @@ test.describe('Doc Editor', () => {
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
});
test('it checks the divider block', async ({ page, browserName }) => {
await createDoc(page, 'divider-block', browserName, 1);
const editor = page.locator('.ProseMirror');
// Trigger slash menu to show menu
await editor.click();
await editor.fill('/');
await page.getByText('Divider', { exact: true }).click();
await expect(
editor.locator('.bn-block-content[data-content-type="divider"]'),
).toBeVisible();
});
test('it checks the quote block', async ({ page, browserName }) => {
await createDoc(page, 'divider-block', browserName, 1);
const editor = page.locator('.ProseMirror');
// Trigger slash menu to show menu
await editor.click();
await editor.fill('/');
await page.getByText('Quote', { exact: true }).click();
await expect(
editor.locator('.bn-block-content[data-content-type="quote"]'),
).toBeVisible();
await editor.fill('Hello World');
await expect(editor.getByText('Hello World')).toHaveCSS(
'font-style',
'italic',
);
});
test('it checks the alert block', async ({ page, browserName }) => {
await createDoc(page, 'divider-block', browserName, 1);
const editor = page.locator('.ProseMirror');
// Trigger slash menu to show menu
await editor.click();
await editor.fill('/');
await page.getByText('Alert', { exact: true }).click();
const alertBlock = editor.locator(
'.bn-block-content[data-content-type="alert"]',
);
await expect(
alertBlock.locator('div[data-alert-type="warning"]'),
).toBeVisible();
await editor.fill('My alert');
await expect(alertBlock.getByText('My alert')).toBeVisible();
await alertBlock.getByText('warning').click();
await expect(
alertBlock.getByRole('menuitem', { name: 'warning Warning' }),
).toBeVisible();
await expect(
alertBlock.getByRole('menuitem', { name: 'error Error' }),
).toBeVisible();
await expect(
alertBlock.getByRole('menuitem', { name: 'info Info' }),
).toBeVisible();
await expect(
alertBlock.getByRole('menuitem', { name: 'check_circle Success' }),
).toBeVisible();
await alertBlock
.getByRole('menuitem', { name: 'check_circle Success' })
.click();
await expect(
alertBlock.locator('div[data-alert-type="success"]'),
).toBeVisible();
});
});
@@ -7,7 +7,7 @@ import pdf from 'pdf-parse';
import { createDoc, verifyDocName } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Doc Export', () => {
@@ -29,7 +29,9 @@ test.describe('Doc Export', () => {
.first(),
).toBeVisible();
await expect(
page.getByText('Download your document in a .docx or .pdf format.'),
page.getByText(
'Upload your docs to a Microsoft Word, Open Office or PDF document',
),
).toBeVisible();
await expect(
page.getByRole('combobox', { name: 'Template' }),
@@ -93,7 +95,7 @@ test.describe('Doc Export', () => {
.click();
await page.getByRole('combobox', { name: 'Format' }).click();
await page.getByRole('option', { name: 'Docx' }).click();
await page.getByRole('option', { name: 'Word / Open Office' }).click();
await page
.getByRole('button', {
@@ -8,14 +8,14 @@ type SmallDoc = {
};
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Documents Grid mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('it checks the grid when mobile', async ({ page }) => {
@@ -76,7 +76,7 @@ test.describe('Documents Grid mobile', () => {
}
});
await page.goto('/');
await page.goto('/docs/');
const docsGrid = page.getByTestId('docs-grid');
await expect(docsGrid).toBeVisible();
@@ -195,7 +195,7 @@ test.describe('Document grid item options', () => {
},
});
});
await page.goto('/');
await page.goto('/docs/');
const button = page.getByTestId(
`docs-grid-actions-button-mocked-document-id`,
@@ -286,7 +286,7 @@ test.describe('Documents filters', () => {
test.describe('Documents Grid', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('checks all the elements are visible', async ({ page }) => {
@@ -10,7 +10,7 @@ import {
} from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Doc Header', () => {
@@ -434,7 +434,7 @@ test.describe('Documents Header mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('it checks the copy link button', async ({ page, browserName }) => {
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
import { createDoc, randomName } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Document create member', () => {
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
import { addNewMember, createDoc, goToGridDoc, verifyDocName } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Document list members', () => {
@@ -4,7 +4,7 @@ import { keyCloakSignIn, mockedDocument } from './common';
test.describe('Doc Routing', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('Check the presence of the meta tag noindex', async ({ page }) => {
@@ -24,7 +24,7 @@ test.describe('Doc Routing', () => {
});
test('checks alias docs url with homepage', async ({ page }) => {
await expect(page).toHaveURL('/');
await expect(page).toHaveURL('/docs/');
const buttonCreateHomepage = page.getByRole('button', {
name: 'New doc',
@@ -62,17 +62,18 @@ test.describe('Doc Routing: Not loggued', () => {
await mockedDocument(page, { link_reach: 'public' });
await page.goto('/docs/mocked-document-id/');
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
await page.getByRole('button', { name: 'Login' }).click();
await keyCloakSignIn(page, browserName);
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
});
test('The homepage redirects to login.', async ({ page }) => {
await page.goto('/');
await expect(
page.getByRole('button', {
name: 'Sign In',
}),
).toBeVisible();
test('Check is redirected to home page if user is not logged', async ({
page,
}) => {
await page.goto('/docs/');
await expect(page.getByTestId('proconnect-button').first()).toBeVisible();
});
});
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
import { createDoc, verifyDocName } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Document search', () => {
@@ -15,7 +15,7 @@ test.describe('Document search', () => {
1,
);
await verifyDocName(page, doc1Title);
await page.goto('/');
await page.goto('/docs/');
const [doc2Title] = await createDoc(
page,
@@ -24,7 +24,7 @@ test.describe('Document search', () => {
1,
);
await verifyDocName(page, doc2Title);
await page.goto('/');
await page.goto('/docs/');
await page.getByRole('button', { name: 'search' }).click();
await expect(
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
import { createDoc, verifyDocName } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Doc Table Content', () => {
@@ -8,7 +8,7 @@ import {
} from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Doc Version', () => {
@@ -6,7 +6,7 @@ const browsersName = ['chromium', 'webkit', 'firefox'];
test.describe('Doc Visibility', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('It checks the copy link button', async ({ page, browserName }) => {
@@ -71,7 +71,8 @@ test.describe('Doc Visibility: Restricted', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
@@ -91,9 +92,9 @@ test.describe('Doc Visibility: Restricted', () => {
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await expect(page.getByTestId('proconnect-button').first()).toBeVisible();
await page.goto(urlDoc);
await page.getByTestId('proconnect-button').first().click();
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
});
@@ -102,7 +103,8 @@ test.describe('Doc Visibility: Restricted', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(page, 'Restricted auth', browserName, 1);
@@ -119,6 +121,7 @@ test.describe('Doc Visibility: Restricted', () => {
const otherBrowser = browsersName.find((b) => b !== browserName);
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
@@ -130,7 +133,8 @@ test.describe('Doc Visibility: Restricted', () => {
test('A doc is accessible when member.', async ({ page, browserName }) => {
test.slow();
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(page, 'Restricted auth', browserName, 1);
@@ -167,6 +171,7 @@ test.describe('Doc Visibility: Restricted', () => {
})
.click();
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
@@ -186,7 +191,8 @@ test.describe('Doc Visibility: Public', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
@@ -247,7 +253,7 @@ test.describe('Doc Visibility: Public', () => {
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await expect(page.getByTestId('proconnect-button').first()).toBeVisible();
await page.goto(urlDoc);
@@ -265,7 +271,8 @@ test.describe('Doc Visibility: Public', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(page, 'Public editable', browserName, 1);
@@ -313,7 +320,7 @@ test.describe('Doc Visibility: Public', () => {
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await expect(page.getByTestId('proconnect-button').first()).toBeVisible();
await page.goto(urlDoc);
@@ -329,7 +336,8 @@ test.describe('Doc Visibility: Authenticated', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
@@ -364,11 +372,12 @@ test.describe('Doc Visibility: Authenticated', () => {
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await expect(page.getByTestId('proconnect-button').first()).toBeVisible();
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeHidden();
await page.getByTestId('proconnect-button').first().click();
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
});
@@ -376,7 +385,8 @@ test.describe('Doc Visibility: Authenticated', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
@@ -412,6 +422,7 @@ test.describe('Doc Visibility: Authenticated', () => {
.click();
const otherBrowser = browsersName.find((b) => b !== browserName);
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
@@ -426,7 +437,8 @@ test.describe('Doc Visibility: Authenticated', () => {
page,
browserName,
}) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
@@ -468,6 +480,7 @@ test.describe('Doc Visibility: Authenticated', () => {
.click();
const otherBrowser = browsersName.find((b) => b !== browserName);
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
@@ -1,12 +1,13 @@
import { expect, test } from '@playwright/test';
import { goToGridDoc } from './common';
import { keyCloakSignIn } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Footer', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('checks all the elements are visible', async ({ page }) => {
const footer = page.locator('footer').first();
@@ -47,9 +48,14 @@ test.describe('Footer', () => {
).toBeVisible();
});
test('checks footer is not visible on doc editor', async ({ page }) => {
await expect(page.locator('footer')).toBeVisible();
await goToGridDoc(page);
test('checks footer is not visible on doc editor', async ({
page,
browserName,
}) => {
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
await page.goto('/docs/');
await expect(page.locator('footer')).toBeHidden();
});
@@ -4,7 +4,7 @@ import { keyCloakSignIn } from './common';
test.describe('Header', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('checks all the elements are visible', async ({ page }) => {
@@ -69,7 +69,7 @@ test.describe('Header mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('it checks the header when mobile', async ({ page }) => {
@@ -89,7 +89,8 @@ test.describe('Header: Log out', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('checks logout button', async ({ page, browserName }) => {
await page.goto('/');
await page.goto('/docs/');
await page.getByTestId('proconnect-button').first().click();
await keyCloakSignIn(page, browserName);
await page
@@ -98,6 +99,6 @@ test.describe('Header: Log out', () => {
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await expect(page.getByTestId('proconnect-button')).toHaveCount(2);
});
});
@@ -0,0 +1,49 @@
import { expect, test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('/docs/');
});
test.describe('Home page', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('checks all the elements are visible', async ({ page }) => {
// Check header content
const header = page.locator('header').first();
const footer = page.locator('footer').first();
await expect(header).toBeVisible();
await expect(
header.getByRole('combobox', { name: 'Language' }),
).toBeVisible();
await expect(
header.getByRole('button', { name: 'Les services de La Suite numé' }),
).toBeVisible();
await expect(
header.getByRole('img', { name: 'Gouvernement Logo' }),
).toBeVisible();
await expect(
header.getByRole('img', { name: 'Docs app logo' }),
).toBeVisible();
await expect(header.getByRole('heading', { name: 'Docs' })).toBeVisible();
await expect(header.getByText('BETA')).toBeVisible();
// Check the ttile and subtitle are visible
await expect(page.getByText('Collaborative writing made')).toBeVisible();
await expect(page.getByText('Collaborate and write in real')).toBeVisible();
await expect(page.getByText('An uncompromising writing')).toBeVisible();
await expect(page.getByText('Docs offers an intuitive')).toBeVisible();
await expect(page.getByText('Simple and secure')).toBeVisible();
await expect(page.getByText('Docs makes real-time')).toBeVisible();
await expect(page.getByText('Flexible export.')).toBeVisible();
await expect(page.getByText('To facilitate the circulation')).toBeVisible();
await expect(page.getByText('A new way to organize')).toBeVisible();
await expect(page.getByText('Docs transforms your')).toBeVisible();
await expect(page.getByTestId('proconnect-button')).toHaveCount(2);
// Footer - The footer is already tested in its entirety in the footer.spec.ts file
await expect(footer).toBeVisible();
await expect(
page.getByRole('link', { name: 'expand_more See more' }),
).toBeVisible();
});
});
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test.describe('Language', () => {
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test';
test.describe('Left panel desktop', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('checks all the elements are visible', async ({ page }) => {
@@ -17,7 +17,7 @@ test.describe('Left panel mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.goto('/docs/');
});
test('checks all the desktop elements are hidden and all mobile elements are visible', async ({
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "app-e2e",
"version": "2.1.0",
"version": "2.0.1",
"private": true,
"scripts": {
"lint": "eslint . --ext .ts",
@@ -12,7 +12,7 @@
"test:ui::chromium": "yarn test:ui --project=chromium"
},
"devDependencies": {
"@playwright/test": "1.50.0",
"@playwright/test": "1.49.1",
"@types/node": "*",
"@types/pdf-parse": "1.1.4",
"eslint-config-impress": "*",
+9 -3
View File
@@ -5,6 +5,7 @@ const config = {
colors: {
'card-border': '#ededed',
'primary-bg': '#FAFAFA',
'primary-action': '#1212FF',
'primary-050': '#F5F5FE',
'primary-100': '#EDF5FA',
'primary-150': '#E5EEFA',
@@ -59,6 +60,11 @@ const config = {
h4: '1.375rem',
h5: '1.25rem',
h6: '1.125rem',
'xl-alt': '5rem',
'lg-alt': '4.5rem',
'md-alt': '4rem',
'sm-alt': '3.5rem',
'xs-alt': '3rem',
},
weights: {
thin: 100,
@@ -224,7 +230,7 @@ const config = {
'color-hover': 'var(--c--theme--colors--primary-700)',
},
border: {
color: 'var(--c--theme--colors--primary-200)',
color: 'var(--c--theme--colors--greyscale-300)',
},
},
tertiary: {
@@ -379,8 +385,8 @@ const config = {
'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-300)',
'color-hover': 'var(--c--theme--colors--greyscale-300)',
},
color: 'var(--c--theme--colors--primary-text)',
},
+21 -21
View File
@@ -1,6 +1,6 @@
{
"name": "app-impress",
"version": "2.1.0",
"version": "2.0.1",
"private": true,
"scripts": {
"dev": "next dev",
@@ -21,42 +21,42 @@
"@blocknote/xl-docx-exporter": "0.21.0",
"@blocknote/xl-pdf-exporter": "0.21.0",
"@gouvfr-lasuite/integration": "1.0.2",
"@hocuspocus/provider": "2.15.1",
"@hocuspocus/provider": "2.15.0",
"@openfun/cunningham-react": "2.9.4",
"@react-pdf/renderer": "4.1.6",
"@sentry/nextjs": "8.52.0",
"@tanstack/react-query": "5.65.1",
"@sentry/nextjs": "8.47.0",
"@tanstack/react-query": "5.62.11",
"cmdk": "1.0.4",
"crisp-sdk-web": "1.0.25",
"docx": "9.1.1",
"i18next": "24.2.2",
"docx": "9.1.0",
"i18next": "24.2.0",
"i18next-browser-languagedetector": "8.0.2",
"idb": "8.0.1",
"lodash": "4.17.21",
"luxon": "3.5.0",
"next": "15.1.6",
"posthog-js": "1.211.3",
"next": "15.1.3",
"posthog-js": "1.204.0",
"react": "*",
"react-aria-components": "1.6.0",
"react-aria-components": "1.5.0",
"react-dom": "*",
"react-i18next": "15.4.0",
"react-intersection-observer": "9.15.1",
"react-select": "5.10.0",
"styled-components": "6.1.14",
"react-intersection-observer": "9.13.1",
"react-select": "5.9.0",
"styled-components": "6.1.13",
"use-debounce": "10.0.4",
"y-protocols": "1.0.6",
"yjs": "13.6.23",
"zustand": "5.0.3"
"yjs": "13.6.21",
"zustand": "5.0.2"
},
"devDependencies": {
"@svgr/webpack": "8.1.0",
"@tanstack/react-query-devtools": "5.65.1",
"@tanstack/react-query-devtools": "5.62.11",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.2.0",
"@testing-library/user-event": "14.6.1",
"@testing-library/react": "16.1.0",
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.14",
"@types/lodash": "4.17.15",
"@types/lodash": "4.17.13",
"@types/luxon": "3.4.2",
"@types/node": "*",
"@types/react": "18.3.12",
@@ -69,9 +69,9 @@
"jest-environment-jsdom": "29.7.0",
"node-fetch": "2.7.0",
"prettier": "3.4.2",
"stylelint": "16.14.1",
"stylelint-config-standard": "37.0.0",
"stylelint-prettier": "5.0.3",
"stylelint": "16.12.0",
"stylelint-config-standard": "36.0.1",
"stylelint-prettier": "5.0.2",
"typescript": "*",
"webpack": "5.97.1",
"workbox-webpack-plugin": "7.1.0"
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

@@ -0,0 +1,4 @@
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M47.2515 62.7264C49.7721 62.0196 52.1861 60.7298 53.7701 58.4505C55.3337 56.2187 55.774 53.3875 55.774 50.6632V10.749C55.774 10.0423 55.7356 9.33431 55.6503 8.63477C56.9023 9.12719 57.8672 9.94087 58.5451 11.0758C59.3448 12.3663 59.7447 14.0657 59.7447 16.1741V56.7153C59.7447 59.5689 59.0449 61.7046 57.6454 63.1223C56.2458 64.54 54.1374 65.2489 51.3202 65.2489H36.0065C36.3692 65.1854 36.7328 65.12 37.0972 65.0528C40.2928 64.4817 43.6701 63.7067 47.2256 62.7336L47.2515 62.7264Z" fill="#C9191E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4521 55.8703V15.2746C10.4521 12.8027 11.1156 10.9306 12.4424 9.65832C13.7874 8.38601 15.5686 7.68624 17.7861 7.55901C21.0577 7.35908 24.1567 7.08644 27.083 6.7411C30.0093 6.37758 32.8084 5.95954 35.4803 5.48697C38.1703 5.01439 40.7876 4.48729 43.3322 3.90567C45.9495 3.28769 47.958 3.59668 49.3575 4.83264C50.757 6.06859 51.4568 8.04067 51.4568 10.7489V50.663C51.4568 53.044 51.0479 54.8161 50.2299 55.9794C49.412 57.1608 48.0307 58.0242 46.0859 58.5695C42.6324 59.5146 39.379 60.2598 36.3254 60.8051C33.2719 61.3685 30.2911 61.7957 27.3829 62.0865C24.4748 62.3773 21.494 62.6045 18.4404 62.7681C15.914 62.9135 13.951 62.3864 12.5515 61.1868C11.1519 60.0053 10.4521 58.2332 10.4521 55.8703ZM20.4387 22.6454C24.4424 22.3905 28.1112 21.9992 31.4447 21.4709C32.2535 21.339 33.0611 21.2017 33.8657 21.0592C34.7076 20.9101 35.3165 20.1764 35.3165 19.3233C35.3165 18.2185 34.3166 17.3865 33.2308 17.5717C32.6071 17.6781 31.9801 17.7815 31.3497 17.8819C28.0496 18.4078 24.4029 18.7979 20.4098 19.052C19.8118 19.0906 19.336 19.2769 19.0302 19.6482C18.741 19.9994 18.5993 20.4218 18.5993 20.9033C18.5993 21.3928 18.7644 21.8188 19.0941 22.1691L19.0987 22.1737C19.456 22.531 19.91 22.6857 20.4387 22.6454ZM20.4374 31.6973C24.4416 31.4424 28.1108 31.051 31.4447 30.5227C34.7931 29.9768 38.1061 29.3397 41.3818 28.6117C42.0465 28.464 42.5444 28.2314 42.7935 27.8701C43.0383 27.5228 43.1594 27.1247 43.1594 26.6834C43.1594 26.1857 42.9698 25.757 42.601 25.4087C42.2038 25.0336 41.6486 24.9462 40.9915 25.0815L40.9873 25.0825C37.8658 25.7902 34.6533 26.4074 31.3497 26.9337C28.0496 27.4596 24.4029 27.8497 20.4098 28.1038C19.8118 28.1424 19.336 28.3287 19.0302 28.7C18.7424 29.0495 18.5993 29.4621 18.5993 29.9278C18.5993 30.4329 18.7625 30.8691 19.0939 31.2211L19.1034 31.2301C19.4595 31.5653 19.9088 31.7177 20.432 31.6976L20.4374 31.6973ZM20.4383 40.7488C24.4422 40.4758 28.111 40.0753 31.4446 39.547C34.7933 39.0192 38.1057 38.3913 41.3818 37.6633C42.0481 37.5152 42.5464 37.2729 42.7951 36.892C43.0376 36.5471 43.1594 36.1594 43.1594 35.735C43.1594 35.2373 42.9698 34.8086 42.601 34.4602C42.2038 34.0852 41.6486 33.9978 40.9915 34.1331L40.9885 34.1338C37.8666 34.8235 34.6537 35.4316 31.3497 35.958C28.0493 36.4839 24.4025 36.8832 20.4092 37.1554C19.8115 37.1941 19.3359 37.3804 19.0302 37.7516C18.7424 38.1011 18.5993 38.5137 18.5993 38.9794C18.5993 39.4845 18.7625 39.9206 19.0939 40.2727L19.1034 40.2816C19.4595 40.6168 19.9088 40.7693 20.432 40.7492L20.4383 40.7488ZM31.4447 48.5358C28.1112 49.0641 24.4424 49.4555 20.4388 49.7103C19.91 49.7506 19.456 49.596 19.0987 49.2386L19.0941 49.2339C18.7645 48.8837 18.5993 48.4577 18.5993 47.9682C18.5993 47.4867 18.741 47.0644 19.0302 46.7132C19.336 46.3418 19.812 46.1555 20.41 46.117C24.4031 45.8629 28.0496 45.4727 31.3497 44.9469C31.9801 44.8464 32.6071 44.743 33.2308 44.6366C34.3166 44.4514 35.3165 45.2834 35.3165 46.3882C35.3165 47.2413 34.7076 47.975 33.8657 48.1241C33.0611 48.2666 32.2535 48.404 31.4447 48.5358Z" fill="#000091"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long
@@ -14,12 +14,11 @@ import { useAuthStore } from './useAuthStore';
* When we will have a homepage design for non-authenticated users, we will remove this restriction to have
* the full website accessible without authentication.
*/
const regexpUrlsAuth = [/\/docs\/$/g, /^\/$/g];
const regexpUrlsAuth = [/\/docs\/$/g];
export const Auth = ({ children }: PropsWithChildren) => {
const { initAuth, initiated, authenticated, login, getAuthUrl } =
useAuthStore();
const { asPath, replace } = useRouter();
const { initAuth, initiated, authenticated, login } = useAuthStore();
const { asPath } = useRouter();
const [pathAllowed, setPathAllowed] = useState<boolean>(
!regexpUrlsAuth.some((regexp) => !!asPath.match(regexp)),
@@ -42,18 +41,6 @@ export const Auth = ({ children }: PropsWithChildren) => {
login();
}, [authenticated, pathAllowed, login, initiated]);
// Redirect to the path before login
useEffect(() => {
if (!authenticated) {
return;
}
const authUrl = getAuthUrl();
if (authUrl) {
void replace(authUrl);
}
}, [authenticated, getAuthUrl, replace]);
if ((!initiated && pathAllowed) || (!authenticated && !pathAllowed)) {
return (
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
@@ -9,7 +9,11 @@ export const ButtonLogin = () => {
if (!authenticated) {
return (
<Button onClick={login} color="primary-text" aria-label={t('Login')}>
<Button
onClick={() => login(true)}
color="primary-text"
aria-label={t('Login')}
>
{t('Login')}
</Button>
);
@@ -11,7 +11,7 @@ interface AuthStore {
authenticated: boolean;
initAuth: () => void;
logout: () => void;
login: () => void;
login: (directLogin?: boolean) => void;
setAuthUrl: (url: string) => void;
getAuthUrl: () => string | undefined;
userData?: User;
@@ -37,10 +37,18 @@ export const useAuthStore = create<AuthStore>((set, get) => ({
set({ initiated: true });
});
},
login: () => {
get().setAuthUrl(window.location.pathname);
login: (directLogin?: boolean) => {
if (directLogin) {
get().setAuthUrl(window.location.pathname);
window.location.replace(`${baseApiUrl()}authenticate/`);
return;
}
window.location.replace(`${baseApiUrl()}authenticate/`);
if (window.location.pathname !== '/') {
get().setAuthUrl(window.location.pathname);
window.location.replace(`/`);
return;
}
},
logout: () => {
terminateCrispSession();
@@ -71,6 +71,7 @@
--c--theme--colors--danger-text: var(--c--theme--colors--greyscale-000);
--c--theme--colors--card-border: #ededed;
--c--theme--colors--primary-bg: #fafafa;
--c--theme--colors--primary-action: #1212ff;
--c--theme--colors--primary-050: #f5f5fe;
--c--theme--colors--primary-150: #e5eefa;
--c--theme--colors--primary-950: #1b1b35;
@@ -122,6 +123,11 @@
--c--theme--font--sizes--ml: 0.938rem;
--c--theme--font--sizes--xl: 1.25rem;
--c--theme--font--sizes--t: 0.6875rem;
--c--theme--font--sizes--xl-alt: 5rem;
--c--theme--font--sizes--lg-alt: 4.5rem;
--c--theme--font--sizes--md-alt: 4rem;
--c--theme--font--sizes--sm-alt: 3.5rem;
--c--theme--font--sizes--xs-alt: 3rem;
--c--theme--font--weights--thin: 100;
--c--theme--font--weights--light: 300;
--c--theme--font--weights--regular: 400;
@@ -316,7 +322,7 @@
--c--theme--colors--primary-700
);
--c--components--button--secondary--border--color: var(
--c--theme--colors--primary-200
--c--theme--colors--greyscale-300
);
--c--components--button--tertiary--color: var(
--c--theme--colors--primary-text
@@ -501,10 +507,10 @@
--c--components--button--secondary--background--color-hover: #f6f6f6;
--c--components--button--secondary--background--color-active: #ededed;
--c--components--button--secondary--border--color: var(
--c--theme--colors--primary-600
--c--theme--colors--greyscale-300
);
--c--components--button--secondary--border--color-hover: var(
--c--theme--colors--primary-600
--c--theme--colors--greyscale-300
);
--c--components--button--secondary--color: var(
--c--theme--colors--primary-text
@@ -874,6 +880,10 @@
color: var(--c--theme--colors--primary-bg);
}
.clr-primary-action {
color: var(--c--theme--colors--primary-action);
}
.clr-primary-050 {
color: var(--c--theme--colors--primary-050);
}
@@ -1302,6 +1312,10 @@
background-color: var(--c--theme--colors--primary-bg);
}
.bg-primary-action {
background-color: var(--c--theme--colors--primary-action);
}
.bg-primary-050 {
background-color: var(--c--theme--colors--primary-050);
}
@@ -1550,6 +1564,31 @@
letter-spacing: var(--c--theme--font--letterspacings--t);
}
.fs-xl-alt {
font-size: var(--c--theme--font--sizes--xl-alt);
letter-spacing: var(--c--theme--font--letterspacings--xl-alt);
}
.fs-lg-alt {
font-size: var(--c--theme--font--sizes--lg-alt);
letter-spacing: var(--c--theme--font--letterspacings--lg-alt);
}
.fs-md-alt {
font-size: var(--c--theme--font--sizes--md-alt);
letter-spacing: var(--c--theme--font--letterspacings--md-alt);
}
.fs-sm-alt {
font-size: var(--c--theme--font--sizes--sm-alt);
letter-spacing: var(--c--theme--font--letterspacings--sm-alt);
}
.fs-xs-alt {
font-size: var(--c--theme--font--sizes--xs-alt);
letter-spacing: var(--c--theme--font--letterspacings--xs-alt);
}
.f-base {
font-family: var(--c--theme--font--families--base);
}
@@ -75,6 +75,7 @@ export const tokens = {
'danger-text': '#fff',
'card-border': '#ededed',
'primary-bg': '#FAFAFA',
'primary-action': '#1212FF',
'primary-050': '#F5F5FE',
'primary-150': '#E5EEFA',
'primary-950': '#1B1B35',
@@ -129,6 +130,11 @@ export const tokens = {
ml: '0.938rem',
xl: '1.25rem',
t: '0.6875rem',
'xl-alt': '5rem',
'lg-alt': '4.5rem',
'md-alt': '4rem',
'sm-alt': '3.5rem',
'xs-alt': '3rem',
},
weights: {
thin: 100,
@@ -315,7 +321,7 @@ export const tokens = {
color: 'white',
'color-hover': 'var(--c--theme--colors--primary-700)',
},
border: { color: 'var(--c--theme--colors--primary-200)' },
border: { color: 'var(--c--theme--colors--greyscale-300)' },
},
tertiary: {
color: 'var(--c--theme--colors--primary-text)',
@@ -502,8 +508,8 @@ export const tokens = {
secondary: {
background: { 'color-hover': '#F6F6F6', '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-300)',
'color-hover': 'var(--c--theme--colors--greyscale-300)',
},
color: 'var(--c--theme--colors--primary-text)',
},
@@ -1,10 +1,4 @@
import {
BlockNoteEditor as BlockNoteEditorCore,
BlockNoteSchema,
Dictionary,
defaultBlockSpecs,
locales,
} from '@blocknote/core';
import { Dictionary, locales } from '@blocknote/core';
import '@blocknote/core/fonts/inter.css';
import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css';
@@ -12,6 +6,7 @@ import { useCreateBlockNote } from '@blocknote/react';
import { HocuspocusProvider } from '@hocuspocus/provider';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import * as Y from 'yjs';
import { Box, TextErrors } from '@/components';
@@ -22,27 +17,95 @@ import { useUploadFile } from '../hook';
import { useHeadings } from '../hook/useHeadings';
import useSaveDoc from '../hook/useSaveDoc';
import { useEditorStore } from '../stores';
import { cssEditor } from '../styles';
import { randomColor } from '../utils';
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
import { BlockNoteToolbar } from './BlockNoteToolbar';
import { AlertBlock, DividerBlock, QuoteBlock } from './custom-blocks';
export const schema = BlockNoteSchema.create({
blockSpecs: {
...defaultBlockSpecs,
alert: AlertBlock,
quote: QuoteBlock,
divider: DividerBlock,
},
});
const cssEditor = (readonly: boolean) => css`
&,
& > .bn-container,
& .ProseMirror {
height: 100%;
export type DocsBlockNoteEditor = BlockNoteEditorCore<
typeof schema.blockSchema,
typeof schema.inlineContentSchema,
typeof schema.styleSchema
>;
.bn-side-menu[data-block-type='heading'][data-level='1'] {
height: 50px;
}
.bn-side-menu[data-block-type='heading'][data-level='2'] {
height: 43px;
}
.bn-side-menu[data-block-type='heading'][data-level='3'] {
height: 35px;
}
h1 {
font-size: 1.875rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
a {
color: var(--c--theme--colors--greyscale-500);
cursor: pointer;
}
.bn-block-group
.bn-block-group
.bn-block-outer:not([data-prev-depth-changed]):before {
border-left: none;
}
}
.bn-editor {
color: var(--c--theme--colors--greyscale-700);
}
.bn-block-outer:not(:first-child) {
&:has(h1) {
padding-top: 32px;
}
&:has(h2) {
padding-top: 24px;
}
&:has(h3) {
padding-top: 16px;
}
}
& .bn-inline-content code {
background-color: gainsboro;
padding: 2px;
border-radius: 4px;
}
@media screen and (width <= 560px) {
& .bn-editor {
${readonly && `padding-left: 10px;`}
}
.bn-side-menu[data-block-type='heading'][data-level='1'] {
height: 46px;
}
.bn-side-menu[data-block-type='heading'][data-level='2'] {
height: 40px;
}
.bn-side-menu[data-block-type='heading'][data-level='3'] {
height: 40px;
}
& .bn-editor h1 {
font-size: 1.6rem;
}
& .bn-editor h2 {
font-size: 1.35rem;
}
& .bn-editor h3 {
font-size: 1.2rem;
}
.bn-block-content[data-is-empty-and-focused][data-content-type='paragraph']
.bn-inline-content:has(> .ProseMirror-trailingBreak:only-child)::before {
font-size: 14px;
}
}
`;
interface BlockNoteEditorProps {
doc: Doc;
@@ -101,7 +164,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
},
},
dictionary: locales[lang as keyof typeof locales] as Dictionary,
schema,
uploadFile,
},
[collabName, lang, provider, uploadFile],
@@ -136,10 +198,8 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
editor={editor}
formattingToolbar={false}
editable={!readOnly}
slashMenu={false}
theme="light"
>
<BlockNoteSuggestionMenu />
<BlockNoteToolbar />
</BlockNoteView>
</Box>
@@ -165,7 +225,6 @@ export const BlockNoteEditorVersion = ({
},
provider: undefined,
},
schema,
},
[initialContent],
);
@@ -1,39 +0,0 @@
import { combineByGroup, filterSuggestionItems } from '@blocknote/core';
import '@blocknote/mantine/style.css';
import {
SuggestionMenuController,
getDefaultReactSlashMenuItems,
useBlockNoteEditor,
} from '@blocknote/react';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { DocsBlockNoteEditor } from './BlockNoteEditor';
import { insertAlert, insertDivider, insertQuote } from './custom-blocks';
export const BlockNoteSuggestionMenu = () => {
const editor = useBlockNoteEditor() as DocsBlockNoteEditor;
const { t } = useTranslation();
const getSlashMenuItems = useMemo(() => {
return async (query: string) =>
Promise.resolve(
filterSuggestionItems(
combineByGroup(
getDefaultReactSlashMenuItems(editor),
[insertAlert(editor, t)],
[insertQuote(editor, t)],
[insertDivider(editor, t)],
),
query,
),
);
}, [editor, t]);
return (
<SuggestionMenuController
triggerCharacter="/"
getItems={getSlashMenuItems}
/>
);
};
@@ -1,179 +0,0 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { defaultProps, insertOrUpdateBlock } from '@blocknote/core';
import { createReactBlockSpec } from '@blocknote/react';
import { Menu } from '@mantine/core';
import { TFunction } from 'i18next';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { DocsBlockNoteEditor } from '../BlockNoteEditor';
// The types of alerts that users can choose from.
export const alertTypes = [
{
title: 'Warning',
value: 'warning',
icon: 'warning',
color: 'warning-500',
backgroundColor: 'warning-300',
},
{
title: 'Error',
value: 'danger',
icon: 'error',
color: 'danger-500',
backgroundColor: 'danger-300',
},
{
title: 'Info',
value: 'info',
icon: 'info',
color: 'info-500',
backgroundColor: 'info-300',
},
{
title: 'Success',
value: 'success',
icon: 'check_circle',
color: 'success-500',
backgroundColor: 'success-100',
},
] as const;
// The Alert block.
export const AlertBlock = createReactBlockSpec(
{
type: 'alert',
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
type: {
default: 'warning',
values: ['warning', 'danger', 'info', 'success'],
},
},
content: 'inline',
},
{
render: (props) => {
const { colorsTokens } = useCunninghamTheme();
const { t } = useTranslation();
let alertType = alertTypes.find(
(a) => a.value === props.block.props.type,
);
if (!alertType) {
alertType = alertTypes[0];
}
return (
<Box
className="alert"
data-alert-type={props.block.props.type}
$direction="row"
$justify="center"
$align="center"
$radius="4px"
$padding="4px"
$background={colorsTokens()[alertType.backgroundColor]}
$minHeight="48px"
$css={css`
flex-grow: 1;
`}
>
<Menu withinPortal={false}>
<Menu.Target>
<Box
className="alert-icon-wrapper"
$margin={{ horizontal: '12px' }}
$radius="16px"
$justify="center"
$align="center"
$height="24px"
$width="24px"
contentEditable={false}
$css="user-select: none; cursor: pointer;"
>
<Text
$isMaterialIcon
$theme={alertType.value}
$variation="500"
$size="20px"
>
{alertType.icon}
</Text>
</Box>
</Menu.Target>
<Menu.Dropdown style={{ zIndex: 9999 }}>
<Menu.Label>{t('Alert Type')}</Menu.Label>
<Menu.Divider />
{alertTypes.map((type) => (
<Menu.Item
key={type.value}
leftSection={
<Text
$isMaterialIcon
$color={colorsTokens()[type.color]}
$size="16px"
>
{type.icon}
</Text>
}
onClick={() =>
props.editor.updateBlock(props.block, {
type: 'alert',
props: { type: type.value },
})
}
>
{t(type.title)}
</Menu.Item>
))}
</Menu.Dropdown>
</Menu>
<Box
className="inline-content"
$css={css`
flex-grow: 1;
& * {
color: ${colorsTokens()[alertType.color]};
}
`}
ref={props.contentRef}
/>
</Box>
);
},
},
);
export const insertAlert = (
editor: DocsBlockNoteEditor,
t: TFunction<'translation', undefined>,
) => ({
title: t('Alert'),
onItemClick: () => {
insertOrUpdateBlock(editor, {
type: 'alert',
});
},
aliases: [
'alert',
'notification',
'emphasize',
'warning',
'error',
'info',
'success',
],
group: t('Others'),
icon: (
<Text $isMaterialIcon $size="18px">
warning
</Text>
),
subtext: t('Add a colored alert box'),
});
@@ -1,55 +0,0 @@
import { defaultProps, insertOrUpdateBlock } from '@blocknote/core';
import { createReactBlockSpec } from '@blocknote/react';
import { TFunction } from 'i18next';
import { useCunninghamTheme } from '@/cunningham';
import { DocsBlockNoteEditor } from '../BlockNoteEditor';
export const DividerBlock = createReactBlockSpec(
{
type: 'divider',
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
},
content: 'none',
},
{
render: () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { colorsTokens } = useCunninghamTheme();
return (
<div
style={{
width: '100%',
height: '2px',
backgroundColor: colorsTokens()['greyscale-300'],
margin: '1rem 0',
}}
/>
);
},
},
);
export const insertDivider = (
editor: DocsBlockNoteEditor,
t: TFunction<'translation', undefined>,
) => ({
title: t('Divider'),
onItemClick: () => {
insertOrUpdateBlock(editor, {
type: 'divider',
});
},
aliases: ['divider', 'hr', 'horizontal rule', 'line', 'separator'],
group: t('Others'),
icon: (
<span className="material-icons" style={{ fontSize: '18px' }}>
remove
</span>
),
subtext: t('Add a horizontal line'),
});
@@ -1,63 +0,0 @@
import { defaultProps, insertOrUpdateBlock } from '@blocknote/core';
import { createReactBlockSpec } from '@blocknote/react';
import { TFunction } from 'i18next';
import React from 'react';
import { useCunninghamTheme } from '@/cunningham';
import { DocsBlockNoteEditor } from '../BlockNoteEditor';
export const QuoteBlock = createReactBlockSpec(
{
type: 'quote',
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
},
content: 'inline',
},
{
render: (props) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { colorsTokens } = useCunninghamTheme();
return (
<div
className="inline-content"
style={{
borderLeft: `4px solid ${colorsTokens()['greyscale-300']}`,
margin: '0 0 1rem 0',
padding: '0.5rem 1rem',
color: colorsTokens()['greyscale-600'],
fontStyle: 'italic',
flexGrow: 1,
}}
ref={props.contentRef}
/>
);
},
parse: () => {
return undefined;
},
},
);
export const insertQuote = (
editor: DocsBlockNoteEditor,
t: TFunction<'translation', undefined>,
) => ({
title: t('Quote'),
onItemClick: () => {
insertOrUpdateBlock(editor, {
type: 'quote',
});
},
aliases: ['quote', 'blockquote', 'citation'],
group: t('Others'),
icon: (
<span className="material-icons" style={{ fontSize: '18px' }}>
format_quote
</span>
),
subtext: t('Add a quote block'),
});
@@ -1,3 +0,0 @@
export * from './AlertBlock';
export * from './DividerBlock';
export * from './QuoteBlock';
@@ -1,9 +1,9 @@
import { BlockNoteEditor } from '@blocknote/core';
import { useEffect } from 'react';
import { DocsBlockNoteEditor } from '../components/BlockNoteEditor';
import { useHeadingStore } from '../stores';
export const useHeadings = (editor: DocsBlockNoteEditor) => {
export const useHeadings = (editor: BlockNoteEditor) => {
const { setHeadings, resetHeadings } = useHeadingStore();
useEffect(() => {
@@ -1,10 +1,9 @@
import { BlockNoteEditor } from '@blocknote/core';
import { create } from 'zustand';
import { DocsBlockNoteEditor } from '../components/BlockNoteEditor';
export interface UseEditorstore {
editor?: DocsBlockNoteEditor;
setEditor: (editor: DocsBlockNoteEditor | undefined) => void;
editor?: BlockNoteEditor;
setEditor: (editor: BlockNoteEditor | undefined) => void;
}
export const useEditorStore = create<UseEditorstore>((set) => ({
@@ -1,6 +1,6 @@
import { BlockNoteEditor } from '@blocknote/core';
import { create } from 'zustand';
import { DocsBlockNoteEditor } from '../components/BlockNoteEditor';
import { HeadingBlock } from '../types';
const recursiveTextContent = (content: HeadingBlock['content']): string => {
@@ -21,7 +21,7 @@ const recursiveTextContent = (content: HeadingBlock['content']): string => {
export interface UseHeadingStore {
headings: HeadingBlock[];
setHeadings: (editor: DocsBlockNoteEditor) => void;
setHeadings: (editor: BlockNoteEditor) => void;
resetHeadings: () => void;
}
@@ -1,96 +0,0 @@
import { css } from 'styled-components';
export const cssEditor = (readonly: boolean) => css`
&,
& > .bn-container,
& .ProseMirror {
height: 100%;
.bn-side-menu[data-block-type='alert'] {
height: 55px;
}
.bn-side-menu[data-block-type='divider'] {
height: 40px;
}
.bn-side-menu[data-block-type='quote'] {
height: 46px;
}
.bn-side-menu[data-block-type='heading'][data-level='1'] {
height: 50px;
}
.bn-side-menu[data-block-type='heading'][data-level='2'] {
height: 43px;
}
.bn-side-menu[data-block-type='heading'][data-level='3'] {
height: 35px;
}
h1 {
font-size: 1.875rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
a {
color: var(--c--theme--colors--greyscale-500);
cursor: pointer;
}
.bn-block-group
.bn-block-group
.bn-block-outer:not([data-prev-depth-changed]):before {
border-left: none;
}
}
.bn-editor {
color: var(--c--theme--colors--greyscale-700);
}
.bn-block-outer:not(:first-child) {
&:has(h1) {
padding-top: 32px;
}
&:has(h2) {
padding-top: 24px;
}
&:has(h3) {
padding-top: 16px;
}
}
& .bn-inline-content code {
background-color: gainsboro;
padding: 2px;
border-radius: 4px;
}
@media screen and (width <= 560px) {
& .bn-editor {
${readonly && `padding-left: 10px;`}
}
.bn-side-menu[data-block-type='heading'][data-level='1'] {
height: 46px;
}
.bn-side-menu[data-block-type='heading'][data-level='2'] {
height: 40px;
}
.bn-side-menu[data-block-type='heading'][data-level='3'] {
height: 40px;
}
& .bn-editor h1 {
font-size: 1.6rem;
}
& .bn-editor h2 {
font-size: 1.35rem;
}
& .bn-editor h3 {
font-size: 1.2rem;
}
.bn-block-content[data-is-empty-and-focused][data-content-type='paragraph']
.bn-inline-content:has(> .ProseMirror-trailingBreak:only-child)::before {
font-size: 14px;
}
}
`;
@@ -179,7 +179,9 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
$gap="1rem"
>
<Text $variation="600" $size="sm">
{t('Download your document in a .docx or .pdf format.')}
{t(
'Upload your docs to a Microsoft Word, Open Office or PDF document.',
)}
</Text>
<Select
clearable={false}
@@ -195,7 +197,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
fullWidth
label={t('Format')}
options={[
{ label: t('Docx'), value: DocDownloadFormat.DOCX },
{ label: t('Word / Open Office'), value: DocDownloadFormat.DOCX },
{ label: t('PDF'), value: DocDownloadFormat.PDF },
]}
value={format}
@@ -1,11 +1,10 @@
import { BlockNoteEditor } from '@blocknote/core';
import { useState } from 'react';
import { BoxButton, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useResponsiveStore } from '@/stores';
import { DocsBlockNoteEditor } from '../../doc-editor/components/BlockNoteEditor';
const leftPaddingMap: { [key: number]: string } = {
3: '1.5rem',
2: '0.9rem',
@@ -18,7 +17,7 @@ export type HeadingsHighlight = {
}[];
interface HeadingProps {
editor: DocsBlockNoteEditor;
editor: BlockNoteEditor;
level: number;
text: string;
headingId: string;
@@ -60,14 +60,13 @@ export const Header = () => {
}
/>
)}
<StyledLink href="/">
<StyledLink href="/docs">
<Box
$align="center"
$gap={spacings['3xs']}
$direction="row"
$position="relative"
$height="fit-content"
$margin={{ top: 'auto' }}
>
<Image priority src={IconDocs} alt={t('Docs Logo')} width={25} />
<Title />
@@ -1,26 +1,47 @@
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, Text } from '@/components/';
import { useCunninghamTheme } from '@/cunningham';
const Title = () => {
type TitleProps = {
size?: 'sm' | 'md';
};
const Title = ({ size = 'sm' }: TitleProps) => {
const { t } = useTranslation();
const theme = useCunninghamTheme();
const spacings = theme.spacingsTokens();
const colors = theme.colorsTokens();
return (
<Box $direction="row" $align="center" $gap={spacings['2xs']}>
<Text $margin="none" as="h2" $color="#000091" $zIndex={1} $size="1.30rem">
<Box
$direction="row"
$align="center"
$gap={size === 'sm' ? spacings['2xs'] : '1.125rem'}
>
<Text
$margin="none"
as="h2"
$color="#000091"
$zIndex={1}
$size={size === 'sm' ? '1.3rem' : spacings['xxl']}
>
{t('Docs')}
</Text>
<Text
$padding={{ horizontal: 'xs', vertical: '1px' }}
$size="11px"
$padding={{
horizontal: size === 'sm' ? '2xs' : 'sm',
vertical: size === 'sm' ? '3xs' : 'xs',
}}
$size={size === 'sm' ? '0.68rem' : spacings['md']}
$theme="primary"
$variation="500"
$weight="bold"
$radius="12px"
$radius={size === 'sm' ? '0.75rem' : '1.5rem'}
$css={css`
line-height: ${size === 'sm' ? '1rem' : '1.25rem'};
`}
$background={colors['primary-200']}
>
BETA
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

@@ -0,0 +1,84 @@
import { Button } from '@openfun/cunningham-react';
import Image from 'next/image';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, Icon, Text } from '@/components';
import { ProConnectButton } from '@/components/ProConnectButton';
import { useCunninghamTheme } from '@/cunningham';
import { useResponsiveStore } from '@/stores';
import firstImage from '../assets/banner.png';
import DocLogo from '../assets/logo-docs.png';
export default function HomeBanner() {
const { t } = useTranslation();
const { spacingsTokens } = useCunninghamTheme();
const spacings = spacingsTokens();
const { isDesktop } = useResponsiveStore();
return (
<Box $maxWidth="78rem" $width="100%" $justify="center" $align="center">
<Box
$width="100%"
$padding={{ top: 'xxxl', bottom: 'calc(3.5rem + 94px)' }}
$justify="space-between"
$align="center"
$height="calc(100dvh - 94px)"
$position="relative"
$direction={isDesktop ? 'row' : 'column'}
>
<Box
$width="100%"
$justify="center"
$align="center"
$padding={{ horizontal: '10px' }}
$gap={spacings['sm']}
>
<Image src={DocLogo} alt="DocLogo" />
<Text
$size={isDesktop ? 'xs-alt' : '2.3rem'}
$variation="800"
$weight="bold"
$textAlign="center"
$css={css`
line-height: 56px;
`}
>
{t('Collaborative writing made simple')}
</Text>
<Text $size="lg" $variation="700" $textAlign="center">
{t(
'Collaborate and write in real time, without layout constraints.',
)}
</Text>
<ProConnectButton />
</Box>
{isDesktop && (
<Image src={firstImage} alt="first" style={{ maxWidth: '50%' }} />
)}
<Box
$position="absolute"
$padding="base"
$justify="center"
$align="center"
$css={css`
bottom: 0;
left: 0;
right: 0;
`}
>
<Button
href="#docs-app-info"
color="secondary"
icon={
<Icon $theme="primary" $variation="800" iconName="expand_more" />
}
>
{t('See more')}
</Button>
</Box>
</Box>
</Box>
);
}
@@ -0,0 +1,125 @@
import { useTranslation } from 'react-i18next';
import { createGlobalStyle } from 'styled-components';
import IconDocs from '@/assets/common/logo-docs.svg';
import { Box, Text } from '@/components';
import { ProConnectButton } from '@/components/ProConnectButton';
import { useCunninghamTheme } from '@/cunningham';
import { Footer } from '@/features/footer';
import Title from '@/features/header/components/Title/Title';
import { useLanguage } from '@/i18n/hooks/useLanguage';
import { useResponsiveStore } from '@/stores';
import SC1Responsive from '../assets/SC1-responsive.png';
import SC2 from '../assets/SC2.png';
import SC3 from '../assets/SC3.png';
import SC4Responsive from '../assets/SC4-responsive.png';
import SC4 from '../assets/SC4.png';
import HomeBanner from './HomeBanner';
import { HomeHeader } from './HomeHeader';
import { HomeSection } from './HomeSection';
const GlobalHomePageStyle = createGlobalStyle`
html {
scroll-behavior: smooth;
}
`;
export default function HomeContent() {
const { t } = useTranslation();
const { isDesktop } = useResponsiveStore();
const { spacingsTokens } = useCunninghamTheme();
const spacings = spacingsTokens();
const lang = useLanguage();
return (
<>
<GlobalHomePageStyle />
<Box $background="white">
<HomeHeader />
<Box $align="center" $justify="center">
<Box
$maxWidth="78rem"
$width="100%"
$justify="center"
$align="center"
>
<HomeBanner />
<Box
id="docs-app-info"
$gap={isDesktop ? '230px' : '115px'}
$padding={{
vertical: spacings['6xl'],
}}
>
<HomeSection
isColumn={true}
illustration={isDesktop ? undefined : SC1Responsive}
video={
isDesktop ? `assets/SC1-${lang.language}.webm` : undefined
}
title={t('An uncompromising writing experience.')}
tag={t('Write')}
description={t(
'Docs offers an intuitive writing experience. Its minimalist interface favors content over layout, while offering the essentials: media import, offline mode and keyboard shortcuts for greater efficiency.',
)}
/>
<HomeSection
isColumn={false}
illustration={SC2}
title={t('Simple and secure collaboration.')}
tag={t('Collaborate')}
description={t(
'Docs makes real-time collaboration simple. Invite collaborators - public officials or external partners - with one click to see their changes live, while maintaining precise access control for data security.',
)}
/>
<HomeSection
isColumn={false}
reverse={true}
illustration={SC3}
title={t('Flexible export.')}
tag={t('Export')}
description={t(
'To facilitate the circulation of documents, Docs allows you to export your content to the most common formats: PDF, Word or OpenDocument.',
)}
/>
<HomeSection
illustration={isDesktop ? SC4 : SC4Responsive}
title={t('A new way to organize knowledge.')}
tag={t('Organize')}
availableSoon={true}
description={t(
'Docs transforms your documents into knowledge bases thanks to subpages, powerful search and the ability to pin your important documents.',
)}
/>
<Box
$gap={spacings['md']}
$justify="center"
$align="center"
$padding={{ vertical: '140px' }}
>
<Box
$align="center"
$gap={spacings['3xs']}
$direction="row"
$position="relative"
$height="fit-content"
>
<IconDocs />
<Title size="md" />
</Box>
<Text $size="md" $variation="1000" $textAlign="center">
{t('Docs is already available, log in to use it now.')}
</Text>
<ProConnectButton />
</Box>
</Box>
</Box>
</Box>
<Footer />
</Box>
</>
);
}
@@ -0,0 +1,56 @@
import Image from 'next/image';
import IconDocs from '@/assets/common/logo-docs-sm.png';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { LaGaufre } from '@/features/header/components/LaGaufre';
import Title from '@/features/header/components/Title/Title';
import { LanguagePicker } from '@/features/language';
import { useResponsiveStore } from '@/stores';
export const HomeHeader = () => {
const { isDesktop } = useResponsiveStore();
const { themeTokens, spacingsTokens } = useCunninghamTheme();
const logo = themeTokens().logo;
const spacings = spacingsTokens();
return (
<Box
$direction="row"
$justify="space-between"
as="header"
$align="center"
$width="100%"
$padding={{ horizontal: '18px', vertical: 'base' }}
>
<Box $align="center" $gap="3rem" $direction="row">
{logo && (
<Image
priority
src={logo.src}
alt={logo.alt}
width={0}
height={0}
style={{ width: 109, height: 'auto' }}
/>
)}
{isDesktop && (
<Box
$align="center"
$gap={spacings['3xs']}
$direction="row"
$position="relative"
$height="fit-content"
>
<Image src={IconDocs} alt="Docs app logo" />
<Title />
</Box>
)}
</Box>
<Box $direction="row" $gap="1rem" $align="center">
<LanguagePicker />
<LaGaufre />
</Box>
</Box>
);
};
@@ -0,0 +1,143 @@
import Image, { ImageProps } from 'next/image';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useResponsiveStore } from '@/stores';
export type HomeSectionProps = {
illustration?: ImageProps['src'];
title: string;
description: string;
tag: string;
availableSoon?: boolean;
isColumn?: boolean;
video?: string;
reverse?: boolean;
};
export const HomeSection = ({
illustration,
title,
description,
tag,
reverse = false,
isColumn = true,
availableSoon = false,
video,
}: HomeSectionProps) => {
const { t } = useTranslation();
const { spacingsTokens } = useCunninghamTheme();
const spacings = spacingsTokens();
const { isDesktop } = useResponsiveStore();
const direction = useMemo(() => {
if (!isDesktop) {
return 'column';
} else if (isColumn) {
return reverse ? 'column-reverse' : 'column';
}
return reverse ? 'row-reverse' : 'row';
}, [isColumn, isDesktop, reverse]);
return (
<Box
$direction={direction}
$gap={spacings['lg']}
$padding={{
horizontal: isDesktop ? '6xl' : spacings['md'],
}}
$align={isDesktop ? 'flex-start' : 'center'}
$justify={isDesktop ? 'flex-start' : 'center'}
>
<Box $gap={spacings['sm']} $maxWidth="850px" $width="100%">
<Box $direction="row" $gap={spacings['sm']} $wrap="wrap">
<SectionTag tag={tag} />
{availableSoon && (
<SectionTag tag={t('Available soon')} availableSoon />
)}
</Box>
<Text
$css={css`
line-height: 50px;
`}
$variation="1000"
$weight="bold"
$size={isDesktop ? 'xs-alt' : 'h1'}
>
{title}
</Text>
<Text $variation="700" $weight="400" $size="md">
{description}
</Text>
</Box>
{video && (
<video
preload="none"
loop
muted
autoPlay
src={video}
style={{
width: '100%',
maxWidth: !isDesktop ? 'calc(100dvw - 50px)' : '1200px',
height: 'auto',
}}
>
<source src={video} type="video/webm" />
</video>
)}
{illustration && (
<Image
src={illustration}
alt="SC4Illustration"
style={{
width: '100%',
maxWidth: !isDesktop ? 'calc(100dvw - 50px)' : '1200px',
height: 'auto',
}}
/>
)}
</Box>
);
};
const SectionTag = ({
tag,
availableSoon,
}: {
tag: string;
availableSoon?: boolean;
}) => {
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
const spacings = spacingsTokens();
const colors = colorsTokens();
return (
<Box
$background={
!availableSoon ? colors['primary-100'] : colors['warning-100']
}
$padding={{ horizontal: spacings['sm'], vertical: '6px' }}
$css={css`
align-self: flex-start;
border-radius: 4px;
`}
>
<Text
$size="md"
$variation={availableSoon ? '600' : '800'}
$weight="bold"
$theme={availableSoon ? 'warning' : 'primary'}
>
{tag}
</Text>
</Box>
);
};
@@ -9,7 +9,7 @@ import { LeftPanelFavorites } from './LeftPanelFavorites';
export const LeftPanelContent = () => {
const router = useRouter();
const isHome = router.pathname === '/';
const isHome = router.pathname === '/docs';
const isDoc = router.pathname === '/docs/[id]';
return (
@@ -81,6 +81,7 @@
"No active search": "Keine aktive Suche",
"No document found": "Kein Dokument gefunden",
"No documents found": "Keine Dokumente gefunden",
"No editor found": "Kein Editor gefunden",
"No versions": "Keine Versionen",
"Nothing exceptional, no special privileges related to a .gouv.fr.": "Nichts Außergewöhnliches, keine besonderen Privilegien im Zusammenhang mit .gouv.fr.",
"Offline ?!": "Offline?!",
@@ -139,6 +140,7 @@
"Unpin": "Lösen",
"Untitled document": "Unbenanntes Dokument",
"Updated at": "Aktualisiert am",
"Upload your docs to a Microsoft Word, Open Office or PDF document.": "Laden Sie Ihre Dokumente zu einem Microsoft Word, Open Office oder PDF Dokument hoch.",
"Use as prompt": "Als Prompt verwenden",
"Version history": "Versionsverlauf",
"Version restored successfully": "Version erfolgreich wiederhergestellt",
@@ -156,13 +158,7 @@
"you have reported to the website manager a lack of accessibility that prevents you from accessing content or one of the services of the portal and you have not received a satisfactory response.": "sie haben dem Website-Manager einen Mangel an Barrierefreiheit gemeldet, der Ihnen den Zugriff auf Inhalte oder einen der Dienste des Portals verwehrt, und Sie haben keine zufriedenstellende Antwort erhalten."
}
},
"en": {
"translation": {
"Shared with {{count}} users_many": "Shared with {{count}} users",
"Shared with {{count}} users_one": "Shared with {{count}} user",
"Shared with {{count}} users_other": "Shared with {{count}} users"
}
},
"en": { "translation": {} },
"fr": {
"translation": {
"\"{{email}}\" is already invited to the document.": "\"{{email}}\" est déjà invité à accéder au document.",
@@ -171,8 +167,6 @@
"AI seems busy! Please try again.": "L'IA semble occupée ! Veuillez réessayer.",
"Accessibility": "Accessibilité",
"Accessibility statement": "Déclaration d'accessibilité",
"Accessible to anyone": "Accessible à tout le monde",
"Accessible to authenticated users": "Accessible aux utilisateurs authentifiés",
"Add": "Ajouter",
"Address:": "Adresse :",
"Administrator": "Administrateur",
@@ -209,14 +203,11 @@
"Docs: Your new companion to collaborate on documents efficiently, intuitively, and securely.": "Docs : Votre nouveau compagnon pour collaborer sur des documents efficacement, intuitivement et en toute sécurité.",
"Document owner": "Propriétaire du document",
"Document title updated successfully": "Titre du document mis à jour avec succès",
"Docx": "Docx",
"Download": "Télécharger",
"Download your document in a .docx or .pdf format.": "Téléchargez votre document au format .docx ou .pdf.",
"E-mail:": "E-mail:",
"Edition": "Édition",
"Editor": "Éditeur",
"Editor unavailable": "Éditeur indisponible",
"Empty template": "Sans modèle",
"Error during delete invitation": "Erreur lors de la suppression de l'invitation",
"Error during invitation update": "Erreur lors de la mise à jour de l'invitation",
"Error during update invitation": "Erreur lors de la mise à jour de l'invitation",
@@ -259,6 +250,7 @@
"No active search": "Aucune recherche active",
"No document found": "Aucun document trouvé",
"No documents found": "Aucun document trouvé",
"No editor found": "Pas d'éditeur trouvé",
"No versions": "Aucune version",
"Nothing exceptional, no special privileges related to a .gouv.fr.": "Rien d'exceptionnel, pas de privilèges spéciaux liés à un .gouv.fr.",
"OK": "OK",
@@ -272,7 +264,6 @@
"Pending invitations": "Invitations en attente",
"Personal data and cookies": "Données personnelles et cookies",
"Pin": "Épingler",
"Pin document icon": "Icône épingler un document",
"Pinned documents": "Documents épinglés",
"Private": "Privé",
"Public": "Public",
@@ -300,18 +291,13 @@
"Share with {{count}} users_one": "Partager avec {{count}} utilisateur",
"Share with {{count}} users_other": "Partager avec {{count}} utilisateurs",
"Shared with me": "Partagés avec moi",
"Shared with {{count}} users_many": "Partager avec {{count}} utilisateurs",
"Shared with {{count}} users_one": "Partager avec {{count}} utilisateur",
"Shared with {{count}} users_other": "Partager avec {{count}} utilisateurs",
"Simple document icon": "Icône simple du document",
"Something bad happens, please retry.": "Une erreur inattendue s'est produite, veuillez réessayer.",
"Stéphanie Schaer: Interministerial Digital Director (DINUM).": "Stéphanie Schaer: Directrice numérique interministériel (DINUM).",
"Summarize": "Résumer",
"Summary": "Sommaire",
"Template": "Modèle",
"Template": "Template",
"The document has been deleted.": "Le document a bien été supprimé.",
"The document visibility has been updated.": "La visibilité du document a été mise à jour.",
"The export failed": "Lexportation a échoué",
"The team in charge of the digital workspace \"La Suite numérique\" can be contacted directly at": "L'équipe responsable de l'espace de travail numérique \"La Suite numérique\" peut être contactée directement à l'adresse",
"This accessibility statement applies to the site hosted on": "Cette déclaration d'accessibilité s'applique au site hébergé sur",
"This allows us to measure the number of visits and understand which pages are the most viewed.": "Cela nous permet de mesurer le nombre de visites et de comprendre quelles pages sont les plus consultées.",
@@ -326,6 +312,7 @@
"Unpin": "Désépingler",
"Untitled document": "Document sans titre",
"Updated at": "Mise à jour le",
"Upload your docs to a Microsoft Word, Open Office or PDF document.": "Téléchargez vos documents dans un document Microsoft Word, Open Office ou PDF.",
"Use as prompt": "Utiliser comme un prompt",
"Version history": "Historique des versions",
"Version restored successfully": "Version restaurée avec succès",
@@ -334,11 +321,11 @@
"Warning": "Attention",
"We simply comply with the law, which states that certain audience measurement tools, properly configured to respect privacy, are exempt from prior authorization.": "Nous nous conformons simplement à la loi, qui stipule que certains outils de mesure daudience, correctement configurés pour respecter la vie privée, sont exemptés de toute autorisation préalable.",
"We try to respond within 2 working days.": "Nous essayons de répondre dans les 2 jours ouvrables.",
"Word / Open Office": "Word / Open Office",
"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.": "Vous êtes le seul propriétaire de ce groupe, faites d'un autre membre le propriétaire du groupe, avant de pouvoir modifier votre propre rôle ou vous supprimer du document.",
"You can oppose the tracking of your browsing on this website.": "Vous pouvez vous opposer au suivi de votre navigation sur ce site.",
"You can:": "Vous pouvez:",
"You cannot update the role or remove other owner.": "Vous ne pouvez pas mettre à jour le rôle ou supprimer un autre propriétaire.",
"You do not have permission to view users sharing this document or modify link settings.": "Vous n'avez pas la permission de voir les utilisateurs partageant ce document ou de modifier les paramètres du lien.",
"Your current document will revert to this version.": "Votre document actuel va revenir à cette version.",
"Your {{format}} was downloaded succesfully": "Votre {{format}} a été téléchargé avec succès",
"accessibility-contact-defenseurdesdroits": "Contacter le délégué du<1>Défenseur des droits dans votre région</1>",
@@ -3,7 +3,6 @@ import { css } from 'styled-components';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Footer } from '@/features/footer';
import { Header } from '@/features/header';
import { HEADER_HEIGHT } from '@/features/header/conf';
import { LeftPanel } from '@/features/left-panel';
@@ -12,13 +11,13 @@ import { useResponsiveStore } from '@/stores';
type MainLayoutProps = {
backgroundColor?: 'white' | 'grey';
withoutFooter?: boolean;
withoutLeftPanel?: boolean;
};
export function MainLayout({
children,
backgroundColor = 'white',
withoutFooter = false,
}: PropsWithChildren<MainLayoutProps>) {
const { isDesktop } = useResponsiveStore();
const { colorsTokens } = useCunninghamTheme();
@@ -57,7 +56,6 @@ export function MainLayout({
{children}
</Box>
</Box>
{!withoutFooter && <Footer />}
</div>
);
}
+1 -1
View File
@@ -32,7 +32,7 @@ const Page: NextPageWithLayout = () => {
</Text>
<Box $margin={{ top: 'large' }}>
<StyledLink href="/">
<StyledLink href="/docs/">
<StyledButton>{t('Back to home page')}</StyledButton>
</StyledLink>
</Box>
@@ -33,7 +33,7 @@ export function DocLayout() {
<meta name="robots" content="noindex" />
</Head>
<MainLayout withoutFooter>
<MainLayout>
<DocPage id={id} />
</MainLayout>
</>
@@ -46,6 +46,7 @@ interface DocProps {
const DocPage = ({ id }: DocProps) => {
const { login } = useAuthStore();
const {
data: docQuery,
isError,
@@ -1,6 +1,7 @@
import { useSearchParams } from 'next/navigation';
import { useRouter, useSearchParams } from 'next/navigation';
import type { ReactElement } from 'react';
import { useAuthStore } from '@/core';
import { DocDefaultFilter } from '@/features/docs';
import { DocsGrid } from '@/features/docs/docs-grid';
import { MainLayout } from '@/layouts';
@@ -9,6 +10,14 @@ import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => {
const searchParams = useSearchParams();
const target = searchParams.get('target');
const router = useRouter();
const auth = useAuthStore();
const url = auth.getAuthUrl();
if (auth.authenticated && url) {
router.replace(url);
return null;
}
return <DocsGrid target={target as DocDefaultFilter} />;
};
+15 -2
View File
@@ -1,3 +1,16 @@
import Docs from './docs';
import { useRouter } from 'next/navigation';
export default Docs;
import { useAuthStore } from '@/core';
import HomeContent from '@/features/home/components/HomeContent';
export default function Index() {
const router = useRouter();
const auth = useAuthStore();
if (auth.authenticated) {
router.push('/docs/');
return null;
}
return <HomeContent />;
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "2.1.0",
"version": "2.0.1",
"private": true,
"workspaces": {
"packages": [
@@ -28,13 +28,13 @@
"server:test": "yarn COLLABORATION_SERVER run test"
},
"resolutions": {
"@types/node": "22.12.0",
"@types/node": "22.10.3",
"@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.22.0",
"@typescript-eslint/parser": "8.22.0",
"@typescript-eslint/eslint-plugin": "8.19.0",
"@typescript-eslint/parser": "8.19.0",
"eslint": "8.57.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"typescript": "5.7.3"
"typescript": "5.7.2"
}
}
@@ -1,24 +1,24 @@
{
"name": "eslint-config-impress",
"version": "2.1.0",
"version": "2.0.1",
"license": "MIT",
"scripts": {
"lint": "eslint --ext .js ."
},
"dependencies": {
"@next/eslint-plugin-next": "15.1.6",
"@tanstack/eslint-plugin-query": "5.65.0",
"@next/eslint-plugin-next": "15.1.3",
"@tanstack/eslint-plugin-query": "5.62.9",
"@typescript-eslint/eslint-plugin": "*",
"@typescript-eslint/parser": "*",
"eslint": "*",
"eslint-config-next": "15.1.6",
"eslint-config-prettier": "10.0.1",
"eslint-config-next": "15.1.3",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jest": "28.11.0",
"eslint-plugin-jest": "28.10.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-playwright": "2.2.0",
"eslint-plugin-prettier": "5.2.3",
"eslint-plugin-react": "7.37.4",
"eslint-plugin-playwright": "2.1.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-react": "7.37.3",
"eslint-plugin-testing-library": "7.1.1",
"prettier": "3.4.2"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "packages-i18n",
"version": "2.1.0",
"version": "2.0.1",
"private": true,
"scripts": {
"extract-translation": "yarn extract-translation:impress",
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "server-y-provider",
"version": "2.1.0",
"version": "2.0.1",
"description": "Y.js provider for docs",
"repository": "https://github.com/numerique-gouv/impress",
"license": "MIT",
@@ -16,25 +16,25 @@
"node": ">=18"
},
"dependencies": {
"@blocknote/server-util": "0.21.0",
"@hocuspocus/server": "2.15.1",
"@sentry/node": "8.52.0",
"@sentry/profiling-node": "8.52.0",
"@blocknote/server-util": "0.22.0",
"@hocuspocus/server": "2.15.0",
"@sentry/node": "8.47.0",
"@sentry/profiling-node": "8.47.0",
"cors": "2.8.5",
"express": "4.21.2",
"express-ws": "5.0.2",
"y-protocols": "1.0.6",
"yjs": "13.6.23"
"yjs": "13.6.21"
},
"devDependencies": {
"@hocuspocus/provider": "2.15.1",
"@hocuspocus/provider": "2.15.0",
"@types/cors": "2.8.17",
"@types/express": "5.0.0",
"@types/express-ws": "3.0.5",
"@types/jest": "29.5.14",
"@types/node": "*",
"@types/supertest": "6.0.2",
"@types/ws": "8.5.14",
"@types/ws": "8.5.13",
"cross-env": "7.0.3",
"eslint-config-impress": "*",
"jest": "29.7.0",
+1864 -1910
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -93,4 +93,4 @@ releases:
environments:
dev:
values:
- version: 2.1.0
- version: 2.0.1
+1 -1
View File
@@ -1,5 +1,5 @@
apiVersion: v2
type: application
name: docs
version: 2.1.0
version: 2.0.1-beta.8
appVersion: latest
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "2.1.0",
"version": "2.0.1",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {