Skip to content

Commit 2a0dfd4

Browse files
committed
Suite cohérence : contributors, App.tsx 404, onboarding, profil-edit
1 parent b861f64 commit 2a0dfd4

2 files changed

Lines changed: 36 additions & 54 deletions

File tree

src/components/onboarding/onboarding-stepper.tsx

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ export function OnboardingStepper() {
7373
const [techSearch, setTechSearch] = useState("");
7474

7575

76-
// Initial form state — pulls from the saved draft first (so a network blip
77-
// doesn't lose the user's input), then falls back to the existing profile,
78-
// then to sane defaults. Hydration runs once.
7976
const draft = useMemo(() => (user ? loadDraft(user.id) : null), [user]);
8077

8178
const DRAFT_KEY = `onboarding_draft_${user?.id}`;
@@ -158,7 +155,6 @@ export function OnboardingStepper() {
158155
metadata.preferred_username ||
159156
`user_${user.id.slice(0, 8)}`;
160157

161-
// upsert (not update) so a fresh user without a row still saves.
162158
await upsertProfile(user.id, {
163159
username,
164160
full_name: fullName.trim(),
@@ -176,7 +172,6 @@ export function OnboardingStepper() {
176172
if (refreshed) setProfile(refreshed);
177173
setIsNewUser(false);
178174

179-
// Nettoyer les brouillons dans localStorage
180175
localStorage.removeItem(`${DRAFT_KEY}_fullName`);
181176
localStorage.removeItem(`${DRAFT_KEY}_bio`);
182177
localStorage.removeItem(`${DRAFT_KEY}_city`);
@@ -186,8 +181,8 @@ export function OnboardingStepper() {
186181
localStorage.removeItem(`${DRAFT_KEY}_openToCollaboration`);
187182
localStorage.removeItem(`${DRAFT_KEY}_avatarUrl`);
188183

189-
toast.success("Bienvenue sur BisoMapTech Map !");
190-
navigate("/");
184+
toast.success("Bienvenue sur BisoMapTech !");
185+
navigate(`/contributeurs/${username}`, { replace: true });
191186
} catch (error) {
192187
const message =
193188
error instanceof Error
@@ -211,7 +206,6 @@ export function OnboardingStepper() {
211206

212207
return (
213208
<div className="flex min-h-screen">
214-
{/* ---- LEFT BRANDING PANEL (desktop only) ---- */}
215209
<div className="relative hidden lg:flex lg:w-5/12 flex-col justify-between overflow-hidden border-r border-white/10 p-12">
216210
<div className="pointer-events-none absolute inset-0">
217211
<div className="absolute right-0 top-0 h-80 w-80 -translate-y-1/3 translate-x-1/3 rounded-full bg-primary/10 blur-[80px]" />
@@ -240,8 +234,7 @@ export function OnboardingStepper() {
240234
<span className="text-primary">Join the ecosystem.</span>
241235
</h1>
242236
<p className="mt-5 text-base leading-relaxed text-muted-foreground max-w-sm">
243-
Positionnez-vous dans l'annuaire des développeurs de la République du Congo — dev, sysadmin,
244-
cybersécurité, cloud, IA, mobile, embarqué, support IT et plus.
237+
Positionnez-vous dans l'annuaire des développeurs de la République du Congo.
245238
</p>
246239
</div>
247240

@@ -262,20 +255,17 @@ export function OnboardingStepper() {
262255
</div>
263256
</div>
264257

265-
{/* ---- RIGHT FORM PANEL ---- */}
266258
<div className="flex flex-1 items-start justify-center overflow-y-auto px-4 py-8 sm:px-8 lg:items-center">
267259
<div className="pointer-events-none fixed left-1/2 top-1/2 h-96 w-96 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary/5 blur-[100px]" />
268260

269261
<div className="relative z-10 w-full max-w-xl">
270-
{/* Mobile branding */}
271262
<div className="mb-8 flex items-center gap-2 text-primary lg:hidden">
272263
<MapPin className="h-5 w-5" />
273264
<span className="text-base font-bold tracking-tight text-foreground">
274265
BisoMapTech
275266
</span>
276267
</div>
277268

278-
{/* Form header */}
279269
<div className="mb-8">
280270
<h2 className="text-2xl font-bold tracking-tight text-foreground lg:text-3xl">
281271
Rejoindre <span className="text-primary">l'écosystème</span>
@@ -291,7 +281,6 @@ export function OnboardingStepper() {
291281
</div>
292282

293283
<div className="glass-panel space-y-6 rounded-2xl border border-white/10 p-6 sm:p-8">
294-
{/* Avatar — real upload */}
295284
{user && (
296285
<div className="flex flex-col items-center">
297286
<AvatarUploader
@@ -307,7 +296,6 @@ export function OnboardingStepper() {
307296
</div>
308297
)}
309298

310-
{/* Full name */}
311299
<div className="space-y-1.5">
312300
<Label
313301
htmlFor="onb-name"
@@ -324,7 +312,6 @@ export function OnboardingStepper() {
324312
/>
325313
</div>
326314

327-
{/* Role — all 15 disciplines */}
328315
<div className="space-y-1.5">
329316
<Label className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
330317
Rôle principal
@@ -348,7 +335,6 @@ export function OnboardingStepper() {
348335
</div>
349336
</div>
350337

351-
{/* City */}
352338
<div className="space-y-1.5">
353339
<Label className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
354340
Localisation
@@ -367,7 +353,6 @@ export function OnboardingStepper() {
367353
</Select>
368354
</div>
369355

370-
{/* Bio */}
371356
<div className="space-y-1.5">
372357
<div className="flex items-center justify-between">
373358
<Label
@@ -390,7 +375,6 @@ export function OnboardingStepper() {
390375
/>
391376
</div>
392377

393-
{/* Tech stack */}
394378
<div className="space-y-3">
395379
<Label className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
396380
Technologies
@@ -406,7 +390,6 @@ export function OnboardingStepper() {
406390
/>
407391
</div>
408392

409-
{/* Selected stack */}
410393
{techStack.length > 0 && (
411394
<div className="flex flex-wrap gap-1.5">
412395
{techStack.map((tech) => (
@@ -423,7 +406,6 @@ export function OnboardingStepper() {
423406
</div>
424407
)}
425408

426-
{/* Tech chips — full taxonomy */}
427409
{filteredAllTechs ? (
428410
<div className="flex max-h-40 flex-wrap gap-1.5 overflow-y-auto">
429411
{filteredAllTechs.map((tech) => (
@@ -436,8 +418,7 @@ export function OnboardingStepper() {
436418
))}
437419
{filteredAllTechs.length === 0 && (
438420
<p className="text-xs text-muted-foreground/70">
439-
Aucune technologie trouvée. Sélectionnez "Autre" comme rôle pour
440-
les profils atypiques.
421+
Aucune technologie trouvée.
441422
</p>
442423
)}
443424
</div>
@@ -464,7 +445,6 @@ export function OnboardingStepper() {
464445
)}
465446
</div>
466447

467-
{/* Experience */}
468448
<div className="space-y-2">
469449
<Label className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
470450
Niveau d'expérience
@@ -488,7 +468,6 @@ export function OnboardingStepper() {
488468
</div>
489469
</div>
490470

491-
{/* Collaboration */}
492471
<button
493472
type="button"
494473
onClick={() => setOpenToCollaboration(!openToCollaboration)}
@@ -520,7 +499,6 @@ export function OnboardingStepper() {
520499
</div>
521500
</button>
522501

523-
{/* Validation summary — explicit so the user knows what's missing */}
524502
{!canSubmit && (
525503
<div className="rounded-lg border border-amber-500/25 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-300">
526504
{!fullName.trim() && <p>• Renseignez votre nom complet</p>}
@@ -531,7 +509,6 @@ export function OnboardingStepper() {
531509
</div>
532510
)}
533511

534-
{/* Submission error with retry */}
535512
{submitError && (
536513
<div
537514
role="alert"
@@ -562,10 +539,8 @@ export function OnboardingStepper() {
562539
</div>
563540
)}
564541

565-
{/* Divider */}
566542
<div className="h-px bg-white/10" />
567543

568-
{/* Actions */}
569544
<div className="flex items-center justify-between">
570545
<button
571546
type="button"

src/pages/contributors-page.tsx

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,20 @@ import { Skeleton } from "@/components/ui/skeleton";
2929
import { FilterPanel } from "@/components/filters/filter-panel";
3030
import { useFilteredProfiles } from "@/hooks/use-filtered-profiles";
3131
import { useFilterStore } from "@/store/filter-store";
32-
import { ROLE_TYPE_LABELS, EXPERIENCE_LABELS, CROSS_DOMAIN_TECHS } from "@/lib/constants";
32+
import { ROLE_TYPE_LABELS, EXPERIENCE_LABELS } from "@/lib/constants";
3333
import { cn } from "@/lib/utils";
34-
import { useState } from "react";
34+
import React, { useState, useEffect } from "react";
3535

36-
// Suggestions de compétences couvrant tous les métiers de l'informatique
37-
// (dev, systèmes, cybersécurité, support, data, design…), pas uniquement le dev web.
38-
const QUICK_FILTERS = CROSS_DOMAIN_TECHS;
36+
const QUICK_FILTERS = [
37+
"React",
38+
"JavaScript",
39+
"Python",
40+
"Flutter",
41+
"Node.js",
42+
"TypeScript",
43+
"PHP",
44+
"Vue.js",
45+
];
3946

4047
function formatLastSeen(lastSeenAt?: string): string | null {
4148
if (!lastSeenAt) return null;
@@ -47,18 +54,15 @@ function formatLastSeen(lastSeenAt?: string): string | null {
4754
return null;
4855
}
4956

50-
function ProfileCard({ profile }: { profile: Profile }) {
57+
const ProfileCard = React.memo(function ProfileCard({ profile }: { profile: Profile }) {
5158
const lastSeen = formatLastSeen(profile.last_seen_at);
5259

5360
return (
5461
<Link to={`/contributeurs/${profile.username}`}>
5562
<article className="glass-panel group relative rounded-xl border border-white/10 p-5 transition-all hover:-translate-y-0.5 hover:border-primary/30 hover:shadow-[0_8px_24px_rgba(78,222,163,0.08)] active:scale-[0.99] cursor-pointer">
56-
{/* Ambient glow */}
5763
<div className="pointer-events-none absolute right-0 top-0 h-20 w-20 rounded-bl-full bg-primary/6 blur-xl transition-colors group-hover:bg-primary/12" />
5864

59-
{/* Header */}
6065
<div className="mb-4 flex items-start justify-between gap-3">
61-
{/* Avatar + badge disponible */}
6266
<div className="relative shrink-0">
6367
<Avatar className="h-14 w-14 border-2 border-white/15">
6468
<AvatarImage src={profile.avatar_url} alt={profile.full_name} />
@@ -76,7 +80,6 @@ function ProfileCard({ profile }: { profile: Profile }) {
7680
)}
7781
</div>
7882

79-
{/* Nom + rôle + ville */}
8083
<div className="flex-1 min-w-0">
8184
<h3 className="truncate text-sm font-bold leading-tight text-foreground group-hover:text-primary transition-colors">
8285
{profile.full_name}
@@ -88,15 +91,13 @@ function ProfileCard({ profile }: { profile: Profile }) {
8891
<MapPin className="h-3 w-3 shrink-0" />
8992
<span className="truncate">{profile.city || "Congo"}</span>
9093
</div>
91-
{/* Badge Disponible visible en texte */}
9294
{profile.open_to_collaboration && (
9395
<span className="mt-1.5 inline-flex items-center gap-1 rounded-full bg-primary/10 border border-primary/25 px-2 py-0.5 text-[11px] font-medium text-primary">
9496
Disponible
9597
</span>
9698
)}
9799
</div>
98100

99-
{/* Niveau d'expérience */}
100101
<Badge
101102
variant="outline"
102103
className="shrink-0 border-white/10 bg-white/5 text-xs text-muted-foreground"
@@ -105,7 +106,6 @@ function ProfileCard({ profile }: { profile: Profile }) {
105106
</Badge>
106107
</div>
107108

108-
{/* Techs + GitHub + activité */}
109109
<div className="flex flex-wrap items-center gap-1.5 border-t border-white/10 pt-3">
110110
{profile.tech_stack.slice(0, 3).map((tech) => (
111111
<span
@@ -143,13 +143,27 @@ function ProfileCard({ profile }: { profile: Profile }) {
143143
</article>
144144
</Link>
145145
);
146-
}
146+
});
147147

148148
export function ContributorsPage() {
149149
const { profiles, isLoading, total, page, totalPages, setPage } =
150150
useFilteredProfiles({ pageSize: 18 });
151151
const { techStack, searchQuery, setTechStack, setSearchQuery } = useFilterStore();
152152
const [mobileFilterOpen, setMobileFilterOpen] = useState(false);
153+
const [localSearch, setLocalSearch] = useState(searchQuery);
154+
155+
useEffect(() => {
156+
setLocalSearch(searchQuery);
157+
}, [searchQuery]);
158+
159+
useEffect(() => {
160+
const t = setTimeout(() => {
161+
if (localSearch !== searchQuery) {
162+
setSearchQuery(localSearch);
163+
}
164+
}, 300);
165+
return () => clearTimeout(t);
166+
}, [localSearch, searchQuery, setSearchQuery]);
153167

154168
if (isLoading && page === 1) {
155169
return (
@@ -166,7 +180,6 @@ export function ContributorsPage() {
166180

167181
return (
168182
<div className="mx-auto max-w-7xl px-4 pb-24 pt-6 md:pb-8">
169-
{/* Page header */}
170183
<div className="mb-5 flex items-end justify-between">
171184
<div>
172185
<h1 className="text-2xl font-bold tracking-tight text-foreground md:text-3xl">
@@ -187,18 +200,16 @@ export function ContributorsPage() {
187200
</Button>
188201
</div>
189202

190-
{/* Search bar */}
191203
<div className="relative mb-4">
192204
<Search className="absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
193205
<Input
194-
placeholder="Rechercher par nom, competence..."
206+
placeholder="Rechercher par nom, compétence..."
195207
value={searchQuery}
196208
onChange={(e) => setSearchQuery(e.target.value)}
197209
className="bg-white/5 border-white/10 pl-10 text-sm focus:border-primary focus:ring-1 focus:ring-primary/30"
198210
/>
199211
</div>
200212

201-
{/* Quick filter chips */}
202213
<div className="mb-6 flex flex-wrap gap-2">
203214
{QUICK_FILTERS.map((tech) => {
204215
const isActive = techStack.includes(tech);
@@ -225,7 +236,6 @@ export function ContributorsPage() {
225236
})}
226237
</div>
227238

228-
{/* Sidebar + grid */}
229239
<div className="grid gap-5 lg:grid-cols-[280px_1fr]">
230240
<aside className="hidden lg:block">
231241
<div className="sticky top-20">
@@ -234,7 +244,6 @@ export function ContributorsPage() {
234244
</aside>
235245

236246
<div>
237-
{/* Talent grid — large cards on desktop, list-style on mobile */}
238247
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
239248
{profiles.map((profile) => (
240249
<ProfileCard key={profile.id} profile={profile} />
@@ -243,13 +252,12 @@ export function ContributorsPage() {
243252
{profiles.length === 0 && !isLoading && (
244253
<div className="col-span-full py-20 text-center">
245254
<Handshake className="mx-auto mb-4 h-12 w-12 text-muted-foreground/30" />
246-
<p className="font-semibold text-muted-foreground">Aucun contributeur trouve</p>
255+
<p className="font-semibold text-muted-foreground">Aucun contributeur trouvé</p>
247256
<p className="mt-1 text-sm text-muted-foreground/60">Essayez de modifier vos filtres</p>
248257
</div>
249258
)}
250259
</div>
251260

252-
{/* Pagination */}
253261
{totalPages > 1 && (
254262
<div className="mt-8 flex items-center justify-center gap-3">
255263
<Button
@@ -260,7 +268,7 @@ export function ContributorsPage() {
260268
className="gap-1 border border-white/10 bg-white/5 hover:bg-white/8 disabled:opacity-30"
261269
>
262270
<ChevronLeft className="h-4 w-4" />
263-
Precedent
271+
Précédent
264272
</Button>
265273
<span className="text-xs text-muted-foreground">
266274
{page} / {totalPages}
@@ -280,7 +288,6 @@ export function ContributorsPage() {
280288
</div>
281289
</div>
282290

283-
{/* Mobile filter drawer */}
284291
{mobileFilterOpen && (
285292
<div className="fixed inset-0 z-[600] flex flex-col lg:hidden">
286293
<div className="flex-1" onClick={() => setMobileFilterOpen(false)} />
@@ -313,7 +320,7 @@ export function ContributorsPage() {
313320
className="w-full bg-primary text-primary-foreground hover:bg-primary/90"
314321
onClick={() => setMobileFilterOpen(false)}
315322
>
316-
Voir {profiles.length} resultat{profiles.length !== 1 ? "s" : ""}
323+
Voir {profiles.length} résultat{profiles.length !== 1 ? "s" : ""}
317324
</Button>
318325
</div>
319326
</div>

0 commit comments

Comments
 (0)