Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • froggit/www/froggit.fr
1 result
Show changes
Commits on Source (15)
Showing
with 684 additions and 674 deletions
......@@ -2,7 +2,7 @@
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
[See here for more](doc/referentiel_liens.md).
[See here for more](https://lab.frogg.it/lydra/www/docusaurus-bootstrap/-/blob/main/doc/referentiel_liens.md). (🇫🇷)
## Installation
......@@ -28,19 +28,19 @@ This command generates static content into the `build` directory and can be serv
## Create a new page
[Follow this doc](doc/pages.md).
[Follow this doc](https://lab.frogg.it/lydra/www/docusaurus-bootstrap/-/blob/main/doc/pages.md). (🇫🇷)
## Create a new section
## Create a new section
[Follow this doc](doc/sections.md).
[Follow this doc](https://lab.frogg.it/lydra/www/docusaurus-bootstrap/-/blob/main/doc/sections.md). (🇫🇷)
## Update the theme
[Follow this doc](doc/theme.md).
[Follow this doc](https://lab.frogg.it/lydra/www/docusaurus-bootstrap/-/blob/main/doc/theme.md). (🇫🇷)
## Components
[Follow this doc](doc/components.md)
[Follow this doc](https://lab.frogg.it/lydra/www/docusaurus-bootstrap/-/blob/main/doc/components.md) (🇫🇷)
## Three structure
......
# Composants
Les composants sont des extraits de code `.js` ou `.jsx` indépendants et réutilisables, ils se situent dans `src/components/`.
## Liste de composants
### Highlight.js
Le composant **Highlight** sert à surligner un élément, en particulier du texte. Il nécessite les props `color` qui déterminera la couleur du surlignage et `children` qui doit accueillir le texte à surligner.
### TextRight.js
Le composant **TextRight** sert à afficher une image à la gauche de son bloc enfant, avec largeur égale. Il nécessite les props `img` qui sera l'image à afficher à gauche du bloc enfant et `children` qui sera le bloc enfant d’éléments HTML.
### Carousel.js
Le composant **Carousel** permet de générer un carrousel en utilisant le _slider_ de [React Slick] (https://react-slick.neostack.com/).
Le composant peut recevoir 5 props. `name` (optionnel) sera le texte affiché sous l'image, `link` (optionnel) permet de rendre l'image cliquable pour ouvrir un lien dans un nouvel onglet, `alt` sera le texte alternatif à l'image ou au svg.
Concernant l'image, il peut s'agir d'un fichier (png, svg, jpeg...) avec le props `img` ou de svg inline en html avec le props `svg` (ne pas mettre de chemin vers un fichier svg avec le props `svg`!). Si les deux arguments sont fournis, seul `img` sera pris en compte.
### CarouselLibre.js
Le composant **CarouselLibre** est utilisé sur la page d'accueil pour afficher un carrousel des logiciels libres (FLOSS) utilisés par Froggit, il utilise le composant générique **Carousel**.
Afin que les logos de FLOSS unis (par exemple blancs ou noirs) restent bien visibles peu importe le thème, les intégrer en [SVG inline](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_In_HTML_Introduction) dans le props `svg` et penser à supprimer les valeurs "fill". Cela leur permettra de prendre la couleur du texte selon le thème.
### HomepagePromises.js
Le composant **HomepagePromises** est utilisé sur la page d'accueil pour afficher les promesses de Froggit (section **À vous de faire la différence**).
Il renvoie une section constituée d'un titre central puis d'une liste React (**PromiseList**) affichée en ligne avec le composant **Promises**.
### Promises.js
Le composant **Promises** permet de générer une carte affichée en colonne et composée d'un titre, une image SVG et une description. Le composant **Promises** prendra respectivement les props `Svg`, `alt`, `title` et `description`.
### HomepageFeatures.js
Le composant **HomepageFeatures** affiche les fonctionnalités de Froggit sur la page d'accueil (section **Optimisez votre manière de façonner vos applications**) sous forme d'onglets à l'aide du composant **Features**.
### Features.js
Le composant **Features** génère des onglets, chaque onglet est représenté par un titre et une fois ouvert, il affiche une image accompagnée d'un court texte. Il prend respectivement les props `title`, `svg` et `description`.
### HomepageResults
Le composant **HomepageResults** affiche les résultats à attendre de Froggit sur la page d'accueil (section **Obtenez de meilleurs résultats…** ). Il utilise des cartes retournées par le composant **Results**.
### Results.js
Le composant **Results** retourne une carte composée d'une colone textuelle avec un titre et une image, ainsi qu'une colone illustratrice composée d'une image. La colone textuelle se situe à gauche pour les enfants impairs et à droite pour les enfants pairs.
Il prend respectivement les props `title`, `description`, `img` et `alt`.
# Création des pages
Docusaurus permet de créer des pages de façons différentes, en Markdown (`.md`), en JavaScript (`.js`).
- Si nous n'avons pas besoin d'un _layout_ spécifique pour la page à créer, nous créons une page en Markdown.
- Si, au contraire, nous avons besoin d'un Layout spécifique ou de charger un certains styles (CSS) pour notre nouvelle page, alors nous créons une page en JavaScript (`.js`).
- Enfin, nous pouvons également utiliser le MDX (une évolution du Markdown). Le MDX utilise la simplicité du Markdown, tout en permettant d'utiliser du [JSX](https://Docusaurus.io/fr/docs/markdown-features/react), d'importer des composants React. Son utilisation permet d'utiliser la coloration syntaxique, les éditeurs de code en direct... Voir doc [MDX](https://mdxjs.com/)
L'utilisation du Markdown offre un meilleur retour sur investissement car il est beaucoup plus simple et rapide à écrire.
## Créer une page en JavaScript
Pour créer une page en JavaScript, nous allons pouvoir nous appuyer sur de nombreux concepts de programmation introduits par React.js, comme les composants, le JSX (syntaxe utilisée par React) car Docusaurus est basé sur la librairie React.js.
Cette section demande des connaissances en JavaScript, et une maîtrise de l'ES6 (voir Pour aller plus loin), une version récente de JavaScript, apportant de nouvelles normes à ce langage, et dont React utilise certains aspects.
Le code affiché dans la seconde partie, nécessite la création d'une fonction qui fait appel à des notions propre à React.js. Nous allons créer un composant React. La création de ce composant va nous permettre de créer notre page en JavaScript (ce composant représente notre nouvelle page).
Afin de mieux comprendre ce chapitre, il peut être intéressant d'apprendre le fonctionnement de [React](https://fr.reactjs.org/), la création de [composants](https://fr.reactjs.org/docs/components-and-props.html) et l'utilisation du [JSX](https://fr.reactjs.org/docs/introducing-jsx.html) (une extension syntaxique de JavaScript utilisée par React).
Vous pouvez consulter la doc officielle de [React](https://fr.reactjs.org/).
Vous retrouverez à la fin de cette doc d'autres liens pour vous aider à comprendre certaines notions de programmation nécessaires à l'utilisation de React.
1. Dans le dossier `pages`, créer un fichier `/src/pages/new-page-react.js`
2. Y créer une fonction qui va permettre de rendre une vue:
```/src/pages/new-page-react.js
import React from 'react';
import Layout from '@theme/Layout';
function Hello() {
return (
<Layout title="Bonjour">
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '50vh',
fontSize: '20px',
}}>
<p>
Editez <code>pages/new-page-react.js</code> et sauvegardez pour recharger.
</p>
</div>
</Layout>
);
}
export default Hello;
```
Docusaurus génère un composant _Layout_ par défaut, c'est un composant de mise en page qui va inclure d'autres composants tels que la `_navbar_`, le `footer`...
Il nous suffit alors de lancer le serveur de développement pour avoir accès à notre nouvelle page.
Nous pouvons désormais nous rendre sur l'URL: http://localhost:3000/new-page-react (le serveur utilise le port 3000 par défaut).
Pour de plus amples détails, veuillez consulter la [doc de Docusaurus](https://Docusaurus.io/fr/docs/creating-pages) sur la création de pages.
### Utiliser le TypeScript au lieu de JavaScript ?
Docusaurus permet également de créer des pages TypeScript avec l'extension `.tsx`, en remplacement du JavaScript. Le TypeScript impose un typage fort (le JavaScript étant un langage très peu typé), si une erreur de type apparaît dans le code, TypeScript fera apparaître cette erreur, là où le JavaScript restera permissif.
TypeScript présente des avantages certains en termes de qualité et de sécurité du code, mais il peut être difficile et long à apprendre et à maîtriser sur un framework comme React.
Pour installer et configurer TypeScript sur votre projet, consulter la page de [Support TypeScript](https://Docusaurus.io/fr/docs/typescript-support) sur la doc de Docusaurus.
## Comment créer une page en Markdown ?
Dans le dossier `pages`, nous allons créer un nouveau fichier `/src/pages/new-page-markdown.md` :
```/src/pages/new-page-markdown.md
---
title: titre de page
description: description de page
---
## Bonjour
Comment allez-vous ?
```
De la même manière, une page sera créée à l'adresse http://localhost:3000/new-page-markdown.
Les pages Markdown sont moins flexibles que les pages React, car elles utilisent toujours la mise en page du thème, mais elles sont bien plus simples et rapides à créer.
Voici un [exemple de page Markdown](https://Docusaurus.io/fr/examples/markdownPageExample).
## Configuration
Comment ajouter un lien vers une page dans les composants `Navbar` et `Footer`:
Nous allons utiliser le fichier `Docusaurus-config.js`, dans lequel nous pourrons configurer notre `Navbar` et notre `Footer` très facilement.
Nous retrouvons dans ce fichier un objet, nommé `themeConfig` qui permet de définir des valeurs pour la `navbar` et le `footer` au travers d'un tableau d'objets clés / valeurs.
Reprenons nos pages précédentes, `new-page-react.js` et `new-page-markdown.js`. Nous pouvons configurer la `navbar` et le `footer` pour créer un lien vers ces pages :
```themeConfig
themeConfig: {
navbar: {
items: [
{to: "/new-page-react", label: "TitreDuLien", position: "left"},
],
},
footer: {
links: [
{
title: "Columne Name",
items: [
{ to: "/new-page-markdown", label: "TitreDuLien"},
],
},
],
},
},
```
Nous pouvons retrouver notre composant `footer` sous la forme d'un objet, qui va contenir par exemple un tableau `items` dans lequel une clé `label` va définir la valeur du titre du lien qui sera affiché : `TitreDuLien`.
Vous trouverez plus d'explications sur la configuration du fichier `Docusaurus.config.js` sur [la page de la doc de Docusaurus](https://Docusaurus.io/fr/docs/Docusaurus.config.js).
## Pour aller plus loin
Voici quelques liens et informations pour approfondir certaines notions abordées dans cette doc.
- [Utiliser les Composants dans React.js](https://fr.reactjs.org/docs/components-and-props.html).
- [Une introduction au JSX](https://fr.reactjs.org/docs/introducing-jsx.html).
- l'ECMAScript et l'ES6 : l'ECMAScript est une norme pour les langages de script tel que JavaScript. Vous pouvez retrouver toutes les versions d'ECMAScript [avec leurs fonctionnalités ici](https://www.w3schools.com/js/js_versions.asp). Une nouvelle version, nommée ECMAScript 2015, ou ES6, a apporté de nombreux changements au JavaScript. React utilise beaucoup l'ES6 (fonctions fléchées, les nouvelles méthodes de manipulation des tableaux, les classes, les instructions `let`, et `const`).
- Vous pouvez retrouver la liste des fonctionnalités apportées à [JavaScript par l'ES6 ici](http://es6-features.org/#StringInterpolation).
- [Pour retrouver toutes les fonctionnalités du JavaScript](https://developer.mozilla.org/fr/docs/Web/JavaScript/A_re-introduction_to_JavaScript).
# Référentiel des liens et ressources pour le projet
## Un guide pour toi qui code le site
Ce guide est un répertoire des sites utiles pour la construction et recherches des éléments constituant le projet **Froggit** sur **Docusaurus**.
Certaines techniques sont utilisées afin de remplacer des éléments, et des problèmes peuvent se résoudre grâce à des **informations spécifiques**.
**[Docusaurus](https://docusaurus.io/) est la référence de base pour :**
- Utiliser une méthode spécifique pour ajouter/remplacer un élément.
- Docusaurus dispose d'un large choix d'éléments de création.
- Il est possible d'ajouter des plugins.
_Note : Docusaurus met régulièrement à jour ses versions_
**[Infima](https://infima.dev/) est la librairie de variables et de composants que Docusaurus utilise**. Cela permet d'ajouter des éléments type bouton, grid, form, puis on leur donne un style au besoin (ça marche comme pour Bootstrap).
#### Ressources
- [Bibliothèque Infima](https://github.com/facebookincubator/infima/tree/main/packages/core/styles/)
___
### Variables Infima
Afin de rester dans les caractéristiques de Docusaurus, on préfère **réutiliser la liste des variables** générée par Infima. Dès lors que nous créons un nouvel élément (par exemple un bouton), **le CSS reprend les propriétés** que nous lui attribuons dans la liste des variables. On perd moins de temps et tout est beaucoup plus simple.
#### Ressources
- [Infima var source 1](https://github.com/facebook/docusaurus/issues/3955#issuecomment-751426089)
- [Infima var source 2](https://github.com/facebookincubator/infima/blob/c270e2cbdaf0659093ae7f6fbc8899430a0e9147/packages/core/styles/common/variables.pcss)
### Couleurs
Afin de se faciliter la tâche, on utilise un **générateur de variation des couleurs**. Il suffit ensuite de récupérer les propriétés hexagone et de les mettre dans le fichier des variables.
#### Ressources
- [Générateur de palette](https://docusaurus.io/fr/docs/styling-layout#css-modules)
- [Variables de base Infima](https://infima.dev/docs/utilities/colors/)
### Table of content (TOC)
La TOC a une taille trop petite et nous utilisons la méthode du _swizzling_ pour augmenter sa taille. On passe de col--2 à col--3 qui est **une propriété d'Infima**.
#### Ressources
- [Github](https://github.com/facebook/docusaurus/search?q=col--2)
- [Swizzling](https://docusaurus.io/fr/docs/swizzling)
- [Personnalisation TOC](https://docusaurus.io/fr/docs/markdown-features/toc#customizing-table-of-contents-generation)
### Avatar as Twitter
Avec Infima, on **récupère un code** nous permettant de créer un petit avatar avec un texte de type citation.
#### Ressources
- [Infima avatar](https://infima.dev/docs/components/avatar)
### Admonitions
Cette ressource utilise les couleurs de base d'Infima dans ses cadres et bordures.
#### Ressources
- [Admonitions](https://docusaurus.io/fr/docs/markdown-features/admonitions)
#### Ajouter des icônes avec React
Pour ajouter des icônes, des logos de marques existantes ou encore des pictogrammes, on utilise la librairie de ressources visuelles dont dispose React.
#### Ressources
- [React Icons](https://react-icons.github.io/react-icons/icons?name=fa)
- [Documentation](https://fontawesome.com/v5/docs/web/use-with/react)
# Créer des sections
Docusaurus étant un Framework de la librairie React, on y retrouve la même logique.
L'affichage d'une page web est un ensemble de composants réutilisables, que l'on peut assembler.
Nous allons nous appuyer sur l'utilisation de ces Composants pour créer nos différentes sections d'affichage, notamment pour gérer le rendu visuel de la page d'accueil du site `index.js`.
Chaque section de pages va être définie dans un Composant enfant dans le dossier `./src/components/`, puis importée dans un Composant parent dans le dossier `./src/pages/`.
Un composant (_Component_) contient : des données qui constituent un état (_state_), des comportements et un rendu (l'affichage), ces trois notions vont interagir entre elles.
## Création du composant
Pour l'exemple, nous allons expliquer comment nous avons créé la section chargée d'afficher les fonctionnalités (_Features_) de Froggit.
Dans le dossier `src/components/`, nous avons créé un fichier React `HomepageFeatures.js`.
Nous avons choisi de nommer notre fichier `HomepageFeatures`, en [PascalCase](https://techlib.fr/definition/pascalcase.html), car ce composant est destiné à être affiché, dans la `Homepage` (page d'Accueil) et doit afficher les `Features` (fonctionnalités) de Froggit.
Ce fichier est composé de trois parties :
- une pour gérer les données de notre section ;
- une pour définir les comportements que nous attendons en utilisant différentes fonctions ;
- une pour définir l'affichage. Nous utilisons le JSX pour cette partie, voir l'[introduction au JSX](https://fr.reactjs.org/docs/introducing-jsx.html) sur la doc de React pour des explications sur cette extension syntaxique du JavaScript.
### Les données de notre section
Nous allons créer un tableau d'objets JavaScript pour définir les données à afficher dans notre section. Il y a 3 objets pour chaque élément du tableau :
1. Un titre, de type `chaîne de charactère` (_String_), donc entouré de guillemets simples `'` ou doubles `"`).
2. Une description, que nous définissons en tant qu' `Element JSX`. Nous pouvons ainsi lui donner un _template_ de rendu comme valeur, avec les balises HTML de notre choix, juste un paragraphe dans notre cas. Pour cela, nous devons définir sa valeur entre des parenthèses et à l'intérieur d'une balise parent, pouvant être une `div`, ou comme nous utilisons ci-dessous, avec une simple balise vide.
3. Une image, que nous définissons en tant que `Composant React`, afin de simplifier la syntaxe de notre code dans les prochaines étapes notamment pour définir notre affichage. Comme cette image est un composant, nous la nommons `Svg` en PascalCase.
Enfin, nous utilisons ici le format `SVG`, mais nous pouvons utiliser d'autres formats tels que le `PNG`, `GIF` et `JPG`. Mais dans ce cas il faudra installer le plugin officiel de gestion d'[images](https://docusaurus.io/fr/docs/api/plugins/@docusaurus/plugin-ideal-image) pour les formats `PNG` ou `JPG`.
Nous appelons notre tableau `featuresList` car c'est la liste des fonctionnalités.
```jsx
const FeatureList = [
{
title: "Le Lab 🧪",
Svg: require("../../static/img/chemistry.svg").default,
description: (
<>
Le laboratoire vous permet d’héberger votre code et de collaborer dessus avec Git. C&apos;est plus qu&apos;un serveur Git puisque vous pouvez gérer un projet complet dessus. Le Lab est le cœur de Froggit et il sert aussi de base centrale pour l’authentification unique (SSO) des autres services.
</>
),
},
{
title: "La CI 🔁",
Svg: require("../../static/img/rainbow.svg").default,
description: (
<>
Fournir un outil de collaboration de code sans Intégration Continue n&apos;était pas pensable pour nous. C&apos;est pour cela que nous avons mis en place une CI avec un serveur et un débordement élastique dans Kubernetes.
</>
),
},
{
title: "Le Chat 💬",
Svg: require("../../static/img/selfie.svg").default,
description: (
<>
Pour collaborer les équipes ont besoin de communiquer. Qu&apos;elles soient physiquement au même endroit ou non une communication asynchrone est importante. C&apos;est pour cela que Froggit inclut un chat avec le service. Vous pourrez même faire remonter vos alertes et messages venant du Lab. L&apos;authentification se fera grâce au Lab, vous n&apos;aurez qu&apos;un seul compte pour tous les services de Froggit. Vous pourrez abandonner Slack sans crainte !
</>
),
},
{
title: "Les Pages 🔖",
Svg: require("../../static/img/read-smile.svg").default,
description: (
<>
Vous avez besoin de créer des sites statiques pour vos documentations. C&apos;est pour cela que nous mettons en place les GitLab pages qui vous permettent d&apos;héberger des sites statiques.
</>
),
},
];
```
Notre section affichera les 4 éléments, chacune avec un titre, une description et une image en Svg.
### Les paramètres d'un composant
On peut passer des paramètres au composant grâce :
- au propriété de la balise ;
- au texte entre les balises ouvrantes et fermantes.
Pour notre exemple, on va créer le compostant `TextRight` avec deux paramètres :
1. une image
2. un bloc de texte
On va utiliser ce composant depuis un fichier MDX.
Le fichier `TextRight.js` se trouve dans le répertoire des composants : `src/components`. Comme on a du CSS exclusif à ce composant, on crée un fichier avec la même base de nom :`TextRight.module.css`. Le `.module.css` signifie qu'il apportera des modifications uniquement au fichier `.js` pourtant le même nom, et pas les autres pages du site.
#### Le composant
```jsx
// src/components/TextRight.js
import React from "react";
import styles from "./TextRight.module.css";
import useBaseUrl from '@docusaurus/useBaseUrl';
export default function TextRight({children, img}) {
return (
<>
<div className="row">
<div className="col col--6">
<img src={useBaseUrl(img)} />
</div>
<div className="col col--6">
{children}
</div>
</div>
</>
);
}
```
D'abord on fait ses imports : React, le module css, et un useBaseUrl.
On utilise `useBaseUrl` pour obtenir l'URL de base du site et pouvoir renvoyer les bonnes URL au HTML qui sera généré. Voir la doc de [Docusaurus](https://docusaurus.io/fr/docs/static-assets#in-jsx).
On exporte la fonction (qui sera importée ensuite dans le fichier mdx). Cette fonction possède deux paramètres :
1. `children`, reprenant les propriétés des "parents" pour les renvoyer en sortie, c'est ce qui se trouve entre les deux balises du composant React.
2. `img` qui contient le chemin relatif de notre image depuis le répertoire` static/` et qui sera passé dans les propriétés de la balise ouvrante de notre composant au moment de l'appel.
La fonction génère du HTML, ce qui se trouve dans `return` :
- Des "blocks" (`div`) avec des `className` (ou des class en html).
- Pour l'image, on utilise la fonction `useBaseUrl` qui renvois l'URL finale de notre `img` (en paramètre).
- Pour notre texte, il est contenu dans le paramètre children.
Dans le dossier `static/` : mettre l'image en veillant à bien choisir son nom.
#### L'appel du composant
Dans le dossier `src/pages` : on a un fichier [MDX](https://docusaurus.io/fr/docs/markdown-features/react), `communaute.mdx` dans notre exemple.
Dans notre fichier MDX il faut :
- Importer la fonction `TextRight` depuis le chemin du composant.
- Utiliser le composant avec la nouvelle balise créé `<TextRight>` et `</TextRight>`
Par exemple :
```markdown
import TextRight from '@site/src/components/TextRight';
<TextRight img="/img/mon_image.png">
# Mon Titre
Mon texte
</TextRight>
```
### Attention aux multiples CSS
⚠️ **Remarque : veiller aux erreurs dans les fichiers `custom.css` et `.module.css`.**
Nous avons constaté que si une classe reçoit des attributs dans le fichier `mon_composant.module.css` et le fichier `custom.css` alors il sera retenu en priorité ce qui se trouve dans le fichier `custom.css` qui est le fichier général de style.
Nous n'avons pas de réponse à la question :
> Est-ce que ce qui se trouve dans le fichier `mon_composant.module.css` s'ajoute à ce qui se trouve dans le fichier `custom.css` ?
### Affichage
Il faut maintenant définir l'affichage que nous souhaitons que ce composant renvoie.
Nous avons créé une fonction qui renvoie le rendu, grâce à un _template_.
L'utilisation du JSX simplifie cette tâche, nous avons trois parties qui sont les clés issues de notre tableau à afficher.
Tout élément JavaScript doit alors être affiché entre accolade : `{---}` afin d'être correctement interprété.
Notre affichage doit impérativement être entouré de balises parentes, telle une `div`.
Nous passons comme argument les clés de notre tableau (Svg, title, description) à notre fonction
```jsx
function Feature({Svg, title, description}) {
return (
<div className="sectionsReverse row">
<div className="col col--5 text--center">
<Svg className={styles.svg} alt={title} />
</div>
<div className="col col--5">
<h3>{title}</h3>
<p>{description}</p>
</div>
</div>
);
}
```
Ainsi, notre composant va renvoyer notre Svg en image, le titre en `h3` et la description en paragraphe.
### Réutilisation du composant
Nous devons également importer certains utilitaires, selon nos besoins (la librairie React, les modules CSS), puis exporter notre composant, pour qu'il puisse être réutilisé.
Il est nécessaire d'utiliser le système d'import/export de [modules JavaScript](https://javascript.info/import-export).
### Définition des Comportements
Nous itérons sur chaque élément de notre tableau, en utilisant la méthode JavaScript `.map`, qui est très souvent utilisé dans les Composants React.
La méthode map() crée un nouveau tableau avec les résultats de chaque élément du tableau. [Voici un tutoriel expliquant cette méthode](https://www.freecodecamp.org/news/javascript-map-how-to-use-the-js-map-function-array-method/).
```jsx
export default function HomepageFeatures() {
return (
<section className="sections container" id="features">
<h2 className={clsx("text--center text--uppercase", styles.title)}>Fonctionnalités</h2>
<div>
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</section>
);
}
```
Nous utilisons grâce à une `fonction fléchée`, une fonction de _Callback_ pour boucler sur notre tableau, qui prend 2 paramètres, les `props` qui représentent les valeurs du tableau, et `idx`, les clés. les `...` est un [Spread Operator](https://www.techiediaries.com/react-spread-operator-props-setstate/).
Notre Composant `HomepageFeatures` est également exporté au début de la déclaration de la fonction avec `export default`.
## Utilisation du composant
Nous pouvons désormais utiliser notre composant dans un composant parent.
Dans notre cas, ce composant parent est la page d'accueil du site `src/pages/index.js`.
Il suffit d'importer notre composant enfant, que nous venons de créer, et de le mettre, comme une balise, dans la partie `return` de la fonction `Home`. La fonction `Home` retourne la vue de notre page :
```jsx
import React from "react";
import HomepagePromises from "../components/HomepagePromises";
import HomepageFeatures from "../components/HomepageFeatures";
// (...)
export default function Home() {
return (
<Layout>
<HomepageHeader />
<main>
<HomepagePromises />
<HomepageFeatures />
</main>
</Layout>
);
}
```
Entre nos balises `Layout`, nous retrouvons 3 composants enfants, d'abord l'entête (_header_) nommé `<HomepageHeader />` qui a été créé à l'intérieur même de ce composant.
Puis nous retrouvons 2 Composants, qui ont été importé depuis le dossier `src/components/`:
1. `<HomepagePromises />` pour la section des promesses
2. `<HomepageFeatures />` pour la section des fonctionnalités
## Feuille de style CSS
Un Composant peut avoir sa propre feuille de style CSS. Nous donnons le même nom que le fichier de composant au fichier CSS suivi du suffixe `.module.css`.
Un composant peut également utiliser la feuille de style générale `custom.css`. Ce fichier regroupe les attributs de styles standard commun à tout le site.
Chaque feuille de style que l'on souhaite utiliser doit être importé dans le Composant.
# Theme dans Docusaurus
## Infima pour le style
**Docusaurus** utilise [Infima](https://infima.dev/) pour générer du style intelligent : c'est un Framework de style.
Notre site utilise les styles d'**Infima** par défaut. Nous pouvons rappeler ces variables ou bien les remplacer. De plus, à noter que le **dark mode** est facilement géré dans notre CSS.
```css
html[data-theme='dark'] .menu__link {
color: var(--ifm-color-tonic-one);
}
```
Pour les couleurs, **Infima** utilise un nuancier sur [Colorbox](https://colorbox.io/).
## Le Swizzling
Docusaurus propose une méthode pour personnaliser le thème : c'est le [Swizzling](https://docusaurus.io/fr/docs/swizzling).
On échange un composant de thème généré par Docusaurus, par le nôtre.
Il en existe deux types :
1. **Éjection** : créer une copie du composant de thème à personnaliser.
2. **Enveloppement** : améliorer le composant de thème existant.
### Éjection
**Pour procéder à l'éjection, on lance dans son terminal la commande suivante :**
```sh
npm run swizzle [nom du thème] [nom du composant] -- --eject
```
### Enveloppement
**Pour l'enveloppement, procéder comme suit :**
```sh
npm run swizzle [theme name] [component name] -- --wrap
```
En exemple, de notre Footer :
En lançant la commande de l'enveloppement, il nous crée le répertoire `theme/Footer`.
Le fichier `index.js` s'y trouvant est notre nouveau Footer.
Nous avons enveloppé notre Footer avec de nouveaux éléments.
......@@ -41,6 +41,10 @@ module.exports = {
label: "Offre Dédiée",
to: "/dedie"
},
{
label: "Mentorat de groupe GitLab",
to: "/accelerateur-gitlab"
},
],
},
{label: "Doc", position: "left", type: "doc", docId: "intro", },
......@@ -50,11 +54,11 @@ module.exports = {
position: "left",
items: [
{
label: "🎁 Antisèche et Communauté git",
label: "🎁 Antisèche et Communauté",
to: "/communaute",
},
{
label: "L'accélérateur : mentorat de groupe",
label: "💶 L'accélérateur : mentorat de groupe",
to: "/accelerateur-gitlab",
},
],
......
/**
* Infima's clickable dropdown (used by Docusaurus) doesn't work out of the box.
* There's an open issue: https://github.com/facebookincubator/infima/issues/110
*
* The suggested solution is to inject some vanilla JavaScript.
* But it would mess with React so this component implements a better abstraction,
* until it's fixed on Docusaurus' or Infima's side.
*/
import React from "react";
const DropdownContext = React.createContext({
open: false,
setOpen: () => {},
});
export function Dropdown({ children }) {
const containerRef = React.useRef(null);
const [open, setOpen] = React.useState(false);
const contextValue = React.useMemo(
() => ({ open, setOpen }),
[open, setOpen]
);
React.useEffect(() => {
const listener = event => {
if (!containerRef.current) {
return;
}
if (containerRef.current.contains(event.target)) {
setOpen(currentOpen => !currentOpen);
} else {
setOpen(false);
}
};
window.document.addEventListener("mouseup", listener);
return () => window.document.removeEventListener("mouseup", listener);
}, []);
const child = React.Children.only(children);
return (
<DropdownContext.Provider value={contextValue}>
{React.cloneElement(child, {
ref: containerRef,
className: `${child.props.className} ${open ? "dropdown--show" : ""}`,
})}
</DropdownContext.Provider>
);
}
/* eslint-disable react/no-unescaped-entities */
import React from "react";
import { FaMinus, FaPlus, FaSortDown } from "react-icons/fa";
import styles from "./PricingComparator.module.css";
import { Dropdown } from "../Dropdown";
import formatNumber from "@site/src/utils/formatNumber";
import plans from "@site/src/constants/plans";
import competitors from "@site/src/constants/competitors";
function getPrices(users, paidAnnually, labCompetitor, chatCompetitor) {
const plan = [
{
users: 1,
paidMonthly: plans.grenouille.paidMonthly,
paidAnnually: plans.grenouille.paidAnnually,
},
{
users: 2,
paidMonthly: plans.grenouille.paidMonthly,
paidAnnually: plans.etang2.paidAnnually,
},
{
users: 5,
paidMonthly: plans.grenouille.paidMonthly,
paidAnnually: plans.etang5.paidAnnually,
},
{
users: 10,
paidMonthly: plans.grenouille.paidMonthly,
paidAnnually: plans.etang10.paidAnnually,
},
{
users: 50,
paidMonthly: plans.grenouille.paidMonthly,
paidAnnually: plans.etang50.paidAnnually,
},
{
users: 100,
paidMonthly: plans.grenouille.paidMonthly,
paidAnnually: plans.etang100.paidAnnually,
},
]
.slice()
.reverse()
.find(plan => plan.users <= users);
let labCompetitorParsed = labCompetitor.plans.find(plan => plan.users <= users);
if (paidAnnually) {
return {
froggit: (plan.paidAnnually * Math.max(plan.users, users)) / users,
lab: labCompetitorParsed.paidAnnually,
chat: chatCompetitor.paidAnnually,
};
}
return {
froggit: (plan.paidMonthly * Math.max(plan.users, users)) / users,
lab: labCompetitorParsed.paidMonthly,
chat: chatCompetitor.paidMonthly,
};
}
export default function PricingComparator() {
const [users, setUsers] = React.useState(1);
const [paidAnnually, setPaidAnnually] = React.useState(true);
const [labCompetitor, setLabCompetitor] = React.useState(competitors.lab[0]);
const [chatCompetitor, setChatCompetitor] = React.useState(
competitors.chat[0]
);
const prices = getPrices(users, paidAnnually, labCompetitor, chatCompetitor);
const decreaseUsers = shiftKey => {
const step = shiftKey ? 10 : 1;
setUsers(currentUsers => Math.max(1, currentUsers - step));
};
const increaseUsers = shiftKey => {
const step = shiftKey ? 10 : 1;
setUsers(currentUsers => currentUsers + step);
};
const value = Math.round(prices.froggit - (prices.lab + prices.chat)) * users * 12;
let message = <>
Économise{users > 1 ? "z" : ""}{" "}
<span className={styles.summaryBadge}>
{formatNumber(
Math.abs(value)
)}
</span>{" "}
par an pour {users} utilisateur
{users > 1 ? "s" : ""} avec Froggit !
</>;
if (value > 0) {
message = <>
Froggit ne {users > 1 ? "vous" : "te"} ferait rien économiser, mais c'est le prix de la souveraineté numérique et de la conformité RGPD !
</>;
}
return (
<div className={styles.container}>
<div className={styles.controls}>
<div className={styles.controlsUsers}>
<div className={styles.controlsUsersLabel}>Utilisateurs</div>
<div className={styles.controlsUsersActions}>
<button
type="button"
className="button button--secondary button--sm"
onClick={event => decreaseUsers(event.shiftKey)}
disabled={users <= 1}
>
<FaMinus size={10} />
</button>
<input
type="text"
onKeyDown={event => {
switch (event.key) {
case "ArrowDown":
return decreaseUsers(event.shiftKey);
case "ArrowUp":
return increaseUsers(event.shiftKey);
}
}}
onChange={event => {
const value = Math.round(Number(event.target.value));
if (isNaN(value)) {
return;
}
setUsers(Math.max(1, value));
}}
pattern="[0-9]*"
value={users}
className={styles.controlsUsersInput}
/>
<button
type="button"
className="button button--secondary button--sm"
onClick={event => increaseUsers(event.shiftKey)}
>
<FaPlus size={10} />
</button>
</div>
</div>
<ul className={`${styles.controlsPeriodicity} pills`}>
<li
role="button"
tabIndex={0}
className={`pills__item ${
!paidAnnually ? "pills__item--active" : ""
}`}
onClick={() => setPaidAnnually(false)}
>
Mensuel
</li>
<li
role="button"
tabIndex={0}
className={`pills__item ${
paidAnnually ? "pills__item--active" : ""
}`}
onClick={() => setPaidAnnually(true)}
>
Annuel <span className="badge badge--success">-20%</span>
</li>
</ul>
</div>
<div className={styles.table}>
<div className={styles.tableHead}>
<div className={styles.tableHeadCell} />
<div className={styles.tableHeadCell}>
<div>
<strong>Froggit</strong>
</div>
<small>GitLab + Mattermost</small>
</div>
<div className={styles.tableHeadCell}>
<div>
<Dropdown>
<div className="dropdown">
<div
tabIndex={0}
role="button"
className={styles.dropdownTrigger}
>
<strong>{labCompetitor.name}</strong> <FaSortDown />
</div>
<ul className="dropdown__menu">
{competitors.lab.map(competitor => (
<li key={competitor.name}>
<div
className="dropdown__link"
onClick={() => setLabCompetitor(competitor)}
>
{competitor.name}
</div>
</li>
))}
</ul>
</div>
</Dropdown>
</div>
<small>&nbsp;</small>
</div>
<div className={styles.tableHeadCell}>
<FaPlus
style={{
position: "absolute",
top: "24px",
left: "0",
transform: "translateX(-50%)",
}}
/>
<div>
<Dropdown>
<div className="dropdown">
<div
tabIndex={0}
role="button"
className={styles.dropdownTrigger}
>
<strong>{chatCompetitor.name}</strong> <FaSortDown />
</div>
<ul className="dropdown__menu">
{competitors.chat.map(competitor => (
<li key={competitor.name}>
<div
className="dropdown__link"
onClick={() => setChatCompetitor(competitor)}
>
{competitor.name}
</div>
</li>
))}
</ul>
</div>
</Dropdown>
</div>
<small>&nbsp;</small>
</div>
</div>
<div className={styles.tableRow}>
<div className={styles.tableRowCell}>
<small>Tarif mensuel HT par utilisateur</small>
</div>
<div className={styles.tableRowCell}>
<small>{formatNumber(prices.froggit)}€</small>
</div>
<div className={styles.tableRowCell}>
<small>{formatNumber(prices.lab)}€</small>
</div>
<div className={styles.tableRowCell}>
<small>{formatNumber(prices.chat)}€</small>
</div>
</div>
<div className={styles.tableRow}>
<div className={styles.tableRowCell} />
<div className={styles.tableRowCell}>
<strong>{formatNumber(prices.froggit)}€</strong>
</div>
<div className={styles.tableRowCell}>
<strong>{formatNumber(prices.lab + prices.chat)}€</strong>
</div>
</div>
</div>
<p className={styles.summary}>
{message}
</p>
</div>
);
}
.container {
margin-bottom: 1em;
}
.controls {
display: flex;
justify-content: space-between;
justify-items: center;
margin-bottom: 0.5rem;
}
.controls input {
margin: 0;
}
.controlsUsers {
display: flex;
align-items: center;
gap: 1rem;
}
.controlsUsersLabel {
}
.controlsUsersActions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.controlsUsersActions button {
width: 1.5rem;
height: 1.5rem;
padding: 0;
margin: 0;
}
.controlsUsersInput {
padding: 0.2rem 0.6rem;
width: 8ch;
}
.controlsPeriodicity {
margin: 0;
}
.dropdownTrigger {
cursor: pointer;
}
.table {
background-color: var(--ifm-color-secondary-lightest);
box-shadow: var(--ifm-global-shadow-tl);
border-radius: var(--ifm-card-border-radius);
}
.tableHead,
.tableRow {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.tableRow:nth-child(even) .tableRowCell {
background-color: var(--ifm-color-gray-100);
}
.tableRow:nth-child(even) .tableRowCell:nth-child(2) {
background-color: var(--ifm-color-primary-dark);
}
.tableHeadCell,
.tableRowCell {
position: relative;
padding: 0.4rem 1rem;
text-align: center;
}
.tableHead .tableHeadCell,
.tableRow:last-child .tableRowCell {
padding: 1rem;
}
.tableHeadCell:first-child,
.tableRowCell:first-child {
text-align: right;
}
.tableRowCell {
display: flex;
align-items: center;
justify-content: center;
}
.tableHeadCell:nth-child(2),
.tableRowCell:nth-child(2) {
color: var(--ifm-color-secondary-lightest);
background-color: var(--ifm-color-light-green);
}
.tableHeadCell li {
cursor: pointer;
text-align: left;
}
.tableHeadCell strong {
font-size: var(--ifm-h3-font-size);
}
.tableRow:last-child .tableRowCell:nth-child(3) {
grid-column: span 2;
}
.summary {
font-size: var(--ifm-h3-font-size);
text-align: center;
margin-top: 2rem;
margin-bottom: 4rem;
}
.summaryBadge {
color: var(--ifm-color-success);
background-color: var(--ifm-color-frame);
padding: 0.4rem 0.8rem;
border-radius: var(--ifm-badge-border-radius);
font-weight: bold;
}
export default {
lab: [
{
name: "GitHub",
plans: [{
users: 0,
paidMonthly: 3.62,
paidAnnually: 3.62
}]
},
{
name: "GitLab",
plans: [
{
users: 5,
paidMonthly: 29.0,
paidAnnually: 29.0,
},
{
users: 0,
paidMonthly: 0,
paidAnnually: 0,
},
]
}
],
chat: [
{
name: "Slack",
paidMonthly: 8.25,
paidAnnually: 6.75,
},
{
name: "Discord",
paidMonthly: 0,
paidAnnually: 0,
},
],
};
export default {
tetard: {
paidAnnually: 2,
},
grenouille: {
paidMonthly: 9.9,
paidAnnually: 8.25,
},
etang2: {
paidAnnually: 8.25,
},
etang5: {
paidAnnually: 8.25,
},
etang10: {
paidAnnually: 7.43,
},
etang50: {
paidAnnually: 6.6,
},
etang100: {
paidAnnually: 5.8,
},
};
......@@ -90,13 +90,6 @@ Les **sauvegardes** des données des services sont conservées avec une durée v
La navigation sur nos sites et nos services peuvent déposer des cookies dont la seule finalité est la personnalisation des services et des interfaces, ou garder votre connexion active entre deux sessions. Nous n'utilisons pas les cookies des grandes régies publicitaires (Google, Facebook et cie) pour vous tracer ou analyser vos comportements, nos outils de statistiques sont auto-hébergés et en logiciels libres. Vous pouvez donc autoriser les cookies sans crainte pour vous faciliter la navigation sur nos services.
## Ne me trackez pas
<iframe className="matomo_optout"
src="https://stats.esprit-libre-conseil.com/index.php?module=CoreAdminHome&action=optOut&language=fr&backgroundColor=&fontColor=&fontSize=&fontFamily=">
</iframe>
:::note Sources
[Lydra dépôt git Legal version 1.0.0](https://gitlab.com/lydra/legal/-/blob/1.0.0/confidentialite.md)
......
......@@ -2,7 +2,9 @@
title: dummy
description: "page factice"
---
import CarouselLibre from "@site/src/components/Carousel/CarouselLibre";
import PricingComparator from "@site/src/components/PricingComparator/PricingComparator";
# Page factice
......@@ -12,7 +14,8 @@ Page factice penser à créer une vraie page !
#!/bin/bash
echo "Hello World"
```
<CarouselLibre/>
<CarouselLibre />
<button className="button button--note">button--note</button>
<button className="button button--success">button--success</button>
......@@ -20,7 +23,6 @@ echo "Hello World"
<button className="button button--warning">button--warning</button>
<button className="button button--danger">button--danger</button>
:::note Encart note
🔜 **Les tarifs arrivent !** <a class="button button--note" href="#">Bouton note !</a>
:::
......@@ -70,3 +72,5 @@ Some **content** with _markdown_ `syntax`. Check [this `api`](#).
Some **content** with _markdown_ `syntax`. Check [this `api`](#).
:::
<PricingComparator />
......@@ -3,11 +3,16 @@ title: "Tarifs"
description: "Les tarifs de Froggit"
keywords: Lydra, SaaS, pricing, tarifs, plans, git, GitLab, Mattermost
---
import useBaseUrl from "@docusaurus/useBaseUrl";
import Highlight from "@site/src/components/Highlight";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import { GiTadpole, GiFrogFoot, GiFrogPrince } from "react-icons/gi";
import plans from "@site/src/constants/plans";
import formatNumber from "@site/src/utils/formatNumber";
import PricingComparator from "@site/src/components/PricingComparator/PricingComparator";
# Les tarifs de Froggit
......@@ -17,6 +22,8 @@ Nos tarifs sont simples, transparents, et tout inclus en fonction du nombre d'ut
Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même email** vous cumulerez les sièges.
:::
<Tabs
className="tabs-monthly-annual"
defaultValue="grenouille"
......@@ -40,8 +47,8 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
<h2>Têtard</h2>
<p>Idéal pour <strong>débuter</strong> !</p>
<p>
<strong>24 €HT/an</strong><br/>
<small class="small_price">soit 2 €HT/mois</small><br/>
<strong>{plans.tetard.paidAnnually * 12} €HT/an</strong><br/>
<small class="small_price">soit {plans.tetard.paidAnnually} €HT/mois</small><br/>
<a class="button disabled button-noclick"><GiTadpole size="20px"/> Démarrer</a>
</p>
</div>
......@@ -57,42 +64,79 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
</div>
</div>
<div class="col">
<div class="col">
<div class="headband">
<p>Le plus populaire</p>
</div>
<div class="card-test">
<div class="card shadow--tl card_price card_price_main">
<div class="card__image margin-top--md">
<img
src={useBaseUrl("img/pages/tarifs/computer.png")}
alt="Image alt text"
title="Plan Grenouille" />
</div>
<div class="card__body">
<h2>Grenouille</h2>
<p>Parfait pour les <strong>solo</strong></p>
<div class="card-test">
<div class="card shadow--tl card_price card_price_main">
<div class="card__image margin-top--md">
<img
src={useBaseUrl("img/pages/tarifs/computer.png")}
alt="Image alt text"
title="Plan Grenouille"
/>
</div>
<div class="card__body">
<h2>Grenouille</h2>
<p>
Parfait pour les <strong>solo</strong>
</p>
<p>
<strong>{formatNumber(plans.grenouille.paidMonthly)} €HT/mois</strong>
<br />
<small class="small_price">
<br />
</small>
<a
class="button button--price margin-bottom--lg"
href="https://www.lydra.eu/cmd_ly_frg_grenouille_1_m"
>
<GiFrogFoot size="20px" /> Coder sans limites
</a>
</p>
</div>
<div class="card__footer">
<small>
<p>
<strong>9,90 €HT/mois</strong><br/>
<small class="small_price"><br/></small>
<a class="button button--price margin-bottom--lg" href="https://www.lydra.eu/cmd_ly_frg_grenouille_1_m"><GiFrogFoot size="20px"/> Coder sans limites</a>
<Highlight color="var(--ifm-color-light-green)">1</Highlight>{" "}
utilisateur
</p>
</div>
<div class="card__footer">
<small>
<p><Highlight color="var(--ifm-color-light-green)">1</Highlight> utilisateur</p>
<p>Groupes <Highlight color="var(--ifm-color-light-green)">illimités</Highlight></p>
<p>Projets <Highlight color="var(--ifm-color-light-green)">illimités</Highlight></p>
<p>Pipeline CI/CD <Highlight color="var(--ifm-color-light-green)">illimité</Highlight></p>
</small>
<small>
<p>Bénéficiez de <Highlight color="var(--ifm-color-light-green)">2 mois offerts</Highlight> en choisissant l'abonnement annuel
<small> soit 8,25 €HT/mois (plan annuel)</small><br/></p>
</small>
</div>
<p>
Groupes{" "}
<Highlight color="var(--ifm-color-light-green)">
illimités
</Highlight>
</p>
<p>
Projets{" "}
<Highlight color="var(--ifm-color-light-green)">
illimités
</Highlight>
</p>
<p>
Pipeline CI/CD{" "}
<Highlight color="var(--ifm-color-light-green)">illimité</Highlight>
</p>
</small>
<small>
<p>
Bénéficiez de{" "}
<Highlight color="var(--ifm-color-light-green)">
2 mois offerts
</Highlight>{" "}
en choisissant l'abonnement annuel
<small>
{" "}
soit {formatNumber(plans.grenouille.paidAnnually)} €HT/mois (plan annuel)
</small>
<br />
</p>
</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="disable_col">
......@@ -111,8 +155,8 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
className="tabs-etang-hide">
<TabItem value="2">
<p>
à partir de <strong>198 €HT/an</strong><br/>
<small class="small_price">soit 8,25 €HT/mois/utilisateur</small><br/>
à partir de <strong>{formatNumber(plans.etang2.paidAnnually * 2 * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.etang2.paidAnnually)} €HT/mois/utilisateur</small><br/>
<a class="button disabled button-noclick" href="#url-2"><GiFrogPrince size="20px"/> Acheter <br/> <small>plusieurs sièges</small></a>
</p>
</TabItem>
......@@ -147,8 +191,8 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
<h2>Têtard</h2>
<p>Idéal pour <strong>débuter</strong> !</p>
<p>
<strong>24 €HT/an</strong><br/>
<small class="small_price">soit 2 €HT/mois</small><br/>
<strong>{formatNumber(plans.tetard.paidAnnually * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.tetard.paidAnnually)} €HT/mois</small><br/>
<a class="button button--price" href="https://www.lydra.eu/cmd_ly_frg_tetard_1_a"><GiTadpole size="20px"/> Démarrer</a>
</p>
</div>
......@@ -164,38 +208,68 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
</div>
</div>
<div class="col">
<div class="col">
<div class="headband">
<p>Le plus populaire</p>
</div>
<div class="card-test">
<div class="card shadow--tl card_price card_price_main">
<div class="card__image margin-top--md">
<img
src={useBaseUrl("img/pages/tarifs/computer.png")}
alt="Image alt text"
title="Plan Grenouille" />
</div>
<div class="card__body">
<h2>Grenouille</h2>
<p>Parfait pour les <strong>solo</strong></p>
<div class="card-test">
<div class="card shadow--tl card_price card_price_main">
<div class="card__image margin-top--md">
<img
src={useBaseUrl("img/pages/tarifs/computer.png")}
alt="Image alt text"
title="Plan Grenouille"
/>
</div>
<div class="card__body">
<h2>Grenouille</h2>
<p>
Parfait pour les <strong>solo</strong>
</p>
<p>
<strong>
{formatNumber(plans.grenouille.paidAnnually * 12)} €HT/an
</strong>
<br />
<small class="small_price">
soit {formatNumber(plans.grenouille.paidAnnually)} €HT/mois
</small>
<a
class="button button--price"
href="https://www.lydra.eu/cmd_ly_frg_grenouille_1_a"
>
<GiFrogFoot size="20px" /> Coder sans limites
<br />
</a>
</p>
</div>
<div class="card__footer">
<small>
<p>
<strong>99 €HT/an</strong><br/>
<small class="small_price">soit 8,25 €HT/mois</small>
<a class="button button--price" href="https://www.lydra.eu/cmd_ly_frg_grenouille_1_a"><GiFrogFoot size="20px"/> Coder sans limites<br/></a>
<Highlight color="var(--ifm-color-light-green)">1</Highlight>{" "}
utilisateur
</p>
</div>
<div class="card__footer">
<small>
<p><Highlight color="var(--ifm-color-light-green)">1</Highlight> utilisateur</p>
<p>Groupes <Highlight color="var(--ifm-color-light-green)">illimités</Highlight></p>
<p>Projets <Highlight color="var(--ifm-color-light-green)">illimités</Highlight></p>
<p>Pipeline CI/CD <Highlight color="var(--ifm-color-light-green)">illimité</Highlight></p>
</small>
</div>
<p>
Groupes{" "}
<Highlight color="var(--ifm-color-light-green)">
illimités
</Highlight>
</p>
<p>
Projets{" "}
<Highlight color="var(--ifm-color-light-green)">
illimités
</Highlight>
</p>
<p>
Pipeline CI/CD{" "}
<Highlight color="var(--ifm-color-light-green)">illimité</Highlight>
</p>
</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card-demo">
......@@ -214,36 +288,36 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
className="tabs-etang-hide">
<TabItem value="2">
<p>
<strong>198 €HT/an</strong><br/>
<small class="small_price">soit 8,25 €HT/mois/utilisateur</small><br/>
<strong>{formatNumber(plans.etang2.paidAnnually * 2 * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.etang2.paidAnnually)} €HT/mois/utilisateur</small><br/>
<a class="button button button--price" href="https://www.lydra.eu/cmd_ly_frg_etang_2_a"><GiFrogPrince size="20px"/> Acheter <br/> <small>2 sièges</small></a>
</p>
</TabItem>
<TabItem value="5">
<p>
<strong>495 €HT/an</strong><br/>
<small class="small_price">soit 8,25 €HT/mois/utilisateur</small><br/>
<strong>{formatNumber(plans.etang5.paidAnnually * 5 * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.etang5.paidAnnually)} €HT/mois/utilisateur</small><br/>
<a class="button button button--price" href="https://www.lydra.eu/cmd_ly_frg_etang_5_a"><GiFrogPrince size="20px"/> Acheter <br/> <small>5 sièges</small></a>
</p>
</TabItem>
<TabItem value="10">
<p>
<strong>891 €HT/an</strong><br/>
<small class="small_price">soit 7,43 €HT/mois/utilisateur</small><br/>
<strong>{formatNumber(plans.etang10.paidAnnually * 10 * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.etang10.paidAnnually)} €HT/mois/utilisateur</small><br/>
<a class="button button button--price" href="https://www.lydra.eu/cmd_ly_frg_etang_10_a"><GiFrogPrince size="20px"/> Acheter <br/> <small>10 sièges</small></a>
</p>
</TabItem>
<TabItem value="50">
<p>
<strong>3 960 €HT/an</strong><br/>
<small class="small_price">soit 6,60 €HT/mois/utilisateur</small><br/>
<strong>{formatNumber(plans.etang50.paidAnnually * 50 * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.etang50.paidAnnually)} €HT/mois/utilisateur</small><br/>
<a class="button button button--price" href="https://www.lydra.eu/cmd_ly_frg_etang_50_a"><GiFrogPrince size="20px"/> Acheter <br/> <small>50 sièges</small></a>
</p>
</TabItem>
<TabItem value="100">
<p>
<strong>6 960 €HT/an</strong><br/>
<small class="small_price">soit 5,80 €HT/mois/utilisateur</small><br/>
<strong>{formatNumber(plans.etang100.paidAnnually * 100 * 12)} €HT/an</strong><br/>
<small class="small_price">soit {formatNumber(plans.etang100.paidAnnually)} €HT/mois/utilisateur</small><br/>
<a class="button button button--price" href="https://www.lydra.eu/cmd_ly_frg_etang_100_a"><GiFrogPrince size="20px"/> Acheter <br/> <small>100 sièges</small></a>
</p>
</TabItem>
......@@ -280,11 +354,18 @@ Si vous achetez plusieurs plans (**Grenouille** ou **Étang**), avec le **même
</TabItem>
</Tabs>
:::tip Offre dédiée
Vous avez mieux à faire que gérer votre forge logicielle ?
Alors _libérez-vous du temps_ avec notre **[Offre dédiée](/dedie)** !
:::
## Faites des économies !
Comparez avec les autres solutions pour voir ce que vous économiserez en choisissant Froggit.
<PricingComparator />
<br/>
:::info tarifs de lancement
Ces **tarifs de lancement** évolueront en fonction du nombre de fonctionnalités que nous ajouterons.
:::
:::tip Offre dédiée
Vous avez mieux à faire que gérer votre forge logicielle ?
Alors _libérez-vous du temps_ avec notre **[Offre dédiée](/dedie)** !
:::
......@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import React from "react";
import { Link } from "react-router-dom";
import {useThemeConfig} from "@docusaurus/theme-common";
import FooterLinks from "@theme/Footer/Links";
import FooterLogo from "@theme/Footer/Logo";
......@@ -13,8 +12,6 @@ import FooterCopyright from "@theme/Footer/Copyright";
import FooterLayout from "@theme/Footer/Layout";
import { FaYoutube, FaLinkedin, FaGitlab, FaTwitter } from "react-icons/fa";
import Froggitblanc from "/static/img/theme/logo_froggit_blanc.png";
import DontTrackMe from "/static/img/theme/do-not-track-me.svg";
import useBaseUrl from "@docusaurus/useBaseUrl";
import { WebsiteCarbonBadge } from "react-websitecarbon-badge";
......@@ -65,11 +62,6 @@ function Footer() {
/>
</div>
<div className="footerBadges">
<Link to={useBaseUrl("/confidentialite#ne-me-trackez-pas")} className="notrack_container">
<div>
<DontTrackMe className="no_track" />
</div>
</Link>
<WebsiteCarbonBadge dark="false" url="https://froggit.fr/" lang="fr"/>
</div>
</div>
......
export default function formatNumber(n) {
if (n % 1 === 0) {
// integer
return n.toLocaleString("fr-FR", { maximumFractionDigits: 0 });
}
// float
return n.toLocaleString("fr-FR", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
<svg xmlns="http://www.w3.org/2000/svg" width="199.32999999999998" height="35" viewBox="0 0 199.32999999999998 35"><rect class="svg__rect" x="0" y="0" width="84.67" height="35" fill="#8EA34E"/><rect class="svg__rect" x="82.67" y="0" width="116.66" height="35" fill="#815E91"/><path class="svg__text" d="M16.67 22L14.22 22L14.22 13.47L16.74 13.47Q17.87 13.47 18.74 13.97Q19.62 14.48 20.10 15.40Q20.58 16.33 20.58 17.52L20.58 17.52L20.58 17.95Q20.58 19.16 20.10 20.08Q19.61 21.00 18.72 21.50Q17.83 22 16.67 22L16.67 22ZM15.70 14.66L15.70 20.82L16.67 20.82Q17.83 20.82 18.46 20.09Q19.08 19.36 19.09 17.99L19.09 17.99L19.09 17.52Q19.09 16.13 18.49 15.40Q17.89 14.66 16.74 14.66L16.74 14.66L15.70 14.66ZM24.78 18.00L24.78 18.00L24.78 17.52Q24.78 16.28 25.22 15.32Q25.66 14.37 26.47 13.86Q27.27 13.35 28.31 13.35Q29.36 13.35 30.16 13.85Q30.97 14.35 31.41 15.29Q31.85 16.23 31.85 17.48L31.85 17.48L31.85 17.96Q31.85 19.21 31.42 20.16Q30.99 21.10 30.18 21.61Q29.38 22.12 28.33 22.12L28.33 22.12Q27.29 22.12 26.48 21.61Q25.67 21.10 25.22 20.17Q24.78 19.23 24.78 18.00ZM26.26 17.46L26.26 17.96Q26.26 19.36 26.81 20.13Q27.35 20.90 28.33 20.90L28.33 20.90Q29.31 20.90 29.84 20.15Q30.37 19.40 30.37 17.96L30.37 17.96L30.37 17.51Q30.37 16.09 29.84 15.34Q29.30 14.58 28.31 14.58L28.31 14.58Q27.35 14.58 26.81 15.33Q26.27 16.09 26.26 17.46L26.26 17.46ZM43.78 22L42.30 22L42.30 13.47L43.78 13.47L47.59 19.54L47.59 13.47L49.06 13.47L49.06 22L47.58 22L43.78 15.95L43.78 22ZM53.53 18.00L53.53 18.00L53.53 17.52Q53.53 16.28 53.98 15.32Q54.42 14.37 55.22 13.86Q56.03 13.35 57.07 13.35Q58.12 13.35 58.92 13.85Q59.73 14.35 60.17 15.29Q60.61 16.23 60.61 17.48L60.61 17.48L60.61 17.96Q60.61 19.21 60.18 20.16Q59.74 21.10 58.94 21.61Q58.13 22.12 57.08 22.12L57.08 22.12Q56.05 22.12 55.24 21.61Q54.42 21.10 53.98 20.17Q53.54 19.23 53.53 18.00ZM55.02 17.46L55.02 17.96Q55.02 19.36 55.56 20.13Q56.11 20.90 57.08 20.90L57.08 20.90Q58.07 20.90 58.60 20.15Q59.13 19.40 59.13 17.96L59.13 17.96L59.13 17.51Q59.13 16.09 58.59 15.34Q58.06 14.58 57.07 14.58L57.07 14.58Q56.11 14.58 55.57 15.33Q55.03 16.09 55.02 17.46L55.02 17.46ZM66.76 14.66L64.12 14.66L64.12 13.47L70.89 13.47L70.89 14.66L68.23 14.66L68.23 22L66.76 22L66.76 14.66Z" fill="#FFFFFF"/><path class="svg__text" d="M98.65 15.48L96.07 15.48L96.07 13.60L103.59 13.60L103.59 15.48L101.02 15.48L101.02 22L98.65 22L98.65 15.48ZM110.34 22L107.96 22L107.96 13.60L111.81 13.60Q112.95 13.60 113.79 13.98Q114.62 14.35 115.08 15.06Q115.54 15.76 115.54 16.71L115.54 16.71Q115.54 17.62 115.11 18.30Q114.68 18.98 113.89 19.36L113.89 19.36L115.70 22L113.16 22L111.64 19.77L110.34 19.77L110.34 22ZM110.34 15.47L110.34 17.93L111.66 17.93Q112.39 17.93 112.76 17.61Q113.14 17.29 113.14 16.71L113.14 16.71Q113.14 16.12 112.76 15.79Q112.39 15.47 111.66 15.47L111.66 15.47L110.34 15.47ZM121.74 22L119.32 22L123.03 13.60L125.37 13.60L129.08 22L126.62 22L125.96 20.37L122.40 20.37L121.74 22ZM124.18 15.93L123.10 18.61L125.26 18.61L124.18 15.93ZM132.81 17.80L132.81 17.80Q132.81 16.54 133.41 15.54Q134.01 14.55 135.06 13.99Q136.11 13.43 137.43 13.43L137.43 13.43Q138.58 13.43 139.51 13.84Q140.43 14.25 141.04 15.02L141.04 15.02L139.53 16.39Q138.72 15.40 137.55 15.40L137.55 15.40Q136.87 15.40 136.33 15.70Q135.80 16 135.50 16.54Q135.21 17.09 135.21 17.80L135.21 17.80Q135.21 18.51 135.50 19.05Q135.80 19.60 136.33 19.90Q136.87 20.20 137.55 20.20L137.55 20.20Q138.72 20.20 139.53 19.22L139.53 19.22L141.04 20.58Q140.43 21.35 139.51 21.76Q138.58 22.17 137.43 22.17L137.43 22.17Q136.11 22.17 135.06 21.61Q134.01 21.05 133.41 20.05Q132.81 19.06 132.81 17.80ZM147.93 22L145.58 22L145.58 13.60L147.93 13.60L147.93 17.09L151.19 13.60L153.80 13.60L150.37 17.32L153.98 22L151.22 22L148.82 18.95L147.93 19.90L147.93 22ZM167.26 22L165.06 22L165.06 13.60L167.02 13.60L169.97 18.45L172.85 13.60L174.81 13.60L174.83 22L172.65 22L172.63 17.55L170.46 21.17L169.41 21.17L167.26 17.67L167.26 22ZM186.73 22L179.99 22L179.99 13.60L186.58 13.60L186.58 15.44L182.35 15.44L182.35 16.85L186.08 16.85L186.08 18.63L182.35 18.63L182.35 20.17L186.73 20.17L186.73 22Z" fill="#FFFFFF" x="95.67"/></svg>
\ No newline at end of file