## Advanced Features

This section covers extended functionality for complex business requirements and system integration.

### Node Extensions

String data types define maximum length and validation rules for text properties.

```typescript
import { StringDataType } from '@sage/xtrem-core';

// Basic string data type with length constraint
export const descriptionDataType = new StringDataType({ maxLength: 250 });

// Email-specific string data type
export const emailDataType = new StringDataType({ maxLength: 100 });
```

**Usage in Properties:**

```typescript
@decorators.stringProperty<ShowCaseProduct, 'product'>({
    isStored: true,
    isPublished: true,
    lookupAccess: true,
    duplicateRequiresPrompt: true,
    dataType: () => xtremShowCase.dataTypes.descriptionDataType,
    async duplicatedValue() {
        return `New ${await this.product}`;
    },
})
readonly product: Promise<string>;

@decorators.stringProperty<ShowCaseProduct, 'email'>({
    isStored: true,
    isPublished: true,
    dataType: () => xtremShowCase.dataTypes.emailDataType,
})
readonly email: Promise<string>;
```

### Specialized Value Types

XTREM provides specialized libraries for handling complex value types that require sophisticated parsing, formatting, and manipulation capabilities. These libraries extend beyond basic data types to provide business-ready functionality for dates, decimals, and other critical value types.

#### Date and DateTime Value Management (@sage/xtrem-date-time)

The `@sage/xtrem-date-time` library provides comprehensive date and datetime handling with the `DateValue` class as its core component.

**DateValue Class Features:**

- **Efficient Storage**: Uses internal packed number format (YYYYMMDD) for optimal performance
- **Comprehensive Manipulation**: Methods for adding/subtracting days, months, years, and working with weeks
- **Formatting & Parsing**: Locale-aware formatting with moment.js integration and robust parsing from various formats
- **Timezone Support**: Built-in timezone handling for datetime operations
- **Business Logic**: Week calculations, beginning/end of periods, and date range operations

```typescript
import { DateValue, Datetime, DatetimeRange } from '@sage/xtrem-date-time';

// Creating DateValue instances
const today = DateValue.today();
const specificDate = DateValue.make(2024, 3, 15);
const parsedDate = DateValue.parse('2024-03-15');
const fromJs = DateValue.fromJsDate(new Date());

// Date manipulation
const tomorrow = today.addDays(1);
const nextMonth = today.addMonths(1);
const nextYear = today.addYears(1);
const weekStart = today.begOfWeek();
const weekEnd = today.endOfWeek();

// Formatting with locale support
const formatted = today.format('YYYY-MM-DD');
const localized = today.format('MMM DD, YYYY');

// DateTime operations
const now = Datetime.now();
const specific = Datetime.make(2024, 3, 15, 14, 30, 0);

// Date ranges
const range = new DatetimeRange(startDate, endDate);
```

**Property Decorators for Date/DateTime:**

```typescript
// Date properties
@decorators.dateProperty<ShowCaseProduct, 'releaseDate'>({
    isStored: true,
    isPublished: true,
    defaultValue() {
        return date.today();
    },
})
readonly releaseDate: Promise<date | null>;

// DateTime properties
@decorators.datetimeProperty<ShowCaseProduct, 'createdAt'>({
    isPublished: true,
    isStored: true,
    isNullable: true,
})
readonly createdAt: Promise<datetime | null>;

// DateTime range properties
@decorators.datetimeRangeProperty<ShowCaseProduct, 'manufacturedWithin'>({
    isPublished: true,
    isStored: true,
    isNullable: true,
})
readonly manufacturedWithin: Promise<DatetimeRange | null>;
```

**UI Integration Examples:**

```typescript
// Date field in UI
@ui.decorators.dateField<ShowCaseProduct>({
    parent() {
        return this.block;
    },
    title: 'Release Date',
})
releaseDate: ui.fields.Date;

// DateTime range field
@ui.decorators.dateTimeRangeField<ShowCaseProduct>({
    parent() {
        return this.block;
    },
    title: 'Manufacturing Period',
    timeZone: 'UTC',
})
manufacturedWithin: ui.fields.DatetimeRange;

// Calendar field for event display
@ui.decorators.calendarField<Calendar>({
    bind: 'products',
    startDateField: 'releaseDate',
    endDateField: 'endingDate',
    eventCard: {
        title: ui.nestedFields.text({ bind: 'product' }),
        line2: ui.nestedFields.numeric({ bind: 'qty' }),
    },
})
calendar: ui.fields.Calendar;
```

#### Decimal Value Management (@sage/xtrem-decimal)

The `@sage/xtrem-decimal` library provides precise decimal arithmetic using the Decimal.js library, ensuring accurate financial and scientific calculations.

**Decimal Class Features:**

- **Arbitrary Precision**: Based on Decimal.js for precise decimal arithmetic
- **Type Safety**: Seamless integration with TypeScript `decimal` type
- **Rounding Control**: Configurable rounding modes (roundUp, roundDown, roundHalfUp, etc.)
- **Validation**: Built-in precision and scale validation
- **Conversion Utilities**: Methods for converting between numbers, strings, and Decimal instances

```typescript
import { Decimal, decimal } from '@sage/xtrem-decimal';
import { DecimalDataType, RoundingMode } from '@sage/xtrem-core';

// Creating Decimal instances
const price = Decimal.make('123.456');
const quantity = new Decimal(10);
const total = price.mul(quantity);

// Rounding operations
const rounded = Decimal.roundAt(123.456, 2); // 123.46
const roundedUp = price.round(); // 123

// Utility functions
import { toDecimal, sum, max, min, abs } from '@sage/xtrem-decimal';

const decimal1 = toDecimal('100.50');
const decimal2 = toDecimal(200.75);
const totalSum = sum(decimal1, decimal2, 50.25);
const maximum = max(decimal1, decimal2, 150);
```

**DecimalDataType Configuration:**

```typescript
// Basic decimal data type
export const defaultDecimalDataType = new DecimalDataType({
    precision: 9, // Total number of digits
    scale: 3, // Digits after decimal point
    roundingMode: RoundingMode.roundHalfUp,
});

// Financial precision for costs
export const costDataType = new DecimalDataType({
    precision: 14,
    scale: 4,
});

// Currency-dependent pricing
export const priceDataType = new DecimalDataType({
    currencyPropertyName: 'currency',
});

// High precision for unit conversions
export const coefficientDataType = new DecimalDataType({
    precision: 16,
    scale: 10,
});

// Exchange rates with maximum precision
export const exchangeRate = new DecimalDataType({
    precision: 20,
    scale: 10,
});
```

**Property Decorators for Decimals:**

```typescript
// Basic decimal property
@decorators.decimalProperty<ShowCaseProduct, 'listPrice'>({
    isStored: true,
    isPublished: true,
    dataType: () => xtremShowCase.dataTypes.defaultDecimalDataType,
    defaultValue() {
        return 0;
    },
})
readonly listPrice: Promise<decimal>;

// Calculated decimal property with dependencies
@decorators.decimalProperty<ShowCaseProduct, 'netPrice'>({
    isStored: true,
    isPublished: true,
    dataType: () => xtremShowCase.dataTypes.defaultDecimalDataType,
    dependsOn: ['listPrice', 'qty'],
    async defaultValue() {
        if ((await this.qty) > 10) {
            return (await this.listPrice) * 0.9; // 10% discount for bulk
        }
        return this.listPrice;
    },
    async control(ctx, val) {
        await ctx.error.if(val).is.negative();
    },
})
readonly netPrice: Promise<decimal>;

// Computed decimal property
@decorators.decimalProperty<ShowCaseProduct, 'total'>({
    isPublished: true,
    lookupAccess: true,
    dataType: () => xtremShowCase.dataTypes.defaultDecimalDataType,
    dependsOn: ['amount', 'tax'],
    async getValue() {
        return (await this.amount) + (await this.tax);
    },
})
readonly total: Promise<decimal>;
```

**Specialized Decimal Types:**

```typescript
// Percentage with constraints
export class PercentageDataType extends DecimalDataType {
    constructor(options: PercentageDataTypeOptions) {
        super({
            ...options,
            minValue: 0,
            maxValue: 100,
        });
    }
}

// Example percentage instances
export const efficiencyPercentage = new PercentageDataType({
    precision: 5,
    scale: 2,
    minValue: 0.01,
    maxValue: 100,
    defaultPercentage: 100,
});

export const scrapFactorPercentage = new PercentageDataType({
    roundingMode: RoundingMode.roundUp,
    minValue: 0,
    maxValue: 99.99,
    defaultPercentage: 0,
});
```

**UI Integration for Decimals:**

```typescript
// Numeric field with decimal support
@ui.decorators.numericField<ShowCaseProduct>({
    parent() {
        return this.block;
    },
    title: 'List Price',
    precision: 2,
    allowEmptyValue: false,
})
listPrice: ui.fields.Numeric;

// Currency field with decimal precision
@ui.decorators.numericField<ShowCaseProduct>({
    parent() {
        return this.block;
    },
    title: 'Amount',
    precision: 4,
    formatStyle: 'currency',
    currency: 'USD',
})
amount: ui.fields.Numeric;
```

#### Practical Examples from Showcase

The `@sage/xtrem-show-case` package demonstrates real-world usage of these specialized value types:

**Date/DateTime Examples:**

```typescript
// Date field showcase with formatting
@ui.decorators.page<DateField>({
    onLoad() {
        const today = DateValue.today();
        const now = Datetime.now();

        // Demonstrate date calculations
        this.today.value = today.toString();
        this.tomorrow.value = today.addDays(1).toString();
        this.yesterday.value = today.addDays(-1).toString();
        this.fourDaysAgo.value = today.addDays(-4).toString();
        this.twoWeeksAgo.value = today.addDays(-14).toString();
        this.threeMonthsAgo.value = today.addMonths(-3).toString();
        this.fiveYearsAgo.value = today.addYears(-5).toString();
    },
})

// Calendar integration for project timelines
@ui.decorators.calendarField<Calendar>({
    bind: 'products',
    startDateField: 'releaseDate',
    endDateField: 'endingDate',
    eventCard: {
        title: ui.nestedFields.text({ bind: 'product' }),
        titleRight: ui.nestedFields.text({ bind: 'description' }),
        line2: ui.nestedFields.numeric({ bind: 'qty' }),
        line2Right: ui.nestedFields.numeric({ bind: 'tax' }),
    },
})
```

**Decimal Examples:**

```typescript
// Complex pricing calculations
@decorators.decimalProperty<ShowCaseProduct, 'total'>({
    dependsOn: ['amount', 'tax'],
    async getValue() {
        const amount = await this.amount;
        const tax = await this.tax;
        return Decimal.make(amount).add(tax) as unknown as decimal;
    },
})

// Quantity-based pricing with decimal precision
export const quantity = new DecimalDataType({ precision: 10, scale: 5 });
export const price = new DecimalDataType({ precision: 10, scale: 3 });
export const unitConversionCoefficient = new DecimalDataType({ precision: 20, scale: 5 });
```

**Integration Best Practices:**

1. **Type Consistency**: Always use the appropriate `decimal` or `date` types from XTREM core
2. **Validation**: Implement proper validation for decimal precision, scale, and date ranges
3. **UI Binding**: Use specialized field decorators that understand the value types
4. **Performance**: Leverage packed number formats for dates and efficient decimal operations
5. **Localization**: Use formatting methods that respect user locale settings

### Internationalization and Localization

