Organization & Malet Management
Configure Malets, connect domains, monitor activity, and manage billing โ all from the management dashboard.
๐จ Malet Designer
Route: /{{malet}}/manage/designer
Backend: updateThemeConfig and updateLayoutConfig mutations
Data: verticalConfig(slug) query for vertical-aware presets
The Malet Designer provides a high-density, professional CAD-style environment allowing Malet Owners to customize their visual brand identity and page layout. The fixed-viewport interface uses a non-scrolling sidebar + live iframe preview architecture, with an accordion-based settings panel for managing cognitive load across many configuration options.
The sidebar features two navigation tabs: Theme and Layout. Each tab uses collapsible accordion sections with inline value summaries in the header (color dots, font names, pixel values), allowing Malet Owners to see current settings at a glance without expanding every section.
Theme Editor
The Theme tab organizes settings into six collapsible accordion sections:
1. Theme Presets
One-click preset cards that apply a complete theme configuration (colors, fonts, mode, animations, radius). Presets are grouped into two tiers:
- Vertical Presets โ Dynamically generated from the malet's vertical config branding defaults via
verticalConfig(slug). Only shown if the malet has averticalTypeset. Includes a Light and Dark variant using the vertical'sprimaryColorandaccentColor. - Universal Presets โ Four curated presets available to all malets:
| Preset | Colors | Font | Mode |
|---|---|---|---|
| Mallnline Default | Blue / Amber | Inter | AUTO |
| Midnight Pro | Indigo / Violet | DM Sans | DARK |
| Clean Slate | Gray / Neutral | Source Sans Pro | LIGHT |
| Warm Editorial | Amber / Warm Tones | Playfair / Lato | LIGHT |
2. Brand Colors
Interactive ColorWheelPicker with pill selectors for Primary, Secondary, Accent, and Background. The accordion header shows four inline color dot swatches reflecting current values.
3. Typography
Custom font picker dropdown that renders each option in its actual typeface (not a native <select>). Includes a specimen preview box showing heading and body text with the current selection. Supports 11 curated fonts: Inter, Roboto, Outfit, Playfair Display, Source Sans Pro, Poppins, Montserrat, Lato, Raleway, Open Sans, and DM Sans.
4. Display
Theme Mode (LIGHT / DARK / AUTO) and Animation Level (MINIMAL / STANDARD / RICH) configured via segmented radio buttons. The accordion summary shows the current values inline (e.g., "AUTO ยท STANDARD").
5. Border Radius
Range slider (0โ64px) with live preview shape and pixel readout.
6. Advanced (Custom CSS)
Monospace textarea for custom CSS overrides with a character counter (2000 limit). Includes a warning badge alerting Malet Owners that overrides may break their Malet layout. The value is persisted via the customCSS field on ThemeConfig.
Preview Toolbar
The preview panel includes a responsive toolbar above the iframe:
| Control | Description |
|---|---|
| Desktop | Full-width iframe (default) |
| Tablet | Constrained to 768px width with centered shadow frame |
| Mobile | Constrained to 375px width with centered shadow frame |
| Refresh | Reloads the preview iframe |
| External Link | Opens the live Malet in a new tab |
Changes are reflected in real-time via cross-origin postMessage with the SYNC_PREVIEW_CONFIG payload. The preview sandbox intercepts navigation clicks and shows an "Action disabled in Preview Builder" toast.
Layout Editor
Configure which Malet sections appear and in what order. The system supports 31 slot types organized into two categories:
Core Sections (10)
Available to all malets regardless of vertical:
| Slot Type | Description |
|---|---|
| ๐ผ๏ธ Hero Banner | Full-width header image |
| โญ Featured Products | Highlighted product showcase |
| ๐๏ธ Product Grid | Main catalog grid |
| ๐ About Section | Malet description & story |
| ๐ฌ Reviews | Visitor reviews/testimonials |
| ๐ฐ Blog Feed | Latest blog posts |
| ๐ง Contact Form | Visitor inquiry form |
| ๐ฌ Newsletter | Email subscription |
| ๐ Social Links | Social media links |
| ๐ง Custom HTML | Freeform HTML block |
Vertical Widgets (21)
Specialized sections unlocked per vertical. Only the vertical matching the malet's verticalType is shown in the "Add Section" picker:
| Vertical | Widget Slots |
|---|---|
| Fashion | Lookbook Gallery, Collection Browser |
| Entertainment | Event Calendar, Credit Store, Leaderboard |
| Professional | Client Portal, Document Exchange, Consultation Calendar |
| Wellness | Wellness Profile, Membership Plans, Practitioner Booking |
| Culture | Exhibition Gallery, Artist Showcase, Season Calendar |
| Tech | Spec Comparison, Warranty Tracker, Trade-In Portal |
| Arcade | Digital Vault, Game Profile, Pre-Order Queue |
| Tour | Itinerary Timeline, Tour Overview, Departure Booking |
| Restaurant | Kitchen Board, Menu Order Cards, Daily Stats |
| Photographer | Gallery Showcase, Proofing Board, License Tiers |
| Author | Subscription Plans, Gated Content, Member Portal |
Vertical widget slots display a purple badge in the active sections list and are visually distinguished with a purple accent in the "Add Section" picker.
Layout Controls
- Drag to reorder: Grab the โ ฟ handle to rearrange sections
- Toggle visibility: Click the ๐๏ธ/โ icon to show/hide
- Custom headings: Type in the heading input to override section titles
- Remove: Hover to reveal the ๐๏ธ remove button
- Add Section: Open the "Add Section" accordion to browse available core and vertical-specific widgets
NOTE
Vertical-specific widgets show a purple badge indicator. The "Add Section" picker only displays widgets relevant to the malet's vertical, keeping the UI clean and contextual.
๐ Custom Domains
Route: /{{malet}}/manage/settings (scroll to Custom Domains section)
Backend: addCustomDomain, verifyCustomDomain, removeCustomDomain mutations
Connect your own domain to your Malet. After adding a domain, the system provides DNS TXT record instructions for verification.
How It Works
- Add your domain: Enter your domain name (e.g.
shop.example.com) in the input field and click "Add Domain". - Add DNS TXT Record: Copy the provided verification details:
- Host:
_ngwenya-verify.your-domain.com - Value: The unique verification token shown Add this as a TXT record in your DNS provider (GoDaddy, Cloudflare, Namecheap, etc.)
- Host:
- Click Verify: Once the DNS propagates (usually 1-24 hours), click the "๐ Verify" button. The system checks for the TXT record.
- SSL Provisioning: After verification, an SSL certificate is automatically provisioned. Watch the cert status badge update to ACTIVE.
Domain Status Reference
| Status | Badge | Meaning |
|---|---|---|
PENDING |
โณ PENDING | Awaiting DNS verification |
VERIFIED |
โ VERIFIED | Domain ownership confirmed |
FAILED |
โ FAILED | DNS check failed โ recheck your records |
๐ Team Activity Feed
Route: /orgs/{{slug}}/manage โ "Activity" tab
Backend: organization.activityFeeds connection (paginated)
The Activity Feed displays a chronological timeline of all team actions within your organization. Every membership change, role update, and setting modification is logged.
Tracked Event Types
| Event | Icon | Example Trigger |
|---|---|---|
MEMBER_INVITED |
๐จ | Admin invites a new team member |
MEMBER_JOINED |
๐ค | Invited member accepts and joins |
MEMBER_REMOVED |
๐ | Member is removed or leaves voluntarily |
ROLE_CHANGED |
๐ | Member promoted/demoted (e.g. MEMBER โ ADMIN) |
VERTICAL_ROLE_CHANGED |
๐ท๏ธ | Per-vertical role assignment changed |
ORG_CREATED |
๐๏ธ | Organization first created |
ORG_UPDATED |
โ๏ธ | Org settings or profile updated |
ORG_DELETED |
๐๏ธ | Organization deleted |
Each event displays the actor (who performed the action), the target (who was affected, if applicable), a relative timestamp ("3h ago"), and any relevant metadata (old/new role, org name, etc.).
TIP
Pagination: The feed loads 15 events at a time with a "Load More" button at the bottom. Uses cursor-based pagination via the activityFeeds GQL connection.
๐ Organization Analytics
Route: /orgs/{{slug}}/manage โ "Analytics" tab
Component: src/lib/components/admin/OrgAnalyticsPanel.svelte
Backend: Client-side aggregation from organizationAuditEvents + organizationTeams + membership data
The Organization Analytics tab provides Org Owners and Admins with visual insights into team activity and composition. Since the organizations subgraph has no dedicated analytics resolver, all aggregation is done client-side from existing queries.
KPI Cards
| Card | Source | Description |
|---|---|---|
| Total Members | members.length |
Current org membership count |
| Total Activity | Audit event count | Total actions logged |
| Active Contributors | Unique actorId count |
Members who have taken actions |
| Teams | organizationTeams count |
Number of teams in the org |
Charts & Visualizations
- Activity Over Time โ Bar chart showing daily event counts for the last 30 days
- Role Distribution โ SVG donut showing OWNER/ADMIN/MEMBER/VIEWER proportions
- Event Type Breakdown โ Horizontal colored bars showing counts per event type
- Top Contributors โ Leaderboard table with ๐ฅ๐ฅ๐ฅ medals, action count, last active
- Teams Table โ Team name, member count, scoped Malets, creation date
Data Fetching
The panel fetches data via Promise.allSettled for resilient parallel loading:
organizationAuditEvents(orgId, limit: 200)โ raw event streamorganizationTeams(orgId)โ team listingmembersโ passed as prop from the parent page (already loaded)
Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
orgId |
string |
โ | Organization ID (required) |
members |
{ userId, role, joinedAt }[] |
[] |
Membership data from parent |
getMemberDisplayName |
(userId: string) => string |
โ | Optional name resolver function |
โ๏ธ Organization Settings
Route: /orgs/{{slug}}/manage โ "General" tab
Backend: updateOrganization mutation with settings input
The Organization Settings page has three panels: Organization Profile, Billing Preferences, and Notification Controls.
๐ณ Billing Preferences
| Field | Type | Description |
|---|---|---|
| Currency | Dropdown | 9 currencies: USD, EUR, GBP, ZAR, KES, NGN, JPY, AUD, CAD |
| Tax ID | Text | Organization's tax identification number |
| Billing Email | Email address for invoices and billing notices |
๐ Notification Controls
| Toggle | Default | Description |
|---|---|---|
| Low Stock Alerts | โ On | Notified when any product inventory drops below threshold |
| New Murchase Notifications | โ On | Alert on every new Visitor Murchase |
| Payout Notifications | โ On | Updates about payments, withdrawals, and settlements |
๐ณ Organization Billing & Subscriptions
Route: /orgs/{{slug}}/billing
Backend: Future implementation in the payments and organizations subgraphs (Pending P0 Epic).
The Subscription Management page allows organization admins to upgrade their tier (Starter/Pro/Enterprise), manage payment methods, and view invoice history. As the backend infrastructure is currently in development, this frontend page operates using robust, strongly-typed mocks that mirror the intended GraphQL schema.
Feature Capabilities
- Tiers Preview: Real-time toggle between monthly/annual pricing (20% discount standard).
- Payment Methods: List verified payment methods via Stripe Setup Intents structure.
- Invoices: Downloadable PDF invoice stubs.
- Design Tokens: Extensive use of
$malltokens (var(--mall-accent),var(--mall-light-bg)) to ensure consistency with the system theme.
Mock Data Architecture
The data structures used here (Subscription, PaymentMethod, Invoice) are defined in src/routes/orgs/[slug]/billing/mockData.ts. These interface models must be directly copied when authoring the backend GraphQL schema in the Phase 2 launch.
๐ก GraphQL Reference
All management features are powered by these GraphQL operations:
Mutations
updateThemeConfig(id: ID!, input: ThemeConfigInput!): Malet
updateLayoutConfig(id: ID!, input: LayoutConfigInput!): Malet
addCustomDomain(id: ID!, input: CustomDomainInput!): Malet
verifyCustomDomain(id: ID!, domain: String!): Malet
removeCustomDomain(id: ID!, domain: String!): Malet
updateOrganization(input: UpdateOrganizationInput!): Organization
# input.settings contains:
# billing: { currency, taxId, billingEmail }
# notifications: { lowStockAlerts, newOrderNotifications, payoutNotifications }
Queries (Activity Feed)
query GetOrgActivityFeed($orgId: ID!, $first: Int, $after: String) {
organization(id: $orgId) {
activityFeeds(first: $first, after: $after) {
edges {
node {
id
eventType
actorId
targetUserId
metadata
createdAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
๐ Org Context Switcher
Component: src/lib/components/OrgSwitcher.svelte
Store: src/stores/orgs.ts
Utilities: src/lib/utils/orgContextUtils.ts
The Org Context Switcher allows authenticated users with organization memberships to toggle between personal and organization scoped contexts. All subsequent GraphQL requests include the x-org-id header so that downstream subgraphs resolve permissions, data, and activity within the correct org boundary.
Architecture
OrgSwitcher.svelte (UI)
โ selectOrg(id)
orgsStore.switchContext(id) (atomic state transition)
โ sets isSwitching=true โ localStorage flag: ngwenya_org_switching
โ updates currentOrgId in localStorage + store
โ returns { success, orgName }
โ isSwitching=false (after 500ms drain window)
goto('/lobby') โ invalidateAll()
โ SvelteKit re-runs load() functions
client.ts headers() โ reads localStorage('currentOrgId')
โ sends x-org-id header
Gateway auth.context.ts โ validateMembership(orgId, userId)
โ sets ctx.orgId if valid
401 Suppression During Switch
When switching contexts, stale in-flight requests from the previous page may hit org-scoped guards and receive 401 responses. The client.ts response middleware reads a ngwenya_org_switching localStorage flag (set by orgsStore.switchContext) to suppress session-expiry handling during the transition window (500ms).
Without this fix, the response middleware would interpret the 401 as a session expiry, logout the user, and redirect to /auth.
Utility Functions
| Function | Purpose |
|---|---|
isOrgScopedError(error) |
Detects if a GraphQL error is org-scope authorization vs real session expiry |
buildOrgHeaders(orgId) |
Builds { 'x-org-id': id } or {} โ spreadable into headers |
validateOrgSwitch(memberships, targetOrgId) |
Pre-validates the user has membership before switching |
Component Props
OrgSwitcher takes no props. It reads from derived stores:
| Store | Type | Description |
|---|---|---|
$currentOrg |
Organization | null |
Active org (null = personal) |
$currentOrgId |
string | null |
Active org ID |
$memberships |
Membership[] |
All user memberships |
$isSwitching |
boolean |
True during context transition |
$authStore |
AuthState |
Only shown when authenticated |
Data Attributes (E2E)
| Selector | Element |
|---|---|
[data-testid="org-switcher"] |
Root container |
[data-testid="org-switcher-toggle"] |
Trigger button |
[data-testid="org-switcher-dropdown"] |
Dropdown menu |
[data-testid="org-switcher-personal"] |
Personal context option |
[data-testid="org-switcher-org-{slug}"] |
Org option by slug |
๐ File Reference
| Feature | File | Purpose |
|---|---|---|
| Designer | [malet]/manage/designer/+page.svelte |
Theme + Layout editor page |
| Color Picker | lib/components/ColorWheelPicker.svelte |
Reusable color wheel component |
| Domains | [malet]/manage/settings/+page.svelte |
Custom Domains section (add/verify/remove) |
| Org Manage | orgs/[slug]/manage/+page.svelte |
General + Members + Analytics + Activity tabs |
| Org Analytics | lib/components/admin/OrgAnalyticsPanel.svelte |
Organization analytics dashboard (client-side aggregation) |
| Org Analytics Tests | tests/orgAnalytics.test.ts |
Unit tests for aggregation utilities |
| Org Data | lib/queries/organizations.ts |
Activity feed query + org settings types |
| Malet Data | lib/queries/malet.ts |
Theme/Layout/Domain mutations + types |
| Org Switcher | lib/components/OrgSwitcher.svelte |
Context switch dropdown (Personal โ Org) |
| Org Store | stores/orgs.ts |
Org memberships, switching state, derived |
| Org Context Utils | lib/utils/orgContextUtils.ts |
Error detection, header building, validation |
Related
- Organizations & Permissions โ Backend subgraph powering memberships, roles, and the permission system
- Teams & Sub-Groups โ Group org members into named teams with LEAD/MEMBER roles
- Custom RBAC โ Admin-defined custom roles with configurable permission matrices
- Edit History Audit Trail โ Cross-entity field-level change tracking and
maletActivityFeed - Settings Architecture โ Frontend settings page structure for user preferences, security, and privacy
- Tag Registry โ Platform-managed curated tags that Malet Owners assign to their storefronts
- Professional Services & Client Portals โ Client engagement portals and document vaults for professional Malets
- Wellness & Beauty โ Membership plans, practitioner booking, and client wellness profiles for spa and beauty Malets
- Revenue Sharing & Payouts โ Commission splitting, Stripe Connect onboarding, and the Malet Owner earnings dashboard
- Org Context & Tier Access โ The complete x-org-id header pipeline, canCreateOrganization() gate, search scoping, and org UX layer
- Advanced Audit Logging UI โ Upgraded Audit Log with client-side filtering, CSV/JSON export, and SIEM webhook management
- Permission Matrix Editor โ Interactive checkbox matrix for composing custom role permissions
- Workspaces & The Tower โ Admin dashboard workspace and Organization Analytics tab architecture
- User Guide: Designing Your Malet Storefront โ End-user guide covering the Designer UI from the Malet Owner perspective