GdzieWjade/src/lib/types.ts
2026-10-03 15:02:13 +02:00

188 lines
5.6 KiB
TypeScript

export type Lang = 'pl' | 'en';
export type ProfileKind = 'wheelchair' | 'stroller';
/** Preferences only — never a diagnosis. Stored on the device. */
export interface Profile {
kind: ProfileKind;
/** Highest kerb the user can cross, cm. */
maxKerbCm: number;
/** Narrowest passage, metres. */
minWidthM: number;
/** Steepest slope, percent. */
maxInclinePct: number;
avoidCobbles: boolean;
needToilet: boolean;
needRest: boolean;
needChanging: boolean;
}
export type PlaceCategory = 'food' | 'culture' | 'shop' | 'stay' | 'health' | 'services' | 'toilets' | 'elevator' | 'transit';
export interface Place {
id: string;
lat: number;
lon: number;
name: string | null;
cat: PlaceCategory;
kind: string | null;
/** ISO date of the last edit of the OSM element (not necessarily of the access tags). */
ts: string | null;
tags: Record<string, string>;
/** Fictional place that exists only to demonstrate data states. */
demo?: boolean;
/** City-feed extras (GTFS stops). */
ext?: { gtfsWheelchair: number | null; feedDate: string | null; mode: 'tram' | 'bus'; osmMatch: string | null };
}
export interface Way {
id: number;
hw: string;
ts: string | null;
nodes: number[];
name?: string;
surface?: string;
smooth?: string;
incline?: string;
width?: string;
stepCount?: number;
ramp?: string;
rampWheelchair?: string;
rampStroller?: string;
handrail?: string;
footway?: 'crossing' | 'sidewalk';
tunnel?: boolean;
bridge?: boolean;
wheelchair?: string;
checked?: string;
}
export interface Snapshot {
meta: {
fetchedAt: string;
bbox: [number, number, number, number];
source: string;
license: string;
endpoint: string;
cityData: { name: string; url: string; feedDates: { mode: string; version: string | null }[]; license: string };
counts: Record<string, number>;
};
places: Place[];
toilets: Place[];
elevators: Place[];
/** [lat, lon, ISO date | null] */
benches: [number, number, string | null][];
nodes: [number, number][];
ways: Way[];
kerbs: Record<string, string>;
}
// ---------------- assessment ----------------
export type Status = 'ok' | 'warn' | 'fail' | 'unknown' | 'conflict';
/**
* How far a piece of information can be trusted. Ordered strongest first.
* - verified: dated survey (check_date / survey:date) within 24 months
* - mapped: present in OSM, edited within 24 months, no survey date
* - stale: present in OSM but not touched for over 24 months
* - inferred: derived from nearby geometry, not stated for this place
* - user: unverified report from a user of this app
* - none: nothing is known
*/
export type Confidence = 'verified' | 'mapped' | 'stale' | 'inferred' | 'user' | 'none';
export type SourceKind = 'osm-tag' | 'osm-geometry' | 'user-report' | 'city-review' | 'city-gtfs' | 'demo' | 'none';
export type FactKey = 'entrance' | 'steps' | 'ramp' | 'width' | 'surface' | 'toilet' | 'rest' | 'changing';
/** Translatable message: key into the i18n dictionary plus parameters. */
export interface Msg {
k: string;
p?: Record<string, string | number>;
}
export interface Fact {
key: FactKey;
status: Status;
headline: Msg;
detail?: Msg;
source: SourceKind;
confidence: Confidence;
/** ISO date this information dates from, when known. */
date: string | null;
/** Required for this profile, i.e. it can decide the verdict. */
required: boolean;
/** Second opinion that disagrees (user report vs OSM). */
alt?: { headline: Msg; source: SourceKind; confidence: Confidence; date: string | null };
}
export type Verdict = 'match' | 'partial' | 'fail' | 'incomplete' | 'conflict';
export interface Assessment {
placeId: string;
verdict: Verdict;
facts: Fact[];
}
export type ReportAttr = 'entrance' | 'steps' | 'width' | 'toilet' | 'ramp';
export type MeasureMethod = 'photo-ref' | 'inclinometer' | 'manual' | 'lidar';
/** A number measured on site. `lidar` is reserved for a future native iOS companion app. */
export interface Measurement {
kind: 'kerb' | 'incline';
value: number;
unit: 'cm' | 'pct';
method: MeasureMethod;
}
export type ReportValue = 'yes' | 'limited' | 'no' | number;
export interface UserReport {
id: string;
placeId: string;
attr: ReportAttr;
/** yes/limited/no, or entrance width in cm. */
value: ReportValue;
note?: string;
createdAt: string;
/** Who wrote it (demo account id). */
authorId?: string;
/** City moderation outcome; absent = pending. */
status?: 'pending' | 'accepted' | 'rejected';
reviewedAt?: string;
reviewedBy?: string;
/** Seeded example, not a real user report. */
demo?: boolean;
/** Photo stored in IndexedDB under this id (EXIF stripped, downscaled). */
photoId?: string;
/** Measured on site, if any. */
measure?: Measurement;
/** Automated first look; a human always decides. */
vetting?: VetState;
}
// ---------------- AI triage ----------------
export type EvidenceFlag = 'blurry' | 'face' | 'text' | 'unrelated' | 'no-photo';
export interface Vetting {
provider: string;
at: string;
validity: 'valid' | 'unclear' | 'invalid';
/** Does the photo agree with what the reporter claims? */
consistency: 'supports' | 'contradicts' | 'neutral';
severity: 'critical' | 'major' | 'minor' | 'none';
accessibilityClass: 'inaccessible' | 'difficult' | 'accessible' | 'unknown';
triage: 'fast-track' | 'review' | 'likely-reject';
confidence: number;
flags: EvidenceFlag[];
/** Top photo labels with probabilities (0..1). */
top: { key: string; score: number }[];
reasons: Msg[];
}
export type VetState =
| { state: 'running' }
| { state: 'done'; result: Vetting }
| { state: 'failed'; reason: 'no-credits' | 'error' | 'skipped' };