The Xtrem framework provides comprehensive internationalization (i18n) support for creating applications that can be translated and adapted for different languages and regions. The localization system is built around JSON-based resource files and a powerful `localize` function that supports parameterized text and fallback locales.

#### Localization Architecture

**File Structure:**
Every Xtrem package contains an `i18n` directory with locale-specific JSON files:

```
lib/i18n/
├── base.json      # Default English strings (source of truth)
├── en-US.json     # US English translations
├── en-GB.json     # British English translations
├── fr-FR.json     # French translations
├── de-DE.json     # German translations
├── es-ES.json     # Spanish translations
├── it-IT.json     # Italian translations
├── zh-CN.json     # Chinese translations
├── pl-PL.json     # Polish translations
├── pt-BR.json     # Brazilian Portuguese
├── pt-PT.json     # European Portuguese
└── ar-SA.json     # Arabic translations
```

**Key Naming Convention:**
Localization keys follow a hierarchical naming pattern:

```
@sage/package-name/category__subcategory__item__property
```

Examples from `@sage/xtrem-show-case`:

```json
{
    "@sage/xtrem-show-case/business-action": "Business action {{0}}",
    "@sage/xtrem-show-case/enums__show_case_product_category__great": "Great",
    "@sage/xtrem-show-case/data_types__show_case_product__name": "Show case product",
    "@sage/xtrem-show-case/activity__show_case_customer_activity__name": "Show case customer activity",
    "@sage/xtrem-show-case/menu_item__application-pages": "Sample Application pages"
}
```

#### Client-Side Localization

**Using the `ui.localize` Function:**

```typescript
import * as ui from '@sage/xtrem-ui';

// Basic localization
const message = ui.localize('@sage/xtrem-show-case/demo', 'Hi Joe');

// Parameterized localization with numbered placeholders
const businessAction = ui.localize(
    '@sage/xtrem-show-case/business-action',
    'Business action {{0}}',
    ['Save']
);

// Parameterized localization with object parameters
const customerRenamed = ui.localize(
    '@sage/xtrem-show-case/async-mutation-customer-renamed-description',
    'Customer {{originalName}} has been renamed to {{newName}}',
    { originalName: 'John Doe', newName: 'Jane Smith' }
);

// Page action usage
@ui.decorators.pageAction<ActionButtons>({
    title: 'Save',
    onClick() {
        this.resultField.value = ui.localize(
            '@sage/xtrem-show-case/crud-button-save',
            'Save CRUD button'
        );
    },
})
saveCrud: ui.PageAction;
```

**Decorator-Driven Localization:**
UI components can automatically use localized strings through decorators:

```typescript
@ui.decorators.page<ShowCaseProduct>({
    title: 'Page - Action Buttons',  // Automatically creates localization key
    // Creates: @sage/package-name/pages__page_name____title
})
export class ShowCaseProduct extends ui.Page { }

@ui.decorators.textField<ShowCaseProduct>({
    title: 'Product Name',
    helperText: 'Enter the product name',
    // Creates: @sage/package-name/pages__page_name__field_name____title
    // Creates: @sage/package-name/pages__page_name__field_name____helperText
})
productName: ui.fields.Text<ShowCaseProduct>;
```

#### Server-Side Localization

**Context-Based Localization:**

```typescript
import { Context, ValidationContext } from '@sage/xtrem-core';

// Using localization in validation context
@decorators.node<ShowCaseProduct>({
    controlEnd(cx: ValidationContext) {
        cx.error.add(cx.localize(
            '@sage/xtrem-show-case/validation-error',
            'Invalid product configuration'
        ));

        // With parameters
        cx.warning.add(cx.localize(
            '@sage/xtrem-show-case/stock-warning',
            'Low stock: {{quantity}} remaining',
            { quantity: 5 }
        ));
    },
})
export class ShowCaseProduct extends Node { }

// Using localization in business logic
async processOrder(context: Context) {
    const message = context.localize(
        '@sage/xtrem-manufacturing/work-order/noSequenceCounter',
        'No sequence counter entry with the code {{code}}',
        { code: this._sequenceNumberCode }
    );

    context.diagnoses.push({
        message,
        path: [],
        severity: ValidationSeverity.warn,
    });
}
```

#### Enum Localization

Enums are automatically localized using a specific naming pattern:

```typescript
export enum ShowCaseProductCategoryEnum {
    great,
    good,
    ok,
    notBad,
    awful,
}

export const showCaseProductCategoryDataType = new EnumDataType<ShowCaseProductCategory>({
    enum: ShowCaseProductCategoryEnum,
    filename: __filename,
});
```

**Corresponding localization keys:**

```json
{
    "@sage/xtrem-show-case/enums__show_case_product_category__great": "Great",
    "@sage/xtrem-show-case/enums__show_case_product_category__good": "Good",
    "@sage/xtrem-show-case/enums__show_case_product_category__ok": "Ok",
    "@sage/xtrem-show-case/enums__show_case_product_category__notBad": "Not bad",
    "@sage/xtrem-show-case/enums__show_case_product_category__awful": "Awful"
}
```

#### Fallback Resolution

The framework provides intelligent fallback resolution when a translation is missing:

```
Requested Locale: fr-FR
Fallback Path: fr-FR → en-US → en-GB → base

1. Look for key in fr-FR.json
2. If not found, look in en-US.json
3. If not found, look in en-GB.json
4. If not found, use base.json
5. If not found, return the default literal
```

#### Best Practices

**1. Key Consistency:**

- Always use the package name as the key prefix
- Use descriptive, hierarchical key names
- Be consistent with naming conventions across your package

**2. Default Values:**

- Always provide meaningful default English text
- Default text should be clear and professional
- Use proper grammar and punctuation

**3. Parameter Usage:**

- Use descriptive parameter names in object-style parameters
- Prefer object parameters over array parameters for readability
- Keep parameter lists small and focused

**4. Extraction and Build Process:**

- Localization keys are extracted during build time (`pnpm run build`)
- Changes to default literals will invalidate existing translations
- Test localization thoroughly in different languages

**5. Package Organization:**

```typescript
// Centralized localization file
// lib/client-functions/localize.ts
import * as ui from '@sage/xtrem-ui';

export const confirmDelete = ui.localize(
    '@sage/xtrem-master-data/pages-confirm-delete',
    'Are you sure you want to delete this item?',
);

export const DialogOptionsYesNo: ui.dialogs.DialogOptions = {
    acceptButton: {
        text: ui.localize('@sage/xtrem-master-data/pages-confirm-yes', 'Yes'),
    },
    cancelButton: {
        text: ui.localize('@sage/xtrem-master-data/pages-confirm-no', 'No'),
    },
    resolveOnCancel: false,
};
```

**6. Translation Management:**

- The `base.json` file is the source of truth
- Localization teams work from extracted base strings
- Empty values in locale files indicate missing translations
- Changes to base strings trigger re-translation requirements

### Decimal Data Types

Decimal data types specify precision and scale for numeric values with decimal places.

```typescript
import { DecimalDataType } from '@sage/xtrem-core';

// Default decimal with precision and scale
export const defaultDecimalDataType = new DecimalDataType({
    precision: 9,
    scale: 3,
});
```

**Usage in Properties:**

```typescript
@decorators.decimalProperty<ShowCaseProduct, 'listPrice'>({
    isStored: true,
    isPublished: true,
    dataType: () => xtremShowCase.dataTypes.defaultDecimalDataType,
    defaultValue() {
        return 0;
    },
})
readonly listPrice: Promise<decimal>;

@decorators.decimalProperty<ShowCaseProvider, 'decimalField'>({
    dataType: () => xtremShowCase.dataTypes.defaultDecimalDataType,
    isStored: true,
    isPublished: true,
    lookupAccess: true,
})
readonly decimalField: Promise<decimal>;
```

### Reference Data Types

Reference data types define how references to other nodes are displayed and behave in lookups.

```typescript
import { ReferenceDataType } from '@sage/xtrem-core';

// Product reference with lookup configuration
export const showCaseProduct = new ReferenceDataType({
    reference: () => ShowCaseProduct,
    isDefault: true,
    lookup: {
        valuePath: 'product',
        helperTextPath: 'description',
        columnPaths: [
            'product',
            'description',
            'hotProduct',
            'qty',
            'category',
            'releaseDate',
            'email',
            'amount',
            'tax',
            'total',
        ],
        tunnelPage: '@sage/xtrem-show-case/StandardShowCaseProduct',
        imageFieldPath: 'imageField',
    },
});

// Provider reference with simpler lookup
export const showCaseProvider = new ReferenceDataType({
    reference: () => ShowCaseProvider,
    isDefault: true,
    lookup: {
        valuePath: 'textField',
        columnPaths: ['textField', 'integerField', 'dateField'],
        tunnelPage: '@sage/xtrem-show-case/StandardShowCaseProvider',
        imageFieldPath: 'logo',
    },
});

// Employee reference with name fields
export const showCaseEmployee = new ReferenceDataType({
    reference: () => ShowCaseEmployee,
    isDefault: true,
    lookup: {
        valuePath: 'firstName',
        helperTextPath: 'lastName',
        columnPaths: ['firstName', 'lastName', 'email'],
        tunnelPage: '@sage/xtrem-show-case/ShowCaseEmployee',
    },
});

// Country reference with code and phone
export const showCaseCountry = new ReferenceDataType({
    reference: () => ShowCaseCountry,
    isDefault: true,
    lookup: {
        valuePath: 'name',
        helperTextPath: 'code',
        columnPaths: ['name', 'code', 'phoneCountryCode'],
        tunnelPage: '@sage/xtrem-show-case/ShowCaseCountry',
    },
});
```

**Usage in Properties:**

```typescript
@decorators.referenceProperty<ShowCaseProduct, 'provider'>({
    isPublished: true,
    lookupAccess: true,
    isStored: true,
    dataType: () => xtremShowCase.dataTypes.showCaseProvider,
    node: () => xtremShowCase.nodes.ShowCaseProvider,
})
readonly provider: Reference<xtremShowCase.nodes.ShowCaseProvider | null>;

@decorators.referenceProperty<ShowCaseProduct, 'designerEmployee'>({
    isPublished: true,
    isNullable: true,
    isStored: true,
    node: () => xtremShowCase.nodes.ShowCaseEmployee,
    dataType: () => xtremShowCase.dataTypes.showCaseEmployee,
})
readonly designerEmployee: Reference<xtremShowCase.nodes.ShowCaseEmployee | null>;
```

### Enum Data Types

IMPORTANT: Each enum must be declared in its own file.

IMPORTANT: Enum members should not be assigned a value, we should use the default value

Enum data types define a fixed set of values that a property can have.

```typescript
import { EnumDataType } from '@sage/xtrem-core';

// Discount type enumeration
export enum ShowCaseDiscountTypeEnum {
    newProductPromotion,
    compensation,
}

export type ShowCaseDiscountType = keyof typeof ShowCaseDiscountTypeEnum;

export const showCaseDiscountTypeDataType = new EnumDataType<ShowCaseDiscountType>({
    enum: ShowCaseDiscountTypeEnum,
    filename: __filename,
});

// Order type enumeration
export enum ShowCaseOrderTypeEnum {
    online,
    inStore,
}

export type ShowCaseOrderType = keyof typeof ShowCaseOrderTypeEnum;

export const showCaseOrderTypeDataType = new EnumDataType<ShowCaseOrderType>({
    enum: ShowCaseOrderTypeEnum,
    filename: __filename,
});

// Product category enumeration
export enum ShowCaseProductCategoryEnum {
    electronics,
    furniture,
    clothing,
    books,
    toys,
}

export type ShowCaseProductCategory = keyof typeof ShowCaseProductCategoryEnum;

export const showCaseProductCategoryDataType = new EnumDataType<ShowCaseProductCategory>({
    enum: ShowCaseProductCategoryEnum,
    filename: __filename,
});
```

