mirror of
https://github.com/arthur-pbty/calculatrice.git
synced 2026-06-03 23:36:29 +02:00
Refactor Dockerfile, update README, and enhance Calculator component
- Refactored Dockerfile for improved multi-stage builds and added development and production configurations. - Updated README with clearer project description, local development instructions, and added contact information. - Enhanced Calculator component to manage theme and history using localStorage, improving user experience. - Added new pages for legal mentions and privacy policy, including relevant metadata. - Updated docker-compose.yml for better service management and added environment variables. - Introduced a new LICENSE file outlining usage rights and responsibilities.
This commit is contained in:
+16
-24
@@ -1,34 +1,26 @@
|
|||||||
# === Étape 1 : Build ===
|
FROM node:22-alpine AS calculatrice-base
|
||||||
FROM node:20-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
# Copier les fichiers de dépendances pour profiter du cache Docker
|
FROM calculatrice-base AS calculatrice-deps
|
||||||
COPY package*.json ./
|
COPY package.json package-lock.json ./
|
||||||
|
|
||||||
# Installer uniquement ce qu'il faut pour le build
|
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# Copier tout le code
|
FROM calculatrice-deps AS calculatrice-dev
|
||||||
COPY . .
|
COPY . .
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["npm", "run", "dev", "--", "--hostname", "0.0.0.0", "--port", "3000"]
|
||||||
|
|
||||||
# Build Next.js pour la production
|
FROM calculatrice-deps AS calculatrice-builder
|
||||||
|
COPY . .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# === Étape 2 : Runner léger ===
|
FROM calculatrice-base AS calculatrice-runner
|
||||||
FROM node:20-alpine AS runner
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copier uniquement ce qui est nécessaire pour la prod
|
|
||||||
COPY --from=builder /app/package*.json ./
|
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
|
||||||
COPY --from=builder /app/.next ./.next
|
|
||||||
COPY --from=builder /app/public ./public
|
|
||||||
|
|
||||||
# Mode production
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
ENV PORT=3000
|
||||||
|
COPY --from=calculatrice-builder /app/.next/standalone ./
|
||||||
|
COPY --from=calculatrice-builder /app/.next/static ./.next/static
|
||||||
|
COPY --from=calculatrice-builder /app/public ./public
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
CMD ["node", "server.js"]
|
||||||
# Lancer le serveur Next.js
|
|
||||||
CMD ["npm", "start"]
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
Copyright (c) 2026 Arthur
|
||||||
|
|
||||||
|
Licence pour tous les projets Arthur
|
||||||
|
|
||||||
|
1. Définition
|
||||||
|
Cette licence définit les droits et obligations concernant l'utilisation, la modification et la redistribution du code fourni par l'auteur.
|
||||||
|
|
||||||
|
2. Autorisation d'utilisation
|
||||||
|
Vous êtes libre d'utiliser ce code pour vos projets personnels ou commerciaux. L'utilisation doit inclure une mention de l'auteur d’une manière libre (ex: "inspiré de ArthurP").
|
||||||
|
|
||||||
|
3. Modification
|
||||||
|
Vous pouvez modifier, adapter ou améliorer le code pour vos besoins. Les modifications doivent être identifiées comme telles et ne doivent pas être présentées comme l'original.
|
||||||
|
|
||||||
|
4. Redistribution
|
||||||
|
- Le code original **ne peut pas être redistribué tel quel**.
|
||||||
|
- Les versions modifiées peuvent être partagées, sous réserve de mentionner l'auteur original.
|
||||||
|
|
||||||
|
5. Usage commercial
|
||||||
|
L’usage commercial des versions modifiées est autorisé. Vous pouvez générer des revenus avec votre version modifiée.
|
||||||
|
|
||||||
|
6. Attribution
|
||||||
|
L'auteur original doit être cité d’une manière libre, mais visible, sur tout projet utilisant ce code ou ses dérivés.
|
||||||
|
|
||||||
|
7. Responsabilité
|
||||||
|
Le code est fourni "tel quel", sans garantie d’aucune sorte. L’auteur décline toute responsabilité pour tout dommage direct ou indirect résultant de l’utilisation du code.
|
||||||
@@ -1,33 +1,32 @@
|
|||||||
# Calculatrice
|
# Calculatrice
|
||||||
|
|
||||||
Application web de calculatrice réalisée avec Next.js.
|
Calculatrice web (simple + scientifique) construite avec Next.js.
|
||||||
|
|
||||||
## Site en ligne
|
## Liens
|
||||||
|
|
||||||
- Projet: https://calculatrice.arthurp.fr
|
- Site: https://calculatrice.arthurp.fr
|
||||||
|
- Site principal: https://arthurp.fr
|
||||||
|
- Contact: https://contact.arthurp.fr
|
||||||
|
- Email: contact@arthurp.fr
|
||||||
|
|
||||||
## Objectif
|
## Stack
|
||||||
|
|
||||||
Proposer une calculatrice simple, rapide et responsive, utilisable sur desktop et mobile.
|
|
||||||
|
|
||||||
## Stack technique
|
|
||||||
|
|
||||||
- Next.js 16
|
- Next.js 16
|
||||||
- React 19
|
- React 19
|
||||||
- TypeScript
|
- TypeScript
|
||||||
|
|
||||||
## Lancement en local
|
## Developpement local
|
||||||
|
|
||||||
Prerequis: Node.js 20+
|
Prerequis: Node.js 22+
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm ci
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Application accessible sur http://localhost:3000
|
Application: http://localhost:3000
|
||||||
|
|
||||||
## Scripts utiles
|
## Scripts
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
@@ -36,21 +35,24 @@ npm run start
|
|||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploiement
|
## Docker
|
||||||
|
|
||||||
Build de production:
|
Mode dev:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
docker compose --profile dev up --build
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensuite deployer sur votre plateforme cible (Vercel, VPS, etc.).
|
Mode production:
|
||||||
|
|
||||||
## Backlinks
|
```bash
|
||||||
|
docker compose --profile prod up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
- Calculatrice en ligne: https://calculatrice.arthurp.fr
|
- Dev: http://localhost:3000
|
||||||
- Site principal: https://arthurp.fr
|
- Prod: http://localhost:3014
|
||||||
|
|
||||||
## Licence
|
## Pages legales
|
||||||
|
|
||||||
Projet personnel.
|
- /mentions-legales
|
||||||
|
- /politique-de-confidentialite
|
||||||
|
|||||||
+145
-110
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { useState, useEffect, useCallback, useRef } from "react";
|
import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════
|
/* ══════════════════════════════════════════════
|
||||||
@@ -151,39 +152,37 @@ export default function Calculator() {
|
|||||||
const [error, setError] = useState(""); // Message d'erreur
|
const [error, setError] = useState(""); // Message d'erreur
|
||||||
const [steps, setSteps] = useState<string[]>([]); // Étapes de calcul
|
const [steps, setSteps] = useState<string[]>([]); // Étapes de calcul
|
||||||
const [showSteps, setShowSteps] = useState(false); // Afficher les étapes
|
const [showSteps, setShowSteps] = useState(false); // Afficher les étapes
|
||||||
const [history, setHistory] = useState<HistoryEntry[]>([]); // Historique
|
const [history, setHistory] = useState<HistoryEntry[]>(() => {
|
||||||
|
if (typeof window === "undefined") return [];
|
||||||
|
try {
|
||||||
|
const savedHistory = localStorage.getItem(HISTORY_KEY);
|
||||||
|
return savedHistory ? JSON.parse(savedHistory) : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}); // Historique
|
||||||
const [showHistory, setShowHistory] = useState(false); // Panneau historique visible
|
const [showHistory, setShowHistory] = useState(false); // Panneau historique visible
|
||||||
const [isScientific, setIsScientific] = useState(false); // Mode scientifique
|
const [isScientific, setIsScientific] = useState(false); // Mode scientifique
|
||||||
const [isDark, setIsDark] = useState(false); // Thème sombre
|
const [isDark, setIsDark] = useState(() => {
|
||||||
|
if (typeof window === "undefined") return false;
|
||||||
|
try {
|
||||||
|
const savedTheme = localStorage.getItem(THEME_KEY);
|
||||||
|
if (savedTheme === "dark") return true;
|
||||||
|
if (savedTheme === "light") return false;
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}); // Thème sombre
|
||||||
const [copied, setCopied] = useState(false); // Feedback copie
|
const [copied, setCopied] = useState(false); // Feedback copie
|
||||||
const [lastKey, setLastKey] = useState(""); // Dernière touche pressée (feedback visuel)
|
const [lastKey, setLastKey] = useState(""); // Dernière touche pressée (feedback visuel)
|
||||||
|
|
||||||
const displayRef = useRef<HTMLDivElement>(null);
|
const displayRef = useRef<HTMLDivElement>(null);
|
||||||
const simpleRef = useRef<HTMLButtonElement>(null);
|
|
||||||
const sciRef = useRef<HTMLButtonElement>(null);
|
|
||||||
|
|
||||||
// ── Chargement initial depuis localStorage ──
|
// ── Synchroniser la classe de thème avec l'état courant ──
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
document.documentElement.classList.toggle("dark", isDark);
|
||||||
const savedHistory = localStorage.getItem(HISTORY_KEY);
|
}, [isDark]);
|
||||||
if (savedHistory) setHistory(JSON.parse(savedHistory));
|
|
||||||
|
|
||||||
const savedTheme = localStorage.getItem(THEME_KEY);
|
|
||||||
if (savedTheme === "dark") {
|
|
||||||
setIsDark(true);
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
} else if (savedTheme === "light") {
|
|
||||||
setIsDark(false);
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
} else {
|
|
||||||
// Détecter la préférence système
|
|
||||||
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
||||||
setIsDark(prefersDark);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// localStorage non disponible
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// ── Sauvegarder l'historique dans localStorage ──
|
// ── Sauvegarder l'historique dans localStorage ──
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -430,7 +429,7 @@ export default function Calculator() {
|
|||||||
════════════════════════════════════════════ */
|
════════════════════════════════════════════ */
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col items-center justify-center p-4 sm:p-8 transition-colors duration-300">
|
<div className="min-h-screen flex flex-col items-center p-4 sm:p-8 transition-colors duration-300">
|
||||||
{/* Lien d'accessibilité : aller au contenu principal */}
|
{/* Lien d'accessibilité : aller au contenu principal */}
|
||||||
<a
|
<a
|
||||||
href="#calculatrice"
|
href="#calculatrice"
|
||||||
@@ -463,23 +462,16 @@ export default function Calculator() {
|
|||||||
<div
|
<div
|
||||||
className="mode-slider"
|
className="mode-slider"
|
||||||
style={{
|
style={{
|
||||||
left: isScientific
|
transform: isScientific ? "translateX(100%)" : "translateX(0)",
|
||||||
? (simpleRef.current?.offsetWidth ?? 80) + 2 + "px"
|
|
||||||
: "2px",
|
|
||||||
width: isScientific
|
|
||||||
? (sciRef.current?.offsetWidth ?? 100) + "px"
|
|
||||||
: (simpleRef.current?.offsetWidth ?? 80) + "px",
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
ref={simpleRef}
|
|
||||||
className={!isScientific ? "active" : ""}
|
className={!isScientific ? "active" : ""}
|
||||||
onClick={() => setIsScientific(false)}
|
onClick={() => setIsScientific(false)}
|
||||||
>
|
>
|
||||||
Simple
|
Simple
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
ref={sciRef}
|
|
||||||
className={isScientific ? "active" : ""}
|
className={isScientific ? "active" : ""}
|
||||||
onClick={() => setIsScientific(true)}
|
onClick={() => setIsScientific(true)}
|
||||||
>
|
>
|
||||||
@@ -747,102 +739,145 @@ export default function Calculator() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Footer SEO avec contenu sémantique ── */}
|
{/* ── Contenu SEO ── */}
|
||||||
<footer className="mt-12 w-full max-w-2xl animate-fade-in" style={{ animationDelay: "0.3s" }}>
|
<section className="mt-10 w-full max-w-3xl space-y-5" aria-labelledby="seo-content-heading">
|
||||||
{/* Contenu textuel riche pour le SEO */}
|
<h2 id="seo-content-heading" className="text-xl font-bold" style={{ color: "var(--foreground)" }}>
|
||||||
<article className="mb-8 px-4">
|
Calculatrice en ligne simple et scientifique
|
||||||
<h2 className="text-xl font-bold mb-3" style={{ color: "var(--foreground)" }}>
|
</h2>
|
||||||
Calculatrice en ligne gratuite
|
<p className="text-sm leading-relaxed" style={{ color: "var(--muted)" }}>
|
||||||
</h2>
|
Cette calculatrice en ligne gratuite permet de faire vos opérations du quotidien et vos calculs plus avances,
|
||||||
<p className="text-sm leading-relaxed mb-4" style={{ color: "var(--muted)" }}>
|
directement depuis votre navigateur. Aucun compte, aucune installation et une interface rapide sur mobile comme
|
||||||
Notre calculatrice en ligne gratuite vous permet d'effectuer tous vos calculs
|
sur ordinateur.
|
||||||
directement dans votre navigateur, sans installation ni inscription. Que vous ayez
|
</p>
|
||||||
besoin d'une simple addition ou d'un calcul scientifique complexe avec des
|
<p className="text-sm leading-relaxed" style={{ color: "var(--muted)" }}>
|
||||||
fonctions trigonométriques, notre outil s'adapte à vos besoins.
|
Le mode simple couvre addition, soustraction, multiplication et division. Le mode scientifique ajoute sin, cos,
|
||||||
</p>
|
tan, logarithmes, racine carree, puissances, factorielle et constantes pi / e. L'historique est conserve
|
||||||
|
localement pour retrouver vos derniers calculs.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2 className="text-lg font-semibold mb-2" style={{ color: "var(--foreground)" }}>
|
<div className="rounded-xl border p-4" style={{ borderColor: "var(--border-color)", background: "var(--surface)" }}>
|
||||||
Mode simple : les opérations essentielles
|
<h3 className="text-base font-semibold" style={{ color: "var(--foreground)" }}>
|
||||||
</h2>
|
Questions frequentes
|
||||||
<p className="text-sm leading-relaxed mb-4" style={{ color: "var(--muted)" }}>
|
</h3>
|
||||||
Le mode simple couvre les quatre opérations fondamentales : addition (+),
|
<div className="mt-3 space-y-2 text-sm" style={{ color: "var(--muted)" }}>
|
||||||
soustraction (−), multiplication (×) et division (÷). Vous pouvez utiliser
|
|
||||||
des parenthèses pour structurer vos expressions et obtenir des résultats
|
|
||||||
précis. La gestion des erreurs vous avertit automatiquement en cas de
|
|
||||||
division par zéro.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2 className="text-lg font-semibold mb-2" style={{ color: "var(--foreground)" }}>
|
|
||||||
Mode scientifique : des fonctions avancées
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm leading-relaxed mb-4" style={{ color: "var(--muted)" }}>
|
|
||||||
Basculez en mode scientifique pour accéder aux fonctions trigonométriques
|
|
||||||
(sinus, cosinus, tangente), aux logarithmes (log décimal, logarithme naturel),
|
|
||||||
à la racine carrée, aux puissances, à la factorielle, ainsi qu'aux
|
|
||||||
constantes mathématiques π et e. Idéal pour les étudiants, ingénieurs et
|
|
||||||
professionnels.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
{/* Section FAQ pour le SEO */}
|
|
||||||
<section className="mb-8 px-4" aria-labelledby="faq-heading">
|
|
||||||
<h2 id="faq-heading" className="text-lg font-semibold mb-3" style={{ color: "var(--foreground)" }}>
|
|
||||||
Questions fréquentes
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
q: "Comment utiliser la calculatrice en ligne ?",
|
q: "Comment faire un calcul rapidement ?",
|
||||||
a: "Cliquez sur les boutons ou utilisez votre clavier pour saisir une expression mathématique, puis appuyez sur « = » ou Entrée pour obtenir le résultat.",
|
a: "Saisissez votre expression puis appuyez sur Entree ou sur le bouton =.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "Quelles fonctions scientifiques sont disponibles ?",
|
q: "Puis-je utiliser le clavier ?",
|
||||||
a: "Sinus, cosinus, tangente, logarithme décimal, logarithme naturel, racine carrée, puissances, factorielle, et les constantes π et e.",
|
a: "Oui. Les chiffres et operateurs sont pris en charge. Entree calcule, Echap efface et Retour arriere supprime le dernier caractere.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "L'historique des calculs est-il sauvegardé ?",
|
q: "A quoi sert le mode scientifique ?",
|
||||||
a: "Oui, vos 50 derniers calculs sont sauvegardés automatiquement dans votre navigateur et persistent même après fermeture de la page.",
|
a: "Il ajoute les fonctions avancees: sin, cos, tan, log, ln, racine carree, puissances et factorielle.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "La calculatrice est-elle vraiment gratuite ?",
|
q: "Est-ce que l'historique est conserve ?",
|
||||||
a: "Oui, 100 % gratuite, sans publicité et sans inscription. Utilisez-la autant que vous le souhaitez.",
|
a: "Oui, les 50 derniers calculs sont stockes localement dans votre navigateur.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "Puis-je utiliser des raccourcis clavier ?",
|
q: "Puis-je reutiliser un ancien calcul ?",
|
||||||
a: "Oui ! Chiffres et opérateurs au clavier, Entrée pour calculer, Échap pour effacer, Retour arrière pour supprimer, Ctrl+C pour copier.",
|
a: "Oui, ouvrez l'historique puis cliquez sur une ligne pour recharger l'expression et le resultat.",
|
||||||
},
|
},
|
||||||
].map((faq, i) => (
|
{
|
||||||
|
q: "La calculatrice fonctionne-t-elle sur mobile ?",
|
||||||
|
a: "Oui, l'interface est adaptee aux ecrans mobiles et ordinateurs.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
q: "Dois-je creer un compte ?",
|
||||||
|
a: "Non, aucun compte n'est necessaire pour utiliser la calculatrice.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
q: "Comment contacter le proprietaire du site ?",
|
||||||
|
a: "Via contact.arthurp.fr ou par email a contact@arthurp.fr.",
|
||||||
|
},
|
||||||
|
].map((item, i) => (
|
||||||
<details
|
<details
|
||||||
key={i}
|
key={i}
|
||||||
className="rounded-lg overflow-hidden transition-all"
|
className="rounded-lg border px-3 py-2"
|
||||||
style={{
|
style={{ borderColor: "var(--border-color)", background: "var(--background)" }}
|
||||||
background: "var(--surface)",
|
|
||||||
border: "1px solid var(--border-color)",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<summary
|
<summary className="cursor-pointer font-medium" style={{ color: "var(--foreground)" }}>
|
||||||
className="px-4 py-3 cursor-pointer text-sm font-medium select-none hover:bg-[var(--surface-hover)] transition-colors"
|
{item.q}
|
||||||
style={{ color: "var(--foreground)" }}
|
|
||||||
>
|
|
||||||
{faq.q}
|
|
||||||
</summary>
|
</summary>
|
||||||
<p
|
<p className="mt-2 leading-relaxed">{item.a}</p>
|
||||||
className="px-4 pb-3 text-sm leading-relaxed"
|
|
||||||
style={{ color: "var(--muted)" }}
|
|
||||||
>
|
|
||||||
{faq.a}
|
|
||||||
</p>
|
|
||||||
</details>
|
</details>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* Copyright */}
|
{/* ── Footer normal en bas de page ── */}
|
||||||
<div className="text-center text-xs pb-4" style={{ color: "var(--muted)" }}>
|
<footer className="mt-auto w-full px-3 pt-10 pb-3">
|
||||||
<p>© {new Date().getFullYear()} Calculatrice en ligne gratuite — Simple & Scientifique</p>
|
<div
|
||||||
<p className="mt-1 opacity-60">
|
className="mx-auto w-full max-w-5xl rounded-2xl border p-4 sm:p-6 backdrop-blur-xl"
|
||||||
Outil de calcul en ligne rapide, gratuit et sans inscription.
|
style={{
|
||||||
</p>
|
background: "color-mix(in srgb, var(--surface) 88%, transparent)",
|
||||||
|
borderColor: "var(--border-color)",
|
||||||
|
boxShadow: "var(--shadow-lg)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
|
<section>
|
||||||
|
<h2 className="text-sm font-semibold tracking-wide uppercase" style={{ color: "var(--foreground)" }}>
|
||||||
|
Navigation
|
||||||
|
</h2>
|
||||||
|
<nav className="mt-3 flex flex-col gap-2 text-sm" aria-label="Navigation du site">
|
||||||
|
<Link href="/" className="hover:underline" style={{ color: "var(--muted)" }}>Accueil</Link>
|
||||||
|
<a href="https://arthurp.fr/projets" target="_blank" rel="noreferrer" className="hover:underline" style={{ color: "var(--muted)" }}>Projets</a>
|
||||||
|
<a href="https://contact.arthurp.fr" target="_blank" rel="noreferrer" className="hover:underline" style={{ color: "var(--muted)" }}>Contact</a>
|
||||||
|
</nav>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="text-sm font-semibold tracking-wide uppercase" style={{ color: "var(--foreground)" }}>
|
||||||
|
Liens
|
||||||
|
</h2>
|
||||||
|
<div className="mt-3 flex flex-col gap-2 text-sm">
|
||||||
|
<a href="https://arthurp.fr" target="_blank" rel="noreferrer" className="hover:underline" style={{ color: "var(--muted)" }}>
|
||||||
|
arthurp.fr
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/arthur-pbty" target="_blank" rel="noreferrer" className="hover:underline" style={{ color: "var(--muted)" }}>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="text-sm font-semibold tracking-wide uppercase" style={{ color: "var(--foreground)" }}>
|
||||||
|
Légal
|
||||||
|
</h2>
|
||||||
|
<div className="mt-3 flex flex-col gap-2 text-sm">
|
||||||
|
<Link href="/mentions-legales" className="hover:underline" style={{ color: "var(--muted)" }}>
|
||||||
|
Mentions légales
|
||||||
|
</Link>
|
||||||
|
<Link href="/politique-de-confidentialite" className="hover:underline" style={{ color: "var(--muted)" }}>
|
||||||
|
Politique de confidentialité
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2 className="text-sm font-semibold tracking-wide uppercase" style={{ color: "var(--foreground)" }}>
|
||||||
|
Contact
|
||||||
|
</h2>
|
||||||
|
<div className="mt-3 flex flex-col gap-2 text-sm" style={{ color: "var(--muted)" }}>
|
||||||
|
<a href="https://contact.arthurp.fr" target="_blank" rel="noreferrer" className="hover:underline">
|
||||||
|
contact.arthurp.fr
|
||||||
|
</a>
|
||||||
|
<a href="mailto:contact@arthurp.fr" className="hover:underline">
|
||||||
|
contact@arthurp.fr
|
||||||
|
</a>
|
||||||
|
<p className="text-xs opacity-80">Fait avec ❤️ et auto-hébergé sur Proxmox.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 border-t pt-3 text-center text-xs" style={{ color: "var(--muted)", borderColor: "var(--border-color)" }}>
|
||||||
|
© {new Date().getFullYear()} Arthur P. Tous droits réservés.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+6
-1
@@ -285,6 +285,7 @@ body {
|
|||||||
.mode-toggle {
|
.mode-toggle {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
min-width: 210px;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -292,9 +293,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mode-toggle button {
|
.mode-toggle button {
|
||||||
|
flex: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
|
text-align: center;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
@@ -311,10 +314,12 @@ body {
|
|||||||
.mode-slider {
|
.mode-slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
bottom: 2px;
|
bottom: 2px;
|
||||||
|
width: calc(50% - 2px);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
transition: left 0.3s ease, width 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════
|
/* ══════════════════════════════════════════════
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Mentions legales",
|
||||||
|
description: "Mentions legales du site calculatrice.arthurp.fr",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function MentionsLegalesPage() {
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen px-4 py-12 sm:px-8">
|
||||||
|
<div className="mx-auto w-full max-w-3xl rounded-2xl border p-6 sm:p-8" style={{ background: "var(--surface)", borderColor: "var(--border-color)" }}>
|
||||||
|
<h1 className="text-2xl font-bold sm:text-3xl">Mentions legales</h1>
|
||||||
|
|
||||||
|
<section className="mt-6 space-y-3 text-sm leading-relaxed" style={{ color: "var(--muted)" }}>
|
||||||
|
<p>
|
||||||
|
Site: calculatrice.arthurp.fr
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Proprietaire et editeur: Arthur P.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Contact principal: <a href="https://contact.arthurp.fr" className="hover:underline">contact.arthurp.fr</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Email: <a href="mailto:contact@arthurp.fr" className="hover:underline">contact@arthurp.fr</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Hebergement: infrastructure auto-hebergee sur Proxmox.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Politique de confidentialite",
|
||||||
|
description: "Politique de confidentialite du site calculatrice.arthurp.fr",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PolitiqueConfidentialitePage() {
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen px-4 py-12 sm:px-8">
|
||||||
|
<div className="mx-auto w-full max-w-3xl rounded-2xl border p-6 sm:p-8" style={{ background: "var(--surface)", borderColor: "var(--border-color)" }}>
|
||||||
|
<h1 className="text-2xl font-bold sm:text-3xl">Politique de confidentialite</h1>
|
||||||
|
|
||||||
|
<section className="mt-6 space-y-3 text-sm leading-relaxed" style={{ color: "var(--muted)" }}>
|
||||||
|
<p>
|
||||||
|
Cette application ne requiert pas de compte et ne collecte pas de donnees personnelles cote serveur pour son fonctionnement courant.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
L'historique de calcul est stocke localement dans votre navigateur (localStorage) pour ameliorer l'experience utilisateur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Vous pouvez supprimer ces donnees a tout moment depuis le bouton "Tout effacer" dans l'historique ou via les reglages de votre navigateur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Pour toute demande, vous pouvez utiliser <a href="https://contact.arthurp.fr" className="hover:underline">contact.arthurp.fr</a> ou <a href="mailto:contact@arthurp.fr" className="hover:underline">contact@arthurp.fr</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,5 +15,17 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|||||||
changeFrequency: "monthly",
|
changeFrequency: "monthly",
|
||||||
priority: 1.0,
|
priority: 1.0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: `${baseUrl}/mentions-legales`,
|
||||||
|
lastModified: new Date(),
|
||||||
|
changeFrequency: "yearly",
|
||||||
|
priority: 0.4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: `${baseUrl}/politique-de-confidentialite`,
|
||||||
|
lastModified: new Date(),
|
||||||
|
changeFrequency: "yearly",
|
||||||
|
priority: 0.4,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-5
@@ -1,11 +1,31 @@
|
|||||||
services:
|
services:
|
||||||
web:
|
calculatrice-dev:
|
||||||
build: .
|
profiles: ["dev"]
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: calculatrice-dev
|
||||||
|
container_name: calculatrice-dev
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/app
|
- .:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
|
- /app/.next
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
NEXT_TELEMETRY_DISABLED: "1"
|
||||||
command: npm run dev
|
NODE_ENV: development
|
||||||
|
|
||||||
|
calculatrice-prod:
|
||||||
|
profiles: ["prod"]
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: calculatrice-runner
|
||||||
|
container_name: calculatrice-prod
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3014:3000"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
NEXT_TELEMETRY_DISABLED: "1"
|
||||||
|
NODE_ENV: production
|
||||||
Reference in New Issue
Block a user