@la-main-verte/shared-types 1.0.83 → 1.0.86

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@la-main-verte/shared-types",
3
- "version": "1.0.83",
3
+ "version": "1.0.86",
4
4
  "description": "Shared TypeScript interfaces for frontend of la-main-verte app",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Audience targeting shared types
3
+ * -------------------------------
4
+ * An Audience is a reusable, named set of targeting criteria. Membership is
5
+ * precomputed into a join table, so these shapes describe the *definition* of
6
+ * an audience, not its resolved members.
7
+ *
8
+ * Criteria are heterogeneous and open-ended: each `type` has its own `params`.
9
+ * The canonical runtime validation lives in the zod discriminated union at
10
+ * `server/domains/audience/criterion.schema.ts`; keep these types in sync with it.
11
+ */
12
+
13
+ /** How an audience combines its criteria. */
14
+ export type AudienceCombinator = 'all' | 'any'
15
+
16
+ /** Member within `radiusKm` of a point. `label` is a human-readable origin (e.g. a city name). */
17
+ export interface GeoRadiusCriterion {
18
+ type: 'geo_radius'
19
+ params: {
20
+ latitude: number
21
+ longitude: number
22
+ radiusKm: number
23
+ label?: string
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Member whose `countryCode` matches (ISO 3166-1 alpha-2, e.g. "CA", "US", "FR").
29
+ * @see documentation/geographic-targeting.md
30
+ */
31
+ export interface CountryCriterion {
32
+ type: 'country'
33
+ params: { countryCode: string }
34
+ }
35
+
36
+ /**
37
+ * Member whose `provinceCode` matches (ISO 3166-2 subdivision code, e.g. "CA-QC", "US-CA", "FR-IDF").
38
+ * @see documentation/geographic-targeting.md
39
+ */
40
+ export interface ProvinceCriterion {
41
+ type: 'province'
42
+ params: { provinceCode: string }
43
+ }
44
+
45
+ /** Member whose `city` matches (case-insensitive). */
46
+ export interface CityCriterion {
47
+ type: 'city'
48
+ params: { cityName: string }
49
+ }
50
+
51
+ /** Member owns at least one GardenMap of the given category (e.g. "greenhouse"). */
52
+ export interface GardenMapCategoryCriterion {
53
+ type: 'garden_map_category'
54
+ params: { category: string }
55
+ }
56
+
57
+ /**
58
+ * Behavioral: member who was active on at least `minDistinctDays` distinct
59
+ * calendar days (counted from SessionEvents) within the last `windowDays`.
60
+ * Time-relative — evaluated at recompute time, so the daily refresh cron keeps
61
+ * the trailing window honest.
62
+ */
63
+ export interface ActivityDistinctDaysCriterion {
64
+ type: 'activity_distinct_days'
65
+ params: { minDistinctDays: number; windowDays: number }
66
+ }
67
+
68
+ export type AudienceCriterion =
69
+ | GeoRadiusCriterion
70
+ | CountryCriterion
71
+ | ProvinceCriterion
72
+ | CityCriterion
73
+ | GardenMapCategoryCriterion
74
+ | ActivityDistinctDaysCriterion
75
+
76
+ export type AudienceCriterionType = AudienceCriterion['type']
77
+
78
+ export interface AudienceI {
79
+ id: number
80
+ name: string
81
+ description: string | null
82
+ combinator: AudienceCombinator
83
+ criteria: AudienceCriterion[]
84
+ createdAt: Date
85
+ updatedAt: Date
86
+ }
package/src/home.api.d.ts CHANGED
@@ -1,276 +1,262 @@
1
- import type { PlantI } from './plant'
2
- import type { SelectionI } from './selection'
3
- import type { TaskI } from './task'
4
-
5
- export namespace HOME {
6
- export type HeroCardMode = 'featuredSelection' | 'featuredPlant'
7
-
8
- export type SectionComponentType =
9
- | 'Header'
10
- | 'HeroCards'
11
- | 'PlantCarousel'
12
- | 'HeroCardsCarousel'
13
- | 'WideCardsCarousel'
14
- | 'Weather'
15
- | 'AdviceCarousel'
16
- | 'OnboardingCard'
17
- | 'UpcomingTasks'
18
-
19
- export type CarouselComponentType = Extract<
20
- SectionComponentType,
21
- 'PlantCarousel' | 'HeroCardsCarousel' | 'WideCardsCarousel'
22
- >
23
-
24
- export interface WeatherDayI {
25
- date: string
26
- minTempC: number
27
- maxTempC: number
28
- iconUrl: string | null
29
- condition?: string | null
30
- }
31
-
32
- export interface WeatherSectionI {
33
- componentType: 'Weather'
34
- title: string
35
- subtitle?: string
36
- locationName: string
37
- timezone: string
38
- days: WeatherDayI[]
39
- lastUpdatedAt: string
40
- dataSource: 'weatherapi'
41
- backgroundColor?: string
42
- }
43
-
44
- export interface HeaderSectionI {
45
- componentType: 'Header'
46
- title: string
47
- subtitle: string
48
- }
49
-
50
- export interface AdviceI {
51
- id?: string | number
52
- title: string
53
- markdownContent: string
54
- titleColor?: string
55
- backgroundColor?: string
56
- }
57
-
58
- export interface AdviceSectionI {
59
- componentType: 'AdviceCarousel'
60
- title?: string
61
- subtitle?: string
62
- advices: AdviceI[]
63
- }
64
-
65
- export interface WideCardAuthorI {
66
- name?: string | null
67
- imageURL?: string | null
68
- }
69
-
70
- export interface WideCardItemI {
71
- id?: string | number
72
- title?: string | null
73
- text: string
74
- badgeText?: string | null
75
- thumbnailImageURL: string
76
- author?: WideCardAuthorI | null
77
- link?: string | null
78
- metadata?: Record<string, unknown>
79
- }
80
-
81
- export interface WideCardsCarouselSectionI {
82
- componentType: 'WideCardsCarousel'
83
- title: string
84
- subtitle?: string
85
- cards: WideCardItemI[]
86
- }
87
-
88
- export interface HeroCardBlueprintI {
89
- mode: HeroCardMode
90
- title?: string | null
91
- description?: string | null
92
- backgroundColor?: string | null
93
- backgroundImageUrl?: string | null
94
- ctaLabel?: string | null
95
- ctaRoute?: string | null
96
- previewPlants?: PlantI[]
97
- selection?: SelectionI | null
98
- plant?: PlantI | null
99
- }
100
-
101
- export interface HeroCardsSectionI {
102
- componentType: 'HeroCards'
103
- title?: string | null
104
- subtitle?: string | null
105
- cards: HeroCardBlueprintI[]
106
- }
107
-
108
- export interface PlantCarouselSectionI {
109
- componentType: 'PlantCarousel'
110
- title: string
111
- subtitle?: string
112
- selection: SelectionI
113
- displayRanking?: boolean
114
- }
115
-
116
- export interface HeroCardsCarouselSectionI {
117
- componentType: 'HeroCardsCarousel'
118
- selections: SelectionI[]
119
- }
120
-
121
- export interface FamilyRecommendationI {
122
- family: string
123
- headline: string
124
- plants: PlantI[]
125
- }
126
-
127
- export interface FamilyExplorerSectionI {
128
- componentType: 'FamilyExplorer'
129
- title: string
130
- subtitle?: string
131
- families: FamilyRecommendationI[]
132
- }
133
-
134
- export interface OnboardingStepI {
135
- id: string
136
- title: string
137
- description: string
138
- isCompleted: boolean
139
- ctaLink: string
140
- }
141
-
142
- export interface OnboardingCardSectionI {
143
- componentType: 'OnboardingCard'
144
- title: string
145
- subtitle: string
146
- steps: OnboardingStepI[]
147
- completedStepsCount: number
148
- totalStepsCount: number
149
- }
150
-
151
- /** Minimal plant info for preview display (e.g. parent plant in grouped tasks) */
152
- export interface TaskPlantPreviewI {
153
- name: string
154
- slug: string
155
- imageURL?: string | null
156
- }
157
-
158
- /** Plant info within a family group — includes the task ID specific to this plant */
159
- export interface TaskFamilyPlantPreviewI extends TaskPlantPreviewI {
160
- taskId: number
161
- }
162
-
163
- /** Family group info when multiple plants share the same task */
164
- export interface TaskFamilyGroupI {
165
- familyName: string
166
- plantsCount: number
167
- plants: TaskFamilyPlantPreviewI[]
168
- }
169
-
170
- export interface UpcomingTaskI {
171
- task: TaskI
172
- /** PlantSelection id used by task list views to group tasks per planted item. */
173
- plantSelectionId?: number
174
- plantName: string
175
- plantSlug: string
176
- plantImageURL?: string | null
177
- gardenName: string
178
- gardenCategory: string
179
- selectionSlug: string
180
- /** If this task is grouped by family */
181
- familyGroup?: TaskFamilyGroupI
182
- }
183
-
184
- export type TaskListView = 'current' | 'completed' | 'earlier'
185
-
186
- export interface TaskListTabI {
187
- key: TaskListView
188
- label: string
189
- }
190
-
191
- export interface TaskListResponseI {
192
- view: TaskListView
193
- title: string
194
- subtitle: string
195
- emptyListMessage: string
196
- tabs: TaskListTabI[]
197
- tasks: UpcomingTaskI[]
198
- }
199
-
200
- export interface UpcomingTaskAdviceI {
201
- /** @deprecated Since 1.9.10, this field is always `''`. */
202
- content: string
203
- taskName: string
204
- plantName: string
205
- plantSlug: string
206
- plantImageURL?: string | null
207
- gardenName: string
208
- gardenCategory: string
209
- selectionSlug: string
210
- task: TaskI
211
- /** If this task is grouped by family */
212
- familyGroup?: TaskFamilyGroupI
213
- }
214
-
215
- export interface UpcomingTasksSectionI {
216
- componentType: 'UpcomingTasks'
217
- title: string
218
- subtitle?: string
219
- advices: UpcomingTaskAdviceI[]
220
- }
221
-
222
- /** Preview plant for WhenToSeedWhat section */
223
- export interface WhenToSeedWhatPlantPreviewI {
224
- id: number
225
- name: string
226
- family?: string | null
227
- slug: string
228
- imageURL?: string | null
229
- }
230
-
231
- /** A generic link card section with title, subtitle, URL, and optional preview images */
232
- export interface LinkCardSectionI {
233
- componentType: 'LinkCard'
234
- title: string
235
- subtitle?: string
236
- url: string
237
- previewPlants?: WhenToSeedWhatPlantPreviewI[]
238
- backgroundColor?: string
239
- /** Show a red badge indicator (replaces the plant preview stack when true) */
240
- showBadge?: boolean
241
- /** Optional badge count to display */
242
- badgeCount?: number
243
- }
244
-
245
- export type SectionI =
246
- | HeaderSectionI
247
- | HeroCardsSectionI
248
- | PlantCarouselSectionI
249
- | HeroCardsCarouselSectionI
250
- | WideCardsCarouselSectionI
251
- | WeatherSectionI
252
- | AdviceSectionI
253
- | OnboardingCardSectionI
254
- | UpcomingTasksSectionI
255
- | LinkCardSectionI
256
-
257
- export interface Response {
258
- sections: SectionI[]
259
- weather?: WeatherSectionI
260
- advice?: AdviceSectionI
261
- }
262
-
263
- export interface HeroCardItemI {
264
- id?: string | null
265
- mode: HeroCardMode
266
- title?: string | null
267
- description?: string | null
268
- backgroundColor?: string | null
269
- backgroundImageUrl?: string | null
270
- previewPlants?: PlantI[]
271
- ctaLabel?: string | null
272
- selection?: SelectionI | null
273
- plant?: PlantI | null
274
- ctaRoute?: string | null
275
- }
276
- }
1
+ import type { PlantI } from './plant'
2
+ import type { SelectionI } from './selection'
3
+ import type { TaskI } from './task'
4
+
5
+ export namespace HOME {
6
+ export type HeroCardMode = 'featuredSelection' | 'featuredPlant'
7
+
8
+ export type SectionComponentType =
9
+ | 'Header'
10
+ | 'HeroCards'
11
+ | 'PlantCarousel'
12
+ | 'HeroCardsCarousel'
13
+ | 'WideCardsCarousel'
14
+ | 'Weather'
15
+ | 'AdviceCarousel'
16
+ | 'OnboardingCard'
17
+ | 'UpcomingTasks'
18
+
19
+ export type CarouselComponentType = Extract<
20
+ SectionComponentType,
21
+ 'PlantCarousel' | 'HeroCardsCarousel' | 'WideCardsCarousel'
22
+ >
23
+
24
+ export interface WeatherDayI {
25
+ date: string
26
+ minTempC: number
27
+ maxTempC: number
28
+ iconUrl: string | null
29
+ condition?: string | null
30
+ }
31
+
32
+ export interface WeatherSectionI {
33
+ componentType: 'Weather'
34
+ title: string
35
+ subtitle?: string
36
+ locationName: string
37
+ timezone: string
38
+ days: WeatherDayI[]
39
+ lastUpdatedAt: string
40
+ dataSource: 'weatherapi'
41
+ backgroundColor?: string
42
+ }
43
+
44
+ export interface HeaderSectionI {
45
+ componentType: 'Header'
46
+ title: string
47
+ subtitle: string
48
+ }
49
+
50
+ export interface AdviceI {
51
+ id?: string | number
52
+ title: string
53
+ markdownContent: string
54
+ titleColor?: string
55
+ backgroundColor?: string
56
+ }
57
+
58
+ export interface AdviceSectionI {
59
+ componentType: 'AdviceCarousel'
60
+ title?: string
61
+ subtitle?: string
62
+ advices: AdviceI[]
63
+ }
64
+
65
+ export interface WideCardAuthorI {
66
+ name?: string | null
67
+ imageURL?: string | null
68
+ }
69
+
70
+ export interface WideCardItemI {
71
+ id?: string | number
72
+ title?: string | null
73
+ text: string
74
+ badgeText?: string | null
75
+ thumbnailImageURL: string
76
+ author?: WideCardAuthorI | null
77
+ link?: string | null
78
+ metadata?: Record<string, unknown>
79
+ }
80
+
81
+ export interface WideCardsCarouselSectionI {
82
+ componentType: 'WideCardsCarousel'
83
+ title: string
84
+ subtitle?: string
85
+ cards: WideCardItemI[]
86
+ }
87
+
88
+ export interface HeroCardBlueprintI {
89
+ mode: HeroCardMode
90
+ title?: string | null
91
+ description?: string | null
92
+ backgroundColor?: string | null
93
+ backgroundImageUrl?: string | null
94
+ ctaLabel?: string | null
95
+ ctaRoute?: string | null
96
+ previewPlants?: PlantI[]
97
+ selection?: SelectionI | null
98
+ plant?: PlantI | null
99
+ }
100
+
101
+ export interface HeroCardsSectionI {
102
+ componentType: 'HeroCards'
103
+ title?: string | null
104
+ subtitle?: string | null
105
+ cards: HeroCardBlueprintI[]
106
+ }
107
+
108
+ export interface PlantCarouselSectionI {
109
+ componentType: 'PlantCarousel'
110
+ title: string
111
+ subtitle?: string
112
+ selection: SelectionI
113
+ displayRanking?: boolean
114
+ }
115
+
116
+ export interface HeroCardsCarouselSectionI {
117
+ componentType: 'HeroCardsCarousel'
118
+ selections: SelectionI[]
119
+ }
120
+
121
+ export interface FamilyRecommendationI {
122
+ family: string
123
+ headline: string
124
+ plants: PlantI[]
125
+ }
126
+
127
+ export interface FamilyExplorerSectionI {
128
+ componentType: 'FamilyExplorer'
129
+ title: string
130
+ subtitle?: string
131
+ families: FamilyRecommendationI[]
132
+ }
133
+
134
+ export interface OnboardingStepI {
135
+ id: string
136
+ title: string
137
+ description: string
138
+ isCompleted: boolean
139
+ ctaLink: string
140
+ }
141
+
142
+ export interface OnboardingCardSectionI {
143
+ componentType: 'OnboardingCard'
144
+ title: string
145
+ subtitle: string
146
+ steps: OnboardingStepI[]
147
+ completedStepsCount: number
148
+ totalStepsCount: number
149
+ }
150
+
151
+ /** Minimal plant info for preview display (e.g. parent plant in grouped tasks) */
152
+ export interface TaskPlantPreviewI {
153
+ name: string
154
+ slug: string
155
+ imageURL?: string | null
156
+ }
157
+
158
+ /** Plant info within a family group — includes the task ID specific to this plant */
159
+ export interface TaskFamilyPlantPreviewI extends TaskPlantPreviewI {
160
+ taskId: number
161
+ }
162
+
163
+ /** Family group info when multiple plants share the same task */
164
+ export interface TaskFamilyGroupI {
165
+ familyName: string
166
+ plantsCount: number
167
+ plants: TaskFamilyPlantPreviewI[]
168
+ }
169
+
170
+ export interface UpcomingTaskI {
171
+ task: TaskI
172
+ /** PlantSelection id used by task list views to group tasks per planted item. */
173
+ plantSelectionId?: number
174
+ plantName: string
175
+ plantSlug: string
176
+ plantImageURL?: string | null
177
+ gardenName: string
178
+ gardenCategory: string
179
+ selectionSlug: string
180
+ /** If this task is grouped by family */
181
+ familyGroup?: TaskFamilyGroupI
182
+ }
183
+
184
+ export type TaskListView = 'current' | 'completed' | 'earlier'
185
+
186
+ export interface UpcomingTaskAdviceI {
187
+ /** @deprecated Since 1.9.10, this field is always `''`. */
188
+ content: string
189
+ taskName: string
190
+ plantName: string
191
+ plantSlug: string
192
+ plantImageURL?: string | null
193
+ gardenName: string
194
+ gardenCategory: string
195
+ selectionSlug: string
196
+ task: TaskI
197
+ /** If this task is grouped by family */
198
+ familyGroup?: TaskFamilyGroupI
199
+ }
200
+
201
+ export interface UpcomingTasksSectionI {
202
+ componentType: 'UpcomingTasks'
203
+ title: string
204
+ subtitle?: string
205
+ advices: UpcomingTaskAdviceI[]
206
+ }
207
+
208
+ /** Preview plant for WhenToSeedWhat section */
209
+ export interface WhenToSeedWhatPlantPreviewI {
210
+ id: number
211
+ name: string
212
+ family?: string | null
213
+ slug: string
214
+ imageURL?: string | null
215
+ }
216
+
217
+ /** A generic link card section with title, subtitle, URL, and optional preview images */
218
+ export interface LinkCardSectionI {
219
+ componentType: 'LinkCard'
220
+ title: string
221
+ subtitle?: string
222
+ url: string
223
+ previewPlants?: WhenToSeedWhatPlantPreviewI[]
224
+ backgroundColor?: string
225
+ /** Show a red badge indicator (replaces the plant preview stack when true) */
226
+ showBadge?: boolean
227
+ /** Optional badge count to display */
228
+ badgeCount?: number
229
+ }
230
+
231
+ export type SectionI =
232
+ | HeaderSectionI
233
+ | HeroCardsSectionI
234
+ | PlantCarouselSectionI
235
+ | HeroCardsCarouselSectionI
236
+ | WideCardsCarouselSectionI
237
+ | WeatherSectionI
238
+ | AdviceSectionI
239
+ | OnboardingCardSectionI
240
+ | UpcomingTasksSectionI
241
+ | LinkCardSectionI
242
+
243
+ export interface Response {
244
+ sections: SectionI[]
245
+ weather?: WeatherSectionI
246
+ advice?: AdviceSectionI
247
+ }
248
+
249
+ export interface HeroCardItemI {
250
+ id?: string | null
251
+ mode: HeroCardMode
252
+ title?: string | null
253
+ description?: string | null
254
+ backgroundColor?: string | null
255
+ backgroundImageUrl?: string | null
256
+ previewPlants?: PlantI[]
257
+ ctaLabel?: string | null
258
+ selection?: SelectionI | null
259
+ plant?: PlantI | null
260
+ ctaRoute?: string | null
261
+ }
262
+ }
package/src/index.ts CHANGED
@@ -1,34 +1,35 @@
1
- export * from './member'
2
- export * from './selection'
3
- export * from './plantSelection'
4
- export * from './plant'
5
- export * from './task'
6
- export * from './calendarView'
7
- export * from './apiError'
8
- export * from './gardenMap'
9
- export * from './gardenOverview'
10
- export * from './alert'
11
- export * from './image'
12
- export * from './note'
13
- export * from './taggedItem'
14
- export * from './fertilizer'
15
- export * from './utmParams'
16
- export * from './plantFilters'
17
- export * from './taxonFamily'
18
- export * from './rotationGroup'
19
- import * as PlantsAPI from './plants.api'
20
- import * as UsersAPI from './users.api'
21
- import * as SessionsAPI from './sessions.api'
22
- import * as HomeAPI from './home.api'
23
- import * as PagesAPI from './pages.api'
24
- import * as PaymentsAPI from './payments.api'
25
-
26
- // Allow access to the API namespaces without conflicts
27
- export namespace API {
28
- export import PLANTS = PlantsAPI
29
- export import USERS = UsersAPI
30
- export import SESSIONS = SessionsAPI
31
- export import HOME = HomeAPI
32
- export import PAGES = PagesAPI
33
- export import PAYMENTS = PaymentsAPI
34
- }
1
+ export * from './member'
2
+ export * from './selection'
3
+ export * from './plantSelection'
4
+ export * from './plant'
5
+ export * from './task'
6
+ export * from './calendarView'
7
+ export * from './apiError'
8
+ export * from './gardenMap'
9
+ export * from './gardenOverview'
10
+ export * from './alert'
11
+ export * from './audience'
12
+ export * from './image'
13
+ export * from './note'
14
+ export * from './taggedItem'
15
+ export * from './fertilizer'
16
+ export * from './utmParams'
17
+ export * from './plantFilters'
18
+ export * from './taxonFamily'
19
+ export * from './rotationGroup'
20
+ import * as PlantsAPI from './plants.api'
21
+ import * as UsersAPI from './users.api'
22
+ import * as SessionsAPI from './sessions.api'
23
+ import * as HomeAPI from './home.api'
24
+ import * as PagesAPI from './pages.api'
25
+ import * as PaymentsAPI from './payments.api'
26
+
27
+ // Allow access to the API namespaces without conflicts
28
+ export namespace API {
29
+ export import PLANTS = PlantsAPI
30
+ export import USERS = UsersAPI
31
+ export import SESSIONS = SessionsAPI
32
+ export import HOME = HomeAPI
33
+ export import PAGES = PagesAPI
34
+ export import PAYMENTS = PaymentsAPI
35
+ }
package/src/member.d.ts CHANGED
@@ -1,72 +1,126 @@
1
- import type { NoteI } from './note'
2
- import type { SelectionI } from './selection'
3
-
4
- export interface MemberI {
5
- id: number
6
- email: string
7
- firstName: string
8
- lastName: string
9
- /** Virtual field to get the user's full name */
10
- fullName?: string | null
11
- city?: string
12
- hardinessZoneName?: string
13
- createdAt: Date
14
- updatedAt: Date
15
- lastFreezingDate?: Date
16
- firstFreezingDate?: Date
17
- /**
18
- * Number of unread Crisp messages
19
- */
20
- unreadMessagesCount?: number
21
- notes?: NoteI[]
22
- selections?: SelectionI[]
23
- userActivities?: UserActivityI[]
24
- /**
25
- * hasAnActiveSubscription is a VIRTUAL FIELD used to check if the user has an active subscription
26
- */
27
- hasAnActiveSubscription?: boolean
28
- /**
29
- * Acts as a one-way not identifiable token.
30
- * Crisp uses this token to identify the user conversation
31
- */
32
- userToken: string
33
- /**
34
- * HMAC signature used by Crisp identity verification.
35
- * Signed on the backend from the user email.
36
- */
37
- crispUserEmailSignature?: string
38
- /**
39
- * neverAddedATask <=> Has the use ever open the task form?
40
- * Yes it is misleading, ticket is open to change it.
41
- * It was too intense to force the user to add a task.
42
- */
43
- neverAddedATask?: boolean
44
- neverAddedAPlant?: boolean
45
- neverAddedANote?: boolean
46
- neverTriedSpacingSection?: boolean
47
- neverTriedAiAgent?: boolean
48
- neverDeletedAPlant?: boolean
49
- neverDeletedATask?: boolean
50
- neverEditedATask?: boolean
51
- finishedOnboarding: boolean
52
- hasDownloadedNativeApp?: boolean
53
- unitSystem: 'imperial' | 'metric'
54
- gardenZoneTimelessPreference?: boolean
55
- subscriptionEndDate?: Date
56
- /**
57
- * Last fertilizer ID chosen by the user.
58
- * Used as the default fertilizer for new garden zones.
59
- */
60
- lastFertilizerIdChosen?: number
61
- /**
62
- * Virtual field to get the app badge count
63
- * ----------------------------------------
64
- * For now, it's just the number of unread messages.
65
- */
66
- badgeCount?: number
67
- /**
68
- * Number of unread alerts, cached version of unread alerts (the table) count
69
- */
70
- unreadAlertsCount?: number
71
- isAdmin: boolean
72
- }
1
+ import type { NoteI } from './note'
2
+ import type { SelectionI } from './selection'
3
+
4
+ export interface MemberI {
5
+ id: number
6
+ email: string
7
+ firstName: string
8
+ lastName: string
9
+ /** Virtual field to get the user's full name */
10
+ fullName?: string | null
11
+ city?: string
12
+ hardinessZoneName?: string
13
+ createdAt: Date
14
+ updatedAt: Date
15
+ lastFreezingDate?: Date
16
+ firstFreezingDate?: Date
17
+ /**
18
+ * Number of unread Crisp messages
19
+ */
20
+ unreadMessagesCount?: number
21
+ notes?: NoteI[]
22
+ selections?: SelectionI[]
23
+ userActivities?: UserActivityI[]
24
+ /**
25
+ * hasAnActiveSubscription is a VIRTUAL FIELD used to check if the user has an active subscription
26
+ */
27
+ hasAnActiveSubscription?: boolean
28
+ /**
29
+ * Acts as a one-way not identifiable token.
30
+ * Crisp uses this token to identify the user conversation
31
+ */
32
+ userToken: string
33
+ /**
34
+ * HMAC signature used by Crisp identity verification.
35
+ * Signed on the backend from the user email.
36
+ */
37
+ crispUserEmailSignature?: string
38
+ /**
39
+ * neverAddedATask <=> Has the use ever open the task form?
40
+ * Yes it is misleading, ticket is open to change it.
41
+ * It was too intense to force the user to add a task.
42
+ */
43
+ neverAddedATask?: boolean
44
+ neverAddedAPlant?: boolean
45
+ neverAddedANote?: boolean
46
+ neverTriedSpacingSection?: boolean
47
+ neverTriedAiAgent?: boolean
48
+ neverDeletedAPlant?: boolean
49
+ neverDeletedATask?: boolean
50
+ neverEditedATask?: boolean
51
+ finishedOnboarding: boolean
52
+ hasDownloadedNativeApp?: boolean
53
+ unitSystem: 'imperial' | 'metric'
54
+ gardenZoneTimelessPreference?: boolean
55
+ subscriptionEndDate?: Date
56
+ /**
57
+ * Last fertilizer ID chosen by the user.
58
+ * Used as the default fertilizer for new garden zones.
59
+ */
60
+ lastFertilizerIdChosen?: number
61
+ /**
62
+ * Virtual field to get the app badge count
63
+ * ----------------------------------------
64
+ * For now, it's just the number of unread messages.
65
+ */
66
+ badgeCount?: number
67
+ /**
68
+ * Number of unread alerts, cached version of unread alerts (the table) count
69
+ */
70
+ unreadAlertsCount?: number
71
+ isAdmin: boolean
72
+ }
73
+
74
+ /**
75
+ * PrivateMemberI
76
+ * --------------
77
+ * The data a member is allowed to see about *themselves* — the shape
78
+ * returned to the front-end by GET/POST /users. (A future `PublicMemberI`
79
+ * will model the much smaller set of fields shown to other members.)
80
+ *
81
+ * Unlike `MemberI` (the full domain model), this is a deliberate
82
+ * allow-list of only the fields the client actually consumes. It exists
83
+ * to prevent leaking sensitive or internal Member columns: anything not
84
+ * listed here is never serialized to the client. Keep this in sync with
85
+ * `privateMember.presenter.ts` on the backend.
86
+ */
87
+ export interface PrivateMemberI {
88
+ id: number
89
+ email: string
90
+ city?: string
91
+ unitSystem: 'imperial' | 'metric'
92
+ gardenZoneTimelessPreference?: boolean
93
+ firstFreezingDate?: Date
94
+ lastFreezingDate?: Date
95
+ subscriptionEndDate?: Date
96
+ hasAnActiveSubscription?: boolean
97
+ hasDownloadedNativeApp?: boolean
98
+ isAdmin: boolean
99
+ /**
100
+ * App badge count (currently unread messages + unread alerts).
101
+ */
102
+ badgeCount?: number
103
+ /**
104
+ * One-way Crisp identification token. Not personally identifiable.
105
+ */
106
+ userToken: string
107
+ /**
108
+ * HMAC signature used by Crisp identity verification.
109
+ */
110
+ crispUserEmailSignature?: string
111
+ unreadMessagesCount?: number
112
+ unreadAlertsCount?: number
113
+ neverAddedANote?: boolean
114
+ neverAddedAPlant?: boolean
115
+ neverDeletedAPlant?: boolean
116
+ neverEditedATask?: boolean
117
+ neverTriedAiAgent?: boolean
118
+ neverTriedSpacingSection?: boolean
119
+ /**
120
+ * True when the member belongs to the "very active" audience (~14 days of
121
+ * distinct activity). Only present when GET /users is called with the
122
+ * `requestAppStoreReview` scope; the app uses it to trigger a one-time
123
+ * in-app store-review prompt.
124
+ */
125
+ requestAppStoreReview?: boolean
126
+ }
package/src/plant.d.ts CHANGED
@@ -21,7 +21,18 @@ export type PlantFilterValueI = string | string[] | number | boolean | RangeFilt
21
21
  * Used in JSONB field and as query parameters
22
22
  * Supports multiple value types for different filter types
23
23
  */
24
- export type PlantAttributesI = Record<string, PlantFilterValueI>
24
+ /**
25
+ * A group of related attribute values, used for nested attribute objects.
26
+ * Ex: { growth: { type: 'déterminée' }}
27
+ */
28
+ export type PlantAttributeGroupI = Record<string, PlantFilterValueI>
29
+
30
+ /**
31
+ * Plant attributes for filtering
32
+ * Used in JSONB field and as query parameters
33
+ * Supports flat values and one level of nested groups.
34
+ */
35
+ export type PlantAttributesI = Record<string, PlantFilterValueI | PlantAttributeGroupI>
25
36
 
26
37
  export interface PlantI {
27
38
  id: number
@@ -1,136 +1,134 @@
1
- import type { DeviceDataI } from './device.d'
2
- import type { MemberI as MemberDataI } from './member.d'
3
- import type { SelectionI } from './selection.d'
4
- import type { PlantI } from './plant.d'
5
-
6
- interface RequestHeaders {
7
- 'x-session-token': string
8
- 'x-native-app-version'?: string
9
- 'x-application-type'?: 'react-native' | 'web'
10
- }
11
-
12
- export namespace USERS {
13
- export namespace GET {
14
- export interface Request {
15
- headers: RequestHeaders
16
- params: {
17
- scopes: ('unreadNotificationsCount' | 'coordinates')[]
18
- }
19
- }
20
- export type Response = MemberDataI & {
21
- unreadAlertsCount?: number
22
- }
23
- }
24
- export namespace DEVICES {
25
- export interface Request {
26
- headers: RequestHeaders
27
- body: {
28
- /** @see DeviceDataI */
29
- userAgent?: DeviceDataI['userAgent']
30
- platform?: DeviceDataI['platform']
31
- version?: DeviceDataI['version']
32
- model?: DeviceDataI['model']
33
- name?: DeviceDataI['name']
34
- appVersion?: DeviceDataI['appVersion']
35
- pushKey?: DeviceDataI['pushKey']
36
- apnsToken?: DeviceDataI['apnsToken']
37
- fcmToken?: DeviceDataI['fcmToken']
38
- webPushEndpoint?: DeviceDataI['webPushEndpoint']
39
- webPushP256dh?: DeviceDataI['webPushP256dh']
40
- webPushAuth?: DeviceDataI['webPushAuth']
41
- webPushSubscription?: DeviceDataI['webPushSubscription']
42
- }
43
- }
44
- export type Response = DeviceDataI
45
- }
46
- export namespace RECOMMENDATIONS {
47
- export type ComponentType = 'PlantCarousel' | 'SelectionCarousel' | 'HeroCardsCarousel' | 'YourSelection'
48
-
49
- interface BaseRecommendation {
50
- id: string
51
- componentType: ComponentType
52
- title?: string
53
- subtitle?: string
54
- cardsVisible?: number
55
- }
56
-
57
- export interface PlantCarouselRecommendation extends BaseRecommendation {
58
- componentType: 'PlantCarousel'
59
- selection: SelectionI & { Plants?: PlantI[] }
60
- displayRanking?: boolean
61
- }
62
-
63
- export interface SelectionCarouselRecommendation extends BaseRecommendation {
64
- componentType: 'SelectionCarousel'
65
- selections: SelectionI[]
66
- displayRanking?: boolean
67
- }
68
-
69
- export interface HeroCardsCarouselRecommendation extends BaseRecommendation {
70
- componentType: 'HeroCardsCarousel'
71
- selections: SelectionI[]
72
- }
73
-
74
- export interface YourSelectionRecommendation extends BaseRecommendation {
75
- componentType: 'YourSelection'
76
- }
77
-
78
- export interface ThemedRecommendationI {
79
- id: string
80
- label: string
81
- icon: string
82
- title?: string
83
- subtitle?: string
84
- selections: SelectionI[]
85
- cardsVisible?: number
86
- displayRanking?: boolean
87
- }
88
-
89
- export type RecommendationI =
90
- | PlantCarouselRecommendation
91
- | SelectionCarouselRecommendation
92
- | HeroCardsCarouselRecommendation
93
- | YourSelectionRecommendation
94
-
95
- export interface Request {
96
- headers: RequestHeaders
97
- }
98
-
99
- export interface Response {
100
- recommendations: RecommendationI[]
101
- themedRecommendations: ThemedRecommendationI[]
102
- }
103
- }
104
- export namespace UPDATE {
105
- export interface Request {
106
- headers: RequestHeaders
107
- body: {
108
- city?: string
109
- lastFreezingDate?: string | Date
110
- firstFreezingDate?: string | Date
111
- moveDatesRequested?: boolean
112
- lastFertilizerIdChosen?: number
113
- lastOrganicMatterPercentage?: number
114
- unitSystem?: 'imperial' | 'metric'
115
- gardenZoneTimelessPreference?: boolean
116
- }
117
- }
118
- export type Response = MemberDataI
119
- }
120
- export namespace GIFT_CARDS {
121
- export namespace REDEEM {
122
- export interface Request {
123
- headers: RequestHeaders
124
- body: {
125
- code: string
126
- }
127
- }
128
- export interface Response {
129
- success: boolean
130
- message?: string
131
- error?: string
132
- error_message?: string
133
- }
134
- }
135
- }
136
- }
1
+ import type { DeviceDataI } from './device.d'
2
+ import type { PrivateMemberI } from './member.d'
3
+ import type { SelectionI } from './selection.d'
4
+ import type { PlantI } from './plant.d'
5
+
6
+ interface RequestHeaders {
7
+ 'x-session-token': string
8
+ 'x-native-app-version'?: string
9
+ 'x-application-type'?: 'react-native' | 'web'
10
+ }
11
+
12
+ export namespace USERS {
13
+ export namespace GET {
14
+ export interface Request {
15
+ headers: RequestHeaders
16
+ params: {
17
+ scopes: ('unreadNotificationsCount' | 'requestAppStoreReview')[]
18
+ }
19
+ }
20
+ export type Response = PrivateMemberI
21
+ }
22
+ export namespace DEVICES {
23
+ export interface Request {
24
+ headers: RequestHeaders
25
+ body: {
26
+ /** @see DeviceDataI */
27
+ userAgent?: DeviceDataI['userAgent']
28
+ platform?: DeviceDataI['platform']
29
+ version?: DeviceDataI['version']
30
+ model?: DeviceDataI['model']
31
+ name?: DeviceDataI['name']
32
+ appVersion?: DeviceDataI['appVersion']
33
+ pushKey?: DeviceDataI['pushKey']
34
+ apnsToken?: DeviceDataI['apnsToken']
35
+ fcmToken?: DeviceDataI['fcmToken']
36
+ webPushEndpoint?: DeviceDataI['webPushEndpoint']
37
+ webPushP256dh?: DeviceDataI['webPushP256dh']
38
+ webPushAuth?: DeviceDataI['webPushAuth']
39
+ webPushSubscription?: DeviceDataI['webPushSubscription']
40
+ }
41
+ }
42
+ export type Response = DeviceDataI
43
+ }
44
+ export namespace RECOMMENDATIONS {
45
+ export type ComponentType = 'PlantCarousel' | 'SelectionCarousel' | 'HeroCardsCarousel' | 'YourSelection'
46
+
47
+ interface BaseRecommendation {
48
+ id: string
49
+ componentType: ComponentType
50
+ title?: string
51
+ subtitle?: string
52
+ cardsVisible?: number
53
+ }
54
+
55
+ export interface PlantCarouselRecommendation extends BaseRecommendation {
56
+ componentType: 'PlantCarousel'
57
+ selection: SelectionI & { Plants?: PlantI[] }
58
+ displayRanking?: boolean
59
+ }
60
+
61
+ export interface SelectionCarouselRecommendation extends BaseRecommendation {
62
+ componentType: 'SelectionCarousel'
63
+ selections: SelectionI[]
64
+ displayRanking?: boolean
65
+ }
66
+
67
+ export interface HeroCardsCarouselRecommendation extends BaseRecommendation {
68
+ componentType: 'HeroCardsCarousel'
69
+ selections: SelectionI[]
70
+ }
71
+
72
+ export interface YourSelectionRecommendation extends BaseRecommendation {
73
+ componentType: 'YourSelection'
74
+ }
75
+
76
+ export interface ThemedRecommendationI {
77
+ id: string
78
+ label: string
79
+ icon: string
80
+ title?: string
81
+ subtitle?: string
82
+ selections: SelectionI[]
83
+ cardsVisible?: number
84
+ displayRanking?: boolean
85
+ }
86
+
87
+ export type RecommendationI =
88
+ | PlantCarouselRecommendation
89
+ | SelectionCarouselRecommendation
90
+ | HeroCardsCarouselRecommendation
91
+ | YourSelectionRecommendation
92
+
93
+ export interface Request {
94
+ headers: RequestHeaders
95
+ }
96
+
97
+ export interface Response {
98
+ recommendations: RecommendationI[]
99
+ themedRecommendations: ThemedRecommendationI[]
100
+ }
101
+ }
102
+ export namespace UPDATE {
103
+ export interface Request {
104
+ headers: RequestHeaders
105
+ body: {
106
+ city?: string
107
+ lastFreezingDate?: string | Date
108
+ firstFreezingDate?: string | Date
109
+ moveDatesRequested?: boolean
110
+ lastFertilizerIdChosen?: number
111
+ lastOrganicMatterPercentage?: number
112
+ unitSystem?: 'imperial' | 'metric'
113
+ gardenZoneTimelessPreference?: boolean
114
+ }
115
+ }
116
+ export type Response = PrivateMemberI
117
+ }
118
+ export namespace GIFT_CARDS {
119
+ export namespace REDEEM {
120
+ export interface Request {
121
+ headers: RequestHeaders
122
+ body: {
123
+ code: string
124
+ }
125
+ }
126
+ export interface Response {
127
+ success: boolean
128
+ message?: string
129
+ error?: string
130
+ error_message?: string
131
+ }
132
+ }
133
+ }
134
+ }