**Usage in Properties:**

```typescript
@decorators.enumProperty<ShowCaseProduct, 'category'>({
    isStored: true,
    isPublished: true,
    lookupAccess: true,
    dataType: () => xtremShowCase.dataTypes.showCaseProductCategoryDataType,
})
readonly category: Promise<xtremShowCase.enums.ShowCaseProductCategory>;

@decorators.enumProperty<ShowCaseInvoiceLine, 'discountType'>({
    isPublished: true,
    isStored: true,
    dataType: () => xtremShowCase.dataTypes.showCaseDiscountTypeDataType,
})
readonly discountType: Promise<xtremShowCase.enums.ShowCaseDiscountType>;
```

### Text Stream Data Types

Text stream data types handle large text content with specific content type restrictions.

```typescript
import { TextStreamDataType } from '@sage/xtrem-core';

// Document text stream with content type restrictions
const documentDataType = new TextStreamDataType({
    maxLength: 1000000,
    allowedContentTypes: ['text/html', 'application/json', 'text/plain'],
});
```

**Usage in Properties:**

```typescript
@decorators.textStreamProperty<ShowCaseProvider, 'document'>({
    dataType: () =>
        new TextStreamDataType({
            maxLength: 1000000,
            allowedContentTypes: ['text/html', 'application/json', 'text/plain'],
        }),
    isPublished: true,
    isStored: true,
})
readonly document: Promise<TextStream>;
```

### String Array Data Types

String array data types define arrays of strings with validation.

```typescript
import { StringArrayDataType } from '@sage/xtrem-core';

// String array with item validation
const entriesDataType = new StringArrayDataType({
    itemDataType: new StringDataType({ maxLength: 100 }),
    maxLength: 10,
});
```

**Usage in Properties:**

```typescript
@decorators.stringArrayProperty<ShowCaseProduct, 'entries'>({
    isStored: true,
    isPublished: true,
    dataType: () => entriesDataType,
})
readonly entries: Promise<string[]>;
```

### Data Type Organization

In the ShowCase package, data types are organized in the `/lib/data-types/` directory:

```typescript
// /lib/data-types/index.ts
export * from './default-decimal-data-type';
export * from './description-data-type';
export * from './email-data-type';
export * from './show-case-country';
export * from './show-case-employee';
export * from './show-case-product';
export * from './show-case-provider';
```

**Module Export Pattern:**

```typescript
// In the main index.ts file
import * as dataTypes from './lib/data-types/_index';

export { dataTypes };
```

This allows clean imports in nodes and other components:

```typescript
import * as xtremShowCase from '@sage/xtrem-show-case';

// Use data types
dataType: () => xtremShowCase.dataTypes.descriptionDataType,
dataType: () => xtremShowCase.dataTypes.showCaseProvider,
```

Node extensions allow you to extend existing nodes from other packages without modifying the original implementation. The `@sage/xtrem-show-case-bundle` package provides comprehensive examples of node extension patterns.

#### Basic Node Extension

A node extension extends an existing node by adding new properties, overriding existing properties, or adding new behavior.

```typescript
import { decorators, NodeExtension } from '@sage/xtrem-core';
import * as xtremShowCase from '@sage/xtrem-show-case';
import * as xtremShowCaseBundle from '../../index';

@decorators.nodeExtension<ShowCaseProductExtension>({
    extends: () => xtremShowCase.nodes.ShowCaseProduct,
})
export class ShowCaseProductExtension extends NodeExtension<xtremShowCase.nodes.ShowCaseProduct> {
    @decorators.referenceProperty<ShowCaseProductExtension, 'biodegradabilityCategory'>({
        isStored: true,
        isNullable: true,
        isPublished: true,
        node: () => xtremShowCaseBundle.nodes.BiodegradabilityCategory,
    })
    readonly biodegradabilityCategory: Promise<xtremShowCaseBundle.nodes.BiodegradabilityCategory | null>;

    @decorators.stringPropertyOverride<ShowCaseProductExtension, 'description'>({})
    readonly description: Promise<string>;
}

declare module '@sage/xtrem-show-case/lib/nodes/show-case-product' {
    export interface ShowCaseProduct extends ShowCaseProductExtension {}
}
```

### Extension Components

#### 1. Extension Decorator

The `@decorators.nodeExtension` decorator specifies which node to extend:

```typescript
@decorators.nodeExtension<ShowCaseProductExtension>({
    extends: () => xtremShowCase.nodes.ShowCaseProduct,
})
```

#### 2. Extension Class

The extension class extends `NodeExtension<T>` where T is the target node type:

```typescript
export class ShowCaseProductExtension extends NodeExtension<xtremShowCase.nodes.ShowCaseProduct> {
    // Extension properties and methods
}
```

#### 3. Module Declaration

The module declaration merges the extension with the original node interface:

```typescript
declare module '@sage/xtrem-show-case/lib/nodes/show-case-product' {
    export interface ShowCaseProduct extends ShowCaseProductExtension {}
}
```

#### Adding New Properties

Node extensions can add completely new properties to existing nodes:

```typescript
@decorators.nodeExtension<ItemExtension>({
    extends: () => xtremMasterData.nodes.Item,
})
export class ItemExtension extends NodeExtension<xtremMasterData.nodes.Item> {
    @decorators.stringProperty<ItemExtension, 'composedDescription'>({
        isPublished: true,
        lookupAccess: true,
        async getValue() {
            return `${await this.id} -- ${await this.name}`;
        },
    })
    readonly composedDescription: Promise<string>;

    @decorators.collectionProperty<ItemExtension, 'stockMovements'>({
        isPublished: true,
        reverseReference: 'item',
        node: () => sales.nodes.StockMovementLine,
    })
    readonly stockMovements: Collection<sales.nodes.StockMovementLine>;
}
```

### Property Overrides

Extensions can override existing properties to modify their behavior or configuration:

```typescript
@decorators.nodeExtension<ShowCaseProductExtension>({
    extends: () => xtremShowCase.nodes.ShowCaseProduct,
})
export class ShowCaseProductExtension extends NodeExtension<xtremShowCase.nodes.ShowCaseProduct> {
    // Override existing string property
    @decorators.stringPropertyOverride<ShowCaseProductExtension, 'description'>({
        // New configuration for the description property
    })
    readonly description: Promise<string>;

    // Override numeric property with new validation
    @decorators.decimalPropertyOverride<ShowCaseProductExtension, 'listPrice'>({
        async validation(value: decimal) {
            if (value < 0) {
                return 'Price cannot be negative';
            }
            return '';
        },
    })
    readonly listPrice: Promise<decimal>;
}
```

### Bundle Extensions with Upgrade Functions

Extensions can include upgrade functions for data migration when the extension is deployed:

```typescript
@decorators.nodeExtension<BaseResourceExtension>({
    extends: () => masterDataNodes.BaseResource,
    bundleId: '@sage/upgrade-bundle-template',
})
export class BaseResourceExtension extends NodeExtension<masterDataNodes.BaseResource> {
    @decorators.referenceProperty<BaseResourceExtension, 'resourceCost'>({
        isPublished: false,
        isStored: true,
        node: () => ResourceCost,
        dependsOn: ['name'],
        upgrade() {
            if (this.resourceCost) return;

            // Create an instance of ResourceCost
            const newNode = this.$.context.create(ResourceCost, {
                label: `ResourceCost for resource ${this.name}`,
            });
            newNode.$.save();
            this.resourceCost = newNode;
        },
    })
    resourceCost: ResourceCost;
}
```

### SubNode Extensions

For extending subnodes at different levels, use the appropriate decorator:

```typescript
// Level 1 subnode extension
@decorators.subNodeExtension1<SubNodeExtension>({
    extends: () => parentPackage.nodes.SubNode,
})
export class SubNodeExtension extends SubNodeExtension1<parentPackage.nodes.SubNode> {}

// Level 2 subnode extension
@decorators.subNodeExtension2<SubNodeExtension>({
    extends: () => parentPackage.nodes.SubNode,
})
export class SubNodeExtension extends SubNodeExtension2<parentPackage.nodes.SubNode> {}

// Up to level 5 subnodes are supported
@decorators.subNodeExtension5<SubNodeExtension>({
    extends: () => parentPackage.nodes.SubNode,
})
export class SubNodeExtension extends SubNodeExtension5<parentPackage.nodes.SubNode> {}
```

### Extension with Additional Configuration

Extensions can add indexes, service options, and other node-level configurations:

```typescript
@decorators.nodeExtension<AdvancedExtension>({
    extends: () => basePackage.nodes.BaseNode,
    indexes: [{ orderBy: { newProperty: 1 }, isUnique: false }],
    serviceOptions: () => [customServiceOption],
    isVitalReferenceChild: true,
    hasAttachments: true,
    hasTags: true,
})
export class AdvancedExtension extends NodeExtension<basePackage.nodes.BaseNode> {
    @decorators.stringProperty<AdvancedExtension, 'newProperty'>({
        isStored: true,
        isPublished: true,
        dataType: () => new StringDataType({ maxLength: 250 }),
    })
    readonly newProperty: Promise<string>;
}
```

### Organization and Export

Extensions are typically organized in a dedicated directory structure:

```typescript
// /lib/node-extensions/index.ts
export * from './show-case-product-extension';
export * from './item-extension';
export * from './customer-extension';

// Main package index.ts
import * as nodeExtensions from './lib/node-extensions/_index';
export { nodeExtensions };
```

### Extension File Structure

A typical extension file includes:

1. **Imports**: Core decorators and target node packages
2. **Extension Decorator**: Specifying the target node
3. **Extension Class**: With new and overridden properties
4. **Module Declaration**: Merging the extension with the original interface

```typescript
// 1. Imports
import { decorators, NodeExtension } from '@sage/xtrem-core';
import * as targetPackage from '@sage/target-package';
import * as currentPackage from '../../index';

// 2. Extension Decorator
@decorators.nodeExtension<MyExtension>({
    extends: () => targetPackage.nodes.TargetNode,
})
// 3. Extension Class
export class MyExtension extends NodeExtension<targetPackage.nodes.TargetNode> {
    // Properties and methods
}

// 4. Module Declaration
declare module '@sage/target-package/lib/nodes/target-node' {
    export interface TargetNode extends MyExtension {}
}
```

### Best Practices

1. **Naming**: Use descriptive names ending with "Extension"
2. **Module Path**: Ensure the module declaration path matches the target node's file structure
3. **Bundle ID**: Include bundleId for upgrade scenarios
4. **Type Safety**: Always specify generic types for proper TypeScript support
5. **Imports**: Use namespace imports for clean references
6. **Documentation**: Document the purpose and behavior of extensions

### Testing Extensions

Extensions can be tested by verifying the extended functionality:

```typescript
describe('ShowCaseProductExtension', () => {
    it('should add biodegradability category property', async () => {
        const product = context.create(ShowCaseProduct, {
            product: 'Test Product',
        });

        const category = context.create(BiodegradabilityCategory, {
            description: 'Biodegradable',
        });

        product.biodegradabilityCategory = category;
        await product.$.save();

        expect(await product.biodegradabilityCategory).to.equal(category);
    });
});
```

### Page Extensions

Page extensions allow you to extend existing pages from other packages without modifying the original implementation. The `@sage/xtrem-show-case-bundle` package provides comprehensive examples of page extension patterns, including field additions, field overrides, navigation panel extensions, and event handling.

