Compare commits
No commits in common. "integration-accueil" and "master" have entirely different histories.
integratio
...
master
2
.env
|
@ -4,4 +4,4 @@
|
|||
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
|
||||
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||
|
||||
DATABASE_URL="mysql://sephigame:SephEsport33!@192.168.1.26:3306/Monelia_Nails?schema=public"
|
||||
DATABASE_URL="mysql://monelia:monelia@localhost:3306/monelia?schema=public"
|
103
copie-page.tsx
|
@ -1,103 +0,0 @@
|
|||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -9,13 +9,10 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.6.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"lucide-react": "^0.511.0",
|
||||
"next": "15.3.0",
|
||||
"prisma": "^6.6.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"swiper": "^11.2.8"
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
|
@ -2464,15 +2461,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
|
||||
"integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==",
|
||||
"license": "BSD-3-Clause",
|
||||
"bin": {
|
||||
"bcrypt": "bin/bcrypt"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
|
@ -4838,15 +4826,6 @@
|
|||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.511.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz",
|
||||
"integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
|
@ -5808,15 +5787,6 @@
|
|||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/slugify": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
|
||||
"integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
@ -6026,25 +5996,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/swiper": {
|
||||
"version": "11.2.8",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.8.tgz",
|
||||
"integrity": "sha512-S5FVf6zWynPWooi7pJ7lZhSUe2snTzqLuUzbd5h5PHUOhzgvW0bLKBd2wv0ixn6/5o9vwc/IkQT74CRcLJQzeg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/swiperjs"
|
||||
},
|
||||
{
|
||||
"type": "open_collective",
|
||||
"url": "http://opencollective.com/swiper"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz",
|
||||
|
|
|
@ -6,18 +6,14 @@
|
|||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"start:migrate:prod": "npx prisma migrate deploy && npm run start"
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.6.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"lucide-react": "^0.511.0",
|
||||
"next": "15.3.0",
|
||||
"prisma": "^6.6.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"swiper": "^11.2.8"
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
|
|
BIN
public/Kiki.png
Before Width: | Height: | Size: 82 KiB |
BIN
public/Ryo.png
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 532 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 391 KiB |
Before Width: | Height: | Size: 548 KiB |
Before Width: | Height: | Size: 218 KiB |
BIN
public/menu.png
Before Width: | Height: | Size: 11 KiB |
|
@ -1,179 +0,0 @@
|
|||
"use server"
|
||||
|
||||
import prisma from "@/lib/db";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { cookies } from "next/headers";
|
||||
import slugify from "slugify";
|
||||
|
||||
export async function register(formData : FormData){
|
||||
//Permet de récupérer les données demandées à l'utilisateur lors de l'inscription
|
||||
const {email,password,passwordRepeat} = Object.fromEntries(formData)
|
||||
|
||||
if(password !== passwordRepeat){
|
||||
throw new Error("Le passwords ne correspond pas")
|
||||
}
|
||||
|
||||
try{
|
||||
|
||||
//Vérification du mail
|
||||
const verifUser = await prisma.utilisateur.findFirst({
|
||||
where : {
|
||||
ut_mail : email.toString()
|
||||
}
|
||||
})
|
||||
if(verifUser){
|
||||
throw new Error("Utilisateur déjà inscrit")
|
||||
}
|
||||
|
||||
|
||||
//slugify de l'email
|
||||
const slug = slugify(email.toString().substring(0,email.toString().indexOf("@")),{lower:true,strict:true})
|
||||
|
||||
//hash du mdp
|
||||
const salt = await bcrypt.genSalt(10)
|
||||
const hashedPassword = await bcrypt.hash(password.toString(),salt)
|
||||
|
||||
//creation de l'utilisateur
|
||||
const result = await prisma.utilisateur.create({
|
||||
data :{
|
||||
ut_mail : email.toString(),
|
||||
ut_mdp : hashedPassword.toString(),
|
||||
ut_slug : slug
|
||||
}
|
||||
})
|
||||
|
||||
if(result){
|
||||
return {success: true}
|
||||
}
|
||||
throw new Error("Anomalie lors de la création du user")
|
||||
|
||||
}catch(err){
|
||||
const error = err as Error
|
||||
throw new Error(error.message || "une erreur à été rencontrée lors de la création de l'utilisateur")
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(formData : FormData){
|
||||
const {email,password} = Object.fromEntries(formData)
|
||||
|
||||
try{
|
||||
//vérification si l'utilisateur est trouvé
|
||||
const user = await prisma.utilisateur.findFirst({
|
||||
where : {
|
||||
ut_mail : email.toString()
|
||||
}
|
||||
})
|
||||
if(!user){
|
||||
throw new Error("Invalide crédential")
|
||||
}
|
||||
//vérification du mdp
|
||||
const isPasswordValid = await bcrypt.compare(password.toString(), user.ut_mdp)
|
||||
if(!isPasswordValid){
|
||||
throw new Error("Invalide crédential")
|
||||
}
|
||||
|
||||
//vérification si une session de connexion existe
|
||||
const existingSession = await prisma.session.findFirst({
|
||||
where : {
|
||||
se_ut_id : user.ut_id
|
||||
}
|
||||
})
|
||||
|
||||
//vérification si la session existe mais expiré
|
||||
// delete de l'enreg pour le recréer
|
||||
let verifDeleteSession = false
|
||||
if(existingSession && new Date() > existingSession.expireAt ){
|
||||
const deleteSession = await prisma.session.delete({
|
||||
where : {
|
||||
se_id : existingSession.se_id
|
||||
}
|
||||
})
|
||||
if(deleteSession){
|
||||
verifDeleteSession = true
|
||||
}
|
||||
}
|
||||
|
||||
//vérification si une session de connexion existe et tjr actif
|
||||
// on regenere le cookie ensuite
|
||||
let verifValiditeSession = false
|
||||
if(existingSession && new Date() < existingSession.expireAt){
|
||||
verifValiditeSession = true
|
||||
}
|
||||
|
||||
let sessionidCreate = ""
|
||||
if(!existingSession || verifDeleteSession){
|
||||
const createSession = await prisma.session.create({
|
||||
data : {
|
||||
se_ut_id : user.ut_id,
|
||||
expireAt : new Date(Date.now() + 7*24*60*60*1000)
|
||||
}
|
||||
})
|
||||
if(createSession){
|
||||
sessionidCreate = createSession.se_id
|
||||
}
|
||||
}
|
||||
|
||||
const cookieStore = await cookies()
|
||||
|
||||
if(sessionidCreate !== ""){
|
||||
cookieStore.set("sessionId",sessionidCreate,{
|
||||
httpOnly : true,
|
||||
secure: process.env.NODE_ENV ==="production",
|
||||
path:"/",
|
||||
maxAge: 24*60*60,
|
||||
sameSite: "lax"
|
||||
})
|
||||
}
|
||||
if(verifValiditeSession && existingSession?.se_id){
|
||||
cookieStore.set("sessionId",existingSession?.se_id,{
|
||||
httpOnly : true,
|
||||
secure: process.env.NODE_ENV ==="production",
|
||||
path:"/",
|
||||
maxAge: 24*60*60,
|
||||
sameSite: "lax"
|
||||
})
|
||||
}
|
||||
|
||||
return {success : true}
|
||||
}catch(err){
|
||||
const error = err as Error
|
||||
throw new Error(error.message || "une erreur à été rencontrée lors de la connexion de l'utilisateur")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function verifSession(){
|
||||
try{
|
||||
const cookie = await cookies()
|
||||
const verif = cookie.get("sessionId")
|
||||
if(verif){
|
||||
const verifExpireSession = await prisma.session.findUnique({
|
||||
where : {
|
||||
se_id : verif.value
|
||||
}
|
||||
})
|
||||
if(verifExpireSession && new Date() > verifExpireSession.expireAt){
|
||||
await prisma.session.delete({
|
||||
where : {
|
||||
se_id : verifExpireSession.se_id
|
||||
}
|
||||
})
|
||||
return {success : false}
|
||||
}else{
|
||||
cookie.set("sessionId",verif.value,{
|
||||
httpOnly : true,
|
||||
secure: process.env.NODE_ENV ==="production",
|
||||
path:"/",
|
||||
maxAge: 24*60*60,
|
||||
sameSite: "lax"
|
||||
})
|
||||
return {success : true}
|
||||
}
|
||||
}
|
||||
return {success : false}
|
||||
|
||||
}catch(err){
|
||||
const error = err as Error
|
||||
throw new Error(error.message || "une erreur à été rencontrée lors de la vérification de la session")
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import prisma from "@/lib/db";
|
||||
|
||||
export async function getInfoUser(slug : string){
|
||||
try{
|
||||
const user = await prisma.utilisateur.findFirst({
|
||||
where : {
|
||||
ut_slug : slug
|
||||
}
|
||||
})
|
||||
if(user){
|
||||
return {success : true, user}
|
||||
}
|
||||
return {success : false, message : "Impossible de trouver les infos de l'utilisateur"}
|
||||
}catch(err){
|
||||
const error = err as Error
|
||||
throw new Error(error.message || "une erreur à été rencontrée lors de la recherche de l'utilisateur")
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUser(slug : string){
|
||||
try{
|
||||
|
||||
const user = await prisma.utilisateur.findFirst({
|
||||
where : {
|
||||
ut_slug : slug
|
||||
}
|
||||
})
|
||||
if(user){
|
||||
await prisma.utilisateur.delete({
|
||||
where : {
|
||||
ut_id : user.ut_id
|
||||
}
|
||||
})
|
||||
return {success : true}
|
||||
}
|
||||
return {success : false, message : "Impossible de supprimer l'utilisateur"}
|
||||
}catch(err){
|
||||
const error = err as Error
|
||||
throw new Error(error.message || "une erreur à été rencontrée lors suppression de l'utilisateur")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
const Footer = () => {
|
||||
|
||||
return (
|
||||
<footer>
|
||||
Footer
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
|
@ -1,130 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { EffectCoverflow, Autoplay } from "swiper/modules";
|
||||
import "swiper/css";
|
||||
import "swiper/css/effect-coverflow";
|
||||
|
||||
const images = [
|
||||
{ src: "/images/nail1.jpg", alt: "Nail art 1" },
|
||||
{ src: "/images/nail2.jpg", alt: "Nail art 2" },
|
||||
{ src: "/images/nail3.jpg", alt: "Nail art 3" },
|
||||
{ src: "/images/nail1.jpg", alt: "Nail art 1" },
|
||||
{ src: "/images/nail2.jpg", alt: "Nail art 2" },
|
||||
{ src: "/images/nail3.jpg", alt: "Nail art 3" },
|
||||
{ src: "/images/nail1.jpg", alt: "Nail art 1" },
|
||||
{ src: "/images/nail2.jpg", alt: "Nail art 2" },
|
||||
{ src: "/images/nail3.jpg", alt: "Nail art 3" },
|
||||
{ src: "/images/nail1.jpg", alt: "Nail art 1" },
|
||||
{ src: "/images/nail2.jpg", alt: "Nail art 2" },
|
||||
{ src: "/images/nail3.jpg", alt: "Nail art 3" },
|
||||
|
||||
// Ajoute plus d’images si tu veux !
|
||||
];
|
||||
|
||||
export default function CoverflowGallery() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalIndex, setModalIndex] = useState<number>(0);
|
||||
|
||||
const openModal = (idx: number) => {
|
||||
setModalIndex(idx);
|
||||
setModalOpen(true);
|
||||
};
|
||||
const closeModal = () => setModalOpen(false);
|
||||
|
||||
const prevImage = () => setModalIndex(prev => (prev === 0 ? images.length - 1 : prev - 1));
|
||||
const nextImage = () => setModalIndex(prev => (prev === images.length - 1 ? 0 : prev + 1));
|
||||
|
||||
return (
|
||||
<section className="px-4 md:px-0 mt-20 mb-10 flex flex-col items-center">
|
||||
<h3 className="text-2xl font-semibold text-[#2D3D53] mb-8 text-center">
|
||||
Nos clientes adorent... et nous aussi !!
|
||||
</h3>
|
||||
<div className="w-full max-w-6xl">
|
||||
<Swiper
|
||||
modules={[EffectCoverflow, Autoplay]}
|
||||
effect="coverflow"
|
||||
grabCursor={true}
|
||||
centeredSlides={true}
|
||||
slidesPerView={window.innerWidth < 768 ? 1.2 : 3}
|
||||
coverflowEffect={{
|
||||
rotate: 0,
|
||||
stretch: 0,
|
||||
depth: 60,
|
||||
modifier: 2.5,
|
||||
slideShadows: false,
|
||||
}}
|
||||
loop
|
||||
autoplay={{
|
||||
delay: 3500,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
}}
|
||||
className="gallerySwiper"
|
||||
>
|
||||
{images.map((img, i) => (
|
||||
<SwiperSlide key={i} className="flex justify-center">
|
||||
<button
|
||||
className="focus:outline-none"
|
||||
onClick={() => openModal(i)}
|
||||
aria-label={`Agrandir ${img.alt}`}
|
||||
>
|
||||
<Image
|
||||
src={img.src}
|
||||
alt={img.alt}
|
||||
width={760}
|
||||
height={640}
|
||||
className="rounded-2xl object-cover shadow-xl border-2 border-white"
|
||||
style={{ maxHeight: 640, width: "auto", height: "auto" }}
|
||||
/>
|
||||
</button>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
|
||||
{/* MODALE NAVIGABLE */}
|
||||
{modalOpen && (
|
||||
<div className="fixed z-50 inset-0 bg-black/60 flex items-center justify-center" onClick={closeModal}>
|
||||
<div
|
||||
className="bg-white rounded-3xl p-4 max-w-2xl w-full flex flex-col items-center relative"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
className="absolute top-2 right-3 text-3xl text-[#2178aa] hover:text-[#C97A44] font-bold"
|
||||
onClick={closeModal}
|
||||
aria-label="Fermer"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<div className="flex items-center justify-center w-full gap-6">
|
||||
<button
|
||||
onClick={prevImage}
|
||||
className="text-3xl text-[#7CB9E8] hover:text-[#2178aa] font-bold px-2"
|
||||
aria-label="Image précédente"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<Image
|
||||
src={images[modalIndex].src}
|
||||
alt={images[modalIndex].alt}
|
||||
width={500}
|
||||
height={400}
|
||||
className="rounded-xl object-cover"
|
||||
style={{ maxHeight: 420, width: "auto", height: "auto" }}
|
||||
/>
|
||||
<button
|
||||
onClick={nextImage}
|
||||
className="text-3xl text-[#7CB9E8] hover:text-[#2178aa] font-bold px-2"
|
||||
aria-label="Image suivante"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-[#2D3D53] text-base mt-2">{images[modalIndex].alt}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
|
@ -1,35 +1,15 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Gruppo&family=Monsieur+La+Doulaise&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400;700&family=Ephesis&family=Gruppo&family=Monsieur+La+Doulaise&display=swap');
|
||||
@import "tailwindcss";
|
||||
|
||||
|
||||
:root {
|
||||
--background: rgb(245,235,221);
|
||||
--color-bgMenu: rgb(204, 195, 181);
|
||||
--color-text: rgb(92,74,66);
|
||||
--color-title: rgb(216,163,157);
|
||||
--color-accent: rgb(203,174,158);
|
||||
--color-buttons: rgb(200,213,193);
|
||||
--color-bold: rgb(238,200,195);
|
||||
}
|
||||
|
||||
|
||||
@theme {
|
||||
--breakpoint-tablet: 768px;
|
||||
--breakpoint-laptop: 1024px;
|
||||
--breakpoint-desktop: 1280px;
|
||||
--breakpoint-big : 1536px;
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--text: var(--color-text);
|
||||
--title: var(--color-title);
|
||||
--accent: var(--color-accent);
|
||||
--buttons: var(--color-buttons);
|
||||
--bold: var(--color-bold);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
@ -42,41 +22,5 @@
|
|||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: "Grupo", Arial, Helvetica, sans-serif;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
b {
|
||||
color : var(--color-bold)
|
||||
}
|
||||
|
||||
button {
|
||||
background: var(--color-buttons);
|
||||
padding: 0.5rem 2rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--color-accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--color-menu);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "Monsieur La Doulaise", cursive;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.menu-ellipse {
|
||||
clip-path: ellipse(50% 34% at 50% 100%)
|
||||
}
|
||||
|
||||
|
||||
/* FILTRES DE COULEUR */
|
||||
|
||||
.filterToText {
|
||||
filter: brightness(0) saturate(100%) invert(28%) sepia(8%) saturate(1314%) hue-rotate(333deg) brightness(91%) contrast(83%);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import Footer from "./components/footer";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
|
@ -14,7 +13,7 @@ const geistMono = Geist_Mono({
|
|||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Monelia Nails",
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
|
@ -24,12 +23,11 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="fr">
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`antialiased grid grid-rows-[auto_1fr_auto] h-screen w-screen`}
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<Footer />
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
217
src/app/page.tsx
|
@ -1,134 +1,103 @@
|
|||
'use client'
|
||||
// Home.tsx ou page.tsx
|
||||
// Placer les images dans /public/images/
|
||||
import Image from "next/image";
|
||||
import GallerySection from "./components/slider";
|
||||
|
||||
export default function Home() {
|
||||
|
||||
return (
|
||||
<main className="bg-[#F8FAFC] min-h-screen text-[#283142] font-sans">
|
||||
{/* HEADER AVEC BANDEAU BLEU */}
|
||||
<header className="fixed w-full top-0 left-0 z-40">
|
||||
<div className="backdrop-blur-lg bg-white/70 border-b border-[#C3E5F9] shadow-sm flex justify-between items-center px-4 md:px-16 py-4">
|
||||
<h1 className="text-2xl font-bold tracking-widest text-[#2178aa]">
|
||||
MONELIA NAILS
|
||||
</h1>
|
||||
<nav className="space-x-8 text-lg font-medium">
|
||||
<a href="#" className="text-[#2D3D53] hover:text-[#2178aa] transition">Accueil</a>
|
||||
<a href="#" className="text-[#2D3D53] hover:text-[#2178aa] transition">Formations</a>
|
||||
<a href="#" className="text-[#2D3D53] hover:text-[#2178aa] transition">Réservation</a>
|
||||
</nav>
|
||||
<button className="px-5 py-2 bg-[#7CB9E8] hover:bg-[#2178aa] text-white rounded-xl font-semibold shadow transition">
|
||||
Connexion
|
||||
</button>
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
{/* HERO */}
|
||||
<section
|
||||
className="relative flex items-center justify-center min-h-[70vh] md:min-h-[80vh] mt-20 overflow-hidden"
|
||||
>
|
||||
{/* Image de fond : Lisa en action ou zoom ongles */}
|
||||
<Image
|
||||
src="/images/lisa-nails.jpg"
|
||||
alt="Nail art par Lisa"
|
||||
fill
|
||||
style={{ objectFit: "cover", objectPosition: "center" }}
|
||||
className="z-0"
|
||||
priority
|
||||
/>
|
||||
{/* Overlay doux avec motif, couleur, ou dégradé */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-[#EEC8C3]/70 via-[#F5EBDD]/40 to-[#C3E5F9]/80 pointer-events-none z-10" />
|
||||
{/* Petits éléments nail art décoratifs (SVG, png) */}
|
||||
<div className="absolute top-10 left-12 z-20 hidden md:block">
|
||||
{/* exemple : flacon de vernis, étoile, etc. */}
|
||||
<svg width="48" height="48" /* ... */> {/* ... */} </svg>
|
||||
</div>
|
||||
{/* Contenu principal */}
|
||||
<div className="relative z-30 flex flex-col items-center text-center px-4">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-4 text-[#2178aa] drop-shadow-xl font-[Dancing_Script,serif]">
|
||||
Le nail art, la douceur & la créativité à portée de main
|
||||
</h2>
|
||||
<p className="text-xl md:text-2xl text-[#C97A44] mb-6 font-semibold drop-shadow">
|
||||
Bienvenue chez Monelia Nails – Manucure, formations & bonne humeur
|
||||
</p>
|
||||
<button className="px-7 py-3 bg-[#7CB9E8] hover:bg-[#2178aa] text-white rounded-2xl text-lg shadow-md transition font-semibold flex items-center gap-2">
|
||||
<svg width="20" height="20" /* ... */> {/* icône vernis */} </svg>
|
||||
Prendre rendez-vous
|
||||
</button>
|
||||
<span className="block mt-2 text-[#CBAE9E] text-sm">Réservez votre parenthèse beauté !</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
{/* CARROUSEL "NOS CLIENTES AD ORENT..." */}
|
||||
<GallerySection />
|
||||
|
||||
{/* À PROPOS DE LISA - GRAND, CENTRÉ */}
|
||||
<section className="flex flex-col items-center px-4 md:px-0 mt-20 mb-16">
|
||||
<div className="bg-[#E9F5FB] rounded-3xl shadow-xl px-8 py-10 flex flex-col items-center max-w-2xl w-full">
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
src="/images/lisa-profil.jpg"
|
||||
alt="Lisa, prothésiste ongulaire"
|
||||
width={140}
|
||||
height={140}
|
||||
className="rounded-full border-4 border-[#7CB9E8] shadow mb-5"
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
<h4 className="text-2xl font-bold text-[#2178aa] mb-3 text-center">
|
||||
À propos de Lisa
|
||||
</h4>
|
||||
<p className="text-lg text-[#2D3D53] text-center leading-relaxed">
|
||||
Lisa, prothésiste ongulaire passionnée, formée à l’art de la manucure.
|
||||
Toujours un sourire, une blague, et deux mascottes à vos pieds !<br />
|
||||
<span className="block text-[#C97A44] font-semibold mt-2">Envie de paillettes et de douceur ? C’est par ici !</span>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* SECTION MASCOTTES */}
|
||||
<section className="px-6 md:px-16 flex flex-col items-center md:flex-row md:justify-between gap-8 mt-8">
|
||||
<div>
|
||||
<h4 className="text-2xl font-semibold text-[#2D3D53] mb-2">
|
||||
Voici Kiki & Ryo, les mascottes du salon !
|
||||
</h4>
|
||||
<p className="text-md text-[#283142]">
|
||||
Toujours là pour accueillir les clientes avec douceur et bonne humeur !
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-7 mt-3">
|
||||
{/* SVG mascottes stylisées (placeholder) */}
|
||||
<svg width="70" height="70" viewBox="0 0 80 80" fill="none">
|
||||
<ellipse cx="40" cy="70" rx="28" ry="10" fill="#C97A44" opacity="0.15" />
|
||||
<circle cx="40" cy="36" r="16" fill="#C97A44" />
|
||||
<ellipse cx="34" cy="34" rx="3" ry="4" fill="#5B3A1B" />
|
||||
<ellipse cx="46" cy="34" rx="3" ry="4" fill="#5B3A1B" />
|
||||
<ellipse cx="40" cy="48" rx="7" ry="3" fill="#AD6131" />
|
||||
{/* Oreilles */}
|
||||
<ellipse cx="28" cy="22" rx="4" ry="7" fill="#C97A44" />
|
||||
<ellipse cx="52" cy="22" rx="4" ry="7" fill="#C97A44" />
|
||||
</svg>
|
||||
<svg width="70" height="70" viewBox="0 0 80 80" fill="none">
|
||||
<ellipse cx="40" cy="70" rx="28" ry="10" fill="#7CB9E8" opacity="0.15" />
|
||||
<circle cx="40" cy="36" r="16" fill="#2178aa" />
|
||||
<ellipse cx="36" cy="36" rx="3" ry="4" fill="#EEE" />
|
||||
<ellipse cx="48" cy="36" rx="3" ry="4" fill="#EEE" />
|
||||
<ellipse cx="40" cy="50" rx="7" ry="3" fill="#283142" />
|
||||
{/* Oreilles */}
|
||||
<ellipse cx="28" cy="22" rx="4" ry="7" fill="#2178aa" />
|
||||
<ellipse cx="52" cy="22" rx="4" ry="7" fill="#2178aa" />
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FOOTER */}
|
||||
<footer className="mt-20 py-10 text-center bg-[#F8FAFC] border-t border-gray-200 text-[#C97A44]">
|
||||
Site réalisé avec amour, paillettes et câlins de Kiki & Ryo 🐾
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import type { Config } from 'tailwindcss'
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx}", // pour les projets Next 13+
|
||||
"./pages/**/*.{js,ts,jsx,tsx}", // si tu n’es pas en app directory
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
screens: {
|
||||
mobile: '640px',
|
||||
tablet: '768px',
|
||||
laptop: '1024px',
|
||||
desktop: '1280px',
|
||||
big: '1536px',
|
||||
fhd: '1920px',
|
||||
qhd: '2560px',
|
||||
uhd: '3840px',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|