Skip to main content
A Target represents a performance goal for an indicator. Targets can be organization-wide or site-specific, expressed as absolute values or relative percentages, and scheduled by period (yearly, quarterly, monthly, or timestamp).

Overview

  • Link to an indicator (optional but recommended)
  • Define a baseline period for relative targets (year, quarter, month)
  • Add one or more target entries with period, type, and value
  • Scope entries to a specific site or leave them organization-wide
  • Optionally store integration metadata (e.g., dataset mappings)

Data Model

Target (simplified):
// apps/azalt/src/server/db/types/target.ts
interface TargetEntry {
  siteId?: string | null;
  period: 'YEARLY' | 'QUARTERLY' | 'MONTHLY' | 'TIMESTAMP';
  year: number;
  month?: number | null;     // 1-12 for MONTHLY
  quarter?: number | null;   // 1-4 for QUARTERLY
  type: 'ABSOLUTE' | 'RELATIVE_PERCENT';
  targetValue: number;       // numeric or percent (for RELATIVE_PERCENT)
}

interface Target {
  id: string;
  indicatorId: string | null;
  organizationId: string;
  title: string | null;
  baselineYear: number | null;
  baselineQuarter: number | null;
  baselineMonth: number | null;
  metadata: Record<string, unknown> | null; // e.g. { indicatorMappings: { [indicatorId]: { name, field } } }
  targetEntries: TargetEntry[];
  createdAt: Date;
  updatedAt: Date;
}
Notes:
  • TIMESTAMP supports event-based targets in addition to calendar periods.
  • If siteId is omitted on an entry, it applies at organization level.
  • Relative targets interpret targetValue as a percentage change from the baseline period.

Target Entries

Each entry represents a goal for a specific period and (optionally) site:
{
  "siteId": "site_123",            // optional
  "period": "MONTHLY",             // YEARLY | QUARTERLY | MONTHLY | TIMESTAMP
  "year": 2025,
  "month": 7,                       // for MONTHLY
  "type": "RELATIVE_PERCENT",      // ABSOLUTE | RELATIVE_PERCENT
  "targetValue": -12.5              // -12.5% reduction vs baseline
}
Common patterns:
  • Organization target: omit siteId on entries
  • Site target: set siteId per entry; different sites may have different trajectories
  • Mixed schedule: combine yearly and monthly entries as needed

Baseline & Status

  • Absolute targets: compare current indicator value vs targetValue
  • Relative targets: evaluate change required from the baseline period (year/quarter/month)
UI cues (Targets page) summarize status per latest entry in the current year:
  • On-track/Warning/Behind for absolute targets (based on % progress)
  • Baseline required for relative targets

API

TRPC procedures (with REST mirrors via OpenAPI):
  • targets.list → GET /targets
  • targets.getById → GET /targets/{id}
  • targets.create → POST /targets
  • targets.update → PUT /targets/{id}
  • targets.delete → DELETE /targets/{id}
Create example:
{
  "indicatorId": "ind_abc",
  "title": "Targets for Electricity Intensity",
  "baselineYear": 2024,
  "baselineQuarter": null,
  "baselineMonth": null,
  "metadata": {
    "indicatorMappings": {
      "ind_usage": { "name": "electricity", "field": "kwh" }
    }
  },
  "targetEntries": [
    { "period": "YEARLY", "year": 2025, "type": "ABSOLUTE", "targetValue": 100 },
    { "siteId": "site_1", "period": "MONTHLY", "year": 2025, "month": 12, "type": "RELATIVE_PERCENT", "targetValue": -10 }
  ]
}
Access control:
  • All operations enforce organization scoping and site-level permissions
  • Site IDs in entries must be accessible; otherwise request is rejected

UI & Workflow

  • Page: /targets lists groups with inline title edit, indicator preview, and status badges
  • Modal: create/edit with preview, per-period entries, and site scoping
  • Timeline/List views: visualize progression and trend arrows between entries
  • Indicator mappings: optional metadata.indicatorMappings to align target context with indicator datasets

Dashboards & Widgets

  • Widgets can reference selected target entries (e.g., draw goal lines)
  • Selection is stored in widget settings as selectedTargetEntries: string[]

Query Patterns

  • Filter by indicator/site/year on list
  • Client usually fetches current indicator values to compare against latest target in the current year

Security

  • Organization isolation is enforced in queries
  • Site access is validated for any siteId present in entries

Migration History (high level)

  • Introduced Target with per-row fields (period, type, value)
  • Restructured to targetEntries (jsonb) to support multiple entries per target
  • Added title, organizationId, and baselineQuarter

See Also

  • Concepts: Dataset, Unit, Dashboard, Widget
  • Server: targets router in apps/azalt/src/server/api/routers/target