Agence immobilière
Un site éditorial pour une agence immobilière au Maroc, avec un back-office où l’équipe publie elle-même ses annonces, ses photos et ses visites filmées.
- Rôle
- Design et développement full-stack
- Statut
- En développement
- Catégorie
- Site d’agence et back-office
- Lieu et année
- Maroc, 2026
- Technologies
- Next.js, React, TypeScript, Tailwind CSS, GSAP, Lenis, SQLite, Zod
- Liens
- Pas encore lancé publiquement, et le code source n’est pas public. Le nom et le logo de l’agence sont masqués.





L’histoire
Le défi
Une agence qui vend et loue des villas, des riads et des appartements au Maroc voulait que chaque bien soit présenté comme un article de magazine, avec un back-office assez simple pour publier annonces, photos et visites filmées sans aide.
L’approche
Un site éditorial en français : chapitres numérotés, photos pleine largeur légendées et une grille de biens asymétrique, avec des animations GSAP sur Lenis. Derrière, un back-office protégé sur SQLite pour les annonces, l’envoi de photos, les visites vidéo verticales et les demandes.
Le résultat
Le site et le back-office sont construits et remplis avec neuf annonces de démonstration. Il reste les photos de l’agence, ses vraies annonces et les notifications par e-mail avant le lancement.
Le site public
- Un catalogue de biens avec recherche et filtres : transaction, type, prix, chambres et surface.
- Des fiches de biens avec une galerie navigable au clavier, les chiffres clés et une fiche technique.
- Des visites filmées : une vidéo verticale lue en 9:16, ou un lien vers la publication TikTok du bien.
- Un parcours vendeur : estimation gratuite, mandat en quatre étapes et formulaire de contact déjà réglé sur « vendre ».
- Des animations qui respectent le réglage d’animations réduites : le grain se fige, le mouvement s’arrête et le contenu reste visible.
Le back-office
- Connexion obligatoire sur chaque page d’administration via le middleware, avec cookies de session signés et mots de passe hachés avec bcrypt.
- Un tableau de bord avec des compteurs, les dernières demandes et les annonces récentes.
- Annonces : créer, modifier, supprimer, mettre en avant et changer leur statut.
- Des photos ré-encodées en JPEG à 2000 px maximum, métadonnées retirées, et chaque fichier vérifié comme vraie image.
- Des demandes suivies de « nouvelle » à « traitée » puis « archivée ».
Sous le capot
de vrais extraits du codeNe jamais faire confiance à un fichier envoyé
Le type MIME déclaré n’est qu’une affirmation du navigateur. Chaque photo est décodée puis ré-encodée sur le serveur, ce qui retire ses métadonnées et rejette tout ce qui n’est pas vraiment une image.
src/app/admin/actions.ts
if (!TYPES_IMAGE.includes(f.type)) {
return { ok: false, message: `Format refusé : ${f.name}. JPEG, PNG, WebP ou AVIF.` };
}
if (f.size > TAILLE_MAX) {
return { ok: false, message: `Fichier trop lourd : ${f.name} (12 Mo maximum).` };
}
const nom = `${crypto.randomUUID()}.jpg`;
try {
// Re-encoding strips metadata and proves the bytes really are an image —
// a declared MIME type is only a claim by the client.
await sharp(Buffer.from(await f.arrayBuffer()))
.rotate()
.resize({ width: 2000, withoutEnlargement: true })
.jpeg({ quality: 82, mozjpeg: true })
.toFile(path.join(dossier, nom));
} catch {
return { ok: false, message: `Fichier illisible : ${f.name}. Ce n'est pas une image valide.` };
}Une seule porte pour le back-office
Chaque page d’administration passe par la même vérification avant de s’afficher ; sans session valide, le visiteur arrive sur la page de connexion puis revient là où il allait.
src/middleware.ts
export async function middleware(req: NextRequest) {
const session = await lireJeton(req.cookies.get(NOM_COOKIE)?.value);
if (!session) {
const url = new URL("/admin/connexion", req.url);
url.searchParams.set("suite", req.nextUrl.pathname);
return NextResponse.redirect(url);
}
return NextResponse.next();
}
// Everything under /admin except the login page itself.
export const config = {
matcher: ["/admin/((?!connexion).*)", "/admin"],
};Choix
Une référence, pas une copie
Un site éditorial de référence a donné la direction. Son échelle typographique et sa grammaire de mise en page ont été reprises ; les textes, l’étalonnage des photos et chaque ligne de code ont été écrits pour ce projet.
Des données structurées honnêtes
Les données d’annonces pour les moteurs de recherche restent désactivées tant que les annonces ne sont pas réelles : publier des offres d’exemple auprès de Google serait trompeur.
La vidéo là où est le public
L’agence filme ses biens à la verticale : chaque annonce accepte donc un fichier en 9:16, ou renvoie simplement vers la publication TikTok qui fait déjà les vues.













Statut
Les photos du site sont des images libres sous licence, étalonnées pour s’accorder, et les neuf annonces comme les demandes sont des contenus de démonstration. Le nom, le logo, le numéro de téléphone et les réseaux sociaux de l’agence sont absents de cette étude de cas.