### Basic Page Extension

A page extension extends an existing page by adding new fields, overriding existing fields, or modifying page behavior.

```typescript
import { GraphApi } from '@sage/xtrem-show-case-bundle-api';
import { ShowCaseProduct } from '@sage/xtrem-show-case/lib/pages/show-case-product';
import * as ui from '@sage/xtrem-ui';

@ui.decorators.pageExtension<ShowCaseProductExtension>({
    extends: '@sage/xtrem-show-case/ShowCaseProduct',
    navigationPanel: {
        listItem: {
            line2: ui.nestedFieldExtensions.reference({
                title: 'Biodegradability Category',
                node: '@sage/xtrem-show-case-bundle/BiodegradabilityCategory',
                bind: 'biodegradabilityCategory',
                valueField: 'description',
                insertBefore: 'description',
                canFilter: true,
            }),
        },
        inlineActions: [
            {
                title: 'Extended action inserted at the end',
                refreshesMainList: 'record',
                async onClick() {
                    this.$.dialog.message('info', '', 'Changed name to banana');
                },
                icon: 'favourite',
            },
        ],
    },
    onOpen() {
        this.block.title = 'This title is set from the extension!';
    },
    headerDropDownActions() {
        return [this.addProduct, this.saveProduct];
    },
})
export class ShowCaseProductExtension extends ui.PageExtension<ShowCaseProduct, GraphApi> {
    myVeryFineMethod(eventName: string) {
        this.$.showToast(`A fine notification on ${eventName}`);
        this.aSampleTextField.isDisabled = true;
    }

    @ui.decorators.referenceField<ShowCaseProductExtension>({
        title: 'Biodegradability Category',
        node: '@sage/xtrem-show-case-bundle/BiodegradabilityCategory',
        isAutoSelectEnabled: true,
        insertBefore() {
            return this.description;
        },
        parent() {
            return this.block;
        },
        valueField: 'description',
        onClick() {
            this.listPrice.isDisabled = true;
        },
        onChange() {
            this.$.showToast('Changed');
        },
    })
    biodegradabilityCategory: ui.fields.Reference;

    @ui.decorators.textField<ShowCaseProductExtension>({
        title: 'Extension field title',
        isTransient: true,
        insertBefore() {
            return this.category;
        },
        parent() {
            return this.block;
        },
        onChange() {
            this.$.showToast('Changed');
        },
    })
    aSampleTextField: ui.fields.Text;
}

declare module '@sage/xtrem-show-case/lib/pages/show-case-product' {
    interface ShowCaseProduct extends ShowCaseProductExtension {}
}
```

### Extension Components

#### 1. Extension Decorator

The `@ui.decorators.pageExtension` decorator specifies which page to extend and can include page-level configurations:

```typescript
@ui.decorators.pageExtension<ShowCaseProductExtension>({
    extends: '@sage/xtrem-show-case/ShowCaseProduct',
    navigationPanel: {
        // Navigation panel extensions
    },
    onOpen() {
        // Custom behavior when page opens
    },
    headerDropDownActions() {
        // Custom header actions
        return [this.addProduct, this.saveProduct];
    },
})
```

#### 2. Extension Class

The extension class extends `ui.PageExtension<TPage, TApi>`:

```typescript
export class ShowCaseProductExtension extends ui.PageExtension<ShowCaseProduct, GraphApi> {
    // Extension fields and methods
}
```

#### 3. Module Declaration

The module declaration merges the extension with the original page interface:

```typescript
declare module '@sage/xtrem-show-case/lib/pages/show-case-product' {
    interface ShowCaseProduct extends ShowCaseProductExtension {}
}
```

### Adding New Fields

Extensions can add completely new fields to existing pages:

```typescript
@ui.decorators.pageExtension<BlockExtension>({
    extends: '@sage/xtrem-show-case/Block',
})
export class BlockExtension extends ui.PageExtension<Block, GraphApi> {
    @ui.decorators.block<BlockExtension>({
        parent() {
            return this.section;
        },
        title: 'Biodegradability',
    })
    biodegradabilityBlock: ui.containers.Block;

    @ui.decorators.referenceField<BlockExtension>({
        columns: [
            ui.nestedFields.text({ bind: 'description', title: 'Description' }),
            ui.nestedFields.text({ bind: '_id', title: 'Id' }),
        ],
        node: '@sage/xtrem-show-case-bundle/BiodegradabilityCategory',
        parent() {
            return this.biodegradabilityBlock;
        },
        title: 'Biodegradability Category',
        validation(value: BiodegradabilityCategory) {
            return value.description === 'Milk' ? '' : 'He needs some milk.';
        },
        valueField: 'description',
    })
    biodegradabilityCategory: ui.fields.Reference;

    @ui.decorators.buttonField<BlockExtension>({
        isTransient: true,
        map() {
            return 'Validate';
        },
        async onClick() {
            const validation = await this.biodegradabilityBlock.validate();
            if (validation.length) {
                this.$.showToast(validation.join('\n'), { type: 'error' });
            } else {
                this.$.showToast('Validation successful', { type: 'success' });
            }
        },
        parent() {
            return this.biodegradabilityBlock;
        },
    })
    biodegradabilityButton: ui.fields.Button;
}
```

### Field Overrides

Extensions can override existing fields to modify their behavior, appearance, or events:

#### Reference Field Override

```typescript
@ui.decorators.pageExtension<ReferenceExtension>({
    extends: '@sage/xtrem-show-case/Reference',
})
export class ReferenceExtension extends ui.PageExtension<Reference, GraphApi> {
    @ui.decorators.referenceFieldOverride<ReferenceExtension>({
        title: 'Extension Title',
        helperText: 'Extension Helper Text',
        onClick() {
            this.$.showToast('Extension onClick has been triggered!', { type: 'success' });
        },
        onClickAfter() {
            this.clickAfterTriggered.isHidden = false;
            setTimeout(() => {
                this.clickAfterTriggered.isHidden = true;
            }, 3000);
        },
        onChange() {
            this.$.showToast('onChange has been triggered!', { type: 'success' });
        },
        onChangeAfter() {
            if (this.field.value !== this.value.value) {
                this.value.value = this.field.value;
                this.changeAfterTriggered.isHidden = false;
                setTimeout(() => {
                    this.changeAfterTriggered.isHidden = true;
                }, 3000);
            }
        },
        onCloseLookupDialog() {
            this.$.showToast('onCloseLookupDialog has been triggered!', { type: 'success' });
        },
        onOpenLookupDialog() {
            this.field.title = 'Extension onOpenLookupDialog title';
        },
        columns: [
            ui.nestedFieldExtensions.reference({
                bind: 'biodegradabilityCategory',
                valueField: 'description',
                node: '@sage/xtrem-show-case-bundle/BiodegradabilityCategory',
                insertBefore: '_id',
            }),
        ],
    })
    field: ui.fields.Reference;
}
```

### Table Field Extensions

Table fields can be extended with new columns, column overrides, and event handling:

```typescript
@ui.decorators.pageExtension<TableExtension>({
    extends: '@sage/xtrem-show-case/Table',
})
export class TableExtension extends ui.PageExtension<Table, GraphApi> {
    @ui.decorators.tableFieldOverride<TableExtension, ShowCaseProduct>({
        title: 'Extension Title',
        helperText: 'Extension Helper Text',
        async onChange() {
            this.calculateTableTotals();
            this.$.showToast('Override Table > onChange');
        },
        onRowSelected() {
            this.$.showToast('onRowSelected extension overrides', { type: 'success' });
            this.calculateSelectedTotal();
        },
        onRowUnselected() {
            this.$.showToast('onRowUnselected extension overrides', { type: 'success' });
            this.calculateSelectedTotal();
        },
        mobileCard: {
            title: ui.nestedFields.text({
                bind: 'description',
                title: 'Description',
            }),
            line2: ui.nestedFields.reference({
                bind: 'biodegradabilityCategory',
                title: 'Biodegradability Category',
                valueField: 'description',
            }),
        },
        columnOverrides: [
            ui.nestedFieldOverrides.numeric({
                bind: 'qty',
                scale: 1,
                insertBefore: 'biodegradabilityCategory',
                async onChange() {
                    this.$.showToast('Override Table > Quantity > onChange');
                },
            }),
            ui.nestedFieldOverrides.reference({
                bind: 'provider',
                title: 'Provider Extended',
                valueField: 'textField',
                insertBefore: 'qty',
                onClick(id: string, raw: any) {
                    console.log('Override Table > Provider > onClick', { id, raw });
                },
            }),
        ],
        columns: [
            ui.nestedFieldExtensions.reference({
                bind: 'biodegradabilityCategory',
                valueField: 'description',
                node: '@sage/xtrem-show-case-bundle/BiodegradabilityCategory',
                insertBefore: 'product',
                title: 'Biodegradability category',
                isAutoSelectEnabled: true,
            }),
            ui.nestedFieldExtensions.text({
                bind: 'someTransientFieldAtTheEnd',
                isTransient: true,
                title: 'Transient ext column',
            }),
        ],
    })
    products: ui.fields.Table;
}
```

### Detail List Extensions

Detail lists can be extended with additional fields and field overrides:

```typescript
@ui.decorators.pageExtension<DetailListExtension>({
    extends: '@sage/xtrem-show-case/DetailList',
})
export class DetailListExtension extends ui.PageExtension<DetailList, GraphApi> {
    @ui.decorators.detailListFieldOverride<DetailListExtension>({
        title: 'Extension Title',
        helperText: 'Extension Helper Text',
        fields: [
            ui.nestedFieldExtensions.numeric({
                bind: 'listPrice',
                title: 'List price',
                scale: 2,
                insertBefore: 'qty',
            }),
        ],
        fieldOverrides: [
            ui.nestedFieldOverrides.text({
                bind: 'description',
                title: 'Overridden description title',
            }),
        ],
        onRecordClick(item: any) {
            this.$.dialog.message(
                'info',
                'Extension Item clicked',
                `Clicked field ${item.product} with Description ${item.description}`,
            );
        },
        onRecordClickAfter() {
            this.$.showToast('onClickAfter has been triggered!', { type: 'success' });
        },
    })
    field: ui.fields.DetailList;
}
```

### Pod Collection Extensions

Pod collections can be extended with event handling and additional columns:

```typescript
@ui.decorators.pageExtension<PodCollectionExtension>({
    extends: '@sage/xtrem-show-case/PodCollection',
})
export class PodCollectionExtension extends ui.PageExtension<PodCollection, GraphApi> {
    @ui.decorators.podCollectionFieldOverride<PodCollectionExtension>({
        title: 'Extension Title',
        helperText: 'Extension Helper Text',
        onClick() {
            this.clickTriggered.isHidden = false;
            setTimeout(() => {
                this.clickTriggered.isHidden = true;
            }, 3000);
        },
        onRowSelected(_id) {
            this.rowSelectedTriggered.isHidden = false;
            this.$.showToast(`Extension: Pod selected with ID ${_id}`, { type: 'info' });
        },
        onRecordAdded() {
            this.$.showToast(`Extension: A new pod was added`, { type: 'info' });
        },
        onRecordRemoved(_id) {
            this.$.showToast(`Extension: A pod was removed with ID: ${_id}`, { type: 'info' });
        },
        canSelect: true,
        canRemoveRecord: true,
        canAddRecord: true,
        columns: [
            ui.nestedFieldExtensions.reference({
                bind: 'invoice',
                title: 'Invoice',
                insertBefore: 'orderQuantity',
                valueField: 'purchaseDate',
                node: '@sage/xtrem-show-case/ShowCaseInvoice',
            }),
        ],
    })
    lines: ui.fields.PodCollection;
}
```

