@simonarcher/fika-types 1.5.1 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Bag-scan event design surface (FIK-124 P1).
3
+ *
4
+ * NOTE: the `bean_scans` table is **not yet migrated**. This file is the
5
+ * type contract for the future bag-scan project — see
6
+ * `plans/bean-schema-redesign.md` (P1 — Bag-scan ready surface) for the SQL
7
+ * DDL and rationale.
8
+ *
9
+ * New types in this package use ISO string timestamps (Supabase / Postgres
10
+ * convention). Firebase-era types like `CoffeeBean` retain `Timestamp` for
11
+ * back-compat during the Firestore→Supabase migration window.
12
+ */
13
+ /**
14
+ * What the user did with the scan after seeing the candidates.
15
+ *
16
+ * - `pending` — scan ingested, no user action yet
17
+ * - `saved` — user kept the matched bean (e.g. added to a list)
18
+ * - `tried` — user marked having drunk this bean
19
+ * - `rejected` — user said the match was wrong
20
+ * - `created_new` — user created a new bean record from this scan
21
+ */
22
+ export type BeanScanAction = "pending" | "saved" | "tried" | "rejected" | "created_new";
23
+ /**
24
+ * One row per user scan event. Powers the bag-scan loop: photograph →
25
+ * OCR + photo similarity → top-N candidate beans → user picks (or rejects).
26
+ */
27
+ export interface BeanScan {
28
+ id: string;
29
+ userId?: string;
30
+ photoUrl: string;
31
+ ocrText?: string;
32
+ /**
33
+ * Top-N matcher candidates as inline jsonb. Promoted to a dedicated
34
+ * `bean_scan_candidates` table only when cross-scan analytics warrant it.
35
+ */
36
+ candidates?: Array<{
37
+ beanId: string;
38
+ score: number;
39
+ source: "ocr" | "vector" | "hybrid";
40
+ }>;
41
+ matchedBeanId?: string;
42
+ action: BeanScanAction;
43
+ deviceMeta?: Record<string, unknown>;
44
+ createdAt: string;
45
+ resolvedAt?: string;
46
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Bag-scan event design surface (FIK-124 P1).
4
+ *
5
+ * NOTE: the `bean_scans` table is **not yet migrated**. This file is the
6
+ * type contract for the future bag-scan project — see
7
+ * `plans/bean-schema-redesign.md` (P1 — Bag-scan ready surface) for the SQL
8
+ * DDL and rationale.
9
+ *
10
+ * New types in this package use ISO string timestamps (Supabase / Postgres
11
+ * convention). Firebase-era types like `CoffeeBean` retain `Timestamp` for
12
+ * back-compat during the Firestore→Supabase migration window.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/coffee.d.ts CHANGED
@@ -126,16 +126,31 @@ export type DecafMethod = "Swiss Water" | "Sugarcane / Ethyl Acetate" | "Carbon
126
126
  export declare const DECAF_METHODS: readonly ["Swiss Water", "Sugarcane / Ethyl Acetate", "Carbon Dioxide (CO₂)", "Methylene Chloride", "Mountain Water", "Unknown"];
127
127
  export type CoffeeProcessingMethod = "Washed (Wet)" | "Natural (Dry)" | "Honey" | "Anaerobic Fermentation" | "Carbonic Maceration" | "Wet-Hulled" | "Semi-Washed" | "Experimental / Other" | "Unknown";
128
128
  export type RoastLevel = "Light" | "Medium" | "Dark" | "Medium-Dark" | "Medium-Light" | "Espresso";
129
+ /**
130
+ * Per-origin row on a bean. Mirrors `coffee_bean_origins`.
131
+ * Multiple origins = blend (use `blendPercentage` when documented).
132
+ */
129
133
  export interface Origin {
130
134
  countryCode: string;
131
135
  countryName?: string;
132
136
  region?: string;
133
137
  subregion?: string;
134
- elevationMinMeters?: number;
135
- elevationMaxMeters?: number;
138
+ altitudeMinM?: number;
139
+ altitudeMaxM?: number;
140
+ farmId?: string;
141
+ farmNameUnverified?: string;
142
+ producerName?: string;
143
+ tradeProgram?: string;
144
+ blendPercentage?: number;
136
145
  }
146
+ /**
147
+ * Recipe-level bean record. Flat fields mirror `coffee_beans`; per-origin detail
148
+ * lives in `origins[]` (joined from `coffee_bean_origins`).
149
+ *
150
+ * Note: `varieties` here is recipe-level (what the roaster shipped). Farm-level
151
+ * varieties (what the farm grows) live on `Farm.varieties`.
152
+ */
137
153
  export interface CoffeeBean {
138
- active: boolean;
139
154
  id: string;
140
155
  name: string;
141
156
  description?: string;
@@ -146,9 +161,10 @@ export interface CoffeeBean {
146
161
  origins?: Origin[];
147
162
  varieties?: string[];
148
163
  harvestYear?: number;
164
+ harvestPeriod?: string;
149
165
  lotName?: string;
150
166
  scaScore?: number;
151
- roastLevel?: "Light" | "Medium-Light" | "Medium" | "Medium-Dark" | "Dark";
167
+ roastLevel?: RoastLevel;
152
168
  processing?: CoffeeProcessingMethod;
153
169
  processingDetails?: string;
154
170
  tastingNotes?: string[];
@@ -160,10 +176,16 @@ export interface CoffeeBean {
160
176
  farmerStory?: string;
161
177
  impactNotes?: string;
162
178
  awards?: string[];
179
+ certifications?: string[];
163
180
  limitedEdition?: boolean;
181
+ active: boolean;
164
182
  createdAt?: Timestamp;
165
183
  updatedAt?: Timestamp;
166
184
  }
185
+ /**
186
+ * Submission payload for a proposed bean. Origins ride along via `submission_origins`
187
+ * once accepted; the server promotes them to `coffee_bean_origins` on approval.
188
+ */
167
189
  export interface SubmissionCoffeeBeanData {
168
190
  name: string;
169
191
  description?: string;
@@ -23,7 +23,6 @@ export interface CoffeeShopEvent extends NewCoffeeShopEvent {
23
23
  createdAt: Date;
24
24
  updatedAt: Date;
25
25
  eventType: CoffeeShopEventType;
26
- rewardStickerId?: string | null;
27
26
  }
28
27
  export interface EventAttendee {
29
28
  userId: string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './shop';
2
2
  export * from './coffee';
3
+ export * from './bean-scans';
3
4
  export * from './farm';
4
5
  export * from './user';
5
6
  export * from './userList';
package/dist/index.js CHANGED
@@ -16,8 +16,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  // Export all shop-related types
18
18
  __exportStar(require("./shop"), exports);
19
- // Export all coffee-related types
19
+ // Export all coffee-related types
20
20
  __exportStar(require("./coffee"), exports);
21
+ // Export bag-scan types (design surface; bean_scans table not yet migrated)
22
+ __exportStar(require("./bean-scans"), exports);
21
23
  // Export all farm-related types
22
24
  __exportStar(require("./farm"), exports);
23
25
  // Export all user-related types
package/dist/shop.d.ts CHANGED
@@ -256,21 +256,7 @@ export type ShopData = {
256
256
  completedAt?: AdminTimestamp;
257
257
  lastUpdated?: AdminTimestamp;
258
258
  };
259
- ownerSectionVerifications?: OwnerSectionVerifications;
260
259
  };
261
- /** Stable section keys exposed in the owner portal and surfaced as "verified
262
- * by owner" badges on user-facing apps. */
263
- export type OwnerSectionKey = 'hours' | 'about' | 'photos' | 'contact' | 'socials' | 'brew_methods' | 'roasters' | 'amenities' | 'specialties' | 'specialty_flags';
264
- export interface OwnerSectionVerification {
265
- /** ISO-8601 timestamp of when the owner last attested this section. */
266
- verifiedAt: string;
267
- /** staff_invites.id of the token used (null if attributed to the
268
- * signed-in accepted_by user, post-claim). */
269
- tokenId: string | null;
270
- /** Email captured from the invite at attestation time. */
271
- actorEmail: string | null;
272
- }
273
- export type OwnerSectionVerifications = Partial<Record<OwnerSectionKey, OwnerSectionVerification>>;
274
260
  export interface ShopCommunityStats {
275
261
  totalCheckIns: number;
276
262
  uniqueVisitors: number;
@@ -298,10 +284,6 @@ export interface DailyVisitorStats {
298
284
  updatedAt: AdminTimestamp;
299
285
  }
300
286
  export interface ShopInfo {
301
- /** FIKA-curated public description. Mirrored to top-level `shop.about`. */
302
- description?: string | null;
303
- /** Owner-authored public note, edited through the owner portal. */
304
- ownerDescription?: string | null;
305
287
  features: Partial<Record<ShopFeatureKey, boolean | null>> & Record<string, boolean | null>;
306
288
  wifiDetails?: {
307
289
  name?: string;
@@ -319,17 +301,6 @@ export interface ShopContactDetails {
319
301
  contactNumber: string;
320
302
  email: string;
321
303
  website: string;
322
- /**
323
- * Social media handles. Stored alongside other contact data so a shop has a
324
- * single source of truth for "how to reach this place". Bare handles only —
325
- * no `@` prefix, no URL — clients construct platform URLs at render time.
326
- * Optional/nullable to keep records sparse for shops that don't publish a
327
- * given platform.
328
- */
329
- instagram?: string | null;
330
- facebook?: string | null;
331
- tiktok?: string | null;
332
- twitter?: string | null;
333
304
  address: {
334
305
  formattedAddress: string;
335
306
  shortFormattedAddress: string;
@@ -342,18 +313,9 @@ export interface ShopContactDetails {
342
313
  };
343
314
  updatedAt?: Date;
344
315
  }
345
- export type ShopFeatureKey = 'powerOutlets' | 'laptopFriendly' | 'outdoorSeating' | 'indoorSeating' | 'accessibility' | 'alcohol' | 'breakfast' | 'lunch' | 'vegetarian' | 'petFriendly' | 'kidFriendly' | 'restrooms' | 'parking' | 'wifi' | 'acceptsCash' | 'acceptsCreditCards' | 'acceptsNfc' | 'discountOnReusableCup' | 'glutenFree' | 'loyaltyCard' | 'takeaway' | 'delivery' | 'walkInOnly';
346
- export type FeatureGroupKey = 'Space & Seating' | 'Work & Facilities' | 'Food & Drink' | 'Service' | 'Payment';
347
- /** Maps each feature key to its display group. Single source of truth for admin and mobile. */
348
- export declare const featureGroupMap: Record<ShopFeatureKey, FeatureGroupKey>;
349
- /** Display labels for each feature key. Single source of truth for admin and mobile. */
350
- export declare const featureLabelMap: Record<ShopFeatureKey, string>;
351
- export type ShopSpecialtyKey = 'inHouseRoastery' | 'pourOverSpecialty' | 'espressoExpert' | 'singleOriginFocus' | 'rotatingRoasters' | 'tastingNotesOnMenu' | 'naturalFermentedFocus' | 'cuppingAndEducation' | 'cozyAmbiance' | 'bustlingEnergy' | 'espressoBarStanding' | 'vinylRecords' | 'uniqueExperience' | 'inHouseBakery' | 'freshPastries' | 'localBakeryPartner' | 'dessertDestination' | 'lgbtqFriendly' | 'womenOwned' | 'zeroWasteFocus' | 'communityHub' | 'laptopWelcome' | 'fastWifi' | 'longStayOk';
352
- export type SpecialtyCategoryKey = 'Coffee' | 'Atmosphere' | 'Food & Treats' | 'Identity & Values' | 'Workspace';
353
- /** Maps each specialty key to its category. Single source of truth for admin and mobile. */
354
- export declare const specialtyCategoryMap: Record<ShopSpecialtyKey, SpecialtyCategoryKey>;
355
- /** Display labels for each specialty key. Single source of truth for admin and mobile. */
356
- export declare const specialtyLabelMap: Record<ShopSpecialtyKey, string>;
316
+ export type ShopFeatureKey = 'powerOutlets' | 'laptopFriendly' | 'outdoorSeating' | 'indoorSeating' | 'accessibility' | 'alcohol' | 'breakfast' | 'lunch' | 'vegetarian' | 'petFriendly' | 'restrooms' | 'parking' | 'wifi' | 'acceptsCash' | 'acceptsCreditCards' | 'acceptsNfc' | 'discountOnReusableCup' | 'glutenFree' | 'loyaltyCard' | 'takeaway' | 'delivery';
317
+ export type ShopSpecialtyKey = 'pourOverSpecialty' | 'espressoExpert' | 'singleOriginFocus' | 'localRoaster' | 'brewingEducation' | 'cuppingEvents' | 'matchaSpecialty' | 'decafSpecialty' | 'inHouseRoastery' | 'relaxingVibes' | 'bustlingEnergy' | 'quietStudySpace' | 'uniqueExperience' | 'instagramWorthy' | 'cozyAmbiance' | 'freshPastries' | 'homemadeTreats' | 'artisanBread' | 'localBakery' | 'healthyOptions' | 'dessertDestination' | 'inHouseBakery' | 'signatureBakedGoods' | 'lgbtqFriendly' | 'womenOwned' | 'familyOwned' | 'communityHub' | 'sociallyConscious' | 'localArtSupport' | 'liveMusic' | 'localArt' | 'bookExchange' | 'boardGames' | 'culturalEvents' | 'workshopSpace' | 'laptopFriendlyPro' | 'meetingSpaceAvailable' | 'quietZones' | 'fastWifi' | 'longStayWelcome' | 'coworkingVibes';
318
+ export type SpecialtyCategoryKey = 'Coffee Excellence' | 'Atmosphere' | 'Food & Treats' | 'Community & Values' | 'Entertainment' | 'Work & Study';
357
319
  export interface ShopSpecialties {
358
320
  highlighted: ShopSpecialtyKey[];
359
321
  additional: ShopSpecialtyKey[];
package/dist/shop.js CHANGED
@@ -1,129 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.specialtyLabelMap = exports.specialtyCategoryMap = exports.featureLabelMap = exports.featureGroupMap = void 0;
4
- /** Maps each feature key to its display group. Single source of truth for admin and mobile. */
5
- exports.featureGroupMap = {
6
- // Space & Seating
7
- indoorSeating: 'Space & Seating',
8
- outdoorSeating: 'Space & Seating',
9
- petFriendly: 'Space & Seating',
10
- kidFriendly: 'Space & Seating',
11
- // Work & Facilities
12
- wifi: 'Work & Facilities',
13
- powerOutlets: 'Work & Facilities',
14
- laptopFriendly: 'Work & Facilities',
15
- restrooms: 'Work & Facilities',
16
- parking: 'Work & Facilities',
17
- accessibility: 'Work & Facilities',
18
- // Food & Drink
19
- breakfast: 'Food & Drink',
20
- lunch: 'Food & Drink',
21
- vegetarian: 'Food & Drink',
22
- glutenFree: 'Food & Drink',
23
- alcohol: 'Food & Drink',
24
- // Service
25
- takeaway: 'Service',
26
- delivery: 'Service',
27
- loyaltyCard: 'Service',
28
- discountOnReusableCup: 'Service',
29
- walkInOnly: 'Service',
30
- // Payment
31
- acceptsCash: 'Payment',
32
- acceptsCreditCards: 'Payment',
33
- acceptsNfc: 'Payment',
34
- };
35
- /** Display labels for each feature key. Single source of truth for admin and mobile. */
36
- exports.featureLabelMap = {
37
- // Space & Seating
38
- indoorSeating: 'Indoor Seating',
39
- outdoorSeating: 'Outdoor Seating',
40
- petFriendly: 'Pet Friendly',
41
- kidFriendly: 'Kid Friendly',
42
- // Work & Facilities
43
- wifi: 'Free WiFi',
44
- powerOutlets: 'Power Outlets',
45
- laptopFriendly: 'Laptop Friendly',
46
- restrooms: 'Restrooms',
47
- parking: 'Parking',
48
- accessibility: 'Wheelchair Accessible',
49
- // Food & Drink
50
- breakfast: 'Serves Breakfast',
51
- lunch: 'Serves Lunch',
52
- vegetarian: 'Vegetarian Options',
53
- glutenFree: 'Gluten-Free Options',
54
- alcohol: 'Serves Alcohol',
55
- // Service
56
- takeaway: 'Takeaway',
57
- delivery: 'Delivery',
58
- loyaltyCard: 'Loyalty Card',
59
- discountOnReusableCup: 'Discount for Reusable Cup',
60
- walkInOnly: 'Walk-In Only',
61
- // Payment
62
- acceptsCash: 'Cash Payments',
63
- acceptsCreditCards: 'Card Payments',
64
- acceptsNfc: 'Contactless / NFC',
65
- };
66
- /** Maps each specialty key to its category. Single source of truth for admin and mobile. */
67
- exports.specialtyCategoryMap = {
68
- // Coffee
69
- inHouseRoastery: 'Coffee',
70
- pourOverSpecialty: 'Coffee',
71
- espressoExpert: 'Coffee',
72
- singleOriginFocus: 'Coffee',
73
- rotatingRoasters: 'Coffee',
74
- tastingNotesOnMenu: 'Coffee',
75
- naturalFermentedFocus: 'Coffee',
76
- cuppingAndEducation: 'Coffee',
77
- // Atmosphere
78
- cozyAmbiance: 'Atmosphere',
79
- bustlingEnergy: 'Atmosphere',
80
- espressoBarStanding: 'Atmosphere',
81
- vinylRecords: 'Atmosphere',
82
- uniqueExperience: 'Atmosphere',
83
- // Food & Treats
84
- inHouseBakery: 'Food & Treats',
85
- freshPastries: 'Food & Treats',
86
- localBakeryPartner: 'Food & Treats',
87
- dessertDestination: 'Food & Treats',
88
- // Identity & Values
89
- lgbtqFriendly: 'Identity & Values',
90
- womenOwned: 'Identity & Values',
91
- zeroWasteFocus: 'Identity & Values',
92
- communityHub: 'Identity & Values',
93
- // Workspace
94
- laptopWelcome: 'Workspace',
95
- fastWifi: 'Workspace',
96
- longStayOk: 'Workspace',
97
- };
98
- /** Display labels for each specialty key. Single source of truth for admin and mobile. */
99
- exports.specialtyLabelMap = {
100
- // Coffee
101
- inHouseRoastery: 'In-House Roastery',
102
- pourOverSpecialty: 'Pour-Over / Filter Focus',
103
- espressoExpert: 'Espresso Specialists',
104
- singleOriginFocus: 'Single Origin Focus',
105
- rotatingRoasters: 'Rotating / Guest Roasters',
106
- tastingNotesOnMenu: 'Tasting Notes on Menu',
107
- naturalFermentedFocus: 'Natural & Fermented Coffees',
108
- cuppingAndEducation: 'Cuppings & Education',
109
- // Atmosphere
110
- cozyAmbiance: 'Cosy & Welcoming',
111
- bustlingEnergy: 'Lively Atmosphere',
112
- espressoBarStanding: 'Espresso Bar / Standing Only',
113
- vinylRecords: 'Vinyl Records',
114
- uniqueExperience: 'Unique Concept',
115
- // Food & Treats
116
- inHouseBakery: 'In-House Bakery',
117
- freshPastries: 'Fresh Pastries',
118
- localBakeryPartner: 'Local Bakery Partner',
119
- dessertDestination: 'Dessert Destination',
120
- // Identity & Values
121
- lgbtqFriendly: 'LGBTQ+ Friendly',
122
- womenOwned: 'Women Owned & Led',
123
- zeroWasteFocus: 'Zero Waste Focused',
124
- communityHub: 'Community Hub',
125
- // Workspace
126
- laptopWelcome: 'Laptop Welcome',
127
- fastWifi: 'Fast WiFi',
128
- longStayOk: 'Long Stay OK',
129
- };
@@ -20,17 +20,15 @@ export type DbFarm = Tables<"farms">;
20
20
  export type DbFranchise = Tables<"franchises">;
21
21
  export type DbCoffeeBean = Tables<"coffee_beans">;
22
22
  export type DbShopRoaster = Tables<"shop_roasters">;
23
- export type DbRoasterFarm = Tables<"roaster_farms">;
24
23
  export type DbFranchiseRoaster = Tables<"franchise_roasters">;
25
24
  export type DbShopBean = Tables<"shop_beans">;
26
25
  export type DbUserFollower = Tables<"user_followers">;
27
- export type DbShopFollower = Tables<"shop_followers">;
28
26
  export type DbShopFavourite = Tables<"shop_favourites">;
27
+ export type DbCoffeeBeanOrigin = Tables<"coffee_bean_origins">;
28
+ export type DbSubmissionOrigin = Tables<"submission_origins">;
29
29
  export type DbAction = Tables<"actions">;
30
30
  export type DbPoints = Tables<"points">;
31
31
  export type DbReward = Tables<"rewards">;
32
- export type DbShot = Tables<"shots">;
33
- export type DbShotLike = Tables<"shot_likes">;
34
32
  export type DbReview = Tables<"reviews">;
35
33
  export type DbVote = Tables<"votes">;
36
34
  export type DbAssertion = Tables<"assertions">;
@@ -80,17 +78,15 @@ export type NewFarm = TablesInsert<"farms">;
80
78
  export type NewFranchise = TablesInsert<"franchises">;
81
79
  export type NewCoffeeBean = TablesInsert<"coffee_beans">;
82
80
  export type NewShopRoaster = TablesInsert<"shop_roasters">;
83
- export type NewRoasterFarm = TablesInsert<"roaster_farms">;
84
81
  export type NewFranchiseRoaster = TablesInsert<"franchise_roasters">;
85
82
  export type NewShopBean = TablesInsert<"shop_beans">;
86
83
  export type NewUserFollower = TablesInsert<"user_followers">;
87
- export type NewShopFollower = TablesInsert<"shop_followers">;
88
84
  export type NewShopFavourite = TablesInsert<"shop_favourites">;
85
+ export type NewCoffeeBeanOrigin = TablesInsert<"coffee_bean_origins">;
86
+ export type NewSubmissionOrigin = TablesInsert<"submission_origins">;
89
87
  export type NewAction = TablesInsert<"actions">;
90
88
  export type NewPoints = TablesInsert<"points">;
91
89
  export type NewReward = TablesInsert<"rewards">;
92
- export type NewShot = TablesInsert<"shots">;
93
- export type NewShotLike = TablesInsert<"shot_likes">;
94
90
  export type NewReview = TablesInsert<"reviews">;
95
91
  export type NewVote = TablesInsert<"votes">;
96
92
  export type NewAssertion = TablesInsert<"assertions">;
@@ -139,7 +135,8 @@ export type CoffeeBeanUpdate = TablesUpdate<"coffee_beans">;
139
135
  export type ActionUpdate = TablesUpdate<"actions">;
140
136
  export type PointsUpdate = TablesUpdate<"points">;
141
137
  export type RewardUpdate = TablesUpdate<"rewards">;
142
- export type ShotUpdate = TablesUpdate<"shots">;
138
+ export type CoffeeBeanOriginUpdate = TablesUpdate<"coffee_bean_origins">;
139
+ export type SubmissionOriginUpdate = TablesUpdate<"submission_origins">;
143
140
  export type ReviewUpdate = TablesUpdate<"reviews">;
144
141
  export type VoteUpdate = TablesUpdate<"votes">;
145
142
  export type AssertionUpdate = TablesUpdate<"assertions">;