Compare commits
3 Commits
72721adaff
...
83b7c42fe4
| Author | SHA1 | Date | |
|---|---|---|---|
| 83b7c42fe4 | |||
| 24bff5f4b5 | |||
| 8766552707 |
10
package-lock.json
generated
@@ -45,6 +45,7 @@
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@laravel/vite-plugin-wayfinder": "^0.1.3",
|
||||
"@types/node": "^22.13.5",
|
||||
"baseline-browser-mapping": "^2.10.10",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-react": "^7.37.3",
|
||||
@@ -2848,10 +2849,15 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.10",
|
||||
"version": "2.10.10",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz",
|
||||
"integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.js"
|
||||
"baseline-browser-mapping": "dist/cli.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@laravel/vite-plugin-wayfinder": "^0.1.3",
|
||||
"@types/node": "^22.13.5",
|
||||
"baseline-browser-mapping": "^2.10.10",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-react": "^7.37.3",
|
||||
|
||||
BIN
public/favicons/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/favicons/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
public/favicons/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
17
public/favicons/favicon.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><style>
|
||||
#light-icon {
|
||||
display: inline;
|
||||
}
|
||||
#dark-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#light-icon {
|
||||
display: none;
|
||||
}
|
||||
#dark-icon {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
</style><g id="light-icon"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g><g transform="matrix(10.75268817204301,0,0,10.75268817204301,5.684341886080802e-14,102.15053763440864)"><svg xmlns="http://www.w3.org/2000/svg" width="93" height="74" viewBox="0 0 93 74" fill="none"><ellipse cx="46.3415" cy="41.389" rx="35.3752" ry="27.9464" fill="white"></ellipse><circle cx="27.9347" cy="37.99" r="4.46941" fill="#FFA8BA"></circle><circle cx="63.6901" cy="37.99" r="4.46941" fill="#FFA8BA"></circle><path d="M46.1219 0C61.5954 0.00108972 74.8403 9.53527 80.3104 23.0479L81.6834 25.0723C83.6532 27.9801 85.8227 31.4382 87.6756 34.8086C89.5186 38.1612 91.0891 41.4998 91.8094 44.1504C92.1654 45.4609 92.3492 46.7271 92.1717 47.7842C92.0805 48.3269 91.8844 48.8619 91.5272 49.3145C91.1614 49.7768 90.6687 50.0974 90.0916 50.2676C88.996 50.589 87.6298 50.3673 86.0868 49.8076C84.7924 49.3378 83.2492 48.5798 81.4295 47.5156C76.8613 62.6894 62.7841 73.7439 46.1219 73.7451C29.4595 73.7447 15.377 62.6912 10.8075 47.5176C8.98891 48.5806 7.4458 49.3382 6.15219 49.8076C4.60898 50.3672 3.24387 50.5893 2.14828 50.2676C1.57071 50.0974 1.07767 49.7774 0.711757 49.3145C0.354546 48.8619 0.159453 48.327 0.0682023 47.7842C-0.109333 46.727 0.0764139 45.4611 0.43246 44.1504C1.15279 41.4997 2.72122 38.1613 4.5643 34.8086C6.4172 31.4382 8.58663 27.9802 10.5565 25.0723L11.9246 23.0498C17.3949 9.53493 30.6464 0.000380988 46.1219 0ZM75.5555 34.3604C68.6179 28.2902 62.5227 27.1288 57.7127 27.9727C52.8444 28.8279 49.0905 31.7745 46.9705 34.2471L46.1151 35.2441L45.2684 34.2432C40.6526 28.7884 34.925 27.6504 29.6131 28.4512C24.2424 29.2619 19.3457 32.0593 16.6844 34.3652L16.0145 34.9434C15.9742 35.5812 15.9539 36.225 15.9539 36.873C15.9542 53.5342 29.4609 67.0405 46.1219 67.041C62.7822 67.0396 76.2906 53.5336 76.2909 36.873C76.2909 36.2273 76.2674 35.5859 76.2274 34.9502L75.5555 34.3604Z" fill="black"></path><path d="M47.4565 41.9402C46.5147 42.6325 45.11 42.6325 44.1682 41.9402L37.7342 37.2104C35.9945 35.9316 37.0689 33.5206 39.3783 33.5206L52.2464 33.5206C54.5559 33.5206 55.6302 35.9316 53.8906 37.2104L47.4565 41.9402Z" fill="#FAAE2B"></path></svg></g></g></svg></g><g id="dark-icon"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g clip-path="url(#SvgjsClipPath1009)"><rect width="1000" height="1000" fill="#ffffff"></rect><g transform="matrix(7.526881720430108,0,0,7.526881720430108,150,221.50537634408602)"><svg xmlns="http://www.w3.org/2000/svg" width="93" height="74" viewBox="0 0 93 74" fill="none"><ellipse cx="46.3415" cy="41.389" rx="35.3752" ry="27.9464" fill="white"></ellipse><circle cx="27.9347" cy="37.99" r="4.46941" fill="#FFA8BA"></circle><circle cx="63.6901" cy="37.99" r="4.46941" fill="#FFA8BA"></circle><path d="M46.1219 0C61.5954 0.00108972 74.8403 9.53527 80.3104 23.0479L81.6834 25.0723C83.6532 27.9801 85.8227 31.4382 87.6756 34.8086C89.5186 38.1612 91.0891 41.4998 91.8094 44.1504C92.1654 45.4609 92.3492 46.7271 92.1717 47.7842C92.0805 48.3269 91.8844 48.8619 91.5272 49.3145C91.1614 49.7768 90.6687 50.0974 90.0916 50.2676C88.996 50.589 87.6298 50.3673 86.0868 49.8076C84.7924 49.3378 83.2492 48.5798 81.4295 47.5156C76.8613 62.6894 62.7841 73.7439 46.1219 73.7451C29.4595 73.7447 15.377 62.6912 10.8075 47.5176C8.98891 48.5806 7.4458 49.3382 6.15219 49.8076C4.60898 50.3672 3.24387 50.5893 2.14828 50.2676C1.57071 50.0974 1.07767 49.7774 0.711757 49.3145C0.354546 48.8619 0.159453 48.327 0.0682023 47.7842C-0.109333 46.727 0.0764139 45.4611 0.43246 44.1504C1.15279 41.4997 2.72122 38.1613 4.5643 34.8086C6.4172 31.4382 8.58663 27.9802 10.5565 25.0723L11.9246 23.0498C17.3949 9.53493 30.6464 0.000380988 46.1219 0ZM75.5555 34.3604C68.6179 28.2902 62.5227 27.1288 57.7127 27.9727C52.8444 28.8279 49.0905 31.7745 46.9705 34.2471L46.1151 35.2441L45.2684 34.2432C40.6526 28.7884 34.925 27.6504 29.6131 28.4512C24.2424 29.2619 19.3457 32.0593 16.6844 34.3652L16.0145 34.9434C15.9742 35.5812 15.9539 36.225 15.9539 36.873C15.9542 53.5342 29.4609 67.0405 46.1219 67.041C62.7822 67.0396 76.2906 53.5336 76.2909 36.873C76.2909 36.2273 76.2674 35.5859 76.2274 34.9502L75.5555 34.3604Z" fill="black"></path><path d="M47.4565 41.9402C46.5147 42.6325 45.11 42.6325 44.1682 41.9402L37.7342 37.2104C35.9945 35.9316 37.0689 33.5206 39.3783 33.5206L52.2464 33.5206C54.5559 33.5206 55.6302 35.9316 53.8906 37.2104L47.4565 41.9402Z" fill="#FAAE2B"></path></svg></g></g><defs><clipPath id="SvgjsClipPath1009"><rect width="1000" height="1000" x="0" y="0" rx="350" ry="350"></rect></clipPath></defs></svg></g></svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
21
public/favicons/site.webmanifest
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Le Retzien Libre",
|
||||
"short_name": "Retzien",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicons/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/favicons/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
BIN
public/favicons/web-app-manifest-192x192.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
public/favicons/web-app-manifest-512x512.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
@@ -1,5 +1,5 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap');
|
||||
@import 'tailwindcss';
|
||||
@import './fonts.css';
|
||||
|
||||
@plugin 'tailwindcss-animate';
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Figtree', 'Inter', 'Instrument Sans', ui-sans-serif, system-ui, sans-serif,
|
||||
/* Fonts */
|
||||
--font-sans: 'Space Grotesk', 'Inter', 'Instrument Sans', ui-sans-serif, system-ui, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
|
||||
/* Radius Sizes */
|
||||
--radius-lg: var(--radius);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius: 0.5rem;
|
||||
|
||||
/* Colors */
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
|
||||
@@ -38,7 +42,7 @@
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
||||
}
|
||||
|
||||
:root {
|
||||
|
||||
44
resources/css/fonts.css
Normal file
@@ -0,0 +1,44 @@
|
||||
/* space-grotesk-300 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('fonts/space-grotesk-v22-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* space-grotesk-regular - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('fonts/space-grotesk-v22-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* space-grotesk-500 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('fonts/space-grotesk-v22-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* space-grotesk-600 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('fonts/space-grotesk-v22-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
|
||||
/* space-grotesk-700 - latin */
|
||||
@font-face {
|
||||
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('fonts/space-grotesk-v22-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||
}
|
||||
BIN
resources/css/fonts/space-grotesk-v22-latin-300.woff2
Normal file
BIN
resources/css/fonts/space-grotesk-v22-latin-500.woff2
Normal file
BIN
resources/css/fonts/space-grotesk-v22-latin-600.woff2
Normal file
BIN
resources/css/fonts/space-grotesk-v22-latin-700.woff2
Normal file
BIN
resources/css/fonts/space-grotesk-v22-latin-regular.woff2
Normal file
@@ -2,13 +2,6 @@ import { SVGAttributes } from 'react';
|
||||
|
||||
export default function AppLogoIcon(props: SVGAttributes<SVGElement>) {
|
||||
return (
|
||||
/* <svg {...props} viewBox="0 0 40 42" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M17.2 5.63325L8.6 0.855469L0 5.63325V32.1434L16.2 41.1434L32.4 32.1434V23.699L40 19.4767V9.85547L31.4 5.07769L22.8 9.85547V18.2999L17.2 21.411V5.63325ZM38 18.2999L32.4 21.411V15.2545L38 12.1434V18.2999ZM36.9409 10.4439L31.4 13.5221L25.8591 10.4439L31.4 7.36561L36.9409 10.4439ZM24.8 18.2999V12.1434L30.4 15.2545V21.411L24.8 18.2999ZM23.8 20.0323L29.3409 23.1105L16.2 30.411L10.6591 27.3328L23.8 20.0323ZM7.6 27.9212L15.2 32.1434V38.2999L2 30.9666V7.92116L7.6 11.0323V27.9212ZM8.6 9.29991L3.05913 6.22165L8.6 3.14339L14.1409 6.22165L8.6 9.29991ZM30.4 24.8101L17.2 32.1434V38.2999L30.4 30.9666V24.8101ZM9.6 11.0323L15.2 7.92117V22.5221L9.6 25.6333V11.0323Z"
|
||||
/>
|
||||
</svg>*/
|
||||
<svg {...props} viewBox="0 0 42 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="21.138" cy="16.5" r="15" stroke="#000" strokeWidth="3"/>
|
||||
<path d="M21.138 15c-4.4-5.2-11-2.167-13.5 0 1.6-10.4 9.333-12 13-12 10.4.4 13.667 8.167 14 12-6.4-5.6-11.5-2.333-13.5 0Z" fill="#000" stroke="#000"/>
|
||||
@@ -17,6 +10,5 @@ export default function AppLogoIcon(props: SVGAttributes<SVGElement>) {
|
||||
<path d="M5.638 11.5c-3.5 5.167-8.4 14.2 0 9v-9ZM36.638 11.5c3.5 5.167 8.4 14.2 0 9v-9Z" fill="#000" stroke="#000"/>
|
||||
<path d="M21.736 18.768a1.28 1.28 0 0 1-1.472 0l-2.879-2.117c-.778-.572-.298-1.651.736-1.651h5.758c1.034 0 1.514 1.079.736 1.651l-2.88 2.117Z" fill="#FAAE2B"/>
|
||||
</svg>
|
||||
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
8
resources/js/components/common/Container.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import React from "react";
|
||||
|
||||
export function Container({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div className={cn('w-full max-w-7xl mx-auto px-4', className)} {...props} />
|
||||
);
|
||||
}
|
||||
13
resources/js/components/common/PawsDecoration.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import {SVGAttributes} from "react";
|
||||
|
||||
export default function PawsDecoration(props: SVGAttributes<SVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox="0 0 400 350" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity=".35" fill="#8A8A8A">
|
||||
<path d="M31.13 292.304c4.61-1.125 10.243 5.075 13.861 14.559 6.197-8.478 13.57-13.138 17.786-10.724 5.002 2.864 3.664 14.605-2.99 26.224-4.347 7.592-9.889 13.252-14.603 15.464-.742.802-1.6 1.344-2.565 1.58a4.613 4.613 0 0 1-.834.122c-4.887 1.893-14.34-.778-23.41-7.031C7.352 324.9 1.068 314.892 4.34 310.146c2.818-4.089 11.807-2.936 21.291 2.319-1.379-10.479.758-19.005 5.498-20.161ZM228.85 25.324c4.409 1.756 5.406 10.073 2.856 19.899 9.964-3.317 18.673-2.84 20.709 1.57 2.416 5.234-5.48 14.026-17.635 19.637-7.945 3.667-15.742 5.067-20.866 4.137-1.069.223-2.081.168-3.004-.2a4.57 4.57 0 0 1-.75-.384c-5.08-1.29-11.234-8.945-15.001-19.298-4.578-12.581-3.898-24.379 1.519-26.35 4.666-1.698 11.323 4.451 16.005 14.231 4.95-9.339 11.634-15.047 16.167-13.242ZM145.999 243.334c4.29 2.031 4.76 10.394 1.597 20.04 10.153-2.683 18.815-1.658 20.569 2.872 2.081 5.375-6.353 13.652-18.838 18.486-8.159 3.159-16.028 4.065-21.084 2.814-1.081.156-2.088.037-2.987-.388a4.656 4.656 0 0 1-.723-.43c-4.989-1.607-10.649-9.636-13.756-20.206-3.777-12.845-2.355-24.576 3.176-26.202 4.764-1.401 11.02 5.156 15.077 15.212 5.529-9.009 12.559-14.285 16.969-12.198ZM366.871 13.708c4.29 2.03 4.76 10.394 1.597 20.04 10.153-2.683 18.815-1.658 20.569 2.872 2.081 5.375-6.353 13.652-18.838 18.486-8.159 3.16-16.029 4.066-21.085 2.815-1.08.155-2.087.036-2.986-.39a4.602 4.602 0 0 1-.723-.43c-4.989-1.606-10.649-9.635-13.756-20.205-3.776-12.845-2.355-24.576 3.176-26.202 4.764-1.4 11.021 5.156 15.077 15.211 5.529-9.008 12.559-14.284 16.969-12.197ZM247.503 117.591c3.358 3.354.991 11.389-5.23 19.411 10.464.885 18.278 4.761 18.408 9.616.154 5.763-10.571 10.724-23.954 11.082-8.747.234-16.464-1.557-20.805-4.434-1.07-.217-1.978-.668-2.681-1.37a4.496 4.496 0 0 1-.537-.648c-4.159-3.19-6.792-12.653-6.167-23.653.76-13.367 6.041-23.938 11.796-23.611 4.958.282 8.647 8.559 9.089 19.393 8.235-6.627 16.628-9.234 20.081-5.786ZM112.344 139.976c4.68.785 7.412 8.703 6.996 18.846 9.038-5.348 17.652-6.722 20.574-2.842 3.467 4.605-2.392 14.867-13.088 22.92-6.99 5.263-14.315 8.28-19.52 8.454-.998.443-1.999.603-2.979.439a4.566 4.566 0 0 1-.814-.217c-5.238-.187-12.87-6.369-18.74-15.692-7.134-11.33-8.962-23.005-4.084-26.076 4.202-2.646 12.008 1.957 18.65 10.526 2.866-10.173 8.192-17.165 13.005-16.358Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
33
resources/js/components/common/ScrollToTop.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ChevronUp } from 'lucide-react';
|
||||
|
||||
export function ScrollToTop() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const hero = document.getElementById('hero');
|
||||
if (!hero) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => setIsVisible(!entry.isIntersecting),
|
||||
{ threshold: 0 },
|
||||
);
|
||||
|
||||
observer.observe(hero);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
const scrollToTop = () => window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
aria-label="Retour en haut"
|
||||
className="fixed bottom-6 right-6 z-50 p-3 rounded-full border-3 border-black bg-primary shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-x-1 hover:translate-y-1 transition duration-200"
|
||||
>
|
||||
<ChevronUp className="size-5" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
27
resources/js/components/common/SectionHeading.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
interface SectionHeadingProps {
|
||||
title: string;
|
||||
color?: string;
|
||||
subtitle?: string;
|
||||
align?: 'left' | 'center';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function SectionHeading({title, color, subtitle, align = 'center', className}: SectionHeadingProps) {
|
||||
return (
|
||||
<div className={cn(
|
||||
'flex gap-10 items-center ',
|
||||
align === 'center' && 'text-center',
|
||||
align === 'left' && 'text-left',
|
||||
className,
|
||||
)}>
|
||||
<h2 className={`text-3xl text-black font-medium bg-${color} rounded p-1`}>
|
||||
{title}
|
||||
</h2>
|
||||
{subtitle && (
|
||||
<p className="text-md text-muted-foreground max-w-2xl">{subtitle}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
resources/js/components/features/home/AboutSection.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {SectionHeading} from "@/components/common/SectionHeading";
|
||||
import {Button} from '@/components/ui/button';
|
||||
|
||||
export function AboutSection() {
|
||||
return (
|
||||
<section className="w-full py-16">
|
||||
<div className="max-w-7xl mx-auto px-4">
|
||||
<SectionHeading title="Qui sommes-nous ?" color="secondary"
|
||||
subtitle="Le Retzien Libre, c’est une association qui promeut l’auto-hébergement et la décentralisation des services en ligne depuis 2017."
|
||||
align='left'/>
|
||||
<div
|
||||
className="bg-white rounded-4xl border-3 border-black mt-10 px-10 pt-20 pb-10 shadow-[4px_4px_0px_rgba(0,0,0,1)]">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="flex flex-col gap-3 lg:border-r-2 border-black lg:pr-10 border-0">
|
||||
<h3 className="text-xl text-primary font-semibold">Une association locale</h3>
|
||||
<p>
|
||||
Nous voulons vous proposer à travers des outils libres, ouverts et solidaires, de
|
||||
quitter l'industrie du G.A.F.A.M.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 border-r-2 border-black pr-10">
|
||||
<h3 className="text-xl text-primary font-semibold">Notre mission</h3>
|
||||
<p>
|
||||
Nous nous positionnons comme "A.M.A.P. informatique", délivrant des services "bio",
|
||||
"éthiques" et "locaux" dans le Pays de Retz et Nantes.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<h3 className="text-xl text-primary font-semibold">Notre solution</h3>
|
||||
<p>
|
||||
Pour seulement 12€/an, adhérez au Retzien Libre pour bénéficier des services de
|
||||
l'association et reprenez le contrôle de vos données.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex justify-center mt-20">
|
||||
<Button variant="default">En savoir plus en lisant notre Blog</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
43
resources/js/components/features/home/AlternativeSection.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Link, usePage } from '@inertiajs/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { dashboard, register } from '@/routes';
|
||||
import { type SharedData } from '@/types';
|
||||
import IllustrationLogo from '@/img/utils/lrl-logo-full.svg';
|
||||
|
||||
export function AlternativeSection() {
|
||||
const { auth } = usePage<SharedData>().props;
|
||||
|
||||
return (
|
||||
<section className="w-full py-16">
|
||||
<div className="bg-gray-100 rounded-4xl max-w-7xl mx-auto px-4">
|
||||
<div className="flex flex-col lg:flex-row items-center gap-12">
|
||||
<div className="lg:w-1/2 flex justify-center">
|
||||
<img
|
||||
src={IllustrationLogo}
|
||||
alt="Le Retzien Libre"
|
||||
className="rounded-lg max-w-md w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-6 lg:w-1/2">
|
||||
<h2 className="text-3xl text-black font-medium">
|
||||
Notre alternative : Le Retzien Libre
|
||||
</h2>
|
||||
<p>
|
||||
Ici, pas d’exploitation des vos données personnelles à des fins commerciales, ni de dépense aux services centralisés.<br/>
|
||||
Le Retzien Libre, c’est une association locale engagée pour la promotion du logiciel libre et la protection de vos données personnelles.
|
||||
</p>
|
||||
{auth.user ? (
|
||||
<Link href={dashboard()}>
|
||||
<Button variant="default">Accéder à mon espace</Button>
|
||||
</Link>
|
||||
) : (
|
||||
<Link href={register()}>
|
||||
<Button variant="secondary">Rejoignez-nous</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
65
resources/js/components/features/home/HeroSection.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Link, usePage } from '@inertiajs/react';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { dashboard, membership } from '@/routes';
|
||||
import { type SharedData } from '@/types';
|
||||
import illustrationImage from '@/img/utils/lrl-illustration.png';
|
||||
import PawsDecoration from '@/components/common/PawsDecoration';
|
||||
import { useParallax } from '@/hooks/use-parallax';
|
||||
|
||||
export function HeroSection() {
|
||||
const { auth } = usePage<SharedData>().props;
|
||||
const pawsRef = useParallax<HTMLDivElement>(0.12);
|
||||
|
||||
const scrollToFirstSection = () => {
|
||||
document.getElementById('first-section')?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
id="hero"
|
||||
className="relative flex flex-col w-full max-w-[335px] lg:max-w-7xl mx-auto min-h-[calc(100vh-80px)] px-4"
|
||||
>
|
||||
{/* Contenu principal */}
|
||||
<div className="flex flex-1 items-center justify-center gap-4 w-full">
|
||||
<div className="flex flex-col w-full items-center text-center lg:items-start lg:text-left justify-center gap-4">
|
||||
<h1 className="text-5xl text-accent max-w-[450px] mb-5">
|
||||
Pour un internet éthique !
|
||||
</h1>
|
||||
<p className="text-xl mb-5">
|
||||
"Dégooglisons"<br />
|
||||
nos ordinateurs, nos tablettes et nos smartphones.<br />
|
||||
<i>"Le chemin est long, mais la voie est libre"</i>
|
||||
</p>
|
||||
{auth.user ? (
|
||||
<Link href={dashboard()}>
|
||||
<Button variant="secondary">Mon espace</Button>
|
||||
</Link>
|
||||
) : (
|
||||
<Link href={membership()}>
|
||||
<Button variant="secondary">Adhérer dès maintenant</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<div className="hidden lg:flex w-full items-center justify-center">
|
||||
<img src={illustrationImage} alt="Illustration Le Retzien Libre" className="max-w-md w-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ref={pawsRef} className="absolute bottom-12 right-0 pointer-events-none hidden lg:block w-48 opacity-40">
|
||||
<PawsDecoration className="w-full h-auto" />
|
||||
</div>
|
||||
|
||||
{/* Flèche vers la première section */}
|
||||
<div className="flex justify-center pb-8">
|
||||
<button
|
||||
onClick={scrollToFirstSection}
|
||||
aria-label="Voir nos services"
|
||||
className="p-2 rounded-full border-3 border-black animate-bounce hover:animate-none transition"
|
||||
>
|
||||
<ChevronDown className="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
25
resources/js/components/features/home/ServiceCard.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import {Service} from "@/types";
|
||||
|
||||
export function ServiceCard({title, colorTitle, bgColor, bgTitle, description, link, illustration}: Service) {
|
||||
return (
|
||||
<div
|
||||
className={`flex gap-1 items-center bg-${bgColor} justify-center gap-4 rounded-4xl p-10 border-3 border-black shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out`}>
|
||||
<div>
|
||||
<div className="max-w-[150px]">
|
||||
<h3 className={`inline text-2xl font-semibold text-${colorTitle} font-medium bg-${bgTitle} rounded p-1 line-clamp-2`}>{title}</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mt-5">{description}</p>
|
||||
<a href={link} className="text-white underline hover:font-medium mt-4 inline-block hover:underline">
|
||||
En savoir plus
|
||||
</a>
|
||||
</div>
|
||||
<div className="relative w-full h-64">
|
||||
<img
|
||||
src={illustration}
|
||||
alt={title}
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
89
resources/js/components/features/home/ServicesSection.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { type Service } from '@/types';
|
||||
import { ServiceCard } from './ServiceCard';
|
||||
import { SectionHeading } from '@/components/common/SectionHeading';
|
||||
import { Container } from '@/components/common/Container';
|
||||
import PawsDecoration from '@/components/common/PawsDecoration';
|
||||
import { useParallax } from '@/hooks/use-parallax';
|
||||
|
||||
const services: Service[] = [
|
||||
{
|
||||
title: 'Boîte mail',
|
||||
description: 'Service de messagerie électronique sécurisé et respectueux de votre vie privée',
|
||||
colorTitle: 'white',
|
||||
bgTitle: 'accent',
|
||||
bgColor: 'secondary',
|
||||
link: '#',
|
||||
illustration: '../../img/utils/lrl-logo.svg'
|
||||
},
|
||||
{
|
||||
title: 'Stockage Cloud',
|
||||
description: 'Stockage en ligne et collaboration avec vos données hébergées localement',
|
||||
colorTitle: 'black',
|
||||
bgTitle: 'white',
|
||||
bgColor: 'primary',
|
||||
link: '#',
|
||||
illustration: '../../img/utils/lrl-logo.svg'
|
||||
},
|
||||
{
|
||||
title: 'Hébergement de site',
|
||||
description: "Solutions d'hébergement web éthiques et performantes",
|
||||
colorTitle: 'black',
|
||||
bgTitle: 'primary',
|
||||
bgColor: 'accent',
|
||||
link: '#',
|
||||
illustration: '../../img/utils/lrl-logo.svg'
|
||||
},
|
||||
{
|
||||
title: 'Email Marketing',
|
||||
description: "Gérez vos communications de groupe efficacement",
|
||||
colorTitle: 'black',
|
||||
bgTitle: 'secondary',
|
||||
bgColor: 'gray',
|
||||
link: '#',
|
||||
illustration: '../../img/utils/lrl-logo.svg'
|
||||
},
|
||||
{
|
||||
title: 'Partage de fichiers',
|
||||
description: 'Partager facielement vos fichiers en toute sécurité',
|
||||
colorTitle: 'black',
|
||||
bgTitle: 'white',
|
||||
bgColor: 'primary',
|
||||
link: '#',
|
||||
illustration: '../../img/utils/lrl-logo.svg'
|
||||
},
|
||||
{
|
||||
title: 'Outil de Sondage',
|
||||
description: 'Créez et partagez des sondages en ligne en toute confidentialité',
|
||||
colorTitle: 'black',
|
||||
bgTitle: 'primary',
|
||||
bgColor: 'accent',
|
||||
link: '#',
|
||||
illustration: '../../img/utils/lrl-logo.svg'
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
export function ServicesSection() {
|
||||
const pawsRef = useParallax<HTMLDivElement>(0.08);
|
||||
|
||||
return (
|
||||
<section id="first-section" className="relative overflow-hidden w-full bg-white py-16">
|
||||
<div ref={pawsRef} className="absolute -top-4 left-0 pointer-events-none hidden lg:block w-48 opacity-30">
|
||||
<PawsDecoration className="w-full h-auto" />
|
||||
</div>
|
||||
<Container className="flex flex-col gap-10">
|
||||
<SectionHeading
|
||||
title="Nos services"
|
||||
color="primary"
|
||||
subtitle="Nous vous proposons, à travers des outils libres, ouverts et solidaires, de quitter l’industrie du G.A.F.A.M. N’acceptons plus d’être leur produit !"
|
||||
align="left"
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{services.map((service) => (
|
||||
<ServiceCard key={service.title} {...service} />
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
42
resources/js/components/footer.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import {Link} from '@inertiajs/react';
|
||||
import {contact, home, membership} from '@/routes';
|
||||
import AppLogoIcon from '@/components/app-logo-icon';
|
||||
|
||||
export function Footer() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="gap-10 bg-accent rounded-t-4xl text-white py-10 px-20 mt-auto mx-5">
|
||||
<div className="max-w-7xl mx-auto px-4 flex flex-col gap-8">
|
||||
<div className="flex flex-col lg:flex-row justify-between gap-8">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Link href={home()} className="flex items-center gap-2 no-underline">
|
||||
<AppLogoIcon className="size-8 text-[var(--foreground)] dark:text-white"/>
|
||||
<span className="font-bold text-white text-lg">Le Retzien Libre</span>
|
||||
</Link>
|
||||
<p className="text-sm max-w-xs">
|
||||
Une association locale pour un internet éthique, libre et respectueux.
|
||||
</p>
|
||||
</div>
|
||||
<nav className="flex flex-col gap-3">
|
||||
<span className="font-semibold">Navigation</span>
|
||||
<Link href={home()} className="text-sm text-white no-underline hover:underline">Accueil</Link>
|
||||
<Link href={contact()} className="text-sm text-white no-underline hover:underline">Contact</Link>
|
||||
<Link href={membership()} className="text-sm text-white no-underline hover:underline">Adhérer</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex justify-between border-t border-black/20 pt-6 text-sm text-center">
|
||||
<div className="text-left">
|
||||
© {currentYear} Le Retzien Libre. Tous droits réservés.
|
||||
</div>
|
||||
<div className="flex items-stretch text-right">
|
||||
<Link href="#" className="text-sm text-white underline mx-4 hover:underline">Mentions Légales</Link>
|
||||
<Link href="#" className="text-sm text-white underline mx-4 hover:underline">CGU</Link>
|
||||
<Link href={"#"} className="text-sm text-white underline mx-4 hover:underline">Confidentialité</Link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -18,7 +18,6 @@ const buttonVariants = cva(
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||
ghost: "bg-accent hover:text-accent-foreground hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2 shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out",
|
||||
|
||||
34
resources/js/hooks/use-parallax.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export function useParallax<T extends HTMLElement>(speed: number = 0.15) {
|
||||
const ref = useRef<T>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
|
||||
let rafId: number;
|
||||
let ticking = false;
|
||||
|
||||
const update = () => {
|
||||
el.style.transform = `translateY(${window.scrollY * speed}px)`;
|
||||
ticking = false;
|
||||
};
|
||||
|
||||
const onScroll = () => {
|
||||
if (!ticking) {
|
||||
rafId = requestAnimationFrame(update);
|
||||
ticking = true;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', onScroll);
|
||||
cancelAnimationFrame(rafId);
|
||||
};
|
||||
}, [speed]);
|
||||
|
||||
return ref;
|
||||
}
|
||||
9
resources/js/img/utils/lrl-logo-full.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
11
resources/js/img/utils/lrl-logo.svg
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
10
resources/js/img/utils/penguin-paws.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="396" height="347" viewBox="0 0 396 347" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.35">
|
||||
<path d="M31.1294 292.304C35.7403 291.179 41.373 297.379 44.9911 306.863C51.1878 298.385 58.5611 293.725 62.7766 296.139C67.779 299.003 66.4409 310.744 59.7878 322.363C55.4402 329.955 49.8983 335.615 45.184 337.827C44.442 338.629 43.5848 339.171 42.6191 339.407C42.3446 339.474 42.0664 339.513 41.7853 339.529C36.8981 341.422 27.4458 338.751 18.3753 332.498C7.35225 324.9 1.06811 314.892 4.33959 310.146C7.1581 306.057 16.1472 307.21 25.6314 312.465C24.252 301.986 26.3892 293.46 31.1294 292.304Z" fill="#8A8A8A"/>
|
||||
<path d="M228.85 25.324C233.259 27.0801 234.256 35.3969 231.706 45.2229C241.67 41.9058 250.379 42.3828 252.415 46.793C254.831 52.0268 246.935 60.8186 234.78 66.4297C226.835 70.0967 219.038 71.4973 213.914 70.5672C212.845 70.79 211.833 70.7348 210.91 70.3671C210.647 70.2626 210.398 70.1332 210.16 69.9832C205.08 68.6932 198.926 61.0376 195.159 50.6849C190.581 38.1036 191.261 26.3061 196.678 24.3347C201.344 22.6367 208.001 28.7864 212.683 38.5661C217.633 29.2274 224.317 23.519 228.85 25.324Z" fill="#8A8A8A"/>
|
||||
<path d="M145.999 243.334C150.289 245.365 150.759 253.728 147.596 263.374C157.749 260.691 166.411 261.716 168.165 266.246C170.246 271.621 161.812 279.898 149.327 284.732C141.168 287.891 133.299 288.797 128.243 287.546C127.162 287.702 126.155 287.583 125.256 287.158C125.001 287.037 124.761 286.892 124.533 286.728C119.544 285.121 113.884 277.092 110.777 266.522C107 253.677 108.422 241.946 113.953 240.32C118.717 238.919 124.973 245.476 129.03 255.532C134.559 246.523 141.589 241.247 145.999 243.334Z" fill="#8A8A8A"/>
|
||||
<path d="M366.871 13.7083C371.161 15.7387 371.631 24.1017 368.468 33.7476C378.621 31.0649 387.283 32.0897 389.037 36.6195C391.118 41.9951 382.684 50.2719 370.199 55.106C362.04 58.2651 354.17 59.1716 349.114 57.9206C348.034 58.0756 347.027 57.9567 346.128 57.5316C345.873 57.4109 345.633 57.266 345.405 57.1016C340.416 55.4951 334.756 47.4663 331.649 36.896C327.873 24.051 329.294 12.3198 334.825 10.6939C339.589 9.29333 345.846 15.8502 349.902 25.9055C355.431 16.8972 362.461 11.6211 366.871 13.7083Z" fill="#8A8A8A"/>
|
||||
<path d="M247.503 117.591C250.861 120.945 248.494 128.98 242.273 137.002C252.737 137.887 260.551 141.763 260.681 146.618C260.835 152.381 250.11 157.342 236.727 157.7C227.98 157.934 220.263 156.143 215.922 153.266C214.852 153.049 213.944 152.598 213.241 151.896C213.041 151.697 212.863 151.48 212.704 151.248C208.545 148.058 205.912 138.595 206.537 127.595C207.297 114.228 212.578 103.657 218.333 103.984C223.291 104.266 226.98 112.543 227.422 123.377C235.657 116.75 244.05 114.143 247.503 117.591Z" fill="#8A8A8A"/>
|
||||
<path d="M112.344 139.976C117.024 140.761 119.756 148.679 119.34 158.822C128.378 153.474 136.992 152.1 139.914 155.98C143.381 160.585 137.522 170.847 126.826 178.9C119.836 184.163 112.511 187.18 107.306 187.354C106.308 187.797 105.307 187.957 104.327 187.793C104.049 187.746 103.777 187.673 103.513 187.576C98.275 187.389 90.6421 181.207 84.7724 171.884C77.6392 160.554 75.8108 148.879 80.6887 145.808C84.891 143.162 92.6967 147.765 99.3398 156.334C102.205 146.161 107.531 139.169 112.344 139.976Z" fill="#8A8A8A"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
0
resources/js/layouts/app/app-footer-layout.tsx
Normal file
@@ -1,86 +1,91 @@
|
||||
import {Link, router, usePage} from "@inertiajs/react";
|
||||
import {dashboard, home, login, logout, register, contact, membership} from "@/routes";
|
||||
import AppLogoIcon from "@/components/app-logo-icon";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import type {SharedData} from "@/types";
|
||||
import {useMobileNavigation} from "@/hooks/use-mobile-navigation";
|
||||
import {LogOut} from "lucide-react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link, router, usePage } from '@inertiajs/react';
|
||||
import { dashboard, home, login, logout, contact, membership } from '@/routes';
|
||||
import AppLogoIcon from '@/components/app-logo-icon';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { type SharedData } from '@/types';
|
||||
import { useMobileNavigation } from '@/hooks/use-mobile-navigation';
|
||||
import { useAppearance } from '@/hooks/use-appearance';
|
||||
import { Menu, Moon, Sun, X } from 'lucide-react';
|
||||
import AppLogo from "@/components/app-logo";
|
||||
|
||||
export default function NavGuestLayout() {
|
||||
const {auth} = usePage<SharedData>().props;
|
||||
|
||||
const { auth } = usePage<SharedData>().props;
|
||||
const cleanup = useMobileNavigation();
|
||||
const { appearance, updateAppearance } = useAppearance();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const handleLogout = () => {
|
||||
cleanup();
|
||||
router.flushAll();
|
||||
};
|
||||
|
||||
const [dark, setDark] = useState(false);
|
||||
const toggleAppearance = () => {
|
||||
updateAppearance(appearance === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
const closeMenu = () => setIsMenuOpen(false);
|
||||
|
||||
// Fermer sur navigation Inertia
|
||||
useEffect(() => {
|
||||
if (dark) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
}, [dark]);
|
||||
return router.on('navigate', closeMenu);
|
||||
}, []);
|
||||
|
||||
// Fermer sur touche Escape
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') closeMenu();
|
||||
};
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
// Bloquer le scroll quand menu ouvert
|
||||
useEffect(() => {
|
||||
document.body.style.overflow = isMenuOpen ? 'hidden' : '';
|
||||
return () => { document.body.style.overflow = ''; };
|
||||
}, [isMenuOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className="flex justify-between my-6 w-full max-w-[335px] text-sm not-has-[nav]:hidden lg:max-w-7xl">
|
||||
<div className="flex items-center justify-start">
|
||||
<Link
|
||||
href={home()}
|
||||
className="flex items-center justify-start gap-2 font-medium no-underline"
|
||||
>
|
||||
<div className="mb-1 flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<AppLogoIcon className="size-9 text-[var(--foreground)] dark:text-white"/>
|
||||
</div>
|
||||
<h1 className="text-black">Le Retzien Libre</h1>
|
||||
</Link>
|
||||
</div>
|
||||
<nav className="flex items-center justify-end gap-4">
|
||||
<Link
|
||||
href={home()}
|
||||
className="inline-block px-5 py-1.5 text-lg leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b] no-underline"
|
||||
>
|
||||
<header className="flex justify-between items-center my-6 w-full max-w-[335px] lg:max-w-7xl text-sm">
|
||||
{/* Logo */}
|
||||
<Link href={home()} className="flex items-center gap-2 font-medium no-underline">
|
||||
<div className="flex items-center justify-center rounded-md">
|
||||
<AppLogo className="max-w-[200px] max-h-[42px] w-full h-auto" />
|
||||
</div>
|
||||
{/*@todo: gérer l'accessibilité de l'image'*/}
|
||||
{/*<span className="font-bold text-black dark:text-white">Le Retzien Libre</span>*/}
|
||||
</Link>
|
||||
|
||||
{/* Navigation desktop */}
|
||||
<nav className="hidden lg:flex items-center gap-1">
|
||||
<Link href={home()} className="px-5 py-1.5 text-lg text-[#1b1b18] dark:text-[#EDEDEC] no-underline hover:underline">
|
||||
Accueil
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
className="inline-block px-5 py-1.5 text-lg leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b] no-underline"
|
||||
>
|
||||
<Link href="#services" className="px-5 py-1.5 text-lg text-[#1b1b18] dark:text-[#EDEDEC] no-underline hover:underline">
|
||||
Nos Services
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
className="inline-block px-5 py-1.5 text-lg leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b] no-underline"
|
||||
>
|
||||
<Link href="#" className="px-5 py-1.5 text-lg text-[#1b1b18] dark:text-[#EDEDEC] no-underline hover:underline">
|
||||
Le Blog
|
||||
</Link>
|
||||
<Link
|
||||
href={contact()}
|
||||
className="inline-block px-5 py-1.5 text-lg leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b] no-underline"
|
||||
>
|
||||
<Link href={contact()} className="px-5 py-1.5 text-lg text-[#1b1b18] dark:text-[#EDEDEC] no-underline hover:underline">
|
||||
Contact
|
||||
</Link>
|
||||
</nav>
|
||||
<nav className="flex items-center justify-end gap-4">
|
||||
|
||||
{/* Actions desktop */}
|
||||
<div className="hidden lg:flex items-center gap-3">
|
||||
|
||||
{auth.user ? (
|
||||
<>
|
||||
<Link
|
||||
href={dashboard()}
|
||||
className=" no-underline"
|
||||
>
|
||||
<Link href={dashboard()} className="no-underline">
|
||||
<Button variant="outline">Tableau de bord</Button>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className="border-3 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 px-4 py-2 shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out font-bold"
|
||||
href={logout()}
|
||||
onClick={handleLogout}
|
||||
className="border-3 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 px-4 py-2 shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out font-bold no-underline"
|
||||
data-test="logout-button"
|
||||
>
|
||||
Se déconnecter
|
||||
@@ -88,26 +93,118 @@ export default function NavGuestLayout() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
href={login()}
|
||||
>
|
||||
<Link href={login()} className="no-underline">
|
||||
<Button variant="outline">Se connecter</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={membership()}
|
||||
>
|
||||
<Button>Adhérer</Button>
|
||||
<Link href={membership()} className="no-underline">
|
||||
<Button variant="secondary">Adhérer</Button>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
<button
|
||||
onClick={() => setDark(!dark)}
|
||||
className="absolute top-4 right-4 px-3 py-1 rounded-xl border border-gray-400 dark:border-gray-600 hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||
>
|
||||
{dark ? "☀️ Mode clair" : "🌙 Mode sombre"}
|
||||
</button>
|
||||
<button
|
||||
onClick={toggleAppearance}
|
||||
className="border-3 bg-primary text-secondary-foreground hover:bg-primary/80 h-10 px-4 py-2 shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out font-bold no-underline"
|
||||
aria-label="Changer le thème"
|
||||
>
|
||||
{appearance === 'dark' ? <Sun className="size-4" /> : <Moon className="size-4" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Actions mobile : thème + hamburger */}
|
||||
<div className="flex lg:hidden items-center gap-2">
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="border-3 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 px-4 py-2 shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out font-bold no-underline"
|
||||
aria-label={isMenuOpen ? 'Fermer le menu' : 'Ouvrir le menu'}
|
||||
aria-expanded={isMenuOpen}
|
||||
>
|
||||
{isMenuOpen ? <X className="size-5" /> : <Menu className="size-5" />}
|
||||
</button>
|
||||
<button
|
||||
onClick={toggleAppearance}
|
||||
className="border-3 bg-primary text-secondary-foreground hover:bg-primary/80 h-10 px-4 py-2 shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out font-bold no-underline"
|
||||
aria-label="Changer le thème"
|
||||
>
|
||||
{appearance === 'dark' ? <Sun className="size-4" /> : <Moon className="size-4" />}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Menu mobile */}
|
||||
{isMenuOpen && (
|
||||
<>
|
||||
{/* Backdrop */}
|
||||
<div
|
||||
className="fixed inset-0 z-40 bg-black/30 lg:hidden"
|
||||
onClick={closeMenu}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
{/* Panel */}
|
||||
<div className="fixed inset-x-0 top-0 z-50 lg:hidden bg-[#F5F5F5] dark:bg-[#0a0a0a] border-b-4 border-black flex flex-col gap-6 p-6">
|
||||
{/* En-tête du panel */}
|
||||
<div className="flex justify-between items-center">
|
||||
<Link href={home()} onClick={closeMenu} className="flex items-center gap-2 no-underline">
|
||||
<AppLogoIcon className="size-8 text-[var(--foreground)] dark:text-white" />
|
||||
<span className="font-bold text-black dark:text-white">Le Retzien Libre</span>
|
||||
</Link>
|
||||
<button
|
||||
onClick={closeMenu}
|
||||
className="p-2 rounded-md border border-black/20 dark:border-white/20 hover:bg-black/5 dark:hover:bg-white/5 transition"
|
||||
aria-label="Fermer le menu"
|
||||
>
|
||||
<X className="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Liens de navigation */}
|
||||
<nav className="flex flex-col">
|
||||
<Link href={home()} onClick={closeMenu} className="text-lg py-3 border-b border-black/10 dark:border-white/10 no-underline">
|
||||
<span className="text-black hover:underline">Accueil</span>
|
||||
</Link>
|
||||
<Link href="#services" onClick={closeMenu} className="text-lg py-3 border-b border-black/10 dark:border-white/10 no-underline">
|
||||
<span className="text-black hover:underline">Nos Services</span>
|
||||
</Link>
|
||||
<Link href="#" onClick={closeMenu} className="text-lg py-3 border-b border-black/10 dark:border-white/10 no-underline">
|
||||
<span className="text-black hover:underline">Le Blog</span>
|
||||
</Link>
|
||||
<Link href={contact()} onClick={closeMenu} className="text-lg py-3 no-underline">
|
||||
<span className="text-black hover:underline">Contact</span>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
{/* Boutons auth */}
|
||||
<div className="flex flex-col gap-3">
|
||||
{auth.user ? (
|
||||
<>
|
||||
<Link href={dashboard()} onClick={closeMenu} className="no-underline mx-auto mb-4">
|
||||
<Button variant="outline" className="max-w-[150px]">Tableau de bord</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={logout()}
|
||||
method="post"
|
||||
as="button"
|
||||
onClick={() => { closeMenu(); handleLogout(); }}
|
||||
className="inline-flex items-center justify-center max-w-[150px] mx-auto gap-2 whitespace-nowrap rounded-md text-sm font-bold cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 px-4 py-2 border-3 border-black shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-2 transition delay-50 duration-200 ease-in-out w-full no-underline"
|
||||
data-test="logout-button"
|
||||
>
|
||||
Se déconnecter
|
||||
</Link>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link href={login()} onClick={closeMenu} className="no-underline">
|
||||
<Button variant="outline" className="w-full">Se connecter</Button>
|
||||
</Link>
|
||||
<Link href={membership()} onClick={closeMenu} className="no-underline">
|
||||
<Button variant="secondary" className="w-full">Adhérer</Button>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
export default function MaintenancePage() {
|
||||
const [dark, setDark] = useState(false);
|
||||
|
||||
|
||||
@@ -1,118 +1,29 @@
|
||||
import {dashboard, home, login, register} from '@/routes';
|
||||
import {type SharedData} from '@/types';
|
||||
import {Head, Link, usePage} from '@inertiajs/react';
|
||||
import {Button} from "@/components/ui/button";
|
||||
import AppLogoIcon from "@/components/app-logo-icon";
|
||||
import illustrationImage from '@/img/utils/lrl-illustration.png';
|
||||
import NavGuestLayout from "@/layouts/nav-guest-layout";
|
||||
|
||||
import { Head } from '@inertiajs/react';
|
||||
import NavGuestLayout from '@/layouts/nav-guest-layout';
|
||||
import { HeroSection } from '@/components/features/home/HeroSection';
|
||||
import { ServicesSection } from '@/components/features/home/ServicesSection';
|
||||
import { AboutSection } from '@/components/features/home/AboutSection';
|
||||
import { AlternativeSection } from '@/components/features/home/AlternativeSection';
|
||||
import { Footer } from '@/components/footer';
|
||||
import { ScrollToTop } from '@/components/common/ScrollToTop';
|
||||
|
||||
export default function Welcome() {
|
||||
const {auth} = usePage<SharedData>().props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title="Bienvenue">
|
||||
<link rel="preconnect" href="https://fonts.bunny.net"/>
|
||||
<link
|
||||
href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</Head>
|
||||
<div
|
||||
className="flex min-h-screen flex-col items-center bg-[#F5F5F5] text-[#1b1b18] lg:justify-center dark:bg-[#0a0a0a]">
|
||||
<NavGuestLayout />
|
||||
<section className="flex items-center justify-center gap-4 w-full max-w-[335px] lg:max-w-7xl">
|
||||
<div className="flex w-full items-center justify-center gap-4">
|
||||
<div
|
||||
className="flex flex-col w-full items-center accent-middle py-50 text-center justify-center gap-4">
|
||||
<h1 className="text-5xl text-accent max-w-[450px] mb-5">Pour un internet éthique !</h1>
|
||||
<p className="text-xl mb-5">"Dégooglisons"<br/>
|
||||
nos ordinateurs, nos tablettes et nos smartphones.<br/>
|
||||
<i>"Le chemin est long, mais la voie est libre"</i></p>
|
||||
<Link
|
||||
href={register()}
|
||||
>
|
||||
<Button variant="secondary">Adhérer dès maintenant</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-center gap-4">
|
||||
<img src={illustrationImage} alt="illustration"/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-col items-center bg-[#F5F5F5] text-[#1b1b18] lg:justify-center dark:bg-[#0a0a0a]">
|
||||
|
||||
<section className="w-full bg-accent pr-0 py-16">
|
||||
<div className="flex flex-col items-center gap-8 w-full max-w-7xl mx-auto">
|
||||
<h2 className="text-3xl font-bold text-center mb-10">Nos services numériques éthiques, libres,
|
||||
ouverts et locaux</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{[
|
||||
{
|
||||
title: 'Boîte mail',
|
||||
description: 'Service de messagerie électronique sécurisé et respectueux de votre vie privée'
|
||||
},
|
||||
{
|
||||
title: 'Cloud (Nextcloud)',
|
||||
description: 'Stockage en ligne et collaboration avec vos données hébergées localement'
|
||||
},
|
||||
{title: 'File2link', description: 'Partage de fichiers simplifié et sécurisé'},
|
||||
{
|
||||
title: 'Hébergement web',
|
||||
description: 'Solutions d\'hébergement web éthiques et performantes'
|
||||
},
|
||||
{
|
||||
title: 'Sondage',
|
||||
description: 'Créez et partagez des sondages en ligne en toute confidentialité'
|
||||
},
|
||||
{
|
||||
title: 'Liste de diffusion Mail',
|
||||
description: 'Gérez vos communications de groupe efficacement'
|
||||
}
|
||||
].map((service, index) => (
|
||||
<div key={index} className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h3 className="text-xl font-semibold mb-3">{service.title}</h3>
|
||||
<p>{service.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section className="w-full max-w-7xl py-16 bg-gray-50 dark:bg-gray-900">
|
||||
<h2 className="text-3xl font-bold text-center mb-10">GAFAM : Quels dangers ?</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h3 className="text-xl font-semibold mb-3">Surveillance massive</h3>
|
||||
<p>Les GAFAM collectent et exploitent vos données personnelles à des fins commerciales</p>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h3 className="text-xl font-semibold mb-3">Monopole numérique</h3>
|
||||
<p>Concentration excessive du pouvoir et dépendance aux services centralisés</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="w-full max-w-7xl py-16">
|
||||
<div className="flex flex-col lg:flex-row items-center gap-8">
|
||||
<div className="lg:w-1/2">
|
||||
<h2 className="text-3xl font-bold mb-6">Notre alternative : Le Retzien Libre</h2>
|
||||
<p className="mb-6">Une association locale engagée pour la promotion du logiciel libre et la
|
||||
protection de vos données personnelles.</p>
|
||||
<Link href={register()}>
|
||||
<Button variant="default">Rejoignez-nous</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="lg:w-1/2">
|
||||
<img src={illustrationImage} alt="Le Retzien Libre" className="rounded-lg"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<Head title="Bienvenue sur le site du Retzien Libre" />
|
||||
<div className="flex flex-col min-h-screen bg-white text-[#1b1b18] dark:bg-[#0a0a0a] dark:text-[#EDEDEC]">
|
||||
<div className="flex flex-col items-center px-4">
|
||||
<NavGuestLayout />
|
||||
</div>
|
||||
<main className="flex flex-col items-center">
|
||||
<HeroSection />
|
||||
<ServicesSection />
|
||||
<AlternativeSection />
|
||||
<AboutSection />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
<ScrollToTop />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
10
resources/js/types/index.d.ts
vendored
@@ -59,6 +59,16 @@ export interface Plans {
|
||||
}
|
||||
|
||||
|
||||
export interface Service {
|
||||
title: string;
|
||||
colorTitle: string;
|
||||
bgTitle: string;
|
||||
bgColor: string;
|
||||
description: string;
|
||||
link: string;
|
||||
illustration: string;
|
||||
}
|
||||
|
||||
export interface PageProps {
|
||||
flash?: FlashMessages;
|
||||
auth?: Auth;
|
||||
|
||||
@@ -32,9 +32,12 @@
|
||||
|
||||
<title inertia>{{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicons/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicons/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png" />
|
||||
<meta name="apple-mobile-web-app-title" content="Le Retzien Libre" />
|
||||
<link rel="manifest" href="/favicons/site.webmanifest" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />
|
||||
|
||||
@@ -7,7 +7,13 @@ import {defineConfig} from 'vite';
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: ['resources/css/app.css', 'resources/css/backend.css', 'resources/js/app.tsx', 'vendor/andreia/filament-nord-theme/resources/css/theme.css'],
|
||||
input: [
|
||||
'resources/css/app.css',
|
||||
'resources/css/backend.css',
|
||||
'resources/css/fonts.css',
|
||||
'resources/js/app.tsx',
|
||||
'vendor/andreia/filament-nord-theme/resources/css/theme.css'
|
||||
],
|
||||
ssr: 'resources/js/ssr.tsx',
|
||||
refresh: true,
|
||||
}),
|
||||
|
||||