File

src/users/dto/customization.dto.ts

Index

Properties

Properties

mode
Type : TrackingMode
Decorators :
@ApiPropertyOptional({enum: TRACKING_MODES, description: 'demo (simulator) or live (telematics)'})
@IsString()
@IsIn(: string[])
import { ApiPropertyOptional } from '@nestjs/swagger';
import {
  IsArray,
  IsBoolean,
  IsIn,
  IsObject,
  IsOptional,
  IsString,
  MaxLength,
  ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';

class OrderColumnDto {
  @IsString()
  key!: string;

  @IsString()
  label!: string;

  @IsOptional()
  @IsBoolean()
  visible?: boolean;
}

/**
 * Telematics provider configuration stored at the organization level.
 * Different providers need different credential shapes — we accept the
 * superset and let the provider-specific code pick the fields it cares
 * about. Sensitive fields (apiKey/clientSecret/password) are write-only
 * via this DTO and are never returned by GET endpoints — the read side
 * masks them and returns boolean `has*` flags instead.
 */
export const TELEMATICS_PROVIDERS = [
  'mix_telematics',
  'geotab',
  'samsara',
  'webfleet',
  'verizon_connect',
  'cartrack',
  'teltonika',
  'other',
] as const;
export type TelematicsProvider = (typeof TELEMATICS_PROVIDERS)[number];

/**
 * Tracking mode for an organization. Default is `demo`, which runs the
 * built-in GPS simulator (vehicles move along Kenyan road routes so the
 * UI / geofencing / dispatch flow is testable without a real provider).
 *
 * Switching to `live` requires a verified telematics provider configured
 * for this org. The live worker pulls real positions from that provider
 * on a 30-second cron and writes them into the same vehicles columns
 * the demo path uses, so the rest of the platform doesn't care which
 * one is feeding it.
 */
export const TRACKING_MODES = ['demo', 'live'] as const;
export type TrackingMode = (typeof TRACKING_MODES)[number];

export class TrackingConfigDto {
  @ApiPropertyOptional({ enum: TRACKING_MODES, description: 'demo (simulator) or live (telematics)' })
  @IsString()
  @IsIn(TRACKING_MODES as unknown as string[])
  mode!: TrackingMode;
}

export class TelematicsConfigDto {
  @ApiPropertyOptional({
    enum: TELEMATICS_PROVIDERS,
    description: 'Telematics provider key',
  })
  @IsString()
  @IsIn(TELEMATICS_PROVIDERS as unknown as string[])
  provider!: TelematicsProvider;

  @ApiPropertyOptional({ description: 'API base URL — provider-specific' })
  @IsOptional()
  @IsString()
  @MaxLength(500)
  baseUrl?: string;

  @ApiPropertyOptional({ description: 'API key / bearer token (Samsara, Cartrack, Teltonika)' })
  @IsOptional()
  @IsString()
  @MaxLength(500)
  apiKey?: string;

  @ApiPropertyOptional({ description: 'OAuth client ID (Mix Telematics)' })
  @IsOptional()
  @IsString()
  @MaxLength(200)
  clientId?: string;

  @ApiPropertyOptional({ description: 'OAuth client secret (Mix Telematics)' })
  @IsOptional()
  @IsString()
  @MaxLength(500)
  clientSecret?: string;

  @ApiPropertyOptional({
    description: 'Provider org/group/database ID (Mix org group, Geotab database, Webfleet account)',
  })
  @IsOptional()
  @IsString()
  @MaxLength(200)
  organizationId?: string;

  @ApiPropertyOptional({ description: 'Username (Geotab, Webfleet, Verizon Connect)' })
  @IsOptional()
  @IsString()
  @MaxLength(200)
  username?: string;

  @ApiPropertyOptional({ description: 'Password (Geotab, Webfleet, Verizon Connect)' })
  @IsOptional()
  @IsString()
  @MaxLength(500)
  password?: string;

  @ApiPropertyOptional({ description: 'Account identifier (Webfleet)' })
  @IsOptional()
  @IsString()
  @MaxLength(200)
  account?: string;
}

export class UpdateCustomizationDto {
  @ApiPropertyOptional({
    description:
      'Map of nav href → custom label. Used by the sidebar to override default labels.',
    example: { '/orders': 'Loads', '/clients': 'Sites' },
  })
  @IsOptional()
  @IsObject()
  navLabels?: Record<string, string>;

  @ApiPropertyOptional({
    description: 'Order list column configuration (visibility + label overrides).',
    type: [OrderColumnDto],
  })
  @IsOptional()
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => OrderColumnDto)
  orderColumns?: OrderColumnDto[];

  @ApiPropertyOptional({
    description:
      'Global toggle for the Intelligence Center features (Ask Assistant, Route Optimizer, etc.).',
  })
  @IsOptional()
  @IsBoolean()
  aiEnabled?: boolean;

  @ApiPropertyOptional({
    description:
      'Telematics provider configuration. Stored at the org level under settings.telematics. ' +
      'Sensitive fields (apiKey, clientSecret, password) are write-only — they are never returned ' +
      'on read endpoints. Submit only the fields you want to change; existing sensitive fields ' +
      'are preserved when omitted.',
    type: TelematicsConfigDto,
  })
  @IsOptional()
  @ValidateNested()
  @Type(() => TelematicsConfigDto)
  telematics?: TelematicsConfigDto;

  @ApiPropertyOptional({
    description:
      'Tracking mode for this organization. Default `demo` runs the built-in GPS simulator. ' +
      'Set to `live` to pull positions from the configured telematics provider on a cron — ' +
      'requires a saved + verified provider in `telematics`.',
    type: TrackingConfigDto,
  })
  @IsOptional()
  @ValidateNested()
  @Type(() => TrackingConfigDto)
  tracking?: TrackingConfigDto;

  @ApiPropertyOptional({
    description:
      'Loading bay timeout config. maxWaitMinutes = how long a bay stays OCCUPIED before being marked LATE. ' +
      'autoReleaseMinutes = grace period after LATE before auto-releasing. alertOnLate = send a platform alert.',
    example: { maxWaitMinutes: 120, autoReleaseMinutes: 60, alertOnLate: true },
  })
  @IsOptional()
  @IsObject()
  bayTimeout?: {
    maxWaitMinutes?: number;
    autoReleaseMinutes?: number;
    alertOnLate?: boolean;
  };

  @ApiPropertyOptional({
    description:
      'IANA timezone string for this organization. All client widgets that render dates/times ' +
      '(dispatch Gantt, loading bays calendar, alerts, audit log) use this so the dispatcher ' +
      'always sees their local time regardless of where their browser is located. ' +
      'Examples: Africa/Nairobi, Africa/Lagos, Europe/London, America/New_York.',
    example: 'Africa/Nairobi',
  })
  @IsOptional()
  @IsString()
  @MaxLength(64)
  timezone?: string;

  @ApiPropertyOptional({
    description: 'Org currency (ISO 4217 code). Used by tariffs, reports, and invoices.',
    example: 'KES',
  })
  @IsOptional()
  @IsString()
  @MaxLength(8)
  currency?: string;
}

results matching ""

    No results matching ""