172 lines
4.3 KiB
TypeScript
172 lines
4.3 KiB
TypeScript
export type PenaltyType = 'water' | 'ob' | 'lost_ball' | 'unplayable' | 'other';
|
|
|
|
export interface PenaltyDetail {
|
|
id: string;
|
|
type: PenaltyType;
|
|
strokes: number;
|
|
shotNumber?: number; // Shot 1 (Tee), Shot 2, etc.
|
|
notes?: string;
|
|
}
|
|
|
|
export type FIRStatus = 'hit' | 'left' | 'right' | 'short' | 'na';
|
|
|
|
export type GIRStatus = 'hit' | 'miss_short' | 'miss_long' | 'miss_left' | 'miss_right' | 'miss';
|
|
|
|
export interface BunkerDetail {
|
|
hit: boolean;
|
|
fairwayBunker: boolean;
|
|
greensideBunker: boolean;
|
|
sandSave: boolean | null; // Did the player make par or better out of a greenside bunker?
|
|
strokesInBunker?: number;
|
|
}
|
|
|
|
export interface HoleScore {
|
|
holeNumber: number;
|
|
grossScore: number;
|
|
fir: FIRStatus;
|
|
gir: GIRStatus;
|
|
putts: number;
|
|
foregreenPutts: number; // Putts taken from off-green / foregreen (fringe)
|
|
firstPuttFromFringe: boolean;
|
|
firstPuttDistanceFt: number | null;
|
|
penalties: PenaltyDetail[];
|
|
bunker: BunkerDetail;
|
|
upAndDown: boolean | null; // Scrambled par or better after missing GIR
|
|
driveClub?: string;
|
|
driveDistanceYds?: number | null;
|
|
notes?: string;
|
|
}
|
|
|
|
export interface LatLngLiteral {
|
|
lat: number;
|
|
lng: number;
|
|
}
|
|
|
|
export interface HoleCoordinates {
|
|
tee: LatLngLiteral;
|
|
fairway?: LatLngLiteral;
|
|
greenFront: LatLngLiteral;
|
|
greenCenter: LatLngLiteral;
|
|
greenBack: LatLngLiteral;
|
|
}
|
|
|
|
export interface Hole {
|
|
holeNumber: number;
|
|
par: number; // 3, 4, 5
|
|
yardage: number; // hole distance in course units (meters or yards)
|
|
handicapIndex: number; // 1 to 18
|
|
coordinates?: HoleCoordinates;
|
|
}
|
|
|
|
export interface Course {
|
|
id: string;
|
|
name: string;
|
|
location: string;
|
|
tee: string; // 'Championship Black' | 'Tournament Blue' | 'Member White' | 'Forward Red'
|
|
parTotal: number;
|
|
rating?: number;
|
|
slope?: number;
|
|
holes: Hole[];
|
|
isCustom?: boolean;
|
|
apiId?: string;
|
|
city?: string;
|
|
state?: string;
|
|
website?: string;
|
|
description?: string;
|
|
}
|
|
|
|
export type AchievementTier = 'bronze' | 'silver' | 'gold' | 'platinum';
|
|
export type AchievementCategory = 'scoring' | 'accuracy' | 'short_game' | 'special';
|
|
|
|
export interface Achievement {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
category: AchievementCategory;
|
|
tier: AchievementTier;
|
|
iconName: string;
|
|
isUnlocked?: boolean;
|
|
unlockedAt?: string;
|
|
unlockedRoundId?: string;
|
|
unlockedRoundName?: string;
|
|
}
|
|
|
|
export interface TrackingSettings {
|
|
// Fairways
|
|
trackFIR: boolean;
|
|
trackFIRDirection: boolean;
|
|
|
|
// Greens in Regulation
|
|
trackGIR: boolean;
|
|
trackGIRDirection: boolean;
|
|
|
|
// Putting
|
|
trackPutts: boolean;
|
|
trackForegreen: boolean; // Foregreen / fringe putts
|
|
trackFirstPuttDistance: boolean;
|
|
|
|
// Penalties
|
|
trackPenalties: boolean;
|
|
trackPenaltyTypes: boolean; // Water, OB, Lost Ball, Unplayable
|
|
|
|
// Bunkers
|
|
trackBunkers: boolean;
|
|
trackBunkerTypes: boolean; // Fairway vs Greenside distinction
|
|
trackSandSaves: boolean;
|
|
|
|
// Scrambling / Up & Down
|
|
trackUpAndDown: boolean;
|
|
|
|
// Club & Distance
|
|
trackDriveClub: boolean;
|
|
trackDriveDistance: boolean;
|
|
|
|
// Notes
|
|
trackHoleNotes: boolean;
|
|
|
|
// Units
|
|
distanceUnit: 'yards' | 'meters';
|
|
puttDistanceUnit: 'feet' | 'meters';
|
|
}
|
|
|
|
export interface Round {
|
|
id: string;
|
|
date: string;
|
|
courseId: string;
|
|
courseName: string;
|
|
teeName: string;
|
|
teeId?: string;
|
|
coursePar: number;
|
|
holesPlayed: 18 | 9;
|
|
nineType?: 'all' | 'front' | 'back';
|
|
scores: Record<number, HoleScore>;
|
|
isCompleted: boolean;
|
|
completedAt?: string;
|
|
settingsSnapshot: TrackingSettings;
|
|
playerHandicap: number;
|
|
weather?: string;
|
|
wind?: 'Calm' | 'Breezy' | 'Strong' | 'Gusty';
|
|
weatherData?: WeatherForecast;
|
|
}
|
|
|
|
export interface WeatherForecast {
|
|
temperature: number; // Celsius
|
|
temperatureF: number; // Fahrenheit
|
|
apparentTemperature?: number; // Celsius
|
|
windSpeed: number; // km/h
|
|
windDirection?: number; // degrees 0-360
|
|
windDirectionCompass: string; // e.g. "NE", "SW"
|
|
windCategory: 'Calm' | 'Breezy' | 'Strong' | 'Gusty';
|
|
precipitationProbability: number; // 0-100%
|
|
weatherCode: number; // WMO code
|
|
conditionText: string; // e.g. "Partly Cloudy", "Clear Sky"
|
|
relativeHumidity?: number; // percentage
|
|
locationLabel: string;
|
|
latitude: number;
|
|
longitude: number;
|
|
fetchedAt: string;
|
|
source: 'gps' | 'course' | 'manual';
|
|
}
|
|
|
|
export type PresetProfile = 'tour' | 'standard' | 'minimal' | 'custom';
|