# How Branding Resolution Sources Are Prioritized in DeskcommCRM

> Discover how DeskcommCRM prioritizes branding resolution sources. Learn about the four-layer stack and field-by-field precedence for consistent branding.

- Repository: [Rafael Melgaço/DeskcommCRM](https://github.com/melgarafael/DeskcommCRM)
- Tags: internals
- Published: 2026-09-12

---

**DeskcommCRM resolves branding through a deterministic four-layer stack where organization settings take precedence over installation records, environment variables, and product defaults, applying field-by-field precedence in [`lib/branding/resolve.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding/resolve.ts).**

DeskcommCRM implements a sophisticated **branding resolution** system that merges configuration from multiple sources to produce a cohesive tenant identity. The architecture, maintained in the `melgarafael/DeskcommCRM` repository, evaluates **branding resolution sources** through a priority-based layer stack to determine the final application name, logo URL, and accent color. Understanding this precedence hierarchy is essential for administrators configuring multi-tenant deployments and white-label installations.

## The Four-Layer Stack for Branding Resolution

The resolver constructs a stack of **layers** (`CamadaDeMarca`) ordered from most specific to most generic. The core logic walks this stack sequentially, accepting the first non-empty value for each branding field.

### Organization Settings (Most Specific)

The top priority layer derives from `organizations.settings.branding`, a JSON column storing tenant-specific brand data. When present, values in this layer override all other sources. The function `camadaDaOrganizacao` in [`lib/branding/resolve.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding/resolve.ts) transforms the organization's branding payload into a structured layer object. This ensures that end-customer configurations always take precedence over reseller defaults.

### Installation Records

The middle layer reads from the `platform_branding` database table, configured by resellers or installers via the administrative UI. Accessed through `camadaDaInstalacao`, this layer provides the default identity for a specific installation while allowing organizations to override it. The database row contains fields for name, logo references, and color seeds that persist across code deployments.

### Environment Variables

The generic layer sources values from the host environment via `camadaDoAmbiente`. It reads `.env` variables including `APP_NAME`, `APP_LOGO_URL`, and `APP_ACCENT_HEX`. These seed values survive code upgrades and provide consistent branding for login screens and email templates when no higher-layer configuration exists.

### Product Defaults (Base Layer)

The final fallback consists of hard-coded constants such as `DEFAULT_APP_NAME`, imported from [`lib/branding.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding.ts) as `reguaDoProduto`. This base layer guarantees that every branding field has a valid value even when no external configuration is provided.

## Core Resolution Logic in lib/branding/resolve.ts

The resolution engine employs two complementary strategies for field extraction. The helper `primeiroDefinido` iterates through the supplied layer array and returns the first non-empty string for textual fields like `nome` and `logoUrl`. For color resolution, `resolverCor` traverses the same stack, stopping at the first layer that yields a valid `accent_hex` value.

This architecture implements **field-by-field precedence**: the resolver never deletes fields from lower layers. If a higher layer provides a name but omits a logo, the logo falls back silently to the next layer that defines it. This granular approach allows administrators to configure partial brands without losing inherited elements.

## Implementing the Priority Stack in Code

The layer ordering is explicitly declared in [`app/layout.tsx`](https://github.com/melgarafael/DeskcommCRM/blob/main/app/layout.tsx), where the application constructs the stack before server-side rendering. The sequence passed to `resolverMarca` determines the evaluation order:

```typescript
resolverMarca(
  [
    camadaDaOrganizacao(orgBranding),   // most specific
    camadaDaInstalacao(installRow),     // reseller configuration
    camadaDoAmbiente(process.env),      // generic environment
  ],
  reguaDoProduto,
);

```

The following example demonstrates building the complete stack and resolving the final brand object:

```typescript
// 1️⃣ Build the layer stack
import { camadaDoAmbiente } from '@/lib/branding/resolve';
import { camadaDaInstalacao } from '@/lib/branding/resolve';
import { camadaDaOrganizacao } from '@/lib/branding/resolve';
import { resolverMarca } from '@/lib/branding/resolve';
import { reguaDoProduto } from '@/lib/branding/regua-do-produto';

const envLayer = camadaDoAmbiente({
  APP_NAME: process.env.APP_NAME,
  APP_LOGO_URL: process.env.APP_LOGO_URL,
  APP_ACCENT_HEX: process.env.APP_ACCENT_HEX,
});

const instalLayer = camadaDaInstalacao(await fetchPlatformBranding()); // DB row
const orgLayer   = camadaDaOrganizacao(await fetchOrgBranding(orgId)); // org JSON

// 2️⃣ Resolve the brand
const brand = resolverMarca([orgLayer, instalLayer, envLayer], reguaDoProduto);

// 3️⃣ Use the resolved brand in a component
function Header() {
  return (
    <header style={{ '--color-brand': brand.cor?.semente }}>
      {brand.logoUrl ? <img src={brand.logoUrl} alt={brand.name} /> : <span>{brand.initial}</span>}
    </header>
  );
}

```

## How Each Branding Field Is Resolved

**Name resolution** follows the strict stack order: organization settings take priority, followed by installation records, then environment variables, finally falling back to `DEFAULT_APP_NAME`. 

**Logo URL resolution** applies the same precedence with an additional business rule implemented in the installation layer: an uploaded file specified via `logo_path` wins over a plain URL string when both are present in the same layer.

**Accent color resolution** behaves differently for invalid values. While `primeiroDefinido` simply skips empty strings, `resolverCor` validates hex codes. Invalid or empty color values in a higher layer generate diagnostic entries but do not override valid colors defined in lower layers, ensuring visual consistency even when administrators enter malformed color codes.

## Debugging Branding Resolution with Diagnostics

Every resolution operation returns a `motivos` array containing `MotivoDaBranca` objects that explain the decision process. These diagnostics identify which layer supplied each value and why specific colors were rejected. Administrators can inspect these logs to trace inheritance conflicts:

```typescript
// Inspect the diagnostics (useful for admin screens)
brand.motivos.forEach(m => {
  console.log(`[${m.origem}] ${m.codigo}: ${m.detalhe}`);
});

```

The diagnostic system records the origin layer (origem), specific reason code (codigo), and detailed explanation (detalhe) for every fallback or override decision, enabling transparent troubleshooting across the **branding resolution** pipeline.

## Summary

- **Organization settings** (`organizations.settings.branding`) provide the highest priority **branding resolution source**, overriding all other configurations.
- **Installation records** (`platform_branding` database) serve as the middle tier for reseller-defined defaults.
- **Environment variables** (`.env` files) supply generic seeds that persist through code upgrades.
- **Product defaults** (`DEFAULT_APP_NAME`) guarantee fallback values when no external configuration exists.
- The `resolverMarca` function in [`lib/branding/resolve.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding/resolve.ts) implements field-by-field precedence, allowing partial brand inheritance.
- The layer stack is constructed in [`app/layout.tsx`](https://github.com/melgarafael/DeskcommCRM/blob/main/app/layout.tsx) and processed via `primeiroDefinido` and `resolverCor` helpers.
- Diagnostic data (`MotivoDaMarca`) tracks every resolution decision for administrative transparency.

## Frequently Asked Questions

### What happens if an organization defines a name but leaves the logo empty?

The system applies **field-by-field precedence**. The organization layer supplies the name while the resolver falls back to the installation or environment layer for the logo URL. This partial inheritance allows flexible branding configurations without requiring complete redefinition of all assets.

### Can environment variables override organization branding?

No. The stack order in [`app/layout.tsx`](https://github.com/melgarafael/DeskcommCRM/blob/main/app/layout.tsx) places `camadaDaOrganizacao` at index 0 (most specific) and `camadaDoAmbiente` at the end. Because `primeiroDefinido` returns the first non-empty value it encounters, organization settings always take precedence over `.env` configurations.

### How does DeskcommCRM handle invalid accent colors in high-priority layers?

Invalid hex codes or empty strings in higher layers are recorded as diagnostics via the `MotivoDaMarca` interface but do not override valid colors from lower layers. The `resolverCor` function continues iterating down the stack until it finds a valid color seed, ensuring the UI receives a properly formatted value even when administrators enter malformed data.

### Where are the TypeScript types for branding layers defined?

The type definitions for installation layers reside in [`lib/branding/instalacao.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding/instalacao.ts) (`LinhaDaInstalacao`), while organization branding types are located in [`lib/branding/organizacao.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding/organizacao.ts) (`MarcaDaOrganizacao`). The public API and default constants are exported from [`lib/branding.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/branding.ts).