feat: add Design-System ThemeProvider with accessible light/dark mode (Closes #30) - #38
Open
waterWang wants to merge 92 commits into
Open
feat: add Design-System ThemeProvider with accessible light/dark mode (Closes #30)#38waterWang wants to merge 92 commits into
waterWang wants to merge 92 commits into
Conversation
still fixing
The booking screen's pay action now calls the real client/bookAppointment
endpoint instead of stubbing the confirmation.
- On pay: read clientId from localStorage ("userId"); if absent, redirect
to /login. Otherwise POST { scheduleTime, category, clientId } via the
existing bookingApi, combining the picked date + slot into scheduleTime
and sending the API category enum (e.g. ELECTRICAL).
- Loading + inline error states on the button; success (response.status)
shows the escrow-locked confirmation.
- Pass apiCategory from the route; drop an unused useMemo import.
Scope note: this books the appointment only. The Soroban escrow contract
is still not integrated (per guildworkman-api "not yet wired up"), so funds
aren't actually locked on-chain yet — the confirmation copy reflects intent.
Point NEXT_PUBLIC_API_BASE_URL at a running backend to exercise it (the
default onrender URL currently 404s; the real API is the Spring service).
The backend Category enum is BEAUTY_CARE (underscore); we were sending
"BEAUTY CARE" (space), which Jackson can't map to the enum — booking a
Beauty pro would 400. Verified all six apiCategory values against
guildworkman-api's Category enum; only Beauty was wrong.
Found by checking the request against the backend controller/DTO source
(scheduleTime LocalDateTime accepts our "YYYY-MM-DDThh:mm"; ApiResponse
is {data, status} so the res.status success check is correct; clientId
is a Long — the localStorage userId must be numeric, unchanged from the
existing flow).
Completes the identity migration: every remaining page now uses the real design-system tokens, and the legacy colour aliases are removed from globals.css. - Migrate login, signup, dashboard/profile, and the appointment screens (appoint/view/cancel/update/appMan) + their components (forms, CategoryButton, SkillCard, Input, Select, WalletButton): cream→sand, ink-900 bg→navy, ink-500→muted, ink-100→line, brand→terra/navy by context, gold-100/600→gold/gold-deep, success/error→ok/err. Dark split-panels use navy with white/70 text and a terracotta accent (active nav item, links, upload icon). - Delete orphaned CategorySection and TrustSection (replaced by the new landing sections; removed the last chain-* usage). - Remove all legacy --color-* aliases from globals.css; keep the font and shadow tokens. Verified: build + tsc pass; zero legacy token classes remain; login and dashboard checked visually.
…n-cleanup Migrate legacy routes off aliased tokens; remove aliases
The appointment endpoints in api.ts didn't match guildworkman-api. Fixed
them against the backend source, and documented the remaining backend gap.
- cancel/update: POST → PUT with the id in the `appointmentId` query param
(not a JSON body); update sends `{ status, ... }` as the body.
- delete: POST → DELETE with `?appointmentId=` + `{ appointment_Id }` body.
- viewAllAppointment: typed to the real `ApiResponse { data, status }`
wrapper; unwrap `data`.
- Generalised the fetch helper to support GET/POST/PUT/DELETE + optional body.
- types.ts: add ApiEnvelope, AppointmentStatus enum union, and the real
(thin) ViewAllAppointmentsResponse; fix UpdateAppointmentRequest; the
Appointment interface now holds the richer shape the redesign will use.
- Legacy screens made honest: /view is read-only over the single record the
API returns; /cancel and /update take a manual appointment-id input (since
the list returns no ids yet) so the corrected calls are actually usable.
Adds docs/appointments-api-spec.md: viewAllAppointment must return a LIST of
appointments with id/status/scheduleTime/category/amount/worker to unblock the
account/appointments redesign; optional COMPLETED status for escrow release.
Mutation endpoints need no change — the frontend now matches them.
Verified: tsc + next build pass. End-to-end still needs a running backend.
…ment-api Correct appointment API client to match backend + appointments-API spec
- globals.css: dark token values under `prefers-color-scheme: dark` and `:root[data-theme="dark"]`, with an explicit `[data-theme="light"]` restore so a user choice wins over the OS in both directions. Flips the neutrals (bg/surface/ink/muted/line) plus the brand tokens that need it for dark legibility (navy-2 -> light periwinkle, navy-tint, gold-deep, ok, err); navy/gold/terra grounds stay constant. Sets color-scheme. - ThemeToggle: sun/moon control that stamps data-theme on <html> and persists to localStorage; defaults to the system preference. - layout: inline pre-paint script applies the stored/OS theme with no flash; suppressHydrationWarning on <html>. Toggle added to the navbar. - Fix hardcoded `bg-white` on form/neutral surfaces (Input, Select, CategoryButton, WalletButton dropdown) -> `bg-surface` so they flip; kept intentional light-on-dark-brand surfaces. Components use tokens, so they adapt automatically. Verified in dark via headless screenshots: landing, browse, login, dashboard. Build passes.
Add dark theme (system default + persistent toggle)
The backend + Soroban contracts now live in one repo, renamed guildworkman-api -> guildworkman-core (backend at backend-api/, contracts at soroban-contracts/); guildworkman-contracts is being archived. - README: ecosystem is now two repos (guildworkman-web + guildworkman-core); repointed backend/contract links and prose. - appointments-api-spec + api.ts/types.ts comments: reference the backend by its new home. Left alone: config.ts's NEXT_PUBLIC_API_BASE_URL default — that's a host URL, not a repo name, and unaffected by the rename.
…-refs docs: repoint references to guildworkman-core
Add the production URL (guildworkman-web.vercel.app) at the top of the README and in the Deployment section, and note that production builds from dev. Also flag the known mismatch: the backend's CORS is scoped to guildworkman.vercel.app, which is not the live frontend origin (guildworkman-web.vercel.app), so restricted endpoints can be blocked in prod until the backend allows the correct origin.
docs: add live Vercel link to the README
…ickers
viewAllAppointment now returns a LIST of appointments, each with an id, status,
amount and worker (guildworkman-core#10). Type and use that shape.
- viewAllAppointmentApi is typed ApiEnvelope<ViewAllAppointmentsResponse[]>, and
ViewAllAppointmentsResponse gains id, status, amount and a worker summary.
- ViewAllAppointments renders every appointment, soonest first, with a status
pill, the pro's name and the escrowed amount — instead of the single
time+category record it could show before.
- CancelAppointmentForm and UpdateAppointmentForm drop the "type the appointment
id" workaround and pick from the real list. That input only existed because the
API gave clients no way to learn an appointment's id; it does now.
- BookingScreen sends the escrow total as `amount`. It still can't send
skilledWorkerId: the browse list is sample data whose ids ("gw-chidi") aren't
the backend's numeric SkilledWorker ids, so that waits on fetching real workers.
- Shared helpers in lib/appointments.ts: schedule-time formatting (the API sends a
zoneless LocalDateTime), category formatting (BEAUTY_CARE -> "Beauty Care"),
status tones, and the signed-in client id.
Verified against a live backend: booked three appointments, and /view, /cancel and
/update all render real data — including the accept button correctly disabled on an
already-accepted job, and "No pro assigned yet" for a booking with no worker.
tsc clean, next build passes.
…s-list feat: consume the appointments list, and restore the cancel/respond pickers
Export the approved Identity System v1 marks as SVG masters plus 4x PNGs: the primary/stacked/reversed lockups, the North Star (full-colour, one-colour, and app-icon tile), and the Adinkra tile and glyph. Wordmarks are set in Inter 800 to match the Logo component, so exported art and rendered UI agree. public/brand/study/ archives the superseded exploration routes (Forge, Chevron, Guild Seal) in the old Workman Labs steel/amber palette, for reference only. The Chevron's canvas is widened to 748 (from 640) because the source viewBox clipped the wordmark's final letter at x=668.9; no artwork coordinates moved.
The Design system section still described the pre-redesign identity (terracotta primary, Fraunces headings) and the Web3 section pointed at TrustSection.tsx, which the legacy-token cleanup deleted. Update both to match what ships: navy/gold/terracotta on sand, Inter throughout, the dark-theme token flip, the added Button `gold` variant and Badge `navy` tone, and the homepage sections that now carry the trust copy.
The workman-chevron lockup is 748x300 (2.5:1), so a square avatar crop truncates it, and the wordmark is unreadable at the 20-40px sizes GitHub renders in listings. Recompose the chevron mark alone, centered on a 512x512 ink tile, with the steel stroke lifted for contrast on the dark ground so it reads on both GitHub themes.
The app still shipped Next's default favicon. Generate the icon set from the North Star mark and wire it up via App Router file conventions (favicon.ico, icon.svg, apple-icon.png, manifest.ts) so no manual link tags are needed. favicon.ico carries 16/32/48. The 16px member drops the verified check, per the identity system's own rule that the check is illegible under 20px; it returns at 32px and up. The apple and manifest tiles are full-bleed (the OS applies its own mask) with the star inside the central 60% so it survives the maskable safe-zone crop.
Replace the steel/amber chevron avatar with the North Star on a navy tile. The chevron version used the rejected palette and shares its peak geometry with the current lockup, so as an org avatar it read as a mis-coloured GuildWorkman logo rather than a distinct mark. The North Star is the mark the identity system designates for icon use, and it stays legible at the 20-40px sizes GitHub renders. The avatar moves out of study/ (now purely an archive, nothing live in it) and into brand/ alongside the rest of the current marks.
The project's history begins in 2024; the notice should run from first publication to the current year, not from when the LICENSE file was added.
* feat(booking): add provider→visitor timezone conversion + client-side slot locking libs
- lib/timezone.ts: converts provider-local (Africa/Lagos) wall-clock
slot times to the visitor's browser timezone using Intl, handling
DST-safe offset lookups and cross-midnight day drift.
- lib/slotLock.ts: localStorage + BroadcastChannel backed soft-lock
with a 5-minute TTL, to stop a visitor double-booking themselves
across tabs. Documented as a frontend-only mitigation pending a
backend per-worker availability endpoint (see module doc comment).
* fix(booking): anchor calendar 'today' to Africa/Lagos instead of host clock
buildDates() previously used the server process's own local Date(),
which on a serverless host (typically UTC) can compute the wrong
'today' — and therefore the wrong 7-day window — around midnight WAT.
Now derives it from todayIsoInZone(PROVIDER_TIME_ZONE).
* feat(booking): timezone-aware calendar UI + slot-lock integration
- Show each time slot converted to the visitor's local timezone, with
a +1/-1 badge when the conversion crosses midnight, plus a 'shown in
your time / provider is in Lagos' banner when they differ.
- Acquire a soft lock on the selected slot on selection and release it
on change/unmount; disable + label slots ('Held') that are locked by
another tab for this worker/date/time; re-check the lock immediately
before submitting payment.
- Reflect the visitor-local time on the escrow summary and the
confirmation screen too, not just the picker.
* chore: add npm run typecheck script; document timezone/slot-lock decisions
CI already ran npx tsc --noEmit directly and already had npm dependency
caching configured (actions/setup-node cache: npm), so no CI workflow
changes were needed for those two task items — added the typecheck
script for local parity with what CI runs. README's Known limitations
section now documents the Lagos-anchored timezone approach and the
frontend-only scope of slot locking, per the issue's request to record
new architectural decisions.
* feat(booking): timezone-aware calendar + client-side slot locking
* test(booking): add unit tests for timezone + slot-lock logic; fix day-offset bug the tests caught
---------
Co-authored-by: GuildWorkman Contributor <dev@example.com>
… links (workman-labs#35) Provide a centralized notification context for transaction toasts with retry capabilities and deep links to relevant escrow/booking pages. - Add TransactionNotification type system with factory helpers (success, error, pending, info) in lib/notifications.ts - Implement React Context-based NotificationProvider with auto-dismiss timers (5s success/info, 10s error/pending) - Create floating toast stack (bottom-right) with animated entrance, per-type icons, retry button, and deep-link navigation - Build notification center dropdown (bell icon) with unread count badge, scrollable list, relative timestamps, and "Clear all" action - Integrate provider + toast into root layout and bell icon into navbar - Wire retry/deep-link actions into both toast and dropdown surfaces Co-authored-by: Maverick <151352447+meshackyaro@users.noreply.github.com>
Adds a progressive, multi-step identity verification flow at /verify-identity: personal details -> document type -> document upload -> selfie -> review & submit. Each step validates before advancing, later steps disclose progressively (e.g. a "back of document" upload only appears for document types that have one), and in-progress answers auto-save to localStorage so the flow can be resumed after a reload. Architectural notes (documented in src/lib/identityVerification.ts): - guildworkman-core has no identity-verification endpoint yet, so submitIdentityVerification() simulates the network round trip (latency + occasional failure, to exercise the error-recovery UI) rather than inventing a REST shape the backend can't answer. - Uploaded files (ID photos, selfie) are intentionally never written to localStorage — only their filenames persist for the resume summary. Resuming past the upload/selfie steps asks the user to re-attach, trading a little resume friction for not storing sensitive images client-side. No new dependencies; built on the existing react-hook-form + react-dropzone stack already used by ProfileForm. Closes workman-labs#33
|
@water is attempting to deploy a commit to the Meshack Yaro 's projects Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the Design-System Theme Provider (navy/gold/terracotta) with accessible light and dark modes persisted per user, as described in issue #30.
Changes
New
ThemeProvider(src/components/ThemeProvider.tsx) — React Context provider that:theme(light/dark),toggleTheme, andsetThemevia theuseTheme()hooklocalStorageon mount, falling back toprefers-color-schemedata-themeon<html>and setscolor-schemefor native UI controlslayout.tsx(no FOUC — the inline script runs before any React hydration)Refactored
ThemeToggle— now usesuseTheme()from the provider instead of managing its own state and directly manipulatinglocalStorage/DOMUpdated
layout.tsx— wraps the app content with<ThemeProvider>Design decisions
NotificationProvider/useNotificationsglobals.css(navy/gold/terracotta identity system) — no new CSS variables neededthemeScriptinlayout.tsxis preserved for the flash-of-wrong-theme prevention; the provider reads the samelocalStoragekey so it stays in syncVerification
npm run typecheck— passesnpm run lint— 0 errors, 0 new warningsnpm run build— production build succeedsnpm test— 35/35 tests pass