### Navigation Panel Extensions

Extensions can modify navigation panel behavior including list items and inline actions:

```typescript
@ui.decorators.pageExtension<ShowCaseProductExtension>({
    extends: '@sage/xtrem-show-case/ShowCaseProduct',
    navigationPanel: {
        listItem: {
            line2: ui.nestedFieldExtensions.reference({
                title: 'Biodegradability Category',
                node: '@sage/xtrem-show-case-bundle/BiodegradabilityCategory',
                bind: 'biodegradabilityCategory',
                valueField: 'description',
                insertBefore: 'description',
                canFilter: true,
            }),
            someRandomIcon: ui.nestedFieldExtensions.icon({
                bind: 'nothing',
                title: 'Some extension icon',
                isTransient: true,
                map() {
                    return 'tick';
                },
            }),
        },
        inlineActions: [
            {
                title: 'Extended action inserted at the end',
                refreshesMainList: 'record',
                async onClick() {
                    this.$.dialog.message('info', '', 'Changed name to banana');
                },
                icon: 'favourite',
            },
            {
                title: 'Extended action inserted before delete',
                refreshesMainList: 'record',
                async onClick() {
                    this.$.dialog.message('info', '', 'Changed name to banana');
                },
                icon: 'arrow_left_boxed',
                insertBefore: 'delete',
            },
        ],
    },
})
export class ShowCaseProductExtension extends ui.PageExtension<ShowCaseProduct, GraphApi> {
    // Extension implementation
}
```

### Page Event Handling

Extensions can override page-level events and lifecycle methods:

```typescript
@ui.decorators.pageExtension<ShowCaseProductExtension>({
    extends: '@sage/xtrem-show-case/ShowCaseProduct',
    onOpen() {
        this.block.title = 'This title is set from the extension!';
    },
    onClose() {
        console.log('on close extension called');
        this.myVeryFineMethod('close');
    },
    headerDropDownActions() {
        return [this.addProduct, this.saveProduct];
    },
    headerQuickActions() {
        return [this.deleteProduct, this.saveProductQuick];
    },
})
export class ShowCaseProductExtension extends ui.PageExtension<ShowCaseProduct, GraphApi> {
    myVeryFineMethod(eventName: string) {
        this.$.showToast(`A fine notification on ${eventName}`);
        this.aSampleTextField.isDisabled = true;
    }
}
```

### Field Positioning

Extensions provide flexible field positioning using insertion methods:

```typescript
// Insert before specific field
@ui.decorators.textField<ShowCaseProductExtension>({
    title: 'Extension field title',
    insertBefore() {
        return this.category;
    },
    parent() {
        return this.block;
    },
})
aSampleTextField: ui.fields.Text;

// Insert after specific field
@ui.decorators.textField<ShowCaseProductExtension>({
    title: 'Extension at the end',
    insertAfter() {
        return this.description;
    },
    parent() {
        return this.block;
    },
})
anotherSampleTextField: ui.fields.Text;
```

### Extension Organization

Page extensions do not have an index.ts

### Best Practices

1. **Naming**: Use descriptive names ending with "Extension"
2. **Module Path**: Ensure the module declaration path matches the target page's file structure
3. **Type Safety**: Always specify generic types for proper TypeScript support
4. **Event Handling**: Use both `on` and `onAfter` events for complete control
5. **Field Positioning**: Use `insertBefore` and `insertAfter` for proper field placement
6. **Transient Fields**: Mark extension-only fields as `isTransient: true`
7. **Testing**: Test both the extended functionality and original page behavior

### Testing Page Extensions

Extensions can be tested by verifying the extended UI functionality:

```typescript
describe('ShowCaseProductExtension', () => {
    it('should add biodegradability category field', async () => {
        const page = await createTestPage(ShowCaseProduct);

        expect(page.biodegradabilityCategory).to.exist;
        expect(page.biodegradabilityCategory.title).to.equal('Biodegradability Category');
    });

    it('should trigger extension events', async () => {
        const page = await createTestPage(ShowCaseProduct);
        let eventTriggered = false;

        page.biodegradabilityCategory.onChange = () => {
            eventTriggered = true;
        };

        await page.biodegradabilityCategory.setValue(testCategory);
        expect(eventTriggered).to.be.true;
    });
});
```

### Widgets

Widgets are dashboard components that display data in various visual formats. The `@sage/xtrem-show-case` package provides comprehensive examples of different widget types, from simple indicators to complex visualizations.

### Widget Fundamentals

All widgets extend `ui.widgets.AbstractWidget` and are defined using specific widget decorators. They use GraphQL queries to fetch data and provide interactive dashboard experiences.

#### Basic Widget Structure

```typescript
import * as ui from '@sage/xtrem-ui';

@ui.widgets.widgetType<WidgetClass>({
    title: 'Widget Title',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    category: 'DEMO_CATEGORY',
    content() {
        // Return processed data for display
        return this.$.data.someQuery.result;
    },
    getQuery() {
        // Return GraphQL query
        return {
            someModule: {
                someNode: {
                    query: {
                        // Query fields
                    },
                },
            },
        };
    },
})
export class WidgetClass extends ui.widgets.AbstractWidget {}
```

### Gauge Widgets

Gauge widgets display ratios and progress with optional evolution indicators.

```typescript
import * as ui from '@sage/xtrem-ui';

@ui.widgets.gauge<AdministratorGauge>({
    title: 'Ratio of administrators',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    content() {
        const body = this.$.data.xtremSystem.user;
        return {
            value: body.adminCount.totalCount,
            totalValue: body.totalUserCount.totalCount,
            evolutionValue: 1,
        };
    },
    scale: 0,
    color: ui.tokens.colorsSemanticPositive500,
    evolutionUnit: 'pcs',
    callToActions: {
        cantDoThis: {
            title: 'Some action',
            onClick() {
                this.$.showToast('Well you cannot do much with these users');
            },
        },
    },
    getQuery() {
        return {
            xtremSystem: {
                user: {
                    adminCount: {
                        __aliasFor: 'query',
                        __args: { filter: '{isAdministrator: true}' },
                        totalCount: true,
                    },
                    totalUserCount: {
                        __aliasFor: 'query',
                        totalCount: true,
                    },
                },
            },
        };
    },
})
export class AdministratorGauge extends ui.widgets.AbstractWidget {}
```

### Table Widgets

Table widgets display data in rows with filtering, selection, and action capabilities.

```typescript
@ui.widgets.table<ListOfUsersTable>({
    title: 'Users',
    description: 'Detailed list about the current users',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    category: 'DEMO_CATEGORY',
    content() {
        return this.$.data.xtremSystem.user.query.edges.map(({ node, cursor }: any) => ({
            _id: node._id,
            title: node.firstName,
            titleRight: node.lastName,
            line2: node.email,
            line2Right: node.userType,
            image: node.photo?.value,
            cursor,
        }));
    },
    filterMenuType: 'dropdown',
    businessIcon: 'bag',
    callToActions: {
        doSomething: {
            title: 'Do something',
            isHidden() {
                const selectedItems = this.$.options.selectedItems;
                return (selectedItems ?? []).length === 0;
            },
            onClick() {
                this.$.showToast('Well you cannot do much with these users');
            },
        },
        cantDoThis: {
            title: 'Disabled action',
            isDisabled: true,
            onClick() {
                this.$.showToast('Well you cannot do much with these users');
            },
        },
    },
    canSwitchViewMode: true,
    displayMode: 'card',
    canSelect: true,
    dataDropdownMenu: {
        userType: {
            all: { title: 'All users' },
            normal: { title: 'Normal accounts' },
            administrative: { title: 'Administrative accounts' },
            inactive: { title: 'Inactive accounts' },
        },
        orderBy: {
            firstName: { title: 'Sort by first name' },
            lastName: { title: 'Sort by last name' },
            userType: { title: 'Sort by user type' },
        },
    },
    rowDefinition: {
        title: { title: 'First name' },
        titleRight: { title: 'Last name' },
        line2: {
            title: 'Email',
            onClick(id: string, record: any) {
                this.$.dialog.message('info', 'User email', record.line2);
            },
        },
        line2Right: { title: 'User type' },
    },
    getQuery() {
        return {
            xtremSystem: {
                user: {
                    query: {
                        edges: {
                            node: {
                                _id: true,
                                firstName: true,
                                lastName: true,
                                email: true,
                                userType: true,
                                photo: { value: true },
                            },
                            cursor: true,
                        },
                    },
                },
            },
        };
    },
})
export class ListOfUsersTable extends ui.widgets.AbstractWidget {}
```

### Chart Widgets

#### Bar Chart Widgets

```typescript
import * as ui from '@sage/xtrem-ui';
import { ShowCaseProduct } from '@sage/xtrem-show-case-api';

interface Product {
    id: string;
    listPrice: number;
    amount: number;
}

@ui.widgets.barChart<ProductBarChart, Product>({
    title: 'Products',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    category: 'OTHER_CATEGORY',
    content() {
        return this.$.data.xtremShowCase.showCaseProduct.query.edges.map(({ node: { listPrice, amount, _id } }) => ({
            id: _id,
            listPrice: Number.parseFloat(listPrice),
            amount: Number.parseFloat(amount),
        }));
    },
    callToActions: {
        doSomething: {
            title: 'Action',
            onClick() {
                this.$.showToast('Perform action on products');
            },
        },
    },
    primaryAxis: {
        bind: 'id',
        title: 'Product ID',
    },
    secondaryAxes: [
        {
            bind: 'listPrice',
            title: 'Price',
        },
        {
            bind: 'amount',
            title: 'Amount',
            onClick(record) {
                this.$.showToast(`Clicked on: ${JSON.stringify(record)}`);
            },
            tooltipContent(record, primaryValue, secondaryValue) {
                return `ID: ${primaryValue} - Amount: ${secondaryValue}`;
            },
        },
    ],
    getQuery() {
        return {
            xtremShowCase: {
                showCaseProduct: {
                    query: {
                        edges: {
                            node: {
                                _id: true,
                                listPrice: true,
                                amount: true,
                            },
                        },
                    },
                },
            },
        };
    },
})
export class ProductBarChart extends ui.widgets.AbstractWidget {}
```

#### Line Chart Widgets

```typescript
@ui.widgets.lineChart<ProductLineChart, Product, string, number>({
    title: 'Products',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    category: 'OTHER_CATEGORY',
    content() {
        return this.$.data.xtremShowCase.showCaseProduct.query.edges.map(({ node: { listPrice, amount, _id } }) => ({
            id: _id,
            listPrice: Number.parseFloat(listPrice),
            amount: Number.parseFloat(amount),
        }));
    },
    callToActions: {
        doSomething: {
            title: 'Action',
            onClick() {
                this.$.showToast('Perform action on products');
            },
        },
    },
    primaryAxis: {
        bind: 'id',
        title: 'Product ID',
    },
    secondaryAxes: [
        {
            bind: 'listPrice',
            title: 'Price',
        },
        {
            bind: 'amount',
            title: 'Amount',
            onClick(record) {
                this.$.showToast(`Clicked on: ${JSON.stringify(record)}`);
            },
            tooltipContent(record, primaryValue, secondaryValue) {
                return `ID: ${primaryValue} - Amount: ${secondaryValue}`;
            },
        },
    ],
    getQuery() {
        return {
            xtremShowCase: {
                showCaseProduct: {
                    query: {
                        edges: {
                            node: {
                                _id: true,
                                listPrice: true,
                                amount: true,
                            },
                        },
                    },
                },
            },
        };
    },
})
export class ProductLineChart extends ui.widgets.AbstractWidget {}
```

