feat: Add download attribute based on file extension
This commit is contained in:
parent
a7f874c64f
commit
9427cb7b5d
@ -5,13 +5,15 @@ export function Link(props: React.AnchorHTMLAttributes<HTMLAnchorElement> & { hr
|
||||
const { urlPathname } = usePageContext();
|
||||
const isActive = props.href === "/" ? urlPathname === props.href : urlPathname.startsWith(props.href);
|
||||
const isSameDomain = !(props.href.startsWith("http") || props.href.startsWith("mailto"));
|
||||
const isDownload = props.href.endsWith(".pdf") || props.href.endsWith(".zip");
|
||||
|
||||
return (
|
||||
<a
|
||||
{...props}
|
||||
href={props.href}
|
||||
className={clsx(isActive && "is-active", props.className)}
|
||||
{...(!isSameDomain ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
||||
{...(isDownload ? { download: true } : {})}
|
||||
{...(!isSameDomain || isDownload ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
||||
>
|
||||
{props.children}
|
||||
</a>
|
||||
|
||||
@ -1,8 +1,105 @@
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
|
||||
import { usePageContext } from "vike-react/usePageContext";
|
||||
import { Link } from "@/components/common/Link";
|
||||
import { navigation } from "@/lib/navigation";
|
||||
import { useState } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type NavigationItemProps = {
|
||||
section: (typeof navigation)[number];
|
||||
onLinkClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
};
|
||||
|
||||
function NavigationItem(props: NavigationItemProps) {
|
||||
const { urlPathname } = usePageContext();
|
||||
|
||||
const [isOpened, setIsOpened] = useState(() => {
|
||||
return props.section.links.some(
|
||||
(link) => link.href === urlPathname || link.subitems?.some((subitem) => subitem.href === urlPathname),
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
setIsOpened((prev) => !prev);
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
className={clsx(
|
||||
"font-display font-medium cursor-pointer",
|
||||
isOpened ? "text-violet-600 dark:text-violet-200" : "text-slate-900 dark:text-white ",
|
||||
)}
|
||||
onClick={() => setIsOpened((prev) => !prev)}
|
||||
>
|
||||
{isOpened ? (
|
||||
<ChevronUpIcon className="inline-block mr-2 h-5 w-5 text-slate-400" />
|
||||
) : (
|
||||
<ChevronDownIcon className="inline-block mr-2 h-5 w-5 text-slate-400" />
|
||||
)}
|
||||
<span className="sr-only">{isOpened ? "Masquer" : "Afficher"}</span>
|
||||
|
||||
{props.section.title}
|
||||
|
||||
<span className="text-slate-400 dark:text-slate-500"> ({props.section.links.length})</span>
|
||||
</h2>
|
||||
{isOpened && (
|
||||
<ul
|
||||
role="list"
|
||||
className="mt-2 space-y-2 border-l-2 border-slate-100 lg:mt-4 lg:space-y-4 lg:border-slate-200 dark:border-slate-800 mb-9"
|
||||
>
|
||||
{props.section.links.map((link) => (
|
||||
<li key={link.href} className="relative">
|
||||
<Link
|
||||
href={link.href}
|
||||
onClick={props.onLinkClick}
|
||||
className={clsx(
|
||||
"block w-full pl-3.5 before:pointer-events-none before:absolute before:-left-1 before:h-1.5 before:w-1.5 before:rounded-full",
|
||||
{ "before:top-1/2 before:-translate-y-1/2": !link.subitems },
|
||||
{ "before:top-3 before:-translate-y-1/2": link.subitems },
|
||||
link.href === urlPathname || link.subitems?.some((subitem) => subitem.href === urlPathname)
|
||||
? "font-semibold text-violet-500 before:bg-violet-500"
|
||||
: "text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300",
|
||||
)}
|
||||
>
|
||||
{link.title}
|
||||
{link.subitems && <span className="text-slate-400 dark:text-slate-500"> ({link.subitems.length})</span>}
|
||||
</Link>
|
||||
{link.subitems && (
|
||||
<ul
|
||||
role="list"
|
||||
className="ml-4 border-l-2 border-slate-100 lg:space-y-1 lg:border-slate-200 dark:border-slate-800 mb-4"
|
||||
>
|
||||
{link.subitems.map((subitem) => (
|
||||
<li key={subitem.href} className="relative">
|
||||
<Link
|
||||
href={subitem.href}
|
||||
onClick={props.onLinkClick}
|
||||
className={clsx(
|
||||
"block w-full pl-3.5 before:pointer-events-none before:absolute before:top-1/2 before:-left-1 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full",
|
||||
subitem.href === urlPathname
|
||||
? "font-semibold text-violet-500 before:bg-violet-500"
|
||||
: "text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300",
|
||||
)}
|
||||
>
|
||||
{subitem.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Navigation({
|
||||
className,
|
||||
onLinkClick,
|
||||
@ -10,35 +107,35 @@ export function Navigation({
|
||||
className?: string;
|
||||
onLinkClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
}) {
|
||||
const { urlPathname } = usePageContext();
|
||||
const firstSections = navigation.filter((section) => section.position === "start");
|
||||
|
||||
const filteredSections = navigation
|
||||
.filter((section) => section.position === "auto" || section.position === undefined)
|
||||
.reduce(
|
||||
(acc, section) => {
|
||||
if (!acc[section.type]) {
|
||||
acc[section.type] = [];
|
||||
}
|
||||
acc[section.type].push(section);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, typeof navigation>,
|
||||
);
|
||||
|
||||
return (
|
||||
<nav className={clsx("text-base lg:text-sm", className)}>
|
||||
<ul role="list" className="space-y-9">
|
||||
{navigation.map((section) => (
|
||||
<ul role="list" className="space-y-4">
|
||||
{firstSections.map((section) => (
|
||||
<li key={section.title}>
|
||||
<h2 className="font-display font-medium text-slate-900 dark:text-white">{section.title}</h2>
|
||||
<ul
|
||||
role="list"
|
||||
className="mt-2 space-y-2 border-l-2 border-slate-100 lg:mt-4 lg:space-y-4 lg:border-slate-200 dark:border-slate-800"
|
||||
>
|
||||
{section.links.map((link) => (
|
||||
<li key={link.href} className="relative">
|
||||
<Link
|
||||
href={link.href}
|
||||
onClick={onLinkClick}
|
||||
className={clsx(
|
||||
"block w-full pl-3.5 before:pointer-events-none before:absolute before:top-1/2 before:-left-1 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full",
|
||||
link.href === urlPathname
|
||||
? "font-semibold text-violet-500 before:bg-violet-500"
|
||||
: "text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300",
|
||||
)}
|
||||
>
|
||||
{link.title}
|
||||
</Link>
|
||||
<NavigationItem section={section} onLinkClick={onLinkClick} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{Object.entries(filteredSections).map(([type, sections]) => (
|
||||
<li key={type}>
|
||||
<h2 className="font-display font-bold text-base text-slate-900 dark:text-white">{type}</h2>
|
||||
{sections.map((section) => (
|
||||
<NavigationItem key={section.title} section={section} onLinkClick={onLinkClick} />
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
63
app/data/certifications/dwwm/at1/cp1/page.md
Normal file
63
app/data/certifications/dwwm/at1/cp1/page.md
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
title: DWWM CP 1 - Installer et configurer son environnement de travail en fonction du projet web ou web mobile
|
||||
description: Synthèse et explications des attentes relatives à la compétence professionnelle 1 du titre professionnel DWWM (01280m04).
|
||||
tags: [DWWM]
|
||||
---
|
||||
|
||||
## 📚 Références
|
||||
|
||||
- REAC _(mise à jour du 02/07/2024)_, pages 15 et 16
|
||||
- RE _(mise à jour du 02/07/2024)_, page 9
|
||||
|
||||
## 📋 En résumé
|
||||
|
||||
Ce qui est attendu de ta part, c'est d'expliquer **comment** on peut installer et configurer les prérequis pour exécuter ton projet.
|
||||
|
||||
Tu as utilisé un framework PHP et React en front ?
|
||||
Tu devras alors expliquer comment installer PHP, Composer, Node.js, npm _(ou autre gestionnaire de dépendances Node)_ et les autres dépendances nécessaires à ton projet comme la base de données !
|
||||
|
||||
Et pour te donner un ordre d'idée, voici ce que ça peut donner :
|
||||
|
||||
- Versionning _(Git, SVN, ...)_
|
||||
- IDE ou éditeur de code _(Visual Studio Code, PhpStorm, ...)_
|
||||
- Langages/runtimes _(PHP, Node.js, ...)_
|
||||
- Gestionnaires de dépendances _(Composer, npm, ...)_
|
||||
- Serveurs web _(Apache, Nginx, ...)_
|
||||
- Base de données _(MySQL, PostgreSQL, ...)_
|
||||
- DevOps _(Docker, Vagrant, ...)_
|
||||
- etc.
|
||||
|
||||
Tu l'as compris, c'est vaste !
|
||||
Mais heureusement, tu dois uniquement expliquer comment installer et configurer les outils que tu as utilisés pour ton projet.
|
||||
|
||||
Si tu fais un projet Laravel et React, pas besoin d'expliquer comment installer et configurer Ruby et Java, par exemple 😉
|
||||
|
||||
{% callout type="note" title="Utilisation de XAMPP, WAMP, MAMP, LAMP, Laragon etc." %}
|
||||
|
||||
Si tu utilises un logiciel comme XAMPP, WAMP, MAMP, LAMP, Laragal etc., tu as évidemment le droit de le mentionner dans ta présentation et dossier de projet.
|
||||
|
||||
Toutefois, il est préférable que tu saches expliquer comment installer et configurer les éléments nécessaires de manières individuelles.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
### ➕ Informations complémentaires
|
||||
|
||||
{% callout type="warning" title="Versions des outils et dépendances" %}
|
||||
|
||||
Même si le choix des outils que tu utilises est libre, il est important de préciser les versions que tu as utilisées pour ton projet.
|
||||
|
||||
Étant donné que chaque version corrige probablement diverses failles de sécurité et/ou ajoute des fonctionnalités, c'est le bon moment pour montrer que tu prends la veille technologique au sérieux.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
### 🛠️ Ressources conseillées
|
||||
|
||||
TODO
|
||||
|
||||
### 🎯 Critères d'évaluation
|
||||
|
||||
- Les outils de développement nécessaires sont installés et configurés
|
||||
- Les outils de gestion de versions et de collaboration sont installés
|
||||
- Les conteneurs implémentes les services requis pour l'environnement de développement
|
||||
- La documentation technique de l'environnement de travail est comprise, en langue française ou anglaise (niveau B1 CECRL pour l'anglais)
|
||||
- Le système de veille permet de suivre les évolutions technologies et les problématiques de sécurité en lien avec l'installation et la configuration d'un environnement de travail
|
||||
23
app/data/certifications/dwwm/at1/page.md
Normal file
23
app/data/certifications/dwwm/at1/page.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Activité Type 1 - Développer la partie front-end d'une application web ou web mobile sécurisée
|
||||
description: Synthèse et explications des attentes relatives à l'activité type 1 du titre professionnel DWWM (01280m04).
|
||||
tags: [DWWM]
|
||||
---
|
||||
|
||||
## 📚 Références
|
||||
|
||||
- REAC _(mise à jour du 02/07/2024)_, pages 15 et 16
|
||||
- RE _(mise à jour du 02/07/2024)_, page 9
|
||||
|
||||
## 📋 En résumé
|
||||
|
||||
Cette activité type concerne tout ce qui est relatif à la conception _(maquettes, arborescence etc.)_ et à la création de l'interface.
|
||||
|
||||
Voyons un peu plus en détail ce qui est attendu pour chacune de ces compétences professionnelles ! 🚀
|
||||
|
||||
Elle est divisée en 4 **compétences professionnelles** _(CP)_ :
|
||||
|
||||
- **CP 1** : Installer et configurer son environnement de travail en fonction du projet web ou web mobile
|
||||
- **CP 2** : Maquetter des interfaces utilisateur web ou web mobile
|
||||
- **CP 3** : Réaliser des interfaces utilisateur statiques web ou web mobile
|
||||
- **CP 4** : Développer la partie dynamique des interfaces utilisateur web ou web mobile
|
||||
59
app/data/certifications/dwwm/page.md
Normal file
59
app/data/certifications/dwwm/page.md
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Résumé du titre professionnel DWWM
|
||||
description: Découvre le résumé du titre professionnel DWWM (TP-01280m04), qui te permettra de te préparer au mieux à l'examen !
|
||||
tags: [DWWM]
|
||||
---
|
||||
|
||||
## Informations administratives
|
||||
|
||||
- Nom complet du titre : **Développeur Web et Web Mobile**
|
||||
- Sigle : **DWWM**
|
||||
- Code RNCP : **37674**
|
||||
- Code titre : **01280m04**
|
||||
|
||||
### Documentations officielles
|
||||
|
||||
- [REAC - Référentiel Emploi Activités Compétences _(02/07/2024)_](/downloads/dwwm/REAC_DWWM_V04_02072024.pdf)
|
||||
- [RE - Référentiel d'Évaluation _(02/07/2024)_](/downloads/dwwm/REV2_DWWM_V04_02072024.pdf)
|
||||
|
||||
> Provenance des documentations : [Site DGEFP Grand public](https://www.banque.di.afpa.fr/EspaceEmployeursCandidatsActeurs/titre-professionnel/01280m04)
|
||||
|
||||
## Activités types et compétences professionnelles
|
||||
|
||||
## 📚 Activité type 1 - Développer la partie front-end d'une application web ou web mobile sécurisée
|
||||
|
||||
- CP 1 - Installer et configurer son environnement de travail en fonction du projet web ou web mobile
|
||||
- CP 2 - Maquetter des interfaces utilisateur web ou web mobile
|
||||
- CP 3 - Réaliser des interfaces utilisateur statiques web ou web mobile
|
||||
- CP 4 - Développer la partie dynamique des interfaces utilisateur web ou web mobile
|
||||
|
||||
## 📚 Activité type 2 - Développer la partie back-end d'une application web ou web mobile sécurisée
|
||||
|
||||
- CP 5 - Mettre en place une base de données relationnelle
|
||||
- CP 6 - Développer des composants d'accès aux données SQL et NoSQL
|
||||
- CP 7 - Développer des composants métier coté serveur
|
||||
- CP 8 - Documenter le déploiement d'une application dynamique web ou web mobile
|
||||
|
||||
## Compétences transverses
|
||||
|
||||
- Communiquer en français et en anglais
|
||||
- Mettre en oeuvre une démarche de résolution de problème
|
||||
- Apprendre en continu
|
||||
|
||||
## Déroulé de l'examen
|
||||
|
||||
{% callout type="note" title="Déroulé relatif au passage de l'épreuve dans sa globalité" %}
|
||||
|
||||
En cas de repassage d'un CCP, se référer au Référentiel d'Évaluation pour connaître les modalités de l'épreuve :
|
||||
|
||||
- Pages 17 et 18 pour l'AT 1
|
||||
- Pages 19 et 20 pour l'AT 2
|
||||
|
||||
{% /callout %}
|
||||
|
||||
**Durée totale de l'examen** : 2h _(dont 1h30 de soutenance face au jury)_
|
||||
|
||||
- Questionnaire professionnel _(30 minutes, sans présence du jury)_
|
||||
- Présentation d'un projet réalisé en amont de la session _(35 minutes, face au jury)_
|
||||
- Entretien technique _(40 minutes, face au jury)_
|
||||
- Entretien final _(15 minutes, face au jury)_
|
||||
@ -1,16 +1,52 @@
|
||||
const navigationsTypes = {
|
||||
GLOBAL: "👋 Général",
|
||||
CERTIFICATIONS: "🎓 Certifications",
|
||||
DOCUMENTATIONS: "📚 Documentations",
|
||||
};
|
||||
|
||||
export const navigation = [
|
||||
{
|
||||
title: "Préambule",
|
||||
type: "global",
|
||||
type: navigationsTypes.GLOBAL,
|
||||
position: "start",
|
||||
links: [
|
||||
{ title: "Memento Dev", href: "/" },
|
||||
{ title: "Certifications", href: "/certifications" },
|
||||
{ title: "Documentations", href: "/docs" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Développeur Web et Web Mobile",
|
||||
type: navigationsTypes.CERTIFICATIONS,
|
||||
position: "auto",
|
||||
links: [
|
||||
{ title: "Résumé", href: "/certifications/dwwm" },
|
||||
{
|
||||
title: "Activité Type 1",
|
||||
href: "/certifications/dwwm/at1",
|
||||
subitems: [
|
||||
{ title: "CP 1", href: "/certifications/dwwm/at1/cp1" },
|
||||
{ title: "CP 2", href: "/certifications/dwwm/at1/cp2" },
|
||||
{ title: "CP 3", href: "/certifications/dwwm/at1/cp3" },
|
||||
{ title: "CP 4", href: "/certifications/dwwm/at1/cp4" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Activité Type 2",
|
||||
href: "/certifications/dwwm/at2",
|
||||
subitems: [
|
||||
{ title: "CP 5", href: "/certifications/dwwm/at2/cp5" },
|
||||
{ title: "CP 6", href: "/certifications/dwwm/at2/cp6" },
|
||||
{ title: "CP 7", href: "/certifications/dwwm/at2/cp7" },
|
||||
{ title: "CP 8", href: "/certifications/dwwm/at2/cp8" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "React",
|
||||
type: "documentation",
|
||||
type: navigationsTypes.DOCUMENTATIONS,
|
||||
position: "auto",
|
||||
links: [
|
||||
{ title: "Introduction", href: "/docs/react" },
|
||||
{ title: "Initialisation", href: "/docs/react/initialisation" },
|
||||
@ -24,7 +60,8 @@ export const navigation = [
|
||||
},
|
||||
{
|
||||
title: "Merise",
|
||||
type: "documentation",
|
||||
type: navigationsTypes.DOCUMENTATIONS,
|
||||
position: "auto",
|
||||
links: [
|
||||
{ title: "Introduction", href: "/docs/merise" },
|
||||
{ title: "Dictionnaire de données", href: "/docs/merise/dictionnaire-de-donnees" },
|
||||
@ -33,7 +70,8 @@ export const navigation = [
|
||||
},
|
||||
{
|
||||
title: "Communauté",
|
||||
type: "global",
|
||||
type: navigationsTypes.GLOBAL,
|
||||
position: "start",
|
||||
links: [
|
||||
{ title: "Influenceurs", href: "/docs/communaute/influenceurs" },
|
||||
{ title: "Partages et réutilisations", href: "/docs/communaute/partages" },
|
||||
|
||||
BIN
app/public/downloads/dwwm/REAC_DWWM_V04_02072024.pdf
Normal file
BIN
app/public/downloads/dwwm/REAC_DWWM_V04_02072024.pdf
Normal file
Binary file not shown.
BIN
app/public/downloads/dwwm/REV2_DWWM_V04_02072024.pdf
Normal file
BIN
app/public/downloads/dwwm/REV2_DWWM_V04_02072024.pdf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user