Leptos-based customer-facing portal SPA shell with module registration. Sibling to platform-staff-ui; lighter chrome (header + footer, no sidebar); public-by-default; signup + login + inquiry + me built in.
| Tier | platform |
| Role | unclassified (baselined) |
| Path | crates/platform/customer-ui |
| Edition | 2021 |
| Targets | platform_customer_ui |
| Public items | 70 across 14 modules |
| Tests | 6 |
What it is for
Leptos-based customer-facing portal SPA shell with module registration.
Sibling to platform-staff-ui. Same App { config, modules } shape but lighter chrome: a PublicLayout with header + footer (no sidebar), routes default-public (no auth gate), and built-in modules cover signup/login/forgot/verify/inquiry/me/delete_me.
Created in Sprint 10D (D-10D.x) per the forge chain plan (docs/archive/forge-legacy/backlog/userstories/sprint-10D-primitives-customer-ui-creation.md in rust-cms-engine). Initial skeleton ships:
CustomerApp+CustomerConfig+ModuleRegistryshapes.AuthProvider(cookie-based; shares the same session cookie
with platform-staff-ui per D-10D.5).
ApiClientdefaulting to/api/v1/customer/*per
Sprint 10B's contract (D-10B.1 + D-10D.4).
PublicLayout(header + content + footer; no sidebar).- Module skeletons for auth + me + contact + saved_searches +
notifications. Full page implementations land in follow-on sprints (Track 3 + Sprint 36+ verticals).
Capabilities
CustomerConfig
Leptos-based customer-facing portal SPA shell with module
| Item |
|---|
pub struct CustomerConfig |
CustomerConfig :: fn default() -> Self |
ApiClient
Customer-side API client. Mirrors platform-staff-ui's
| Item |
|---|
pub struct ApiClient |
ApiClient :: fn new(base_url : & str) -> Self |
ApiClient :: fn with_csrf_cookie(mut self, name : impl Into <String>) -> Self |
ApiClient :: fn with_unauthorized_handler(mut self, cb : Callback <()>) -> Self |
ApiClient :: fn csrf_cookie_name(& self) -> & str |
ApiClient :: fn base_url(& self) -> & str |
ApiClient :: async fn get <T : DeserializeOwned>(& self, path : & str) -> Result <T, ApiError> |
ApiClient :: async fn post <T : DeserializeOwned, B : Serialize>(& self, path : & str, body : & B,) -> Result <T, ApiError> |
ApiClient :: async fn patch <T : DeserializeOwned, B : Serialize>(& self, path : & str, body : & B,) -> Result <T, ApiError> |
ApiClient :: async fn delete_no_content(& self, path : & str) -> Result <(), ApiError> |
fn use_api() -> ApiClient |
attachments (other)
Customer-side attachments module — Sprint 18.C Phase 2
| Item |
|---|
fn MyUploadsPage() -> impl IntoView |
Attachment
Customer-side attachments module — Sprint 18.C Phase 2
| Item |
|---|
fn attachments_module() -> CustomerModule |
pub struct Attachment |
auth (other)
Customer authentication context + pages.
| Item |
|---|
fn AuthProvider(children : ChildrenFn) -> impl IntoView |
fn auth_module() -> CustomerModule |
fn LoginPage() -> impl IntoView |
fn SignupPage() -> impl IntoView |
fn ForgotPasswordPage() -> impl IntoView |
fn ResetPasswordPage() -> impl IntoView |
fn VerifyEmailPage() -> impl IntoView |
AuthContext
Customer authentication context + pages.
| Item |
|---|
pub struct AuthContext |
AuthContext :: fn mark_authenticated(& self) |
AuthContext :: fn mark_unauthenticated(& self) |
AuthContext :: fn finish_loading(& self) |
fn use_auth() -> AuthContext |
catalog (other)
Customer catalog module — Sprint 25.UI.
| Item |
|---|
fn catalog_module() -> CustomerModule |
fn CatalogBrowsePage() -> impl IntoView |
fn BasketPage() -> impl IntoView |
fn MyOrdersPage() -> impl IntoView |
contact (other)
Customer-side contact inquiry form — POSTs to
| Item |
|---|
fn contact_module() -> CustomerModule |
fn InquiryPage() -> impl IntoView |
downloads (other)
Customer-side downloads page — Sprint 26.
| Item |
|---|
fn DownloadsPage() -> impl IntoView |
Download
Customer-side downloads page — Sprint 26.
| Item |
|---|
fn downloads_module() -> CustomerModule |
pub struct Download |
layout (other)
<PublicLayout> — customer-side chrome.
| Item |
|---|
fn PublicLayout(children : ChildrenFn) -> impl IntoView |
me (other)
Customer "me" module — the customer-ui mount of the shared
| Item |
|---|
fn me_module() -> CustomerModule |
fn AccountPage() -> impl IntoView |
CustomerModule
Module registration system for the customer portal. Mirrors
| Item |
|---|
pub struct CustomerModule |
CustomerModuleBuilder
Module registration system for the customer portal. Mirrors
| Item |
|---|
pub struct CustomerModuleBuilder |
CustomerModuleBuilder :: fn new(id : impl Into <String>) -> Self |
CustomerModuleBuilder :: fn name(mut self, name : impl Into <String>) -> Self |
CustomerModuleBuilder :: fn nav_item(mut self, item : NavItem) -> Self |
CustomerModuleBuilder :: fn route(mut self, r : CustomerRoute) -> Self |
CustomerModuleBuilder :: fn build(self) -> CustomerModule |
CustomerRoute
Module registration system for the customer portal. Mirrors
| Item |
|---|
pub struct CustomerRoute |
CustomerRoute :: fn new <F>(path : impl Into <String>, view : F) -> Self where F : Fn() -> View + Send + Sync + 'static, |
ModuleRegistry
Module registration system for the customer portal. Mirrors
| Item |
|---|
pub struct ModuleRegistry |
ModuleRegistry :: fn new() -> Self |
ModuleRegistry :: fn register(mut self, module : CustomerModule) -> Self |
ModuleRegistry :: fn all_nav_items(& self) -> Vec <NavItem> |
ModuleRegistry :: fn modules(& self) -> & Arc <CustomerModule> |
ModuleRegistry :: fn all_routes(& self) -> Vec <(String, CustomerRoute)> |
fn provide_modules(registry : ModuleRegistry) |
fn use_modules() -> ModuleRegistry |
NavItem
Module registration system for the customer portal. Mirrors
| Item |
|---|
pub struct NavItem |
NavItem :: fn new(label : impl Into <String>, path : impl Into <String>) -> Self |
NavItem :: fn with_icon(mut self, icon : impl Into <String>) -> Self |
notifications (other)
Notification inbox module — customer-side display of system
| Item |
|---|
fn notifications_module() -> CustomerModule |
posts (other)
Customer-side blog — Sprint 12 (Track 3 customer-side surface).
| Item |
|---|
fn BlogIndex(# prop(into, optional) limit : Option <u32>, # prop(into, optional) mount_path : Option <String>,) -> impl IntoView |
fn PostDetail(# prop(into) slug : String) -> impl IntoView |
Post
Customer-side blog — Sprint 12 (Track 3 customer-side surface).
| Item |
|---|
fn posts_module() -> CustomerModule |
pub struct Post |
reviews (other)
Customer-side reviews widget — Sprint 14 (Track 3 customer-side
| Item |
|---|
fn ReviewsWidget(# prop(into, optional) limit : Option <u32>, # prop(into, optional) business_name : Option <String>,) -> impl IntoView |
Review
Customer-side reviews widget — Sprint 14 (Track 3 customer-side
| Item |
|---|
fn reviews_module() -> CustomerModule |
pub struct Review |
router (other)
<CustomerApp> — the main customer SPA entry point.
| Item |
|---|
fn CustomerApp(config : CustomerConfig, modules : ModuleRegistry) -> impl IntoView |
saved_searches (other)
Saved searches module — customer-side persistence of filter
| Item |
|---|
fn saved_searches_module() -> CustomerModule |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
platform_customer_ui
apiattachmentsauthcatalogcontactdownloadslayoutmemodulesnotificationspostsreviewsroutersaved_searches
flowchart TD n_platform_customer_ui["platform_customer_ui"] n_platform_customer_ui --> n_api["api"] n_platform_customer_ui --> n_attachments["attachments"] n_platform_customer_ui --> n_auth["auth"] n_platform_customer_ui --> n_catalog["catalog"] n_platform_customer_ui --> n_contact["contact"] n_platform_customer_ui --> n_downloads["downloads"] n_platform_customer_ui --> n_layout["layout"] n_platform_customer_ui --> n_me["me"] n_platform_customer_ui --> n_modules["modules"] n_platform_customer_ui --> n_notifications["notifications"] n_platform_customer_ui --> n_posts["posts"] n_platform_customer_ui --> n_reviews["reviews"] n_platform_customer_ui --> n_router["router"] n_platform_customer_ui --> n_saved_searches["saved_searches"]
Public surface
`crate root`
| Item | What it is |
|---|---|
pub struct CustomerConfig | Configuration for the customer shell |
CustomerConfig :: fn default() -> Self | — |
`api`
| Item | What it is |
|---|---|
pub struct ApiClient | — |
ApiClient :: fn new(base_url : & str) -> Self | — |
ApiClient :: fn with_csrf_cookie(mut self, name : impl Into <String>) -> Self | — |
ApiClient :: fn with_unauthorized_handler(mut self, cb : Callback <()>) -> Self | — |
ApiClient :: fn csrf_cookie_name(& self) -> & str | — |
ApiClient :: fn base_url(& self) -> & str | — |
ApiClient :: async fn get <T : DeserializeOwned>(& self, path : & str) -> Result <T, ApiError> | — |
ApiClient :: async fn post <T : DeserializeOwned, B : Serialize>(& self, path : & str, body : & B,) -> Result <T, ApiError> | — |
ApiClient :: async fn patch <T : DeserializeOwned, B : Serialize>(& self, path : & str, body : & B,) -> Result <T, ApiError> | — |
ApiClient :: async fn delete_no_content(& self, path : & str) -> Result <(), ApiError> | — |
fn use_api() -> ApiClient | — |
`attachments`
| Item | What it is |
|---|---|
fn attachments_module() -> CustomerModule | — |
pub struct Attachment | — |
fn MyUploadsPage() -> impl IntoView | MyUploadsPage lists attachments owned by the current customer |
`auth`
| Item | What it is |
|---|---|
pub struct AuthContext | — |
AuthContext :: fn mark_authenticated(& self) | — |
AuthContext :: fn mark_unauthenticated(& self) | — |
AuthContext :: fn finish_loading(& self) | — |
fn AuthProvider(children : ChildrenFn) -> impl IntoView | — |
fn use_auth() -> AuthContext | — |
fn auth_module() -> CustomerModule | — |
fn LoginPage() -> impl IntoView | — |
fn SignupPage() -> impl IntoView | — |
fn ForgotPasswordPage() -> impl IntoView | — |
fn ResetPasswordPage() -> impl IntoView | — |
fn VerifyEmailPage() -> impl IntoView | — |
`catalog`
| Item | What it is |
|---|---|
fn catalog_module() -> CustomerModule | — |
fn CatalogBrowsePage() -> impl IntoView | — |
fn BasketPage() -> impl IntoView | — |
fn MyOrdersPage() -> impl IntoView | — |
`contact`
| Item | What it is |
|---|---|
fn contact_module() -> CustomerModule | — |
fn InquiryPage() -> impl IntoView | — |
`downloads`
| Item | What it is |
|---|---|
fn downloads_module() -> CustomerModule | — |
pub struct Download | — |
fn DownloadsPage() -> impl IntoView | — |
`layout`
| Item | What it is |
|---|---|
fn PublicLayout(children : ChildrenFn) -> impl IntoView | — |
`me`
| Item | What it is |
|---|---|
fn me_module() -> CustomerModule | — |
fn AccountPage() -> impl IntoView | — |
`modules`
| Item | What it is |
|---|---|
pub struct NavItem | — |
NavItem :: fn new(label : impl Into <String>, path : impl Into <String>) -> Self | — |
NavItem :: fn with_icon(mut self, icon : impl Into <String>) -> Self | — |
pub struct CustomerModule | A registered customer module |
pub struct CustomerRoute | View binding for a customer module's page |
CustomerRoute :: fn new <F>(path : impl Into <String>, view : F) -> Self where F : Fn() -> View + Send + Sync + 'static, | — |
pub struct CustomerModuleBuilder | — |
CustomerModuleBuilder :: fn new(id : impl Into <String>) -> Self | — |
CustomerModuleBuilder :: fn name(mut self, name : impl Into <String>) -> Self | — |
CustomerModuleBuilder :: fn nav_item(mut self, item : NavItem) -> Self | — |
CustomerModuleBuilder :: fn route(mut self, r : CustomerRoute) -> Self | — |
CustomerModuleBuilder :: fn build(self) -> CustomerModule | — |
pub struct ModuleRegistry | — |
ModuleRegistry :: fn new() -> Self | — |
ModuleRegistry :: fn register(mut self, module : CustomerModule) -> Self | — |
ModuleRegistry :: fn all_nav_items(& self) -> Vec <NavItem> | — |
ModuleRegistry :: fn modules(& self) -> & Arc <CustomerModule> | — |
ModuleRegistry :: fn all_routes(& self) -> Vec <(String, CustomerRoute)> | All module routes as (path, route) pairs |
fn provide_modules(registry : ModuleRegistry) | — |
fn use_modules() -> ModuleRegistry | — |
`notifications`
| Item | What it is |
|---|---|
fn notifications_module() -> CustomerModule | — |
`posts`
| Item | What it is |
|---|---|
fn posts_module() -> CustomerModule | — |
pub struct Post | — |
fn BlogIndex(# prop(into, optional) limit : Option <u32>, # prop(into, optional) mount_path : Option <String>,) -> impl IntoView | Blog index — paginated card grid of published posts |
fn PostDetail(# prop(into) slug : String) -> impl IntoView | Post detail page — looks up by slug from the URL parameter, calls the public /api/v1/customer/posts/:slug endpoint, renders the excerpt + featured image + meta |
`reviews`
| Item | What it is |
|---|---|
fn reviews_module() -> CustomerModule | — |
pub struct Review | — |
fn ReviewsWidget(# prop(into, optional) limit : Option <u32>, # prop(into, optional) business_name : Option <String>,) -> impl IntoView | Widget rendering the top-N active reviews |
`router`
| Item | What it is |
|---|---|
fn CustomerApp(config : CustomerConfig, modules : ModuleRegistry) -> impl IntoView | — |
`saved_searches`
| Item | What it is |
|---|---|
fn saved_searches_module() -> CustomerModule | — |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
CustomerApp | router::CustomerApp |
PublicLayout | layout::PublicLayout |
notifications_module | notifications::notifications_module |
saved_searches_module | saved_searches::saved_searches_module |
{attachments_module,MyUploadsPage} | attachments::{attachments_module,MyUploadsPage} |
{auth_module,use_auth,AuthContext,AuthProvider,ForgotPasswordPage,LoginPage,ResetPasswordPage,SignupPage,VerifyEmailPage,} | auth::{auth_module,use_auth,AuthContext,AuthProvider,ForgotPasswordPage,LoginPage,ResetPasswordPage,SignupPage,VerifyEmailPage,} |
{catalog_module,BasketPage,CatalogBrowsePage,MyOrdersPage} | catalog::{catalog_module,BasketPage,CatalogBrowsePage,MyOrdersPage} |
{contact_module,InquiryPage} | contact::{contact_module,InquiryPage} |
{downloads_module,DownloadsPage} | downloads::{downloads_module,DownloadsPage} |
{me_module,AccountPage} | me::{me_module,AccountPage} |
{posts_module,BlogIndex,PostDetail} | posts::{posts_module,BlogIndex,PostDetail} |
{provide_modules,use_modules,CustomerModule,CustomerModuleBuilder,CustomerRoute,ModuleRegistry,NavItem,} | modules::{provide_modules,use_modules,CustomerModule,CustomerModuleBuilder,CustomerRoute,ModuleRegistry,NavItem,} |
{reviews_module,ReviewsWidget} | reviews::{reviews_module,ReviewsWidget} |
{use_api,ApiClient} | api::{use_api,ApiClient} |
Boundary
Reaches into domain.
Shares tier platform with 9 other crates: platform-api, platform-corpus-console, platform-disclosure-lab, platform-dto, platform-html-components, platform-leptos-components, platform-privacy-scan-api, platform-staff-ui, … (9 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | platform |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/platform/customer-ui |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_platform["platform"] --> n_domain["domain"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `domain-reviews` | domain | no | always |
| `platform-dto` | platform | no | always |
| `platform-leptos-components` | platform | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
chrono | ^0.4 | serde | no | always |
gloo-net | ^0.6 | — | no | always |
gloo-storage | ^0.3 | — | no | always |
gloo-timers | ^0.3 | — | no | always |
leptos | ^0.6 | csr | no | always |
leptos_meta | ^0.6 | csr | no | always |
leptos_router | ^0.6 | csr | no | always |
rust_decimal | ^1 | serde | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
wasm-bindgen | ^0.2 | — | no | always |
wasm-bindgen-futures | ^0.4 | — | no | always |
web-sys | ^0.3 | Window, Document, Storage, console, HtmlDocument, Location, … (12 total) | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
wasm-bindgen-test | ^0.3 | — | no | always |
Build. None.
Depended on by. 1 workspace crate.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_customer_ui["application-customer-ui"] -->|uses| SELF SELF["platform-customer-ui"] SELF -->|runtime| n_domain_reviews["domain-reviews"] SELF -->|runtime| n_platform_dto["platform-dto"] SELF -->|runtime| n_platform_leptos_components["platform-leptos-components"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| cdylib | platform_customer_ui | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | none detected |
| database access | none detected |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
1 workspace crate depends on this one: application-customer-ui.
Verification
| Kind | Count |
|---|---|
| Unit tests | 6 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 1 | 0 | 0 |
api | 2 | 0 | 1 |
attachments | 3 | 0 | 0 |
auth | 9 | 0 | 0 |
catalog | 4 | 0 | 0 |
contact | 2 | 0 | 0 |
downloads | 3 | 0 | 0 |
layout | 1 | 0 | 0 |
me | 2 | 0 | 0 |
modules | 7 | 0 | 0 |
notifications | 1 | 0 | 0 |
posts | 4 | 0 | 0 |
reviews | 3 | 0 | 0 |
router | 1 | 0 | 0 |
saved_searches | 1 | 0 | 0 |
What the tests establish, by name:
is_third_party_matches_primitive_semantics—src/reviews.rsjsonld_carries_organization_aggregate—src/reviews.rsjsonld_omitted_entirely_when_only_internal—src/reviews.rsjsonld_prefers_body_short_over_text—src/reviews.rsjsonld_third_party_filter_drops_internal—src/reviews.rsstars_handle_half_ratings—src/reviews.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 8 | 70 |
Public modules with a //! block | 14 | 14 |
pie showData
title Public items with rustdoc
"Documented" : 8
"No rustdoc detected" : 62
Metrics
| Metric | Value |
|---|---|
| Rust source files | 15 |
| Source lines | 2483 |
| Code lines | 2068 |
| Public API items | 70 |
| Public modules | 14 |
| Tests | 6 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 17 |
| Workspace reverse dependencies | 1 |
pie showData
title Public API by kind
"function" : 32
"method" : 26
"struct" : 12
pie showData
title Rust source composition
"Code" : 2068
"Blank or comment" : 415
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.