#### Pie Chart Widgets

```typescript
import { Decimal } from '@sage/xtrem-decimal';

@ui.widgets.pieChart<PieChart>({
    title: 'Pie Chart',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    category: 'OTHER_CATEGORY',
    content() {
        const total =
            this.$.data?.xtremShowCase.showCaseProduct.queryAggregate.edges.reduce(
                (
                    previousValue,
                    {
                        node: {
                            values: {
                                _id: { distinctCount },
                            },
                        },
                    },
                ) => {
                    return new Decimal(previousValue + distinctCount).toNumber();
                },
                0,
            ) ?? 1;

        const aggregatedData = this.$.data?.xtremShowCase.showCaseProduct.queryAggregate.edges.reduce(
            (
                previousValue,
                {
                    node: {
                        group: { category },
                        values: {
                            _id: { distinctCount },
                        },
                    },
                },
            ) => {
                previousValue.push({
                    title: category || 'Other',
                    _id: category || 'Other',
                    value: new Decimal((distinctCount / total) * 100).toNumber(),
                });
                return previousValue;
            },
            [] as { title: string; _id: string; value: number }[],
        );

        return aggregatedData || [];
    },
    getQuery() {
        return {
            xtremShowCase: {
                showCaseProduct: {
                    queryAggregate: {
                        edges: {
                            node: {
                                group: { category: true },
                                values: { _id: { distinctCount: true } },
                            },
                        },
                    },
                },
            },
        };
    },
})
export class PieChart extends ui.widgets.AbstractWidget {}
```

### Indicator Widgets

#### Single Indicator Tile

```typescript
@ui.widgets.indicatorTile<NumberOfUsers>({
    title: 'Number of users',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    settingsPage: '@sage/xtrem-show-case/NumberOfUsersSettings',
    icon() {
        return 'person';
    },
    value() {
        return String(this.$.data.xtremSystem.user.query.totalCount);
    },
    headerActions: [
        {
            title: 'View list',
            icon: 'bullet_list_dotted',
            onClick() {
                this.$.router.goTo('@sage/xtrem-authorization/UserList');
            },
        },
    ],
    getQuery() {
        const filter: any = {};
        if (this.$.settings.shouldExcludeAdministrativeUsers) {
            filter.isAdministrator = false;
        }
        if (this.$.settings.shouldExcludeInactiveUsers) {
            filter.isActive = true;
        }

        return {
            xtremSystem: {
                user: {
                    query: {
                        __args: { filter: JSON.stringify(filter) },
                        totalCount: true,
                    },
                },
            },
        };
    },
})
export class NumberOfUsers extends ui.widgets.AbstractWidget {}
```

#### Indicator Tile Group

```typescript
@ui.widgets.indicatorTileGroup<ListOfInvoicesIndicatorGroup>({
    title: 'Invoices Overview Tile Group',
    description: 'Overview of key invoice data in tiles',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    settingsPage: '@sage/xtrem-show-case/InvoicesOverviewSettings',
    category: 'DEMO_CATEGORY',
    content() {
        const edges = this.$.data?.xtremShowCase.showCaseInvoice.query.edges || [];
        return edges.flatMap((edge: any) => {
            const invoice = edge.node;
            return [
                {
                    title: 'Invoice Date',
                    value: ui.formatDateToCurrentLocale(invoice.purchaseDate),
                    valueColor: 'black',
                    icon: 'calendar',
                    iconColor: 'black',
                    hasSeparatorAfter: true,
                    contentAlignment: 'left',
                    onClick: () => {
                        this.$.router.goTo('@sage/xtrem-show-case/ShowCaseProduct');
                    },
                },
                {
                    title: 'Total Quantity',
                    value: String(
                        invoice.lines.query.edges.reduce(
                            (sum: number, lineEdge: any) => sum + lineEdge.node.orderQuantity,
                            0,
                        ),
                    ),
                    valueColor: 'green',
                    icon: 'cart',
                    iconColor: 'green',
                    hasSeparatorAfter: true,
                    contentAlignment: 'left',
                },
                {
                    title: 'Customer',
                    value: invoice.customer.name,
                    valueColor: 'black',
                    icon: 'user',
                    iconColor: 'black',
                    hasSeparatorAfter: true,
                    contentAlignment: 'left',
                },
            ];
        });
    },
    getQuery() {
        return {
            xtremShowCase: {
                showCaseInvoice: {
                    query: {
                        edges: {
                            node: {
                                purchaseDate: true,
                                customer: { name: true },
                                lines: {
                                    query: {
                                        edges: {
                                            node: {
                                                orderQuantity: true,
                                            },
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
            },
        };
    },
})
export class ListOfInvoicesIndicatorGroup extends ui.widgets.AbstractWidget {}
```

### Static Content Widgets

```typescript
@ui.widgets.staticContent<ListOfUsers>({
    title: 'List of users',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    description: 'Unordered list of the current users',
    category: 'OTHER_CATEGORY',
    content() {
        const body = this.$.data.xtremSystem.user.query.edges
            .map((e: any) => `- ${e.node.firstName} ${e.node.lastName} (${e.node.email})`)
            .join('\n');

        return `**List of users**\n${body}`;
    },
    getQuery() {
        return {
            xtremSystem: {
                user: {
                    query: {
                        edges: {
                            node: {
                                email: true,
                                firstName: true,
                                lastName: true,
                            },
                        },
                    },
                },
            },
        };
    },
})
export class ListOfUsers extends ui.widgets.AbstractWidget {}
```

### Visual Process Widgets

Visual process widgets create custom drawings and interactive diagrams.

```typescript
const providerBoxWidth = 300;
const providerBoxHeight = 100;
const padding = 20;

@ui.widgets.visualProcess<ProvidersProcess>({
    title: 'Suppliers',
    cacheLifespan: ui.widgets.WidgetCacheLifespan.day,
    category: 'DEMO_CATEGORY',
    content() {
        const edges: { node: any }[] = this.$.data.xtremShowCase.showCaseProvider.query.edges;
        const totalHeight = edges.length * (providerBoxHeight + padding) + padding;
        const totalWidth = providerBoxWidth + padding * 2;

        const body = edges
            .map((e, index) =>
                this.createProviderObject(padding, padding + index * (providerBoxHeight + padding), e.node),
            )
            .reduce((prevValue: ui.VP.XShape[], currentValue: ui.VP.XShape[]) => {
                return [...prevValue, ...currentValue];
            }, [] as ui.VP.XShape[]);

        return {
            acts: { a: [] },
            layersArr: [
                {
                    id: 1,
                    alpha: 1,
                    lock: true,
                    visible: true,
                    xpropsArr: body,
                },
            ],
            contentSize: {
                xheight: totalHeight,
                xwidth: totalWidth,
            },
            currentLayerId: 1,
            docDims: {
                xtop: 0,
                xleft: 0,
                xheight: totalHeight,
                xwidth: totalWidth,
            },
            reachedGroupNum: 1,
        };
    },
    getQuery() {
        return {
            xtremShowCase: {
                showCaseProvider: {
                    query: {
                        edges: {
                            node: {
                                textField: true,
                                _id: true,
                                products: {
                                    query: {
                                        totalCount: true,
                                    },
                                },
                            },
                        },
                    },
                },
            },
        };
    },
})
export class ProvidersProcess extends ui.widgets.AbstractWidget {
    createProviderObject = (startX: number, startY: number, provider: any): ui.VP.XShape[] => {
        // Custom shape creation logic
        return [
            // Shape definitions for visual elements
        ];
    };
}
```

### Widget Features

#### Call to Actions

Widgets can include interactive buttons and actions:

```typescript
callToActions: {
    doSomething: {
        title: 'Do something',
        isHidden() {
            const selectedItems = this.$.options.selectedItems;
            return (selectedItems ?? []).length === 0;
        },
        onClick() {
            this.$.showToast('Action performed');
        },
    },
    cantDoThis: {
        title: 'Disabled action',
        isDisabled: true,
        onClick() {
            this.$.showToast('This action is disabled');
        },
    },
}
```

#### Settings Integration

Widgets can have settings pages for user customization:

```typescript
@ui.widgets.indicatorTile<NumberOfUsers>({
    title: 'Number of users',
    settingsPage: '@sage/xtrem-show-case/NumberOfUsersSettings',
    getQuery() {
        const filter: any = {};
        if (this.$.settings.shouldExcludeAdministrativeUsers) {
            filter.isAdministrator = false;
        }
        return {
            // Query using settings
        };
    },
})
```

#### Cache Lifespan

Widgets support different cache lifespans:

```typescript
// Available cache lifespans
cacheLifespan: ui.widgets.WidgetCacheLifespan.minute,  // 1 minute
cacheLifespan: ui.widgets.WidgetCacheLifespan.hour,    // 1 hour
cacheLifespan: ui.widgets.WidgetCacheLifespan.day,     // 1 day
cacheLifespan: ui.widgets.WidgetCacheLifespan.week,    // 1 week
```

#### Categories

Widgets can be organized into categories:

```typescript
category: 'DEMO_CATEGORY',      // Demo widgets
category: 'OTHER_CATEGORY',     // General widgets
category: 'CUSTOM_CATEGORY',    // Custom categories
```

### Widget Organization

Widgets do not have an index.ts.

### Best Practices

1. **Data Processing**: Process data in the `content()` function rather than in the template
2. **Query Efficiency**: Only query the fields you need for display
3. **Error Handling**: Handle cases where data might be undefined or empty
4. **User Interaction**: Provide meaningful actions and feedback through call-to-actions
5. **Performance**: Use appropriate cache lifespans based on data volatility
6. **Accessibility**: Include proper titles, descriptions, and alternative text
7. **Responsive Design**: Consider how widgets will appear on different screen sizes

### Testing Widgets

Widgets can be tested by verifying their data processing and interaction behavior:

```typescript
describe('AdministratorGauge', () => {
    it('should calculate correct ratio', async () => {
        const widget = new AdministratorGauge();
        widget.$.data = {
            xtremSystem: {
                user: {
                    adminCount: { totalCount: 5 },
                    totalUserCount: { totalCount: 20 },
                },
            },
        };

        const content = widget.content();
        expect(content.value).to.equal(5);
        expect(content.totalValue).to.equal(20);
    });

    it('should handle call to action clicks', async () => {
        const widget = new AdministratorGauge();
        let toastShown = false;

        widget.$.showToast = (message: string) => {
            toastShown = true;
        };

        widget.callToActions.cantDoThis.onClick();
        expect(toastShown).to.be.true;
    });
});
```

### Stickers

Stickers are package-level invokable containers that provide run-in-the-background capabilities. They persist across page navigation and can update display information of their decorating menu item. Stickers are ideal for notification centers, user profiles, settings panels, and other utility interfaces that need to remain accessible throughout the application.

### Basic Sticker Implementation

The simplest sticker implementation demonstrates the core decorator pattern:

```typescript
import * as ui from '@sage/xtrem-ui';

@ui.decorators.sticker<InactiveSticker>({
    title: 'Inactive sticker',
    icon: 'tick',
    isActive() {
        return Promise.resolve(false);
    },
})
export class InactiveSticker extends ui.Sticker {
    @ui.decorators.section<InactiveSticker>({
        title: 'Nothing good',
    })
    section1: ui.containers.Section;

    @ui.decorators.block<InactiveSticker>({
        title: 'Block 1',
        parent() {
            return this.section1;
        },
    })
    block1: ui.containers.Block;

    @ui.decorators.numericField<InactiveSticker>({
        title: 'Just this',
        isMandatory: true,
        parent() {
            return this.block1;
        },
    })
    field1: ui.fields.Numeric;
}
```

