Skip to content

Feat/multi tenant#4

Merged
kipsang01 merged 3 commits into
devfrom
feat/multi-tenant
May 15, 2026
Merged

Feat/multi tenant#4
kipsang01 merged 3 commits into
devfrom
feat/multi-tenant

Conversation

@kipsang01
Copy link
Copy Markdown
Collaborator

No description provided.

@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@willymwai
Copy link
Copy Markdown
Member

/agentic_describe

@willymwai
Copy link
Copy Markdown
Member

/agentic_review

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 15, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (4)

Context used
✅ Compliance rules (platform): 24 rules

Grey Divider


Action required

1. Multiple functions missing return types 📘 Rule violation ⚙ Maintainability
Description
Several new/modified functions omit explicit return type annotations, relying on inference. This
violates the requirement and can allow unintended return-type changes to slip through reviews.
Code

src/contexts/TenantContext.tsx[R8-45]

+export function TenantConfigProvider({
+  children,
+  config,
+}: {
+  children: ReactNode;
+  config: PublicTenantConfig;
+}) {
+  const value = useMemo(() => config, [config]);
+
+  return (
+    <TenantContext.Provider value={value}>{children}</TenantContext.Provider>
+  );
+}
+
+export function useTenantConfig(): PublicTenantConfig {
+  const context = useContext(TenantContext);
+  if (!context) {
+    throw new Error(
+      "useTenantConfig must be used within a TenantConfigProvider",
+    );
+  }
+  return context;
+}
+
+export function useTenantTheme() {
+  return useTenantConfig().theme;
+}
+
+export function useTenantNavigation() {
+  return useTenantConfig().navigation;
+}
+
+export function useTenantPayments() {
+  return useTenantConfig().paymentKeys;
+}
+
+export function useTenantSpree() {
+  return useTenantConfig().spree;
Evidence
Rule 638178 requires explicit return type annotations on all functions. The cited
functions/components are declared without a return type (e.g., TenantConfigProvider,
useTenantTheme, cachedListProducts, getHeightClass, ImageBannerSection, and the
default-exported HomePage).

Rule 638178: Require explicit return types on all functions
src/contexts/TenantContext.tsx[8-45]
src/lib/data/products.ts[67-74]
src/components/page-builder/ImageBannerSection.tsx[19-33]
src/app/[country]/[locale]/(storefront)/page.tsx[75-82]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New/modified functions do not declare explicit return types.

## Issue Context
The compliance standard requires explicit return types on all functions (including exported components/hooks and helpers).

## Fix Focus Areas
- src/contexts/TenantContext.tsx[8-45]
- src/lib/data/products.ts[67-74]
- src/components/page-builder/ImageBannerSection.tsx[19-33]
- src/app/[country]/[locale]/(storefront)/page.tsx[75-82]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Relative imports in new tests 📘 Rule violation ⚙ Maintainability
Description
New test files import internal modules using relative paths (e.g., ../TenantContext) instead of
@/ absolute imports. This makes refactors harder and violates the import-path standard.
Code

src/contexts/tests/TenantContext.test.tsx[R1-3]

+import { renderHook } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+import { TenantConfigProvider, useTenantConfig } from "../TenantContext";
Evidence
Rule 638185 disallows relative imports into internal modules when @/ imports are the standard. The
new tests import TenantContext, normalize, olitt, and surface via ../... paths.

Rule 638185: Use absolute @/ imports instead of relative paths
src/contexts/tests/TenantContext.test.tsx[1-3]
src/lib/tenant/tests/olitt.test.ts[3-8]
src/lib/tenant/tests/surface.test.ts[3-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New imports use `../...` relative paths for internal modules instead of `@/...`.

## Issue Context
The project standard requires absolute `@/` imports for app-internal modules.

## Fix Focus Areas
- src/contexts/__tests__/TenantContext.test.tsx[1-3]
- src/lib/tenant/__tests__/olitt.test.ts[3-8]
- src/lib/tenant/__tests__/surface.test.ts[3-11]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Tenant config type mismatch 🐞 Bug ≡ Correctness
Description
src/app/[country]/[locale]/(checkout)/layout.tsx passes PublicTenantConfig (from
useTenantConfig()) into resolveTenantBranding/Navigation/Footer, which are typed to accept
TenantConfig, causing a TypeScript incompatibility and preventing the app from building. Even if
forced via casting, these resolvers read raw/design/seo fields that are not present on
PublicTenantConfig, degrading tenant-driven branding/navigation behavior.
Code

src/app/[country]/[locale]/(checkout)/layout.tsx[R10-28]

+import { useTenantConfig } from "@/contexts/TenantContext";
import { POLICY_LINKS } from "@/lib/constants/policies";
-import { getStoreName } from "@/lib/store";
+import {
+  resolveTenantBranding,
+  resolveTenantFooter,
+  resolveTenantNavigation,
+} from "@/lib/tenant";
import { extractBasePath } from "@/lib/utils/path";

-const storeName = getStoreName();
-
function CheckoutHeader() {
  const pathname = usePathname();
  const basePath = extractBasePath(pathname);
  const t = useTranslations("checkoutLayout");
+  const tenantConfig = useTenantConfig();
+  const branding = resolveTenantBranding(tenantConfig, {
+    name: tenantConfig.storeName ?? "Store",
+    logoUrl: "/spree.png",
+  });
+  const navigation = resolveTenantNavigation(tenantConfig);
Evidence
useTenantConfig() returns PublicTenantConfig, but the tenant resolvers require TenantConfig;
PublicTenantConfig lacks many required TenantConfig fields (including raw/seo) that the
resolvers attempt to read.

src/contexts/TenantContext.tsx[3-30]
src/lib/tenant/types.ts[10-34]
src/lib/tenant/resolvers.ts[113-156]
src/app/[country]/[locale]/(checkout)/layout.tsx[9-29]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Client checkout layout calls `resolveTenantBranding/resolveTenantNavigation/resolveTenantFooter` with `tenantConfig` returned from `useTenantConfig()`; that hook returns `PublicTenantConfig`, but the resolver APIs are typed to accept `TenantConfig`. This is a compile-time type error and also indicates the resolvers are relying on fields (`raw`, `seo`, nested `design`) that cannot exist in the client-safe config.

## Issue Context
- The client context intentionally stores `PublicTenantConfig`.
- Resolver APIs currently accept only `TenantConfig | null`.

## Fix Focus Areas
- Update resolver/surface function signatures to accept a safe shared type (e.g., `TenantConfig | PublicTenantConfig`) and only read optional fields when present, OR
- Introduce dedicated client-safe resolver(s) that operate solely on `PublicTenantConfig`.

- src/app/[country]/[locale]/(checkout)/layout.tsx[10-28]
- src/contexts/TenantContext.tsx[3-30]
- src/lib/tenant/resolvers.ts[113-160]
- src/lib/tenant/types.ts[1-34]
- src/lib/tenant/surface.ts[22-125]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Request-only Spree resolution 🐞 Bug ☼ Reliability
Description
resolveSpreeConfig() now always derives Spree credentials from request headers
(getTenantConfigFromRequest) and throws when no request-scoped tenant config exists, which breaks
or permanently forces fallbacks in build-time/static code paths that call Spree APIs. For example,
sitemap and generateStaticParams() call Spree-backed helpers during build, but build-time has no
request headers to resolve a tenant.
Code

src/lib/spree/config.ts[R56-70]

+export async function resolveSpreeConfig(): Promise<SpreeNextConfig> {
+  const tenantConfig = await getTenantConfigFromRequest();
+
+  if (tenantConfig?.spree.apiUrl && tenantConfig.spree.publishableKey) {
+    return {
+      baseUrl: tenantConfig.spree.apiUrl,
+      publishableKey: tenantConfig.spree.publishableKey,
+      defaultCountry: tenantConfig.defaultCountry,
+      defaultLocale: tenantConfig.defaultLocale,
+    };
+  }
+
+  throw new Error(
+    "Tenant Spree config could not be resolved for this request.",
+  );
Evidence
Spree config resolution depends on headers() and throws if a tenant cannot be resolved; sitemap
and static params call Spree-backed functions in build/static contexts where request headers are
unavailable.

src/lib/spree/config.ts[56-71]
src/lib/tenant/request.ts[36-42]
src/app/sitemap.ts[257-265]
src/app/[country]/[locale]/(storefront)/page.tsx[28-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolveSpreeConfig()` throws when tenant config cannot be resolved from request headers. This makes any build-time/static execution that touches Spree (e.g., `generateStaticParams`, sitemap generation) fail to access Spree config and/or degrade to fallback behavior.

## Issue Context
- `resolveSpreeConfig()` calls `getTenantConfigFromRequest()` (requires `next/headers`).
- Some code explicitly runs during build-time/static generation and calls Spree helpers.

## Fix Focus Areas
- Provide an explicit build/non-request fallback path (e.g., `resolveSpreeConfig({ allowEnvFallback: true })` or a separate `resolveSpreeConfigFromEnv()`), and update build-time callers to use it.
- Alternatively, have `resolveSpreeConfig()` fall back to `_config` / `SPREE_API_URL`+`SPREE_PUBLISHABLE_KEY` when request-scope is unavailable.

- src/lib/spree/config.ts[56-71]
- src/lib/tenant/request.ts[36-42]
- src/app/sitemap.ts[257-265]
- src/app/[country]/[locale]/(storefront)/page.tsx[28-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Unsafe tenant link href 🐞 Bug ⛨ Security
Description
Tenant navigation links accept any non-empty href string and are rendered directly into `<Link
href={...}>, allowing dangerous schemes like javascript:` to become clickable XSS vectors. This
affects checkout navigation links and any header/footer links resolved from tenant config.
Code

src/lib/tenant/resolvers.ts[R31-40]

+function getLink(value: unknown): TenantLink | null {
+  const record = getRecord(value);
+  if (!record) return null;
+
+  const label = getString(record.label);
+  const href = getString(record.href);
+  if (!label || !href) return null;
+
+  return { label, href };
+}
Evidence
getLink() accepts any trimmed string for href without scheme validation, and checkout layout
renders link.href directly into Link, making javascript: URLs viable.

src/lib/tenant/resolvers.ts[31-40]
src/app/[country]/[locale]/(checkout)/layout.tsx[42-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Tenant navigation `href` values are not validated; a malicious/compromised tenant config could supply `javascript:` (or similar) which will be emitted into anchors via `next/link` and executed on click.

## Issue Context
`getLink()` currently only checks that `label` and `href` are non-empty strings.

## Fix Focus Areas
- In `getLink()` (or a shared URL sanitizer), allow only safe patterns (e.g., relative `/...`, `#...`, and absolute `https?://...`; optionally `mailto:`/`tel:`) and drop anything else.
- Consider adding unit tests covering rejected schemes.

- src/lib/tenant/resolvers.ts[31-40]
- src/app/[country]/[locale]/(checkout)/layout.tsx[42-49]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. EMPTY_CART_CONTEXT should use satisfies 📘 Rule violation ⚙ Maintainability
Description
The PR introduces object-literal type checks via a variable annotation and a broad type assertion
(as never) instead of using the satisfies operator, which can hide shape/type mistakes. This
reduces type-safety and makes refactors riskier.
Code

src/contexts/CartContext.tsx[R37-49]

+const EMPTY_CART_CONTEXT: CartContextType = {
+  cart: null,
+  loading: false,
+  updating: false,
+  itemCount: 0,
+  isOpen: false,
+  openCart: () => {},
+  closeCart: () => {},
+  addItem: async () => {},
+  updateItem: async () => {},
+  removeItem: async () => {},
+  refreshCart: async () => {},
+};
Evidence
Rule 638182 requires using satisfies for object literal shape checking rather than variable type
annotations or assertions. The PR adds const EMPTY_CART_CONTEXT: CartContextType = { ... } and
also uses } as never for a config literal in a new test, both of which fit the rule's failure
patterns.

Rule 638182: Prefer satisfies operator for object literal type checking
src/contexts/CartContext.tsx[37-49]
src/contexts/tests/TenantContext.test.tsx[5-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New object literals are being type-checked using `: Type = { ... }` and `as ...` assertions instead of the `satisfies` operator.

## Issue Context
The codebase standard requires `satisfies` for object literal shape checking to avoid masking errors.

## Fix Focus Areas
- src/contexts/CartContext.tsx[37-49]
- src/contexts/__tests__/TenantContext.test.tsx[5-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. HomePage fetches not parallel 📘 Rule violation ➹ Performance
Description
Independent server-side fetches are awaited sequentially, increasing TTFB unnecessarily. These
should be started together and awaited via Promise.all.
Code

src/app/[country]/[locale]/(storefront)/page.tsx[R76-80]

  const { country, locale } = await params;
  const basePath = `/${country}/${locale}`;
  const currency = await resolveCurrency(country);
+  const tenantConfig = await getTenantConfigFromRequest();
+  const storeName = getTenantBrandName(tenantConfig) ?? getStoreName();
Evidence
Rule 638177 requires independent server-side fetches to run in parallel. In HomePage,
resolveCurrency(country) and getTenantConfigFromRequest() are awaited one after another even
though they are logically independent once country is known; similarly, DynamicStorePage awaits
currency and tenant config sequentially.

Rule 638177: Execute independent server-side data fetches in parallel with Promise.all
src/app/[country]/[locale]/(storefront)/page.tsx[76-80]
src/app/[country]/[locale]/(storefront)/pages/[...slug]/page.tsx[45-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Independent server-side data fetches are executed sequentially.

## Issue Context
This adds avoidable latency during SSR/RSC rendering; the guideline requires parallelizing independent fetches with `Promise.all`.

## Fix Focus Areas
- src/app/[country]/[locale]/(storefront)/page.tsx[76-82]
- src/app/[country]/[locale]/(storefront)/pages/[...slug]/page.tsx[45-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Payment keys misparsed 🐞 Bug ≡ Correctness
Description
collectPaymentKeys() iterates Object.entries(record.paymentKeys ?? {}) without verifying
paymentKeys is an object, so non-object values (string/array) will be expanded into unexpected
key/value pairs and silently corrupt TenantConfig.paymentKeys. This can cause incorrect
publishable keys to be surfaced (e.g., Stripe publishable key extraction).
Code

src/lib/tenant/normalize.ts[R141-152]

+function collectPaymentKeys(
+  record: Record<string, unknown>,
+): Record<string, string> {
+  const result: Record<string, string> = {};
+
+  Object.assign(result, collectStringMap(record.paymentKeys));
+
+  for (const [key, value] of Object.entries(record.paymentKeys ?? {})) {
+    if (typeof value === "string" && value.trim()) {
+      result[key] = value.trim();
+    }
+  }
Evidence
The first merge uses collectStringMap() which checks isRecord(), but the subsequent
Object.entries(record.paymentKeys ?? {}) loop does not, allowing unexpected iteration over
non-object values.

src/lib/tenant/normalize.ts[127-155]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`collectPaymentKeys()` does an `Object.entries(record.paymentKeys ?? {})` loop without checking that `paymentKeys` is a record/object, which can misparse malformed API payloads.

## Issue Context
There is already a safe `collectStringMap()` helper that gates on `isRecord()`, but the second loop bypasses that guard.

## Fix Focus Areas
- Add an `isRecord(record.paymentKeys)` guard before `Object.entries`, or remove the redundant second loop entirely.

- src/lib/tenant/normalize.ts[141-155]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

9. Unbounded Spree client cache 🐞 Bug ➹ Performance
Description
resolvedClients is a module-global Map keyed by baseUrl::publishableKey with no eviction, so a
long-lived process can grow memory usage unbounded as new tenants/credentials are accessed. This is
especially relevant for multi-tenant deployments with many distinct stores.
Code

src/lib/spree/config.ts[R6-16]

+let _clientProxy: Client | null = null;
+
+const resolvedClients = new Map<string, Client>();
+
+function buildEnvConfig(): SpreeNextConfig {
+  const baseUrl = process.env.SPREE_API_URL;
+  const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;
+
+  if (!baseUrl || !publishableKey) {
+    throw new Error(
+      "Spree client is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables.",
Evidence
The cache is created at module scope and entries are only added/cleared, with no eviction path in
normal runtime.

src/lib/spree/config.ts[5-9]
src/lib/spree/config.ts[34-53]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The module-global `resolvedClients` cache never evicts entries, which can lead to unbounded growth in a long-lived multi-tenant server.

## Issue Context
Clients are cached per credential pair and only cleared via `resetClient()`.

## Fix Focus Areas
- Introduce an LRU/TTL eviction policy (configurable max size), or add lightweight metrics/logging around cache size.

- src/lib/spree/config.ts[6-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Multi-tenant architecture with Olitt API integration and dynamic configuration system

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Implements comprehensive multi-tenant architecture with tenant configuration resolution from Olitt
  API
• Adds tenant-aware branding, navigation, footer, and theme configuration throughout the application
• Introduces dynamic page builder and homepage configuration system with template interpolation
• Integrates tenant-specific Spree client configuration for products, categories, markets, and
  countries
• Implements request-based tenant resolution with host extraction and proxy support
• Adds tenant context provider for application-wide configuration access
• Refactors metadata generation (SEO, store, product, category, home) to support tenant-specific
  branding
• Updates layout components (Header, Footer, Checkout) to use tenant configuration
• Adds Docker containerization support with multi-platform builds and GitHub Actions workflow
• Includes comprehensive test coverage for Olitt integration, Spree configuration, and tenant
  surface functions
• Provides fallback landing page for unconfigured Olitt domains
Diagram
flowchart LR
  Request["HTTP Request"]
  HostExtract["Extract Host<br/>from Headers"]
  OlittAPI["Olitt API<br/>Lookup"]
  TenantConfig["Tenant Config<br/>Resolution"]
  TenantProvider["TenantConfigProvider<br/>Context"]
  Components["Layout Components<br/>Header/Footer/Checkout"]
  PageBuilder["Dynamic Page<br/>Builder"]
  SpreeClient["Multi-tenant<br/>Spree Client"]
  Metadata["Tenant-aware<br/>Metadata"]
  
  Request --> HostExtract
  HostExtract --> OlittAPI
  OlittAPI --> TenantConfig
  TenantConfig --> TenantProvider
  TenantProvider --> Components
  TenantProvider --> PageBuilder
  TenantProvider --> SpreeClient
  TenantProvider --> Metadata
Loading

Grey Divider

File Changes

1. src/lib/tenant/resolvers.ts ✨ Enhancement +275/-0

Tenant configuration resolvers for multi-tenant support

• Introduces tenant configuration resolvers for branding, navigation, footer, and fixed page slots
• Implements helper functions to extract and validate tenant config data from various sources
• Provides fallback mechanisms to merge layout, design, and raw configuration layers
• Exports interfaces for TenantBrandingConfig, TenantNavigationConfig, TenantFooterConfig, and
 TenantFixedPageSlotsConfig

src/lib/tenant/resolvers.ts


2. src/lib/tenant/olitt.ts ✨ Enhancement +245/-0

Olitt API integration for tenant configuration fetching

• Implements Olitt API integration for fetching tenant configurations by host
• Supports mocked responses for local development via OLITT_MOCK_RESPONSES environment variable
• Includes caching logic with configurable TTL for production environments
• Provides fetchTenantConfigFromOlitt and resolveTenantConfigByHost functions

src/lib/tenant/olitt.ts


3. src/lib/tenant/__tests__/olitt.test.ts 🧪 Tests +213/-0

Tests for Olitt tenant configuration integration

• Comprehensive test suite for Olitt integration and tenant config normalization
• Tests host normalization, store record discovery, and config building
• Validates mock response handling and error scenarios (404 responses)
• Covers edge cases like mismatched hosts and port preservation

src/lib/tenant/tests/olitt.test.ts


View more (56)
4. src/lib/tenant/normalize.ts ✨ Enhancement +226/-0

Tenant configuration normalization and building utilities

• Implements host normalization with protocol and port handling
• Provides findOlittStoreRecord to locate store data in API responses
• Builds normalized TenantConfig from Olitt store records
• Includes toPublicTenantConfig to expose only safe public configuration

src/lib/tenant/normalize.ts


5. src/lib/page-builder/index.ts ✨ Enhancement +238/-0

Dynamic page builder configuration system

• Implements dynamic page configuration system with template interpolation
• Merges default pages with tenant-specific overrides by slug matching
• Provides getDynamicPagesConfig and resolveDynamicPage functions
• Supports variable substitution in page content and configuration

src/lib/page-builder/index.ts


6. src/lib/data/products.ts ✨ Enhancement +113/-8

Multi-tenant Spree client integration for products

• Updates product data functions to use tenant-specific Spree configuration
• Adds validation for Spree config and returns empty responses when unconfigured
• Integrates resolveSpreeConfig and getClientForConfig for multi-tenant support
• Passes cache scope parameters to cached functions for proper cache isolation

src/lib/data/products.ts


7. src/lib/spree/config.ts ✨ Enhancement +121/-21

Multi-tenant Spree client configuration and proxy

• Refactors Spree client initialization to support multi-tenant configurations
• Implements client proxy pattern for lazy resolution of tenant-specific clients
• Adds resolveSpreeConfig to fetch tenant config from request context
• Maintains client cache keyed by baseUrl and publishableKey combination

src/lib/spree/config.ts


8. src/lib/data/categories.ts ✨ Enhancement +102/-11

Multi-tenant Spree client integration for categories

• Updates category data functions to use tenant-specific Spree configuration
• Adds validation for Spree config and returns empty responses when unconfigured
• Integrates resolveSpreeConfig and getClientForConfig for multi-tenant support
• Passes cache scope parameters to cached functions for proper cache isolation

src/lib/data/categories.ts


9. src/lib/homepage/index.ts ✨ Enhancement +164/-0

Homepage configuration builder with section merging

• Implements homepage configuration system with template interpolation
• Merges default homepage sections with tenant-specific overrides by type
• Provides getHomepageConfig and getHomepageSections functions
• Supports variable substitution in homepage content

src/lib/homepage/index.ts


10. src/lib/tenant/css-vars.ts ✨ Enhancement +105/-0

Tenant theme CSS variables builder

• Builds CSS custom properties from tenant theme configuration
• Maps spacing and border radius presets to CSS values
• Merges design style and theme configurations with proper precedence
• Exports buildCssVars and cssVarsToString for theme application

src/lib/tenant/css-vars.ts


11. src/lib/tenant/surface.ts ✨ Enhancement +125/-0

Tenant surface data accessor functions

• Provides accessor functions for tenant branding, description, logo, and social links
• Implements fallback chains to resolve values from multiple config sources
• Exports TenantLink interface and navigation link extraction
• Includes functions like getTenantBrandName, getTenantLogoUrl, getTenantSocialLinks

src/lib/tenant/surface.ts


12. src/lib/data/markets.ts ✨ Enhancement +54/-11

Multi-tenant Spree client integration for markets

• Updates market data functions to use tenant-specific Spree configuration
• Integrates resolveSpreeConfig and getClientForConfig for multi-tenant support
• Passes cache scope parameters to cached functions for proper cache isolation
• Maintains backward compatibility with existing market resolution logic

src/lib/data/markets.ts


13. src/lib/spree/config.test.ts 🧪 Tests +100/-0

Tests for multi-tenant Spree configuration

• Tests tenant Spree config resolution with mocked tenant configuration
• Validates client proxy routing through tenant-specific Spree clients
• Tests cache scope generation from tenant credentials
• Verifies error propagation when tenant resolution fails

src/lib/spree/config.test.ts


14. src/lib/seo.ts ✨ Enhancement +24/-7

Tenant-aware organization schema generation

• Updates buildOrganizationJsonLd to accept optional TenantConfig parameter
• Integrates tenant-specific branding, description, and social links into schema
• Falls back to environment variables when tenant config is unavailable
• Includes tenant social links in the sameAs array

src/lib/seo.ts


15. src/lib/metadata/store.ts ✨ Enhancement +17/-5

Tenant-aware store metadata generation

• Updates generateStoreMetadata to accept optional TenantConfig parameter
• Uses tenant branding, description, and site URL when available
• Falls back to environment variables and default store functions
• Applies tenant-specific Twitter handle to metadata

src/lib/metadata/store.ts


16. src/lib/metadata/category.ts ✨ Enhancement +15/-4

Tenant-aware category metadata generation

• Updates generateCategoryMetadata to accept optional TenantConfig parameter
• Resolves tenant config from request if not provided
• Uses tenant site URL and branding in OpenGraph metadata
• Applies tenant description to category metadata

src/lib/metadata/category.ts


17. src/lib/homepage/types.ts ✨ Enhancement +83/-0

Homepage configuration type definitions

• Defines TypeScript interfaces for homepage section configurations
• Includes hero, features, and featured products section types
• Provides button, theme, and media configuration interfaces
• Exports HomepageConfig with version and sections array

src/lib/homepage/types.ts


18. src/lib/tenant/__tests__/surface.test.ts 🧪 Tests +59/-0

Tests for tenant surface accessor functions

• Tests tenant surface accessor functions with sample tenant configuration
• Validates extraction of branding, description, site URL, and social links
• Tests navigation link extraction from tenant config
• Verifies fallback behavior across multiple config sources

src/lib/tenant/tests/surface.test.ts


19. src/lib/data/countries.ts ✨ Enhancement +23/-3

Multi-tenant Spree client integration for countries

• Updates country data functions to use tenant-specific Spree configuration
• Integrates resolveSpreeConfig and getClientForConfig for multi-tenant support
• Passes cache scope parameters to cached functions for proper cache isolation
• Maintains backward compatibility with existing country resolution logic

src/lib/data/countries.ts


20. src/lib/page-builder/types.ts ✨ Enhancement +55/-0

Dynamic page builder type definitions

• Defines TypeScript interfaces for dynamic page section configurations
• Includes rich-text and image-banner section types
• Reuses homepage section types for consistency
• Exports DynamicPageConfig and DynamicPagesConfig interfaces

src/lib/page-builder/types.ts


21. src/lib/metadata/product.ts ✨ Enhancement +8/-1

Tenant-aware product metadata generation

• Updates generateProductMetadata to accept optional TenantConfig parameter
• Resolves tenant config from request if not provided
• Uses tenant site URL in canonical URL generation
• Maintains backward compatibility with environment-based configuration

src/lib/metadata/product.ts


22. src/lib/tenant/request.ts ✨ Enhancement +42/-0

Request-based tenant configuration resolver

• Implements request-based tenant configuration resolution
• Extracts host from request headers with proxy support via TRUST_PROXY env var
• Provides getTenantConfigFromRequest to fetch tenant config by host
• Includes getRequestHost utility for header-based host extraction

src/lib/tenant/request.ts


23. src/lib/metadata/home.ts ✨ Enhancement +16/-3

Tenant-aware home page metadata generation

• Updates generateHomeMetadata to accept optional TenantConfig parameter
• Resolves tenant config from request if not provided
• Uses tenant branding, description, and site URL in metadata
• Falls back to environment variables and default store functions

src/lib/metadata/home.ts


24. next.config.ts ⚙️ Configuration changes +6/-0

Next.js configuration updates for multi-tenant deployment

• Adds output: "standalone" for containerized deployment support
• Adds Unsplash image domain for external image optimization
• Includes commented-out conditional cacheComponents configuration

next.config.ts


25. src/lib/tenant/types.ts ✨ Enhancement +34/-0

Tenant configuration type definitions

• Defines core tenant configuration interfaces
• Exports TenantConfig, PublicTenantConfig, and TenantSpreeConfig
• Includes payment keys, theme, SEO, and navigation configuration
• Specifies tenant source and fetch timestamp

src/lib/tenant/types.ts


26. src/lib/metadata/products.ts ✨ Enhancement +8/-1

Tenant-aware products listing metadata generation

• Updates generateProductsMetadata to accept optional TenantConfig parameter
• Resolves tenant config from request if not provided
• Uses tenant site URL in canonical URL generation
• Maintains backward compatibility with environment-based configuration

src/lib/metadata/products.ts


27. src/lib/store.ts Miscellaneous +1/-1

Default store name update to Olitt

• Updates default store name from "Spree Store" to "Olitt Store"

src/lib/store.ts


28. src/lib/spree/locale.ts ✨ Enhancement +2/-2

Tenant-aware locale configuration resolution

• Updates getLocaleOptions to use resolveSpreeConfig instead of getConfig
• Enables tenant-specific locale configuration resolution

src/lib/spree/locale.ts


29. src/lib/spree/index.ts ✨ Enhancement +8/-1

Multi-tenant Spree exports

• Exports new multi-tenant functions: getClientForConfig, resolveSpreeConfig,
 getSpreeCacheScope
• Maintains backward compatibility by keeping existing exports

src/lib/spree/index.ts


30. src/lib/tenant/index.ts ✨ Enhancement +4/-0

Tenant module barrel export

• Exports all tenant-related modules: normalize, olitt, resolvers, and types
• Provides centralized access to tenant configuration utilities

src/lib/tenant/index.ts


31. src/components/marketing/OlittFallbackPage.tsx ✨ Enhancement +222/-0

Olitt fallback marketing landing page component

• Implements fallback landing page for unconfigured Olitt domains
• Displays marketing content highlighting Olitt platform features
• Includes call-to-action buttons linking to Olitt services
• Shows the connected domain in messaging

src/components/marketing/OlittFallbackPage.tsx


32. src/components/home/HeroSection.tsx ✨ Enhancement +236/-40

Dynamic configurable hero section component

• Refactors hero section to accept HomepageHeroSectionConfig instead of translations
• Implements dynamic theming with customizable colors, spacing, and alignment
• Adds support for badges, stats, media with floating badges, and action buttons
• Includes animation effects and responsive layout with gradient backgrounds

src/components/home/HeroSection.tsx


33. src/app/[country]/[locale]/(checkout)/layout.tsx ✨ Enhancement +77/-27

Tenant-aware checkout layout with branding

• Integrates tenant configuration for checkout branding and navigation
• Uses resolveTenantBranding, resolveTenantNavigation, and resolveTenantFooter
• Applies tenant-specific logo, name, and policy links
• Implements CSS custom variables for theme colors in checkout layout

src/app/[country]/[locale]/(checkout)/layout.tsx


34. src/app/layout.tsx ✨ Enhancement +10/-5

Root layout async conversion and hydration fixes

• Converts root layout to async function for server-side operations
• Adds suppressHydrationWarning to html and body elements
• Updates CartProvider Suspense fallback to use CartProviderFallback
• Reorders imports for better organization

src/app/layout.tsx


35. src/components/layout/Footer.tsx ✨ Enhancement +93/-81

Multi-tenant footer configuration and dynamic link rendering

• Refactored footer to use tenant configuration for branding and navigation instead of hardcoded
 values
• Replaced static store name/description with resolveTenantBranding() function
• Converted hardcoded footer links into configurable arrays (resourceLinks, shopLinks,
 accountLinks, policyLinks)
• Made footer sections render dynamically from configuration with conditional rendering based on
 footerConfig

src/components/layout/Footer.tsx


36. src/app/[country]/[locale]/layout.tsx ✨ Enhancement +95/-25

Tenant configuration integration and provider setup

• Added tenant configuration loading via getTenantConfigByHost() and
 getTenantConfigFromRequest()
• Wrapped layout in TenantConfigProvider to make tenant config available to child components
• Added CSS variables generation from tenant config via buildCssVars()
• Improved error handling for market loading with fallback page rendering
• Added Suspense boundary with loading state for better UX

src/app/[country]/[locale]/layout.tsx


37. src/lib/page-builder/default-pages.json ⚙️ Configuration changes +107/-0

Default dynamic pages configuration schema

• New configuration file defining default dynamic pages structure
• Includes "about/brand-story" page with hero, rich-text, image-banner, and featured-products
 sections
• Each section includes theme configuration for colors and styling
• Demonstrates runtime-configurable page building without custom JSX

src/lib/page-builder/default-pages.json


38. src/components/home/FeaturedProductsSection.tsx ✨ Enhancement +58/-21

Featured products section configuration and theming

• Refactored to accept section prop with configuration instead of hardcoded translations
• Added theme support with dynamic styling via CSS variables
• Implemented resolveHref() helper for flexible link resolution
• Enhanced layout with better responsive design and conditional CTA rendering

src/components/home/FeaturedProductsSection.tsx


39. src/lib/homepage/default-homepage.json ⚙️ Configuration changes +103/-0

Default homepage configuration schema

• New configuration file defining default homepage structure with three sections
• Includes hero, features, and featured-products sections with theme colors
• Uses template variables like {{storeName}} for dynamic content
• Provides complete styling configuration for each section

src/lib/homepage/default-homepage.json


40. src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx ✨ Enhancement +42/-8

Checkout page tenant slots and configuration

• Added tenant configuration and currency resolution
• Integrated PageSectionsRenderer for before/after main content slots
• Implemented resolveTenantFixedPageSlots() to get configurable page sections
• Improved locale handling in redirect logic

src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx


41. src/components/page-builder/ImageBannerSection.tsx ✨ Enhancement +95/-0

Image banner section component for page builder

• New component for rendering image banner sections from configuration
• Supports overlay, height variants, and theme-based styling
• Includes CTA button with icon support and href resolution
• Responsive design with proper accessibility attributes

src/components/page-builder/ImageBannerSection.tsx


42. src/components/layout/Header.tsx ✨ Enhancement +31/-6

Multi-tenant header branding and navigation

• Added tenant configuration support for branding and navigation
• Replaced hardcoded store name with resolveTenantBranding()
• Added dynamic header navigation links from tenant config
• Integrated logo URL from tenant branding configuration

src/components/layout/Header.tsx


43. src/components/home/FeaturesSection.tsx ✨ Enhancement +81/-0

Features section component for homepage

• New component for rendering features section from configuration
• Supports dynamic icon rendering based on icon name
• Implements theme-based styling with background, foreground, and accent colors
• Responsive grid layout with hover effects

src/components/home/FeaturesSection.tsx


44. src/components/page-builder/RichTextSection.tsx ✨ Enhancement +68/-0

Rich text section component for page builder

• New component for rendering rich text sections from configuration
• Supports eyebrow, title, body paragraphs, and CTA button
• Implements theme-based styling and alignment options
• Includes href resolution for flexible link handling

src/components/page-builder/RichTextSection.tsx


45. src/app/[country]/[locale]/(storefront)/products/[slug]/page.tsx ✨ Enhancement +29/-0

Product page tenant slots and configuration

• Added tenant configuration and currency resolution
• Integrated PageSectionsRenderer for before/after product main content
• Implemented resolveTenantFixedPageSlots() for configurable page sections
• Enhanced product page with dynamic section rendering capability

src/app/[country]/[locale]/(storefront)/products/[slug]/page.tsx


46. src/app/[country]/[locale]/(storefront)/page.tsx ✨ Enhancement +37/-9

Homepage configuration-driven section rendering

• Refactored homepage to use configuration-driven section rendering
• Added getHomepageConfig() and getHomepageSections() for dynamic sections
• Integrated tenant branding via getTenantBrandName()
• Replaced hardcoded sections with dynamic switch-based rendering

src/app/[country]/[locale]/(storefront)/page.tsx


47. src/app/[country]/[locale]/(storefront)/pages/[...slug]/page.tsx ✨ Enhancement +69/-0

Dynamic page routing and rendering system

• New dynamic page route for runtime-configured pages
• Supports arbitrary page slugs with metadata generation
• Integrates DynamicPageRenderer for section-based rendering
• Uses tenant branding for page titles and descriptions

src/app/[country]/[locale]/(storefront)/pages/[...slug]/page.tsx


48. .github/workflows/docker-publish.yml ⚙️ Configuration changes +58/-0

Docker image build and publish workflow

• New GitHub Actions workflow for building and publishing Docker images
• Supports multi-platform builds (linux/amd64, linux/arm64)
• Implements branch-based tagging for main and dev branches
• Uses Docker Hub credentials from secrets for authentication

.github/workflows/docker-publish.yml


49. src/components/page-builder/PageSectionsRenderer.tsx ✨ Enhancement +70/-0

Page sections renderer component

• New component for rendering multiple page sections based on type
• Supports hero, features, featured-products, rich-text, and image-banner sections
• Provides flexible key generation and section mapping
• Serves as central renderer for dynamic page content

src/components/page-builder/PageSectionsRenderer.tsx


50. src/app/[country]/[locale]/(storefront)/layout.tsx ✨ Enhancement +8/-0

Storefront layout tenant configuration integration

• Added tenant configuration loading via host-based lookup
• Passed tenantConfig to Header and Footer components
• Integrated request header parsing for tenant host resolution
• Enhanced layout with multi-tenant support

src/app/[country]/[locale]/(storefront)/layout.tsx


51. src/contexts/__tests__/TenantContext.test.tsx 🧪 Tests +54/-0

Tenant context provider unit tests

• New test file for TenantContext provider and hook
• Tests that tenant config is properly accessible within provider
• Validates config structure including spree, theme, and payment keys

src/contexts/tests/TenantContext.test.tsx


52. src/app/[country]/[locale]/(storefront)/policies/[slug]/page.tsx ✨ Enhancement +8/-4

Policy page tenant branding integration

• Added tenant configuration loading for policy pages
• Integrated getTenantBrandName() and getTenantDescription() for dynamic metadata
• Enhanced metadata generation with tenant-specific branding

src/app/[country]/[locale]/(storefront)/policies/[slug]/page.tsx


53. src/contexts/CartContext.tsx ✨ Enhancement +22/-0

Cart context fallback provider

• Added EMPTY_CART_CONTEXT constant for fallback state
• Introduced CartProviderFallback component for graceful degradation
• Provides empty cart context when provider is unavailable

src/contexts/CartContext.tsx


54. src/contexts/TenantContext.tsx ✨ Enhancement +46/-0

Tenant configuration context and hooks

• New context for managing tenant configuration across the application
• Provides TenantConfigProvider wrapper and useTenantConfig() hook
• Includes specialized hooks for theme, navigation, payments, and spree config
• Implements memoization for performance optimization

src/contexts/TenantContext.tsx


55. Dockerfile ⚙️ Configuration changes +38/-0

Docker containerization configuration

• New multi-stage Docker build configuration for Next.js application
• Optimizes image size with separate stages for dependencies, building, and runtime
• Configures Node.js user for security and sets environment variables
• Exposes port 3001 for application access

Dockerfile


56. src/app/[country]/[locale]/loading.tsx ✨ Enhancement +22/-0

Locale layout loading state component

• New loading component for locale layout with tenant-aware branding
• Displays animated spinner with tenant store name
• Resolves tenant config from request headers for consistent branding

src/app/[country]/[locale]/loading.tsx


57. src/components/page-builder/DynamicPageRenderer.tsx ✨ Enhancement +29/-0

Dynamic page renderer wrapper component

• New wrapper component for rendering dynamic pages
• Delegates to PageSectionsRenderer with page-specific configuration
• Provides clean interface for page rendering with slug-based key prefix

src/components/page-builder/DynamicPageRenderer.tsx


58. .env.example ⚙️ Configuration changes +6/-2

Environment configuration for multi-tenant setup

• Commented out default Spree configuration variables
• Added new Olitt tenant config API environment variables
• Includes OLITT_API_URL and OLITT_LOOKUP_PATH for host-based store lookup

.env.example


59. .dockerignore ⚙️ Configuration changes +14/-0

Docker build context exclusion rules

• New file specifying files and directories to exclude from Docker build context
• Excludes git, node_modules, build artifacts, and environment files
• Preserves example environment files for reference

.dockerignore


Grey Divider

Qodo Logo

@kipsang01 kipsang01 changed the base branch from main to dev May 15, 2026 09:53
Comment thread src/contexts/TenantContext.tsx
@kipsang01 kipsang01 merged commit 039def0 into dev May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants