<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width,initial-scale=1.0"/>
<title>Wade
Advisory — Plateforme Consultants</title>
<link
rel="preconnect"
href="https://fonts.googleapis.com"/>
<link
href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600&display=swap"
rel="stylesheet"/>
<script
src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--ink:#0e0e0e;--sand:#f5f0e8;--cream:#faf8f4;--gold:#c8a96e;--gold-light:#e8d5a8;--gold-pale:#f5eed8;--muted:#7a7060;--border:#ddd8ce;--white:#fff;--green:#2e7d32;--green-bg:#e8f5e9;--red:#c0392b;--red-bg:#fdecea;--blue:#1565c0;--blue-bg:#e3f2fd;}
html{scroll-behavior:smooth}
body{font-family:'DM Sans',sans-serif;background:var(--cream);color:var(--ink);line-height:1.6;overflow-x:hidden}
#loadingOverlay{position:fixed;inset:0;background:var(--cream);z-index:9998;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:1rem;transition:opacity .4s}
#loadingOverlay.hide{opacity:0;pointer-events:none}
.loading-logo{font-family:'DM Serif Display',serif;font-size:2rem}
.loading-logo
span{color:var(--gold)}
.loading-bar{width:200px;height:3px;background:var(--border);border-radius:2px;overflow:hidden}
.loading-bar-inner{height:100%;background:var(--gold);animation:loadBar 1.2s ease-in-out infinite}
@keyframes loadBar{0%{width:0%;margin-left:0%}50%{width:60%;margin-left:20%}100%{width:0%;margin-left:100%}}
#toast{position:fixed;top:1.5rem;right:1.5rem;z-index:9999;padding:.85rem 1.5rem;border-radius:.75rem;font-size:.875rem;font-weight:600;box-shadow:0 8px 32px rgba(0,0,0,.15);transform:translateY(-120%);opacity:0;transition:all .35s cubic-bezier(.34,1.56,.64,1);max-width:360px;}
#toast.show{transform:translateY(0);opacity:1}
#toast.success{background:var(--green-bg);color:var(--green);border:1px solid #a5d6a7}
#toast.error{background:var(--red-bg);color:var(--red);border:1px solid #ef9a9a}
#toast.info{background:var(--blue-bg);color:var(--blue);border:1px solid #90caf9}
nav{position:fixed;top:0;left:0;right:0;z-index:200;display:flex;align-items:center;justify-content:space-between;padding:1rem
3rem;background:rgba(250,248,244,.9);backdrop-filter:blur(16px);border-bottom:1px solid var(--border);}
.nav-logo{font-family:'DM
Serif Display',serif;font-size:1.45rem;color:var(--ink);cursor:pointer}
.nav-logo span{color:var(--gold)}
.nav-links{display:flex;gap:1.5rem;list-style:none;align-items:center}
.nav-links a{font-size:.875rem;font-weight:500;color:var(--muted);text-decoration:none;cursor:pointer;transition:color .2s}
.nav-links a:hover{color:var(--ink)}
.nav-btn{background:var(--ink);color:var(--white)!important;padding:.45rem 1.1rem;border-radius:2rem;font-size:.8rem!important;transition:background .2s!important}
.nav-btn:hover{background:var(--gold)!important;color:var(--ink)!important}
#userMenu{display:none;align-items:center;gap:1rem}
.user-avatar-nav{width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:'DM
Serif Display',serif;font-size:.9rem;color:var(--white);cursor:pointer}
.logout-btn{font-size:.8rem;color:var(--muted);cursor:pointer;background:none;border:none;font-family:'DM Sans',sans-serif;transition:color .2s}
.logout-btn:hover{color:var(--red)}
.page{display:none;min-height:100vh;padding-top:72px}
.page.active{display:block}
#homePage{background:radial-gradient(ellipse 70% 50% at 50% -10%,rgba(200,169,110,.15),transparent 65%),var(--cream);}
.hero{display:flex;align-items:center;justify-content:center;flex-direction:column;text-align:center;padding:6rem 2rem 5rem;position:relative;}
.hero-grid{position:absolute;inset:0;background-image:linear-gradient(var(--border) 1px,transparent 1px),linear-gradient(90deg,var(--border) 1px,transparent
1px);background-size:56px 56px;opacity:.35;pointer-events:none;}
.hero-badge{display:inline-flex;align-items:center;gap:.5rem;background:var(--gold-light);color:var(--ink);font-size:.75rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;padding:.4rem 1rem;border-radius:2rem;margin-bottom:1.75rem;}
h1.hero-title{font-family:'DM
Serif Display',serif;font-size:clamp(2.6rem,6.5vw,4.8rem);line-height:1.06;letter-spacing:-.03em;margin-bottom:1.25rem;}
h1.hero-title em{color:var(--gold);font-style:italic}
.hero-sub{font-size:1.05rem;color:var(--muted);max-width:500px;margin:0
auto 2.5rem;}
.hero-actions{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap;}
.btn{display:inline-flex;align-items:center;gap:.4rem;padding:.8rem 1.75rem;border-radius:3rem;font-family:'DM Sans',sans-serif;font-size:.9rem;font-weight:600;border:none;cursor:pointer;transition:all
.2s;text-decoration:none}
.btn-primary{background:var(--ink);color:var(--white)}
.btn-primary:hover{background:var(--gold);color:var(--ink);transform:translateY(-2px)}
.btn-ghost{background:transparent;color:var(--ink);border:1.5px solid var(--border)}
.btn-ghost:hover{border-color:var(--ink);transform:translateY(-2px)}
.btn-gold{background:var(--gold);color:var(--ink)}
.btn-gold:hover{background:var(--gold-light);transform:translateY(-2px)}
.btn-sm{padding:.5rem
1.1rem;font-size:.8rem}
.btn-danger{background:var(--red-bg);color:var(--red);border:1px solid #ef9a9a}
.btn-danger:hover{background:var(--red);color:var(--white)}
.stats-bar{display:flex;border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:var(--white)}
.stat{flex:1;text-align:center;padding:1.75rem 1rem;border-right:1px solid var(--border)}
.stat:last-child{border-right:none}
.stat-num{font-family:'DM Serif Display',serif;font-size:2rem}
.stat-label{font-size:.75rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-top:.2rem}
.section{padding:4rem
3rem}
.section-label{font-size:.72rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--gold);margin-bottom:.6rem}
h2.section-title{font-family:'DM
Serif Display',serif;font-size:clamp(1.8rem,3.5vw,2.6rem);line-height:1.12;letter-spacing:-.02em;margin-bottom:1rem}
.section-intro{color:var(--muted);font-size:1rem;max-width:520px;margin-bottom:2.5rem}
.filters{display:flex;gap:.6rem;flex-wrap:wrap;margin-bottom:2rem}
.filter-chip{padding:.4rem .9rem;border:1.5px
solid var(--border);border-radius:2rem;font-size:.78rem;font-weight:500;cursor:pointer;background:var(--cream);transition:all .15s}
.filter-chip.active{background:var(--ink);color:var(--white);border-color:var(--ink)}
.filter-chip:hover:not(.active){border-color:var(--gold)}
.profiles-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1.25rem}
.profile-card{background:var(--white);border:1px solid var(--border);border-radius:1rem;padding:1.6rem;transition:box-shadow .2s,transform
.2s;cursor:pointer}
.profile-card:hover{box-shadow:0
10px 36px rgba(0,0,0,.09);transform:translateY(-3px)}
.profile-header{display:flex;align-items:center;gap:1rem;margin-bottom:.9rem}
.avatar{width:50px;height:50px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:'DM
Serif Display',serif;font-size:1.2rem;color:var(--white);flex-shrink:0}
.profile-name{font-weight:600;font-size:.95rem}
.profile-title-card{font-size:.8rem;color:var(--muted)}
.profile-tags{display:flex;flex-wrap:wrap;gap:.35rem;margin:.7rem
0}
.tag{background:var(--sand);color:var(--ink);font-size:.7rem;font-weight:500;padding:.22rem .6rem;border-radius:2rem}
.profile-footer{display:flex;align-items:center;justify-content:space-between;margin-top:1rem;padding-top:1rem;border-top:1px solid var(--border)}
.profile-rate{font-family:'DM
Serif Display',serif;font-size:1.05rem}
.profile-rate span{font-family:'DM Sans',sans-serif;font-size:.75rem;color:var(--muted);font-weight:400}
.avail-badge{font-size:.7rem;font-weight:600;padding:.22rem
.6rem;border-radius:2rem}
.avail-now{background:var(--green-bg);color:var(--green)}
.avail-soon{background:#fff8e1;color:#f57f17}
.avail-open{background:var(--blue-bg);color:var(--blue)}
.modal-overlay{position:fixed;inset:0;background:rgba(14,14,14,.5);backdrop-filter:blur(4px);z-index:500;display:none;align-items:center;justify-content:center;padding:1rem}
.modal-overlay.open{display:flex}
.modal{background:var(--white);border-radius:1.25rem;padding:2.5rem;width:100%;max-width:440px;position:relative;animation:modalIn .3s ease
both}
@keyframes modalIn{from{opacity:0;transform:scale(.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}
.modal-close{position:absolute;top:1.25rem;right:1.25rem;background:none;border:none;font-size:1.25rem;cursor:pointer;color:var(--muted)}
.modal-title{font-family:'DM Serif Display',serif;font-size:1.6rem;margin-bottom:.4rem}
.modal-sub{font-size:.85rem;color:var(--muted);margin-bottom:1.75rem}
.modal-tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:1.75rem}
.modal-tab{flex:1;padding:.75rem;font-size:.85rem;font-weight:600;text-align:center;cursor:pointer;color:var(--muted);border-bottom:2px solid transparent;background:none;border-left:none;border-right:none;border-top:none;font-family:'DM Sans',sans-serif;transition:all .2s}
.modal-tab.active{color:var(--ink);border-bottom-color:var(--gold)}
.form-group{margin-bottom:1.1rem}
label.lbl{display:block;font-size:.75rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:.4rem}
input[type=text],input[type=email],input[type=password],input[type=tel],input[type=number],select,textarea{width:100%;padding:.7rem
.9rem;border:1.5px solid var(--border);border-radius:.5rem;font-family:'DM Sans',sans-serif;font-size:.875rem;color:var(--ink);background:var(--cream);transition:border-color
.2s;outline:none;appearance:none}
input:focus,select:focus,textarea:focus{border-color:var(--gold);background:var(--white)}
textarea{resize:vertical;min-height:90px}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.input-icon{position:relative}
.input-icon
input{padding-right:2.5rem}
.input-icon .eye{position:absolute;right:.75rem;top:50%;transform:translateY(-50%);cursor:pointer;color:var(--muted);font-size:.9rem;user-select:none}
.field-error{font-size:.75rem;color:var(--red);margin-top:.3rem;display:none}
.field-error.show{display:block}
#dashPage{background:var(--cream)}
.dash-header{background:var(--white);border-bottom:1px solid var(--border);padding:2.5rem 3rem}
.dash-welcome{font-family:'DM
Serif Display',serif;font-size:2rem;margin-bottom:.35rem}
.dash-sub{color:var(--muted);font-size:.9rem}
.dash-grid{display:grid;grid-template-columns:320px 1fr;gap:1.5rem;padding:2rem 3rem}
.dash-card{background:var(--white);border:1px solid var(--border);border-radius:1rem;overflow:hidden;margin-bottom:1rem}
.dash-card-header{padding:1.25rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.dash-card-title{font-weight:600;font-size:.9rem}
.dash-card-body{padding:1.5rem}
.profile-preview-avatar{width:72px;height:72px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:'DM
Serif Display',serif;font-size:1.8rem;color:var(--white);margin:0 auto 1rem}
.profile-preview-name{font-family:'DM
Serif Display',serif;font-size:1.2rem;text-align:center}
.profile-preview-title{font-size:.82rem;color:var(--muted);text-align:center;margin-bottom:1rem}
.profile-stats{display:grid;grid-template-columns:1fr 1fr;gap:.75rem;margin-top:1rem}
.p-stat{background:var(--sand);border-radius:.6rem;padding:.75rem;text-align:center}
.p-stat-num{font-family:'DM Serif Display',serif;font-size:1.3rem}
.p-stat-label{font-size:.7rem;color:var(--muted);margin-top:.15rem}
.edit-section-title{font-weight:600;font-size:.85rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin:1.5rem 0 .75rem;padding-bottom:.5rem;border-bottom:1px solid var(--border)}
.edit-section-title:first-child{margin-top:0}
.chips{display:flex;flex-wrap:wrap;gap:.4rem}
.chip{padding:.35rem
.8rem;border:1.5px solid var(--border);border-radius:2rem;font-size:.78rem;font-weight:500;cursor:pointer;transition:all .15s;background:var(--cream);user-select:none}
.chip.sel{background:var(--ink);color:var(--white);border-color:var(--ink)}
.toggle-group{display:flex;gap:.6rem;flex-wrap:wrap}
.toggle-opt{flex:1;min-width:120px;text-align:center;padding:.55rem .5rem;border:1.5px solid var(--border);border-radius:.5rem;font-size:.8rem;font-weight:500;cursor:pointer;transition:all
.15s;background:var(--cream)}
.toggle-opt.sel{background:var(--ink);color:var(--white);border-color:var(--ink)}
.drop-zone{border:2px
dashed var(--border);border-radius:.75rem;padding:2rem 1rem;text-align:center;cursor:pointer;transition:all .2s;background:var(--cream)}
.drop-zone:hover,.drop-zone.drag-over{border-color:var(--gold);background:var(--gold-pale)}
.cv-item{display:flex;align-items:center;gap:.75rem;padding:.9rem;background:var(--sand);border-radius:.6rem;margin-bottom:.6rem}
.cv-icon{font-size:1.5rem}
.cv-info{flex:1}
.cv-name{font-size:.85rem;font-weight:600}
.cv-meta{font-size:.75rem;color:var(--muted)}
.cv-actions{display:flex;gap:.4rem}
.activity-item{display:flex;gap:.75rem;padding:.9rem 0;border-bottom:1px solid var(--border)}
.activity-item:last-child{border-bottom:none}
.activity-dot{width:10px;height:10px;border-radius:50%;margin-top:.35rem;flex-shrink:0}
.activity-text{font-size:.85rem;line-height:1.5}
.activity-time{font-size:.75rem;color:var(--muted);margin-top:.2rem}
#profilePage{background:var(--cream)}
.profile-detail-header{background:var(--white);border-bottom:1px solid var(--border);padding:3rem}
.profile-detail-top{display:flex;gap:2rem;align-items:flex-start;max-width:900px}
.profile-detail-avatar{width:90px;height:90px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:'DM
Serif Display',serif;font-size:2rem;color:var(--white);flex-shrink:0}
.profile-detail-name{font-family:'DM
Serif Display',serif;font-size:2rem;line-height:1.1;margin-bottom:.3rem}
.profile-detail-title{color:var(--muted);font-size:1rem;margin-bottom:.75rem}
.profile-detail-meta{display:flex;gap:1rem;flex-wrap:wrap;font-size:.82rem;color:var(--muted)}
.profile-detail-body{display:grid;grid-template-columns:1fr
280px;gap:1.5rem;padding:2rem
3rem;max-width:1200px}
.detail-section{margin-bottom:1.75rem}
.detail-section
h3{font-family:'DM
Serif Display',serif;font-size:1.2rem;margin-bottom:.75rem}
.detail-section
p{color:var(--muted);font-size:.9rem;line-height:1.7}
.detail-sidebar-card{background:var(--white);border:1px solid var(--border);border-radius:1rem;padding:1.5rem;margin-bottom:1rem}
.contact-btn{width:100%;text-align:center;justify-content:center;margin-bottom:.75rem}
.email-preview{background:var(--white);border:1px solid var(--border);border-radius:1rem;overflow:hidden;margin-top:1.5rem}
.email-header-bar{background:var(--ink);padding:1.5rem;text-align:center}
.email-logo{font-family:'DM
Serif Display',serif;font-size:1.4rem;color:var(--white)}
.email-logo span{color:var(--gold)}
.email-body{padding:2rem}
.email-greeting{font-family:'DM Serif Display',serif;font-size:1.3rem;margin-bottom:.75rem}
.email-text{font-size:.875rem;color:var(--muted);line-height:1.7;margin-bottom:1rem}
.email-box{background:var(--sand);border-radius:.75rem;padding:1.25rem;margin:1rem 0}
.email-box-row{display:flex;justify-content:space-between;font-size:.82rem;padding:.3rem 0;border-bottom:1px solid var(--border)}
.email-box-row:last-child{border-bottom:none}
.email-box-label{color:var(--muted)}
.email-box-value{font-weight:600}
.email-footer-text{font-size:.75rem;color:var(--muted);text-align:center;margin-top:1.5rem;padding-top:1rem;border-top:1px
solid var(--border)}
@keyframes fadeUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
@keyframes spin{to{transform:rotate(360deg)}}
.spinner{display:inline-block;width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:var(--white);border-radius:50%;animation:spin .7s linear infinite;vertical-align:middle}
@media(max-width:768px){
nav{padding:1rem 1.25rem}.nav-links{display:none}
.section{padding:3rem
1.25rem}.dash-grid{grid-template-columns:1fr;padding:1.25rem}
.profile-detail-body{grid-template-columns:1fr}.profile-detail-header,.dash-header{padding:1.75rem
1.25rem}
.form-row{grid-template-columns:1fr}.hero{padding:5rem
1.25rem 3.5rem}
}
</style>
</head>
<body>
<!-- LOADING -->
<div id="loadingOverlay">
<div class="loading-logo">Wade <span>Advisory</span></div>
<div class="loading-bar"><div class="loading-bar-inner"></div></div>
</div>
<!-- TOAST -->
<div
id="toast"></div>
<!-- AUTH MODAL -->
<div
class="modal-overlay" id="authModal">
<div
class="modal">
<button
class="modal-close" onclick="closeAuth()">✕</button>
<div
class="modal-title">Bienvenue</div>
<div
class="modal-sub">Rejoignez le réseau
des consultants indépendants</div>
<div
class="modal-tabs">
<button class="modal-tab active" onclick="switchAuthTab('login')">Connexion</button>
<button class="modal-tab" onclick="switchAuthTab('register')">Créer un compte</button>
</div>
<div id="loginForm">
<div
class="form-group">
<label
class="lbl">Adresse e-mail</label>
<input
type="email" id="loginEmail"
placeholder="sophie@exemple.com"/>
<div
class="field-error" id="loginErr">E-mail ou mot de
passe incorrect</div>
</div>
<div
class="form-group">
<label
class="lbl">Mot de passe</label>
<div
class="input-icon">
<input
type="password" id="loginPwd" placeholder="••••••••"
onkeydown="if(event.key==='Enter')doLogin()"/>
<span class="eye" onclick="togglePwd('loginPwd',this)">👁</span>
</div>
</div>
<button class="btn btn-primary" style="width:100%;justify-content:center;margin-top:.5rem" onclick="doLogin()"
id="loginBtn">Se connecter</button>
<p
style="font-size:.78rem;color:var(--muted);text-align:center;margin-top:1rem">Pas
encore de compte ? <a onclick="switchAuthTab('register')"
style="color:var(--gold);cursor:pointer;font-weight:600">Inscrivez-vous
→</a></p>
</div>
<div id="registerForm" style="display:none">
<div
class="form-row">
<div
class="form-group"><label
class="lbl">Prénom</label><input
type="text" id="regFirst"
placeholder="Sophie"/></div>
<div
class="form-group"><label
class="lbl">Nom</label><input
type="text" id="regLast"
placeholder="Carmichael"/></div>
</div>
<div
class="form-group">
<label
class="lbl">E-mail
professionnel</label>
<input
type="email" id="regEmail"
placeholder="sophie@exemple.com"/>
<div
class="field-error" id="regEmailErr">Cet e-mail
est déjà utilisé</div>
</div>
<div
class="form-group">
<label
class="lbl">Mot de passe</label>
<div
class="input-icon">
<input
type="password" id="regPwd" placeholder="8
caractères minimum"/>
<span class="eye" onclick="togglePwd('regPwd',this)">👁</span>
</div>
<div
class="field-error" id="regPwdErr">Minimum 8 caractères</div>
</div>
<div
class="form-group"><label
class="lbl">Titre de
poste</label><input type="text"
id="regTitle" placeholder="Directeur
de projet · Finance"/></div>
<button class="btn btn-primary" style="width:100%;justify-content:center;margin-top:.5rem" onclick="doRegister()"
id="registerBtn">Créer mon compte</button>
</div>
</div>
</div>
<!-- CONTACT MODAL -->
<div
class="modal-overlay" id="contactModal">
<div
class="modal">
<button
class="modal-close" onclick="closeContact()">✕</button>
<div
class="modal-title">Contacter ce
consultant</div>
<div
class="modal-sub" id="contactModalSub"></div>
<div class="form-group"><label class="lbl">Votre nom / société</label><input
type="text" id="contactName"
placeholder="Acme
Corp"/></div>
<div class="form-group"><label class="lbl">E-mail de
contact</label><input type="email"
id="contactEmail" placeholder="rh@acme.com"/></div>
<div class="form-group"><label class="lbl">Description de la mission</label><textarea id="contactMsg"
placeholder="Contexte, durée, localisation,
TJM…"></textarea></div>
<button
class="btn btn-primary"
style="width:100%;justify-content:center"
onclick="sendContact()">Envoyer la demande</button>
</div>
</div>
<!-- NAV -->
<nav>
<div class="nav-logo" onclick="showPage('home')">Wade <span>Advisory</span></div>
<ul
class="nav-links">
<li><a onclick="showPage('home')">Accueil</a></li>
<li><a onclick="showPage('profiles')">Consultants</a></li>
<li><a onclick="requireAuth()">Mon tableau de bord</a></li>
<li id="guestNav"><a class="nav-btn"
onclick="openAuth()">Rejoindre →</a></li>
</ul>
<div id="userMenu">
<div
class="user-avatar-nav" id="navAvatar" onclick="showPage('dashboard')"></div>
<button
class="logout-btn" onclick="doLogout()">Déconnexion</button>
</div>
</nav>
<!-- HOME -->
<div class="page
active" id="homePage">
<div class="hero">
<div class="hero-grid"></div>
<div style="position:relative">
<div
class="hero-badge">✦
Plateforme de mise en relation</div>
<h1
class="hero-title">Votre prochaine <em>mission</em><br>commence ici</h1>
<p
class="hero-sub">La plateforme dédiée
aux consultants indépendants qui souhaitent valoriser leur expertise et
décrocher les meilleures missions.</p>
<div
class="hero-actions">
<button class="btn btn-primary" onclick="openAuth()">Créer
mon profil gratuit</button>
<button class="btn btn-ghost" onclick="showPage('profiles')">Voir les consultants</button>
</div>
</div>
</div>
<div
class="stats-bar">
<div
class="stat"><div class="stat-num"
id="statCount">—</div><div
class="stat-label">Consultants inscrits</div></div>
<div
class="stat"><div class="stat-num">340</div><div
class="stat-label">Missions ouvertes</div></div>
<div
class="stat"><div class="stat-num">98%</div><div
class="stat-label">Satisfaction</div></div>
<div
class="stat"><div class="stat-num">72h</div><div
class="stat-label">Délai moyen</div></div>
</div>
<div
class="section" style="background:var(--white)">
<div
class="section-label">Fonctionnement</div>
<h2
class="section-title">En 4 étapes,
trouvez votre mission</h2>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));border:1px solid var(--border);border-radius:1rem;overflow:hidden;margin-top:2rem">
<div
style="padding:2rem 1.75rem;border-right:1px solid var(--border)"><div
style="font-family:'DM Serif Display',serif;font-size:3rem;color:var(--gold-light);line-height:1;margin-bottom:.75rem">01</div><h3
style="font-size:1rem;font-weight:600;margin-bottom:.4rem">Créez
votre profil</h3><p style="font-size:.85rem;color:var(--muted)">Renseignez
vos compétences, TJM et disponibilités.</p></div>
<div
style="padding:2rem 1.75rem;border-right:1px solid var(--border)"><div
style="font-family:'DM Serif Display',serif;font-size:3rem;color:var(--gold-light);line-height:1;margin-bottom:.75rem">02</div><h3
style="font-size:1rem;font-weight:600;margin-bottom:.4rem">Déposez
votre CV</h3><p style="font-size:.85rem;color:var(--muted)">Uploadez
vos documents depuis votre tableau de bord.</p></div>
<div
style="padding:2rem 1.75rem;border-right:1px solid var(--border)"><div
style="font-family:'DM Serif Display',serif;font-size:3rem;color:var(--gold-light);line-height:1;margin-bottom:.75rem">03</div><h3
style="font-size:1rem;font-weight:600;margin-bottom:.4rem">Soyez
visible</h3><p style="font-size:.85rem;color:var(--muted)">Votre
profil est mis en avant auprès de nos clients.</p></div>
<div
style="padding:2rem 1.75rem"><div
style="font-family:'DM Serif Display',serif;font-size:3rem;color:var(--gold-light);line-height:1;margin-bottom:.75rem">04</div><h3
style="font-size:1rem;font-weight:600;margin-bottom:.4rem">Recevez
des offres</h3><p style="font-size:.85rem;color:var(--muted)">Les
décideurs vous contactent directement.</p></div>
</div>
</div>
<div
class="section">
<div
class="section-label">Consultants en vedette</div>
<h2
class="section-title">Des profils
d'exception</h2>
<div
class="profiles-grid" id="homeProfilesGrid"><div style="color:var(--muted);font-size:.9rem">Chargement…</div></div>
<div
style="text-align:center;margin-top:2rem"><button
class="btn btn-ghost"
onclick="showPage('profiles')">Voir
tous les consultants →</button></div>
</div>
<footer
style="background:var(--ink);color:rgba(255,255,255,.55);padding:2.5rem 3rem;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:1rem">
<div style="font-family:'DM
Serif Display',serif;font-size:1.3rem;color:var(--white)">Wade
<span style="color:var(--gold)">Advisory</span></div>
<p
style="font-size:.78rem">© 2025 Wade
Advisory — Tous droits réservés</p>
<p
style="font-size:.78rem">CGU ·
Confidentialité · Contact</p>
</footer>
</div>
<!-- PROFILES PAGE -->
<div class="page"
id="profilesPage">
<div
class="section">
<div
class="section-label">Annuaire</div>
<h2
class="section-title">Tous les
consultants</h2>
<div class="filters">
<span class="filter-chip
active" onclick="filterProfiles('all',this)">Tous</span>
<span class="filter-chip"
onclick="filterProfiles('Disponible maintenant',this)">Disponible</span>
<span class="filter-chip"
onclick="filterProfiles('Management',this)">Management</span>
<span class="filter-chip"
onclick="filterProfiles('Data',this)">Data / IA</span>
<span class="filter-chip"
onclick="filterProfiles('Cloud',this)">Cloud /
DevOps</span>
<span class="filter-chip"
onclick="filterProfiles('Finance',this)">Finance</span>
</div>
<div
class="profiles-grid" id="allProfilesGrid"><div style="color:var(--muted)">Chargement…</div></div>
</div>
</div>
<!-- PROFILE DETAIL -->
<div class="page"
id="profilePage">
<div class="profile-detail-header">
<button
class="btn btn-ghost btn-sm" onclick="showPage('profiles')" style="margin-bottom:1.5rem">← Retour</button>
<div
class="profile-detail-top">
<div
class="profile-detail-avatar" id="pdAvatar"></div>
<div>
<div
class="profile-detail-name"
id="pdName"></div>
<div
class="profile-detail-title"
id="pdTitle"></div>
<div
class="profile-detail-meta">
<span id="pdCity"></span><span id="pdExp"></span><span id="pdRate"></span><span id="pdAvailSpan"></span>
</div>
</div>
</div>
</div>
<div class="profile-detail-body">
<div>
<div
class="detail-section"><h3>À
propos</h3><p id="pdBio"></p></div>
<div
class="detail-section"><h3>Compétences</h3><div
class="profile-tags" id="pdTags"></div></div>
<div
class="detail-section"><h3>Secteurs</h3><div
class="profile-tags" id="pdSectors"></div></div>
</div>
<div>
<div
class="detail-sidebar-card">
<div
style="font-family:'DM Serif Display',serif;font-size:1.6rem;margin-bottom:.25rem" id="pdSideRate"></div>
<div
style="font-size:.8rem;color:var(--muted);margin-bottom:1.25rem">Taux journalier moyen</div>
<button class="btn btn-primary contact-btn" onclick="openContact()">Envoyer une offre de mission</button>
<button class="btn btn-ghost contact-btn" onclick="openContact()">📄 Demander le CV
complet</button>
</div>
<div
class="detail-sidebar-card">
<div
style="font-size:.8rem;color:var(--muted);margin-bottom:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em">Informations</div>
<div
style="font-size:.85rem;display:flex;flex-direction:column;gap:.5rem">
<div
style="display:flex;justify-content:space-between"><span style="color:var(--muted)">Modalité</span><span id="pdMode"
style="font-weight:500"></span></div>
<div
style="display:flex;justify-content:space-between"><span style="color:var(--muted)">Domaine</span><span id="pdDomain"
style="font-weight:500"></span></div>
<div
style="display:flex;justify-content:space-between"><span style="color:var(--muted)">Expérience</span><span id="pdExpInfo"
style="font-weight:500"></span></div>
</div>
</div>
</div>
</div>
</div>
<!-- DASHBOARD -->
<div class="page"
id="dashPage">
<div class="dash-header">
<div class="dash-welcome" id="dashWelcome">Bonjour
👋</div>
<div class="dash-sub">Gérez votre profil, vos CV et suivez vos
statistiques</div>
</div>
<div class="dash-grid">
<div>
<div
class="dash-card">
<div
class="dash-card-header"><span class="dash-card-title">Mon
profil</span><span
id="profileCompleteness" style="font-size:.75rem;color:var(--gold);font-weight:600"></span></div>
<div
class="dash-card-body"
style="text-align:center">
<div
class="profile-preview-avatar" id="dashAvatar"></div>
<div
class="profile-preview-name"
id="dashName"></div>
<div
class="profile-preview-title"
id="dashTitlePreview"></div>
<div
id="dashAvailBadge" style="margin:.5rem auto;display:inline-block"></div>
<div
class="profile-stats">
<div class="p-stat"><div class="p-stat-num" id="statViews">—</div><div
class="p-stat-label">Vues profil</div></div>
<div class="p-stat"><div class="p-stat-num" id="statContacts">—</div><div
class="p-stat-label">Contacts reçus</div></div>
</div>
</div>
</div>
<div
class="dash-card">
<div
class="dash-card-header">
<span class="dash-card-title">Mes
CV & Documents</span>
<button class="btn btn-sm btn-ghost" onclick="document.getElementById('cvUploadInput').click()">+
Ajouter</button>
<input
type="file" id="cvUploadInput" accept=".pdf,.doc,.docx"
style="display:none"
onchange="uploadCV(this)"/>
</div>
<div
class="dash-card-body">
<div
class="drop-zone" onclick="document.getElementById('cvUploadInput').click()"
id="dashDropZone" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)">
<div style="font-size:2rem;margin-bottom:.6rem">📄</div>
<div style="font-size:.85rem;color:var(--muted)"><strong>Glissez votre CV ici</strong><br>PDF, Word · 10 Mo max</div>
</div>
<div
id="cvList" style="margin-top:1rem"></div>
</div>
</div>
</div>
<div>
<div
class="dash-card">
<div
class="dash-card-header"><span class="dash-card-title">Modifier
mon profil</span><button
class="btn btn-sm btn-gold" onclick="saveProfile()">Enregistrer</button></div>
<div
class="dash-card-body">
<div
class="edit-section-title">Informations
personnelles</div>
<div
class="form-row">
<div class="form-group"><label
class="lbl">Prénom</label><input
type="text" id="eFirst"/></div>
<div class="form-group"><label
class="lbl">Nom</label><input
type="text" id="eLast"/></div>
</div>
<div
class="form-row">
<div class="form-group"><label
class="lbl">Téléphone</label><input
type="tel" id="ePhone"/></div>
<div class="form-group"><label
class="lbl">Ville</label><input
type="text" id="eCity"/></div>
</div>
<div
class="form-group"><label
class="lbl">Titre du
profil</label><input type="text"
id="eTitle"/></div>
<div
class="form-group"><label
class="lbl">À propos</label><textarea id="eBio"></textarea></div>
<div
class="edit-section-title">Expertise</div>
<div
class="form-row">
<div class="form-group"><label
class="lbl">Domaine</label>
<select id="eDomain">
<option>Management de projet /
PMO</option><option>Direction financière / CFO</option>
<option>Transformation
digitale</option><option>Data & Intelligence
artificielle</option>
<option>Architecture IT /
Cloud</option><option>Développement logiciel</option>
<option>Marketing &
Communication</option><option>Ressources humaines</option>
<option>Supply Chain &
Opérations</option><option>Stratégie &
Organisation</option>
</select>
</div>
<div class="form-group"><label
class="lbl">Expérience</label>
<select id="eExp"><option>2
– 5 ans</option><option>5 – 10 ans</option><option>10 –
15 ans</option><option>15 ans et plus</option></select>
</div>
</div>
<div
class="form-group"><label
class="lbl">Compétences
clés</label>
<div class="chips" id="eSkillChips">
<span class="chip" onclick="toggleChip(this)">Gestion de crise</span><span class="chip" onclick="toggleChip(this)">Transformation
agile</span>
<span class="chip" onclick="toggleChip(this)">Finance d'entreprise</span><span class="chip" onclick="toggleChip(this)">Cloud
AWS / Azure</span>
<span class="chip" onclick="toggleChip(this)">Data Analytics</span><span class="chip" onclick="toggleChip(this)">Machine
Learning</span>
<span class="chip" onclick="toggleChip(this)">DevOps / CI/CD</span><span class="chip" onclick="toggleChip(this)">Marketing
digital</span>
<span class="chip" onclick="toggleChip(this)">M&A / Due diligence</span><span
class="chip" onclick="toggleChip(this)">Change
management</span>
<span class="chip" onclick="toggleChip(this)">SAP / ERP</span>
</div>
</div>
<div
class="form-group"><label
class="lbl">Secteurs</label>
<div class="chips" id="eSectorChips">
<span class="chip" onclick="toggleChip(this)">Banque & Finance</span><span class="chip" onclick="toggleChip(this)">Retail</span>
<span class="chip" onclick="toggleChip(this)">Industrie</span><span class="chip" onclick="toggleChip(this)">Santé</span>
<span class="chip" onclick="toggleChip(this)">Énergie</span><span class="chip" onclick="toggleChip(this)">Luxe</span>
<span class="chip" onclick="toggleChip(this)">Tech / Startup</span><span class="chip" onclick="toggleChip(this)">Public
/ GovTech</span>
</div>
</div>
<div
class="edit-section-title">Conditions</div>
<div
class="form-row">
<div class="form-group"><label
class="lbl">TJM (€/jour)</label><input
type="number" id="eTJM"/></div>
<div class="form-group"><label
class="lbl">Modalité</label>
<select id="eMode"><option>Hybride</option><option>Sur
site</option><option>Full remote</option></select>
</div>
</div>
<div
class="form-group"><label
class="lbl">Disponibilité</label>
<div class="toggle-group">
<div class="toggle-opt" onclick="selectToggle(this)">Disponible maintenant</div>
<div class="toggle-opt" onclick="selectToggle(this)">Sous 1 mois</div>
<div class="toggle-opt" onclick="selectToggle(this)">En mission — à l'écoute</div>
</div>
</div>
<div
class="form-group"><label
class="lbl">LinkedIn</label><input
type="text" id="eLinkedin"/></div>
<div
style="display:flex;align-items:center;justify-content:space-between;padding:1rem;background:var(--sand);border-radius:.75rem;margin-top:1rem">
<div><div style="font-weight:600;font-size:.9rem">Profil public</div><div
style="font-size:.78rem;color:var(--muted)">Visible sur
l'annuaire</div></div>
<input type="checkbox"
id="ePublic" checked
style="width:18px;height:18px;cursor:pointer;accent-color:var(--gold)"/>
</div>
</div>
</div>
<div
class="dash-card">
<div
class="dash-card-header"><span class="dash-card-title">Activité
récente</span></div>
<div
class="dash-card-body"
id="activityFeed"></div>
</div>
<div
class="dash-card">
<div
class="dash-card-header"><span class="dash-card-title">📧
E-mail de confirmation</span></div>
<div
class="dash-card-body"><div
class="email-preview"
id="emailPreview"></div></div>
</div>
</div>
</div>
</div>
<script>
// ═══════════════════════════════════════
// SUPABASE INIT
// ═══════════════════════════════════════
const
SUPABASE_URL = 'https://mpvpjvazpthsvqbedgld.supabase.co';
const
SUPABASE_KEY =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1wdnBqdmF6cHRoc3ZxYmVkZ2xkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzc5MDcxMTMsImV4cCI6MjA5MzQ4MzExM30.OQorqP_-IFkxbkS7EwIfmbdPUrHiUvYgC5vSM_IWj8c';
const sb
= supabase.createClient(SUPABASE_URL,
SUPABASE_KEY);
let currentUser
= null;
let currentProfile
= null;
let currentProfileId
= null;
let allProfiles
= [];
let currentFilter
= 'all';
// ═══════════════════════════════════════
// INIT
// ═══════════════════════════════════════
async function init() {
const
{ data: { session } } = await sb.auth.getSession();
if (session) {
currentUser
= session.user;
await
loadCurrentProfile();
updateNavAuth();
}
await
loadProfiles();
renderHomeProfiles();
updateStatCount();
document.getElementById('loadingOverlay').classList.add('hide');
sb.auth.onAuthStateChange(async (event, session) => {
if (event === 'SIGNED_IN') {
currentUser
= session.user;
await
loadCurrentProfile();
updateNavAuth();
} else if (event === 'SIGNED_OUT') {
currentUser
= null;
currentProfile
= null;
updateNavAuth();
}
});
}
// ═══════════════════════════════════════
// PROFILES — SUPABASE
// ═══════════════════════════════════════
async function loadProfiles() {
const
{ data, error } = await sb.from('profiles').select('*').eq('is_public', true).order('created_at', { ascending:
false });
if (!error && data) allProfiles
= data;
}
async function loadCurrentProfile() {
if (!currentUser) return;
const
{ data } = await sb.from('profiles').select('*').eq('user_id', currentUser.id).single();
currentProfile
= data;
}
async function updateStatCount() {
const
{ count } = await sb.from('profiles').select('*', { count: 'exact', head: true }).eq('is_public', true);
document.getElementById('statCount').textContent
= count || allProfiles.length;
}
// ═══════════════════════════════════════
// AUTH
// ═══════════════════════════════════════
function openAuth()
{ document.getElementById('authModal').classList.add('open'); }
function closeAuth()
{ document.getElementById('authModal').classList.remove('open'); }
function switchAuthTab(tab) {
document.querySelectorAll('.modal-tab').forEach((t,i) => t.classList.toggle('active', i===(tab==='login'?0:1)));
document.getElementById('loginForm').style.display = tab==='login'?'block':'none';
document.getElementById('registerForm').style.display = tab==='register'?'block':'none';
}
function togglePwd(id,
eye) {
const
inp = document.getElementById(id);
inp.type
= inp.type==='password'?'text':'password';
eye.textContent
= inp.type==='password'?'👁':'🙈';
}
function requireAuth()
{ if (currentUser) showPage('dashboard'); else openAuth(); }
async function doLogin() {
const
email = document.getElementById('loginEmail').value.trim();
const
pwd = document.getElementById('loginPwd').value;
const
btn = document.getElementById('loginBtn');
document.getElementById('loginErr').classList.remove('show');
if (!email
|| !pwd) {
toast('Veuillez remplir tous les champs', 'error'); return; }
btn.innerHTML
= '<span class="spinner"></span> Connexion…';
btn.disabled
= true;
const
{ error } = await sb.auth.signInWithPassword({ email, password:
pwd });
if (error)
{
document.getElementById('loginErr').classList.add('show');
btn.textContent
= 'Se connecter';
btn.disabled
= false;
return;
}
closeAuth();
toast('Bienvenue ! 👋',
'success');
showPage('dashboard');
btn.textContent
= 'Se connecter';
btn.disabled
= false;
}
async function doRegister() {
const
first = document.getElementById('regFirst').value.trim();
const
last = document.getElementById('regLast').value.trim();
const
email = document.getElementById('regEmail').value.trim();
const
pwd = document.getElementById('regPwd').value;
const
title = document.getElementById('regTitle').value.trim();
const
btn = document.getElementById('registerBtn');
document.getElementById('regEmailErr').classList.remove('show');
document.getElementById('regPwdErr').classList.remove('show');
if (!first||!last||!email) {
toast('Veuillez remplir tous les champs','error'); return; }
if (pwd.length < 8) { document.getElementById('regPwdErr').classList.add('show'); return; }
btn.innerHTML
= '<span class="spinner"></span> Création…';
btn.disabled
= true;
const
{ data, error } = await sb.auth.signUp({ email, password: pwd });
if (error)
{
if (error.message.includes('already')) document.getElementById('regEmailErr').classList.add('show');
else
toast(error.message, 'error');
btn.textContent
= 'Créer mon compte';
btn.disabled
= false;
return;
}
// Create profile in DB
const
colors =
['#2c3e50','#8e6d3e','#5d4e8c','#1a5276','#7d3c98','#1e8449'];
const
color = colors[Math.floor(Math.random()*colors.length)];
const
{ error: profileError } = await sb.from('profiles').insert({
user_id: data.user.id,
first_name: first,
last_name: last,
email: email,
title: title || 'Consultant
indépendant',
bio: '',
domain: 'Management de projet / PMO',
experience: '5 – 10 ans',
skills: [],
sectors: [],
tjm: null,
work_mode: 'Hybride',
availability: 'Disponible maintenant',
city: '',
linkedin: '',
is_public: true,
color: color,
views: 0,
contacts: 0
});
if (profileError)
{ toast('Erreur lors de la création du profil: ' + profileError.message,
'error'); }
await
loadProfiles();
await
loadCurrentProfile();
updateNavAuth();
closeAuth();
renderEmailPreview({ first_name: first, last_name:
last, email, title: title||'Consultant indépendant',
availability: 'Disponible maintenant' });
toast('Compte créé !
Vérifiez votre e-mail pour confirmer votre inscription
📧',
'success');
showPage('dashboard');
btn.textContent
= 'Créer mon compte';
btn.disabled
= false;
}
async function doLogout() {
await
sb.auth.signOut();
showPage('home');
toast('Vous êtes
déconnecté(e)', 'info');
}
function updateNavAuth()
{
const
u = currentUser;
document.getElementById('guestNav').style.display = u ? 'none' : 'flex';
const
um = document.getElementById('userMenu');
um.style.display
= u ? 'flex' : 'none';
if (u && currentProfile) {
const
av = document.getElementById('navAvatar');
av.textContent
= (currentProfile.first_name||'?')[0]
+ (currentProfile.last_name||'?')[0];
av.style.background
= currentProfile.color || '#2c3e50';
}
}
// ═══════════════════════════════════════
// NAVIGATION
// ═══════════════════════════════════════
function showPage(name) {
document.querySelectorAll('.page').forEach(p
=> p.classList.remove('active'));
document.getElementById(name+'Page').classList.add('active');
window.scrollTo({ top: 0, behavior: 'smooth' });
if (name
=== 'home') renderHomeProfiles();
if (name
=== 'profiles') renderAllProfiles();
if (name
=== 'dashboard') renderDashboard();
}
// ═══════════════════════════════════════
// TOAST
// ═══════════════════════════════════════
let toastTimer;
function toast(msg, type='success') {
const
t = document.getElementById('toast');
t.textContent
= msg; t.className
= type + ' show';
clearTimeout(toastTimer);
toastTimer
= setTimeout(() => t.classList.remove('show'),
4000);
}
// ═══════════════════════════════════════
// PROFILE CARDS
// ═══════════════════════════════════════
function availClass(a) {
if (a==='Disponible
maintenant') return 'avail-now';
if (a==='Sous 1 mois')
return 'avail-soon';
return 'avail-open';
}
function availLabel(a) {
if (a==='Disponible
maintenant') return '● Disponible';
if (a==='Sous 1 mois')
return '● Sous 1 mois';
return '● À l\'écoute';
}
function makeCard(p) {
const
initials = ((p.first_name||'?')[0]+(p.last_name||'?')[0]).toUpperCase();
const
tags = (p.skills||[]).slice(0,3).map(s=>`<span class="tag">${s}</span>`).join('');
return `<div
class="profile-card" onclick="openProfileDetail('${p.id}')">
<div
class="profile-header">
<div
class="avatar" style="background:${p.color||'#2c3e50'}">${initials}</div>
<div><div
class="profile-name">${p.first_name} ${p.last_name}</div><div class="profile-title-card">${p.title||''}</div></div>
</div>
<div
class="profile-tags">${tags}${(p.sectors||[]).slice(0,1).map(s=>`<span
class="tag">${s}</span>`).join('')}</div>
<div
class="profile-footer">
<div
class="profile-rate">${p.tjm?p.tjm+'
€':'—'} <span>/
jour</span></div>
<span class="avail-badge
${availClass(p.availability)}">${availLabel(p.availability)}</span>
</div>
</div>`;
}
function renderHomeProfiles() {
const
grid = document.getElementById('homeProfilesGrid');
const
list = allProfiles.slice(0,3);
grid.innerHTML
= list.length ? list.map(makeCard).join('') : '<p style="color:var(--muted)">Aucun
profil pour l\'instant.</p>';
}
function renderAllProfiles(filter) {
if (filter !== undefined) currentFilter = filter;
const
grid = document.getElementById('allProfilesGrid');
let list
= allProfiles;
if (currentFilter
&& currentFilter !==
'all') {
list
= list.filter(p =>
(p.availability||'').includes(currentFilter)
||
(p.domain||'').includes(currentFilter)
||
(p.skills||[]).some(s=>s.includes(currentFilter))
||
(p.title||'').includes(currentFilter)
);
}
grid.innerHTML
= list.length ? list.map(makeCard).join('') : '<div style="color:var(--muted);padding:2rem;text-align:center;grid-column:1/-1">Aucun
consultant trouvé.</div>';
}
function filterProfiles(f,
el) {
document.querySelectorAll('.filter-chip').forEach(c => c.classList.remove('active'));
el.classList.add('active');
renderAllProfiles(f);
}
// ═══════════════════════════════════════
// PROFILE DETAIL
// ═══════════════════════════════════════
async function openProfileDetail(id) {
const
p = allProfiles.find(p => p.id === id);
if (!p)
return;
currentProfileId
= id;
// Increment views
await
sb.from('profiles').update({ views: (p.views||0)+1
}).eq('id', id);
p.views
= (p.views||0)+1;
const
initials = ((p.first_name||'?')[0]+(p.last_name||'?')[0]).toUpperCase();
document.getElementById('pdAvatar').style.background = p.color||'#2c3e50';
document.getElementById('pdAvatar').textContent
= initials;
document.getElementById('pdName').textContent
= `${p.first_name} ${p.last_name}`;
document.getElementById('pdTitle').textContent
= p.title||'';
document.getElementById('pdCity').textContent
= p.city ? '📍
'+p.city : '';
document.getElementById('pdExp').textContent
= p.experience ? '🏆
'+p.experience : '';
document.getElementById('pdRate').textContent
= p.tjm ? '💶 '+p.tjm+'
€/j' : '';
document.getElementById('pdAvailSpan').innerHTML
= `<span class="avail-badge
${availClass(p.availability)}">${availLabel(p.availability)}</span>`;
document.getElementById('pdBio').textContent
= p.bio || 'Aucune description renseignée.';
document.getElementById('pdTags').innerHTML
= (p.skills||[]).map(s=>`<span class="tag">${s}</span>`).join('');
document.getElementById('pdSectors').innerHTML
= (p.sectors||[]).map(s=>`<span class="tag">${s}</span>`).join('');
document.getElementById('pdSideRate').textContent
= p.tjm ? p.tjm+' € / jour'
: 'Non renseigné';
document.getElementById('pdMode').textContent
= p.work_mode||'—';
document.getElementById('pdDomain').textContent
= p.domain||'—';
document.getElementById('pdExpInfo').textContent
= p.experience||'—';
document.getElementById('contactModalSub').textContent
= `Envoyer une proposition à ${p.first_name}
${p.last_name}`;
showPage('profile');
}
// ═══════════════════════════════════════
// CONTACT
// ═══════════════════════════════════════
function openContact()
{ document.getElementById('contactModal').classList.add('open'); }
function closeContact()
{ document.getElementById('contactModal').classList.remove('open'); }
async function sendContact() {
const
name = document.getElementById('contactName').value.trim();
const
email = document.getElementById('contactEmail').value.trim();
const
msg = document.getElementById('contactMsg').value.trim();
if (!name||!email||!msg) { toast('Veuillez remplir
tous les champs','error'); return; }
// Save contact request in Supabase
await
sb.from('contact_requests').insert({ profile_id: currentProfileId, sender_name: name,
sender_email: email, message:
msg });
// Update contacts count
const
p = allProfiles.find(p => p.id === currentProfileId);
if (p) {
await
sb.from('profiles').update({ contacts: (p.contacts||0)+1 }).eq('id', currentProfileId);
p.contacts
= (p.contacts||0)+1;
}
closeContact();
toast('Message envoyé ! Le
consultant sera notifié par e-mail. ✓','success');
document.getElementById('contactName').value='';
document.getElementById('contactEmail').value='';
document.getElementById('contactMsg').value='';
}
// ═══════════════════════════════════════
// DASHBOARD
// ═══════════════════════════════════════
async function renderDashboard() {
if (!currentUser) { openAuth(); return; }
await
loadCurrentProfile();
const
p = currentProfile;
if (!p)
{
toast('Profil
introuvable, veuillez contacter le support.','error');
return;
}
const
initials = ((p.first_name||'?')[0]+(p.last_name||'?')[0]).toUpperCase();
document.getElementById('dashWelcome').textContent
= 'Bonjour '+p.first_name+' 👋';
const
av = document.getElementById('dashAvatar');
av.textContent
= initials;
av.style.background = p.color||'#2c3e50';
document.getElementById('dashName').textContent
= p.first_name+' '+p.last_name;
document.getElementById('dashTitlePreview').textContent
= p.title||'';
document.getElementById('dashAvailBadge').innerHTML
= `<span class="avail-badge
${availClass(p.availability)}">${availLabel(p.availability)}</span>`;
document.getElementById('statViews').textContent
= p.views||0;
document.getElementById('statContacts').textContent
= p.contacts||0;
// Completeness
let score = 0;
if(p.bio)
score+=20; if((p.skills||[]).length>0) score+=20;
if((p.sectors||[]).length>0)
score+=20; if(p.tjm) score+=20;
score += 20;
// base
document.getElementById('profileCompleteness').textContent
= score+'% complet';
// Fill form
document.getElementById('eFirst').value = p.first_name||'';
document.getElementById('eLast').value = p.last_name||'';
document.getElementById('ePhone').value = p.phone||'';
document.getElementById('eCity').value = p.city||'';
document.getElementById('eTitle').value = p.title||'';
document.getElementById('eBio').value = p.bio||'';
document.getElementById('eTJM').value = p.tjm||'';
document.getElementById('eLinkedin').value = p.linkedin||'';
document.getElementById('ePublic').checked
= p.is_public!==false;
setSelectVal('eDomain', p.domain);
setSelectVal('eExp', p.experience);
setSelectVal('eMode', p.work_mode);
document.querySelectorAll('#eSkillChips
.chip').forEach(c
=> c.classList.toggle('sel',(p.skills||[]).includes(c.textContent)));
document.querySelectorAll('#eSectorChips
.chip').forEach(c
=> c.classList.toggle('sel',(p.sectors||[]).includes(c.textContent)));
document.querySelectorAll('.toggle-opt').forEach(o => o.classList.toggle('sel', o.textContent.trim()===p.availability));
await
renderCVList();
await
renderActivity();
renderEmailPreview(p);
}
function setSelectVal(id,
val) {
const
s = document.getElementById(id);
for (let o of s.options) { if(o.value===val||o.text===val){s.value=o.value;break;} }
}
async function saveProfile() {
if (!currentUser || !currentProfile) return;
const
skills = [...document.querySelectorAll('#eSkillChips .chip.sel')].map(c=>c.textContent);
const
sectors = [...document.querySelectorAll('#eSectorChips .chip.sel')].map(c=>c.textContent);
const
selAvail = document.querySelector('.toggle-opt.sel');
const
updates = {
first_name: document.getElementById('eFirst').value.trim(),
last_name: document.getElementById('eLast').value.trim(),
phone: document.getElementById('ePhone').value.trim(),
city: document.getElementById('eCity').value.trim(),
title: document.getElementById('eTitle').value.trim(),
bio: document.getElementById('eBio').value.trim(),
domain: document.getElementById('eDomain').value,
experience: document.getElementById('eExp').value,
tjm: parseInt(document.getElementById('eTJM').value)||null,
work_mode: document.getElementById('eMode').value,
linkedin: document.getElementById('eLinkedin').value.trim(),
is_public: document.getElementById('ePublic').checked,
skills,
sectors,
availability: selAvail ? selAvail.textContent.trim() : currentProfile.availability,
updated_at: new Date().toISOString()
};
const
{ error } = await sb.from('profiles').update(updates).eq('id',
currentProfile.id);
if (error)
{ toast('Erreur lors de la sauvegarde:
'+error.message,'error'); return; }
Object.assign(currentProfile, updates);
// Update in allProfiles list too
const
idx = allProfiles.findIndex(p=>p.id===currentProfile.id);
if(idx>=0)
allProfiles[idx] = {...allProfiles[idx], ...updates};
// Log activity
await
sb.from('activities').insert({ profile_id:
currentProfile.id, text: 'Profil mis à jour', dot_color: '#c8a96e' });
renderDashboard();
toast('Profil enregistré
avec succès ✓','success');
}
// ═══════════════════════════════════════
// CV MANAGEMENT — SUPABASE
STORAGE
// ═══════════════════════════════════════
async function uploadCV(input) {
const
file = input.files[0];
if (!file)
return;
if (file.size > 10*1024*1024) {
toast('Fichier trop volumineux (max 10 Mo)','error'); return; }
if (!currentProfile) return;
toast('Upload
en cours…','info');
const
path = `${currentProfile.user_id}/${Date.now()}_${file.name}`;
const
{ error: uploadError } = await sb.storage.from('cvs').upload(path, file);
if (uploadError)
{ toast('Erreur upload: '+uploadError.message,'error'); return; }
const
{ data: urlData } = sb.storage.from('cvs').getPublicUrl(path);
await
sb.from('documents').insert({
profile_id: currentProfile.id,
name: file.name,
size: formatSize(file.size),
file_type: file.name.split('.').pop().toUpperCase(),
storage_path: path,
url: urlData.publicUrl
});
await
sb.from('activities').insert({ profile_id:
currentProfile.id, text: 'CV déposé : '+file.name, dot_color: '#2e7d32' });
await
renderCVList();
await
renderActivity();
toast('CV
"'+file.name+'" déposé avec succès 📄','success');
input.value='';
}
async function deleteCV(docId, path)
{
await
sb.storage.from('cvs').remove([path]);
await
sb.from('documents').delete().eq('id',
docId);
await
sb.from('activities').insert({ profile_id:
currentProfile.id, text: 'Document supprimé', dot_color: '#c0392b' });
await
renderCVList();
await
renderActivity();
toast('Document supprimé','info');
}
async function renderCVList() {
if (!currentProfile) return;
const
{ data } = await sb.from('documents').select('*').eq('profile_id', currentProfile.id).order('created_at', { ascending: false
});
const
list = document.getElementById('cvList');
if (!data
|| !data.length)
{ list.innerHTML='<p
style="font-size:.82rem;color:var(--muted);text-align:center">Aucun
document déposé</p>'; return;
}
list.innerHTML
= data.map(cv=>`
<div
class="cv-item">
<div
class="cv-icon">${cv.file_type==='PDF'?'📕':'📘'}</div>
<div
class="cv-info"><div class="cv-name">${cv.name}</div><div class="cv-meta">${cv.file_type}
· ${cv.size} · ${new Date(cv.created_at).toLocaleDateString('fr-FR')}</div></div>
<div
class="cv-actions">
<a
href="${cv.url}" target="_blank" class="btn btn-sm btn-ghost">⬇</a>
<button class="btn btn-sm btn-danger" onclick="deleteCV('${cv.id}','${cv.storage_path}')">✕</button>
</div>
</div>`).join('');
}
async function renderActivity() {
if (!currentProfile) return;
const
{ data } = await sb.from('activities').select('*').eq('profile_id', currentProfile.id).order('created_at', { ascending: false
}).limit(6);
const
feed = document.getElementById('activityFeed');
if (!data||!data.length)
{ feed.innerHTML='<p
style="font-size:.82rem;color:var(--muted)">Aucune
activité</p>'; return;
}
feed.innerHTML
= data.map(a=>{
const
date = new Date(a.created_at);
const
diff = Math.round((Date.now()-date)/60000);
const
timeStr = diff<1?"À
l'instant":diff<60?`Il y a ${diff} min`:diff<1440?`Il y a ${Math.round(diff/60)}h`:`Le ${date.toLocaleDateString('fr-FR')}`;
return `<div
class="activity-item">
<div
class="activity-dot" style="background:${a.dot_color||'#c8a96e'}"></div>
<div><div
class="activity-text">${a.text}</div><div
class="activity-time">${timeStr}</div></div>
</div>`;
}).join('');
}
function renderEmailPreview(p) {
document.getElementById('emailPreview').innerHTML
= `
<div class="email-header-bar"><div class="email-logo">Wade <span>Advisory</span></div></div>
<div class="email-body">
<div class="email-greeting">Bienvenue,
${p.first_name} ! 🎉</div>
<p class="email-text">Votre
compte a été créé avec succès sur Wade Advisory. Votre profil est désormais
visible par des centaines d'entreprises à la recherche de consultants.</p>
<div class="email-box">
<div
class="email-box-row"><span class="email-box-label">Nom</span><span class="email-box-value">${p.first_name} ${p.last_name}</span></div>
<div
class="email-box-row"><span class="email-box-label">E-mail</span><span class="email-box-value">${p.email}</span></div>
<div
class="email-box-row"><span class="email-box-label">Profil</span><span class="email-box-value">${p.title||'Consultant indépendant'}</span></div>
<div
class="email-box-row"><span class="email-box-label">Disponibilité</span><span class="email-box-value">${p.availability||'—'}</span></div>
</div>
<p class="email-text">Complétez
votre profil en ajoutant vos compétences et en déposant votre CV pour maximiser
vos chances d'être contacté.</p>
<p class="email-footer-text">Wade Advisory · La plateforme des consultants
indépendants<br>Cet e-mail
a été envoyé suite à la création de votre compte.</p>
</div>`;
}
// ═══════════════════════════════════════
// DRAG & DROP
// ═══════════════════════════════════════
function handleDragOver(e) { e.preventDefault();
document.getElementById('dashDropZone').classList.add('drag-over'); }
function handleDragLeave(e) { document.getElementById('dashDropZone').classList.remove('drag-over'); }
function handleDrop(e) {
e.preventDefault();
document.getElementById('dashDropZone').classList.remove('drag-over');
const
file = e.dataTransfer.files[0];
if (file) uploadCV({ files:[file],
value:'' });
}
// ═══════════════════════════════════════
// UI HELPERS
// ═══════════════════════════════════════
function toggleChip(el) { el.classList.toggle('sel'); }
function selectToggle(el) {
el.closest('.toggle-group').querySelectorAll('.toggle-opt').forEach(o=>o.classList.remove('sel'));
el.classList.add('sel');
}
function formatSize(b) {
if(b<1024) return b+'o';
if(b<1048576) return Math.round(b/1024)+'Ko';
return (b/1048576).toFixed(1)+'Mo';
}
// ═══════════════════════════════════════
// START
// ═══════════════════════════════════════
init();
</script>
</body>
</html>