### Active Sticker with Business Actions

The ActiveSticker from ShowCase demonstrates a comprehensive implementation with business actions, dynamic content updates, and user interactions:

```typescript
import { GraphApi } from '@sage/xtrem-show-case-api';
import * as ui from '@sage/xtrem-ui';

@ui.decorators.sticker<ActiveSticker>({
    title: 'Printer destination',
    icon: 'home',
    // category: 'ADC',
    businessActions() {
        return [this.action1, this.action2, this.action3];
    },
    isActive() {
        return true;
    },
    onOpen() {
        this.$.showToast('This sticker is open!');
    },
})
export class ActiveSticker extends ui.Sticker<GraphApi> {
    @ui.decorators.section<ActiveSticker>({
        title: 'Section 1',
    })
    section1: ui.containers.Section;

    @ui.decorators.block<ActiveSticker>({
        title: 'Block 1',
        parent() {
            return this.section1;
        },
    })
    block1: ui.containers.Block;

    @ui.decorators.numericField<ActiveSticker>({
        title: 'Numeric badgeContent test',
        onChange() {
            this.updateLabel(this.field1.value);
        },
        parent() {
            return this.block1;
        },
    })
    field1: ui.fields.Numeric;

    @ui.decorators.block<ActiveSticker>({
        title: 'Block 2',
        parent() {
            return this.section1;
        },
    })
    block2: ui.containers.Block;

    @ui.decorators.textField<ActiveSticker>({
        title: 'Text badgeContent test',
        onChange() {
            this.updateLabel(this.field2.value);
        },
        parent() {
            return this.block2;
        },
    })
    field2: ui.fields.Text;

    @ui.decorators.block<ActiveSticker>({
        title: 'Block 3',
        parent() {
            return this.section1;
        },
    })
    block3: ui.containers.Block;

    @ui.decorators.buttonField<ActiveSticker>({
        map() {
            return 'Finish sticker';
        },
        onClick() {
            this.$.finish();
        },
        parent() {
            return this.block3;
        },
    })
    finishButton: ui.fields.Button;

    @ui.decorators.pageAction<ActiveSticker>({
        title: 'First',
        onClick() {
            this.$.showToast('First action was clicked.');
        },
    })
    action1: ui.PageAction;

    @ui.decorators.pageAction<ActiveSticker>({
        title: 'Second',
        onClick() {
            this.$.showToast('Second action was clicked.');
        },
    })
    action2: ui.PageAction;

    @ui.decorators.pageAction<ActiveSticker>({
        title: 'Third',
        onClick() {
            this.$.showToast('Third action was clicked.');
        },
    })
    action3: ui.PageAction;

    updateLabel(value: string | number) {
        this.$.sticker.updateMenuItem(value);
    }
}
```

### Profile Sticker Example

The ProfileSticker demonstrates a simple user profile interface:

```typescript
import { GraphApi } from '@sage/xtrem-show-case-api';
import * as ui from '@sage/xtrem-ui';

@ui.decorators.sticker<ProfileSticker>({
    title: 'User Profile',
    icon: 'person',
    isActive() {
        return true;
    },
})
export class ProfileSticker extends ui.Sticker<GraphApi> {
    @ui.decorators.section<ProfileSticker>({
        title: 'Profile',
    })
    section: ui.containers.Section;

    @ui.decorators.block<ProfileSticker>({
        parent() {
            return this.section;
        },
    })
    block1: ui.containers.Block;

    @ui.decorators.imageField<ProfileSticker>({
        title: 'You look awesome today!',
        height: '200px',
        isReadOnly: true,
        parent() {
            return this.block1;
        },
    })
    field1: ui.fields.Image;
}
```

### Sticker Decorator Properties

The sticker decorator supports several key properties:

- **title**: The display title for the sticker menu item
- **icon**: Icon identifier from the design system (e.g., 'home', 'person', 'tick')
- **category**: Optional grouping category for organizing stickers
- **isActive()**: Function determining if the sticker should be active/available
- **onOpen()**: Function executed when the sticker opens
- **onClose()**: Function executed before the sticker closes
- **onLoad()**: Function executed after the sticker loads
- **businessActions()**: Function returning array of business actions available in the sticker
- **access**: Authorization configuration for the sticker

### Sticker API Methods

Stickers provide several API methods for interaction:

- **this.$.finish()**: Closes the sticker
- **this.$.sticker.updateMenuItem(value)**: Updates the menu item display information with dynamic content
- **this.$.sticker.validate()**: Triggers validation of all fields in the sticker
- **this.$.showToast(message)**: Shows toast notification

### Dynamic Menu Item Updates

Stickers can dynamically update their menu item display:

```typescript
updateLabel(value: string | number) {
    this.$.sticker.updateMenuItem(value);
}

@ui.decorators.numericField<ActiveSticker>({
    title: 'Badge content test',
    onChange() {
        this.updateLabel(this.field1.value);
    },
    parent() {
        return this.block1;
    },
})
field1: ui.fields.Numeric;
```

### Business Actions in Stickers

Business actions provide additional operations available in the sticker:

```typescript
@ui.decorators.sticker<ActiveSticker>({
    businessActions() {
        return [this.action1, this.action2, this.action3];
    },
})
export class ActiveSticker extends ui.Sticker<GraphApi> {
    @ui.decorators.pageAction<ActiveSticker>({
        title: 'First',
        onClick() {
            this.$.showToast('First action was clicked.');
        },
    })
    action1: ui.PageAction;

    @ui.decorators.pageAction<ActiveSticker>({
        title: 'Second',
        onClick() {
            this.$.showToast('Second action was clicked.');
        },
    })
    action2: ui.PageAction;
}
```

### Sticker Lifecycle Management

Stickers support comprehensive lifecycle management:

```typescript
@ui.decorators.sticker<NotificationSticker>({
    async onOpen() {
        // Initialize data when sticker opens
        this.executions.value = await this.getSysNotificationState();
    },
    async onLoad() {
        // Start background processes
        while (this.isLoopExecuted) {
            if (this.isEnabled.value) await this.loop();
            await sleepMillis(6000);
        }
        this.isEnabled.value = false;
    },
    onClose() {
        // Cleanup when sticker closes
        this.isLoopExecuted = false;
    },
})
export class NotificationSticker extends ui.Sticker<GraphApi> {
    isLoopExecuted = true;
    // ... sticker implementation
}
```

### Access Control and Authorization

Stickers support access control configuration:

```typescript
@ui.decorators.sticker<DemoPersonaSticker>({
    title: 'Demo Persona',
    icon: 'person',
    access: {
        node: '@sage/xtrem-system/User',
        bind: 'demoPersonas',
    },
    isActive() {
        return true;
    },
})
export class DemoPersonaSticker extends ui.Sticker<GraphApi> {
    // ... implementation
}
```

### Sticker Organization

Stickers do not have an index.ts file.

### Best Practices for Stickers

1. **Keep stickers lightweight**: Avoid heavy computations in sticker initialization
2. **Use isActive() effectively**: Control when stickers are available based on context
3. **Implement proper cleanup**: Use onClose() to clean up resources and stop background processes
4. **Update menu items dynamically**: Use updateMenuItem() to provide real-time status updates
5. **Handle errors gracefully**: Implement error handling for background operations
6. **Use business actions sparingly**: Only include essential actions to avoid cluttering the interface
7. **Consider user experience**: Design stickers for quick access to frequently needed functionality

### Menu Items

Menu items provide the navigation structure for Xtrem applications, organizing pages and functionality into logical hierarchies. The framework supports both root menu items and sub-menu items, enabling the creation of comprehensive navigation structures that guide users through the application.

### Root Menu Items

Root menu items appear at the top level of the navigation and serve as primary categories:

```typescript
import type { RootMenuItem } from '@sage/xtrem-shared';

export const showCase: RootMenuItem = {
    id: '@sage/xtrem-show-case/show-case-root',
    priority: 800,
    title: 'Showcase',
    icon: 'gift',
};
```

```typescript
export const fields: RootMenuItem = {
    id: '@sage/xtrem-show-case/fields',
    priority: 10,
    title: 'Fields',
    icon: 'gift',
};
```

```typescript
export const applicationPages: RootMenuItem = {
    id: '@sage/xtrem-show-case/application-pages',
    priority: 50,
    title: 'Sample Application pages',
    icon: 'gift',
};
```

### Sub Menu Items

Sub menu items provide organized groupings under root menu items:

```typescript
import type { SubMenuItem } from '@sage/xtrem-shared';
import { fields } from './fields';

export const textField: SubMenuItem = {
    id: '@sage/xtrem-show-case/text-field',
    priority: 20,
    title: 'Text',
    parentMenuItem: fields,
};
```

```typescript
export const tableField: SubMenuItem = {
    id: '@sage/xtrem-show-case/table-field',
    priority: 20,
    title: 'Table Field',
    parentMenuItem: fields,
};
```

```typescript
import { SubMenuItem } from '@sage/xtrem-shared';
import { showCase } from './show-case';

export const pageElements: SubMenuItem = {
    id: '@sage/xtrem-show-case/page-elements',
    priority: 10,
    title: 'Page Elements',
    parentMenuItem: showCase,
};
```

### Menu Item Properties

Menu items support several key properties:

- **id**: Unique identifier for the menu item (typically includes package namespace)
- **priority**: Numeric value determining display order (lower numbers appear first)
- **title**: Display text for the menu item
- **icon**: Icon identifier for root menu items (from design system)
- **parentMenuItem**: Reference to parent menu item for sub-menu items

### Using Menu Items in Pages

Pages reference menu items through the `menuItem` property in the page decorator:

```typescript
import { fields } from '../menu-items/fields';

@ui.decorators.page<DetailList, ShowCaseProvider>({
    authorizationCode: 'BSCFLDDL',
    module: 'show-case',
    title: 'Field - Detail list',
    node: '@sage/xtrem-show-case/ShowCaseProvider',
    category: 'SHOWCASE',
    menuItem: fields,
    defaultEntry: () => '1',
})
export class DetailList extends ui.Page<GraphApi> {
    // Page implementation
}
```

```typescript
import { tableField } from '../menu-items/_index';

@ui.decorators.page<TableOptionsMenu, ShowCaseProvider>({
    authorizationCode: 'TBLOPTMNU',
    module: 'show-case',
    category: 'SHOWCASE',
    mode: 'tabs',
    navigationPanel: {
        listItem: {
            title: ui.nestedFields.text({ bind: 'textField' }),
            titleRight: ui.nestedFields.text({ bind: '_id' }),
        },
    },
    node: '@sage/xtrem-show-case/ShowCaseProvider',
    title: 'Field - Table (Options Menu)',
    menuItem: tableField,
})
export class TableOptionsMenu extends ui.Page {
    // Page implementation
}
```

### Menu Structure Examples

#### ShowCase Package Structure

The ShowCase package demonstrates a comprehensive menu structure:

