Commit de0573c
refactor(profile,ui): flat grouped profile edit + component library dedup (#116)
* refactor(profile): flatten edit-profile into one grouped page, no tabs
Replaced the Profile/Preferences tab shell (NyuchiProfileSettings)
with a single continuously-scrollable page -- every section (Avatar,
Name, Personal details, Location, Interests, Notifications, Language,
Appearance) is always visible and directly editable, grouped into
clearly labelled cards. Matches the flat, grouped-and-labelled
settings pattern the rest of the ecosystem uses, instead of hiding
half the form behind a tab switch.
* refactor(ui): consolidate avatar-initials logic onto one shared util
Nine components each reimplemented "initials from a name" slightly
differently -- some skipped .trim()/.toUpperCase(), split on a literal
space instead of /\s+/, or ordered slice/uppercase differently, so the
same name could render different initials depending which component
showed it. Added src/lib/avatar-initials.ts (getInitials, with tests)
as the one shared implementation and pointed every call site at it,
including mappers.ts's existing initialsFromName (now a thin
delegate, keeping its stable name/signature for its own callers).
* refactor(ui): NyuchiReviewCard uses the shared Rating component
Replaced its own inline Array.from({length:5}).map(...) star loop
(the only place in the app duplicating what src/components/ui/
rating.tsx already does) with <Rating readOnly size="sm" />.
* refactor(ui): trim useNyuchiHarness to the fields components actually use
Audited all 27 harness consumers: theme/locale/reportHealth/
announceUrgent/prefersReducedMotion were computed and returned on
every hook call but never read by a single one. theme was the
expensive one -- a MutationObserver on document.documentElement
stood up per mount across all 27 components purely to produce a
value nobody consumed. announceUrgent was also just announce under
another name; there is no assertive live region anywhere to justify
a separate "urgent" path.
useNyuchiHarness now returns { log, motion, animStyle, announce } --
the four fields real call sites destructure. Left NyuchiHarness (the
declarative wrapper) alone: it has its own passing tests and is
documented as one of the harness's two entry points, so removing it
outright is a bigger call than trimming a hook's dead return fields.
* feat(profile): split preferences into its own page using NyuchiProfileSettings
Adopts NyuchiProfileSettings for real -- the one nyuchi-* component
with zero call sites anywhere in the app. Its shape (section sidebar +
active content + sticky save bar) doesn't fit /profile/edit's flat
identity form, but it's exactly right for app-behavior preferences
(notifications, language, appearance), which now live at their own
/profile/preferences route with three real, switchable sections.
/profile/edit keeps only identity fields (avatar, name, personal
details, location, interests) and links out to the new page. The
/profile hub's menu splits into distinct "Profile" and "Preferences"
sections pointing at the right destination.
* refactor(profile): promote AvatarPicker to a harness-wired nyuchi component
Converts the plain AvatarPicker into NyuchiAvatarPicker: wired through
useNyuchiHarness for motion/announce, matching every other branded
component in the library. /profile/edit is the only consumer.
No canonical avatar-picker exists in the Mzizi registry (confirmed via
search) — filed as a new-component proposal via component feedback
(issue #28) so the ecosystem gets one shared implementation instead of
each app rebuilding the upload/Gravatar/sticker picker independently.
* refactor(profile): consolidate edit + preferences into one sidebar-tab settings page
Undoes the earlier two-page split (/profile/edit + /profile/preferences).
Per feedback, everything belongs in one "profile settings" page with a
sidebar tab experience (Profile / Location / Interests / Notifications /
Language / Appearance) — the pattern most settings surfaces (Claude,
Slack, etc.) use — rather than separate pages or components.
/profile/edit now renders NyuchiProfileSettings with all six sections and
a single save action that diffs every field (identity + notifications +
locale) into one updateMyProfile call. The profile hub's Language/Event
update rows deep-link via `?section=`, read once on mount (matches the
existing /search `?q=` pattern — no useSearchParams Suspense boundary).
/profile/preferences is removed.
* fix(mobile): resolve two mobile-first defects in the new settings page
The global floating MobileBottomNav (a fixed pill bar) already had an
exclusion list for pages with their own sticky bottom action bar
(/events/create, /signage, /kiosk, /manage) — /profile/edit was missing
from it, so on mobile the floating nav sat on top of the avatar sticker
grid and collided with the settings page's own Save/Cancel bar.
Also: NyuchiProfileSettings' section tabs live in a horizontally-
scrolling row on mobile. Deep-linking into a section past the first
screenful (e.g. /profile/edit?section=language, used by the profile
hub's Language/Notifications rows) rendered the right content with no
matching tab visible or highlighted. The active tab now scrolls itself
into view on mount/change, honoring reduced-motion.
* style(mobile-nav): dock the bottom bar to the screen edge, drop the floating pill
The primary mobile nav (Home/Discover/Calendar/My Events/Profile) was a
floating rounded pill with side margins and a gap above the safe area.
Restyled to a standard docked, full-width bottom tab bar flush to the
edge — matching how Instagram/TikTok/X render their own primary nav —
instead of introducing a second, competing "floating vs. dashboard" nav
idiom. Same items, same active-state logic, same hidden-path rules.
---------
Co-authored-by: Bryan Fawcett <noreply@anthropic.com>1 parent 680fac5 commit de0573c
21 files changed
Lines changed: 355 additions & 412 deletions
File tree
- src
- app/profile
- edit
- components
- layout
- ui
- lib
- mongo
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
190 | 190 | | |
191 | 191 | | |
192 | 192 | | |
193 | | - | |
| 193 | + | |
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| |||
211 | 211 | | |
212 | 212 | | |
213 | 213 | | |
214 | | - | |
| 214 | + | |
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
| |||
0 commit comments