```typescript
// Root menu items
export const showCase: RootMenuItem = {
    id: '@sage/xtrem-show-case/show-case-root',
    priority: 800,
    title: 'Showcase',
    icon: 'gift',
};

export const fields: RootMenuItem = {
    id: '@sage/xtrem-show-case/fields',
    priority: 10,
    title: 'Fields',
    icon: 'gift',
};

export const misc: RootMenuItem = {
    id: '@sage/xtrem-show-case/misc',
    priority: 100,
    title: 'Misc',
    icon: 'gift',
};

// Sub menu items under showCase
export const pageElements: SubMenuItem = {
    id: '@sage/xtrem-show-case/page-elements',
    priority: 10,
    title: 'Page Elements',
    parentMenuItem: showCase,
};

export const containers: SubMenuItem = {
    id: '@sage/xtrem-show-case/containers',
    priority: 10,
    title: 'Containers',
    icon: 'gift',
};

// Sub menu items under fields
export const textField: SubMenuItem = {
    id: '@sage/xtrem-show-case/text-field',
    priority: 20,
    title: 'Text',
    parentMenuItem: fields,
};

export const tableField: SubMenuItem = {
    id: '@sage/xtrem-show-case/table-field',
    priority: 20,
    title: 'Table Field',
    parentMenuItem: fields,
};

// Sub menu items under misc
export const utils: SubMenuItem = {
    id: '@sage/xtrem-show-case/utils',
    priority: 10,
    title: 'Utils',
    parentMenuItem: misc,
};

export const navigationPanel: SubMenuItem = {
    id: '@sage/xtrem-show-case/navigation-panel',
    priority: 10,
    title: 'Navigation Panel',
    parentMenuItem: misc,
};
```

#### Restaurant Package Structure

The Restaurant package shows a focused application menu:

```typescript
export const applicationPages: RootMenuItem = {
    id: '@sage/xtrem-restaurant/application-pages',
    priority: 50,
    title: 'Restaurant pages',
    icon: 'gift',
};

export const navigationPanel: SubMenuItem = {
    id: '@sage/xtrem-restaurant/navigation-panel',
    priority: 10,
    title: 'Navigation Panel',
    parentMenuItem: misc,
};
```

### Menu Item Organization

Organize menu items using index files for clean exports and maintainability:

```typescript
// menu-items/index.ts
export * from './application-pages';
export * from './fields';
export * from './vital-pod';
export * from './pod-collection';
export * from './reference-field';
export * from './containers';
export * from './nested-grid';
export * from './table-field';
export * from './misc';
export * from './utils';
export * from './navigation-panel';
export * from './pod';
export * from './chart-field';
export * from './nested-pod';
export * from './plugins';
export * from './text-field';
export * from './validation';
```

### Complex Page Examples with Menu Items

#### Advanced Page with Sub-Menu Integration

```typescript
import { containers } from '../menu-items/containers';

@ui.decorators.page<DetailPanel, ShowCaseProvider>({
    authorizationCode: 'HLPRPNL',
    category: 'SHOWCASE',
    businessActions() {
        return [this.action1, this.action2, this.action3, this.action4];
    },
    detailPanel() {
        return {
            footerActions: [this.action1, this.action2, this.action3, this.action4],
            header: this.detailPanelHeaderSection,
            sections: [
                this.detailPanelBodySection1,
                this.detailPanelBodySection2,
                this.detailPanelBodySection3,
                this.detailPanelBodySection4,
                this.detailPanelBodySection5,
            ],
        };
    },
    isTransient: true,
    module: 'show-case',
    node: '@sage/xtrem-show-case/ShowCaseProvider',
    title: 'Page - Detail panel',
    menuItem: containers,
})
export class DetailPanel extends ui.Page<GraphApi> {
    // Page implementation
}
```

#### Transient Page with Menu Integration

```typescript
import { containers } from '../menu-items/containers';

@ui.decorators.page<PageTabs>({
    category: 'SHOWCASE',
    mode: 'tabs',
    isTransient: true,
    module: 'show-case',
    title: 'Page - Tabs Layout',
    menuItem: containers,
})
export class PageTabs extends ui.Page {
    // Page implementation
}
```

### Menu Item Naming Conventions

Follow consistent naming patterns for menu items:

1. **Root Items**: Use descriptive names that represent major application areas
2. **Sub Items**: Use specific feature or functionality names
3. **IDs**: Include package namespace for uniqueness (`@sage/package-name/menu-item-id`)
4. **File Names**: Use kebab-case matching the menu item purpose

### Priority Management

Use priority values to control menu order:

- **Root menu items**: Use larger gaps (50, 100, 800) to allow insertion
- **Sub menu items**: Use smaller increments (10, 20, 30) for fine control
- **Lower numbers appear first** in the navigation

### Best Practices for Menu Items

1. **Use meaningful hierarchies**: Group related functionality under logical parent items
2. **Maintain consistent priority gaps**: Allow space for future menu items
3. **Use descriptive titles**: Make navigation intent clear to users
4. **Leverage proper namespacing**: Include package prefixes in IDs to avoid conflicts
5. **Organize exports centrally**: Use index files to manage menu item exports
6. **Match icons consistently**: Use appropriate design system icons for root items
7. **Consider user workflows**: Structure menus to support common user tasks
8. **Document menu structure**: Maintain clear documentation of menu hierarchies

## Async Processing Utilities

The Xtrem framework provides async processing utilities through the `@sage/xtrem-async-helper` package. For server-side code, these utilities are re-exported from `@sage/xtrem-core` for convenience.

### Import Patterns

```typescript
// Client-side import
import { asyncArray, funnel, getAsync, AsyncReader } from '@sage/xtrem-async-helper';

// Server-side import (re-exported from core)
import { asyncArray, funnel, getAsync, AsyncReader } from '@sage/xtrem-core';
```

### AsyncArray

The primary utility for async array operations with chainable methods similar to ES5 Array methods but optimized for async operations.

```typescript
import { asyncArray } from '@sage/xtrem-async-helper';

// Basic async array operations
await asyncArray(items).forEach(async (item, index) => {
    await processItem(item);
});

// Sequential processing with map
const results = await asyncArray(items)
    .map(async item => await transform(item))
    .filter(async result => result.isValid)
    .toArray();

// Parallel processing with controlled concurrency
await asyncArray(items).forEachParallel(5, async (item, index) => {
    await processItemConcurrently(item);
});

// Reduce operations for aggregation
const total = await asyncArray(numbers).reduce(async (sum, num) => {
    return sum + (await computeValue(num));
}, 0);

// Find operations
const firstMatch = await asyncArray(items).find(async item => {
    return await checkCondition(item);
});

// Check conditions
const hasValid = await asyncArray(items).some(async item => {
    return await validateItem(item);
});

const allValid = await asyncArray(items).every(async item => {
    return await validateItem(item);
});
```

### Funnel

Controls concurrency by limiting the number of parallel executions. Essential for preventing resource exhaustion.

```typescript
import { funnel } from '@sage/xtrem-async-helper';

// Create a funnel with max 5 concurrent operations
const processDataFunnel = funnel(5);

// Use the funnel to control concurrency
const results = await Promise.all(
    items.map(item =>
        processDataFunnel(async () => {
            return await processItem(item);
        }),
    ),
);

// Sequential processing (max = 1)
const sequentialFunnel = funnel(1);

for (const item of items) {
    await sequentialFunnel(async () => {
        await processSequentially(item);
    });
}

// Monitor funnel metrics
console.log(`Queue length: ${processDataFunnel.length}`);
console.log(`Capacity: ${processDataFunnel.capacity}`);
console.log(`High water mark: ${processDataFunnel.highWaterMark}`);

// Close funnel when done
processDataFunnel.close();
```

### GetAsync

Safely retrieves values from nested object structures where properties might be Promises.

```typescript
import { getAsync } from '@sage/xtrem-async-helper';

// Simple property access
const name = await getAsync(user, 'profile.name');

// Deep nested access with async properties
const result = await getAsync(customer, 'orders.latest.items.firstItem.product.name');

// Handling potentially null values
try {
    const value = await getAsync(data, 'nested.path.value');
} catch (error) {
    // Handle case where path doesn't exist
    console.log('Path not found:', error.message);
}
```

### AsyncReader

Advanced async iteration with support for streaming data processing.

```typescript
import { AsyncReader, createAsyncGenerator } from '@sage/xtrem-async-helper';

// Create async reader from generator
const reader = new AsyncGenericReader({
    read: async () => {
        // Return next item or undefined when done
        return await getNextItem();
    },
    stop: async () => {
        // Cleanup when reader is stopped
        await cleanupResources();
    },
});

// Process with reader
await reader.forEach(async (item, index) => {
    await processStreamedItem(item);
});

// Convert to array when needed
const allItems = await reader.readAll();

// Use as async generator
for await (const item of reader.toAsyncGenerator()) {
    await processItem(item);
}
```

### Real-World Usage Patterns from Xtrem Core

#### Sequential Table Creation

```typescript
// From application-create-sql-schema.ts
await asyncArray(context.application.getAllFactories()).forEach(factory => factory.verifyTable(context));

await asyncArray(factoriesToCreate).forEach(async factory => {
    loggers.application.info(`Creating table: ${factory.table.name}`);
    await factory.table.createTable(context, { skipForeignKeys: true });
});
```

#### Service Option Processing

```typescript
// From service-option-manager.ts
await asyncArray(activatedOptions).forEach(async activatedOption => {
    await this.updateActivation(activatedOption, true);
});

return asyncArray(activatedByOptions)
    .filter(async serviceOption => await this.shouldActivate(serviceOption))
    .map(serviceOption => serviceOption.name);
```

#### Controlled Concurrency with Funnel

```typescript
// From monitored-funnel.ts
import { funnel } from '@sage/xtrem-async-helper';

export function monitoredFunnel(funnelName: string, max = 1): Funnel {
    const fun = funnel(max, () => {
        // Callback executed when queue changes
        updateMetrics(funnelName);
    });
    return fun;
}

// Usage in global locks
private readonly _initFunnel = funnel(1);

await this._initFunnel(async () => {
    // Critical section - only one at a time
    await initializeResource();
});
```

#### Async Property Access

```typescript
// From node-$.ts - accessing nested async properties
import { getAsync } from '@sage/xtrem-async-helper';

async get<K extends keyof T>(path: K): Promise<UnPromised<T[K]>> {
    return getAsync(this, path as string) as Promise<UnPromised<T[K]>>;
}
```

### Performance Considerations

1. **Event Loop Health**: AsyncArray automatically yields control to prevent blocking the event loop during long operations
2. **Memory Management**: Use AsyncReader for streaming large datasets instead of loading everything into memory
3. **Concurrency Control**: Always use funnel to limit concurrent operations, especially for database or external API calls
4. **Sequential vs Parallel**: Use `forEach` for sequential processing, `forEachParallel` for controlled concurrency

### Best Practices

1. **Use `asyncArray`** for array operations that need async processing
2. **Control concurrency** with `funnel` to prevent resource exhaustion
3. **Handle nested async properties** with `getAsync` for safe access
4. **Process streams** with `AsyncReader` for large datasets
5. **Monitor funnel metrics** to optimize concurrency settings
6. **Always close funnels** when no longer needed to prevent memory leaks

### Migration Guide

If you're updating from custom async utilities to `@sage/xtrem-async-helper`:

```typescript
// Before: Custom parallel processing
await Promise.all(items.map(async item => await process(item)));

// After: Controlled concurrency with funnel
const processFunnel = funnel(10);
await Promise.all(items.map(item => processFunnel(async () => await process(item))));

// Before: Manual array iteration
for (const item of items) {
    await processItem(item);
}

// After: AsyncArray with event loop yielding
await asyncArray(items).forEach(async item => {
    await processItem(item);
});
```
