@la-main-verte/shared-types 1.0.82 → 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 +1 -1
- package/src/audience.d.ts +86 -0
- package/src/home.api.d.ts +4 -0
- package/src/index.ts +1 -0
- package/src/member.d.ts +126 -72
- package/src/plant.d.ts +200 -189
- package/src/plantSelection.d.ts +0 -1
- package/src/taxonFamily.d.ts +15 -15
- package/src/users.api.d.ts +134 -136
package/package.json
CHANGED
|
@@ -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
|
@@ -169,6 +169,8 @@ export namespace HOME {
|
|
|
169
169
|
|
|
170
170
|
export interface UpcomingTaskI {
|
|
171
171
|
task: TaskI
|
|
172
|
+
/** PlantSelection id used by task list views to group tasks per planted item. */
|
|
173
|
+
plantSelectionId?: number
|
|
172
174
|
plantName: string
|
|
173
175
|
plantSlug: string
|
|
174
176
|
plantImageURL?: string | null
|
|
@@ -179,6 +181,8 @@ export namespace HOME {
|
|
|
179
181
|
familyGroup?: TaskFamilyGroupI
|
|
180
182
|
}
|
|
181
183
|
|
|
184
|
+
export type TaskListView = 'current' | 'completed' | 'earlier'
|
|
185
|
+
|
|
182
186
|
export interface UpcomingTaskAdviceI {
|
|
183
187
|
/** @deprecated Since 1.9.10, this field is always `''`. */
|
|
184
188
|
content: string
|
package/src/index.ts
CHANGED
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
|
@@ -1,189 +1,200 @@
|
|
|
1
|
-
import type { TaxonFamilyI } from './taxonFamily'
|
|
2
|
-
import type { SeedingInfoI } from './pages.api'
|
|
3
|
-
import type { PlantFilterI } from './plantFilters'
|
|
4
|
-
import type { RotationGroup } from './rotationGroup'
|
|
5
|
-
/**
|
|
6
|
-
* Range filter value for min/max filters
|
|
7
|
-
*/
|
|
8
|
-
export interface RangeFilterValueI {
|
|
9
|
-
min: number
|
|
10
|
-
max: number
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Plant filter value types
|
|
15
|
-
* Represents the possible values for a single filter
|
|
16
|
-
*/
|
|
17
|
-
export type PlantFilterValueI = string | string[] | number | boolean | RangeFilterValueI
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Plant attributes for filtering
|
|
21
|
-
* Used in JSONB field and as query parameters
|
|
22
|
-
* Supports multiple value types for different filter types
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
*
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
*
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1
|
+
import type { TaxonFamilyI } from './taxonFamily'
|
|
2
|
+
import type { SeedingInfoI } from './pages.api'
|
|
3
|
+
import type { PlantFilterI } from './plantFilters'
|
|
4
|
+
import type { RotationGroup } from './rotationGroup'
|
|
5
|
+
/**
|
|
6
|
+
* Range filter value for min/max filters
|
|
7
|
+
*/
|
|
8
|
+
export interface RangeFilterValueI {
|
|
9
|
+
min: number
|
|
10
|
+
max: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plant filter value types
|
|
15
|
+
* Represents the possible values for a single filter
|
|
16
|
+
*/
|
|
17
|
+
export type PlantFilterValueI = string | string[] | number | boolean | RangeFilterValueI
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Plant attributes for filtering
|
|
21
|
+
* Used in JSONB field and as query parameters
|
|
22
|
+
* Supports multiple value types for different filter types
|
|
23
|
+
*/
|
|
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>
|
|
36
|
+
|
|
37
|
+
export interface PlantI {
|
|
38
|
+
id: number
|
|
39
|
+
name: string
|
|
40
|
+
slug: string
|
|
41
|
+
description: string
|
|
42
|
+
descriptionSource?: string
|
|
43
|
+
female: boolean
|
|
44
|
+
family?: string | null
|
|
45
|
+
taxonFamilyId?: number | null
|
|
46
|
+
spaceBetweenSeedMin: number
|
|
47
|
+
spaceBetweenSeedMax: number
|
|
48
|
+
spaceBetweenAlleyMin: number
|
|
49
|
+
spaceBetweenAlleyMax: number
|
|
50
|
+
weeksInTransplant: number
|
|
51
|
+
weeksDuringWhichYouCanSeedOutdoor: number
|
|
52
|
+
weeksToMaturity: number
|
|
53
|
+
daysToMaturity: number | null
|
|
54
|
+
weeksToWaitAfterFreezingDate: number
|
|
55
|
+
weeksToHarvest: number
|
|
56
|
+
indoorSeeding: boolean
|
|
57
|
+
outdoorSeeding: boolean
|
|
58
|
+
azoteNeedsKgPerHa: number
|
|
59
|
+
hibernate: boolean
|
|
60
|
+
germinationTemperature: number | null
|
|
61
|
+
minGerminationTemperature: number | null
|
|
62
|
+
germinationNumberOfDays: string | null
|
|
63
|
+
germinationSeedDepth: string | null
|
|
64
|
+
cultivationInfo: string | null
|
|
65
|
+
parentId: number | null
|
|
66
|
+
memberId?: number
|
|
67
|
+
memberFirstName?: string | null
|
|
68
|
+
shared: boolean
|
|
69
|
+
hexColor: string | null
|
|
70
|
+
sunRequirements: 'partialShade' | 'fullSun' | 'fullShade' | null
|
|
71
|
+
isHardy: boolean | null
|
|
72
|
+
quantityForFiveInJardinVivrier?: number | null
|
|
73
|
+
imageURL: string
|
|
74
|
+
/**
|
|
75
|
+
* Filename of the plant's 3D mesh asset stored on S3.
|
|
76
|
+
* - Ideally a `.webp` file (e.g. `ail.webp`).
|
|
77
|
+
* - Stored in S3 under the `images/meshes/` folder.
|
|
78
|
+
* - By default named after the `Plant.slug` (e.g. `ail.webp` for the garlic plant).
|
|
79
|
+
* - Only the filename is persisted; the public URL is exposed through the
|
|
80
|
+
* virtual `meshURL` field.
|
|
81
|
+
*/
|
|
82
|
+
meshFilename: string | null
|
|
83
|
+
/**
|
|
84
|
+
* Public ImageKit URL to the plant's 3D mesh asset.
|
|
85
|
+
* Virtual field computed from `meshFilename`. The underlying file is
|
|
86
|
+
* stored on S3 at `images/meshes/{meshFilename}` and rewritten to
|
|
87
|
+
* ImageKit for CDN delivery and transformations.
|
|
88
|
+
*
|
|
89
|
+
* Falls back to `imageURL` when `meshFilename` is not set.
|
|
90
|
+
*
|
|
91
|
+
* Example: `https://ik.imagekit.io/lamainverte/meshes/ail.webp`
|
|
92
|
+
*/
|
|
93
|
+
meshURL: string
|
|
94
|
+
totalWeeksToMaturity: number
|
|
95
|
+
hasNoInformation: boolean
|
|
96
|
+
createdAt: Date
|
|
97
|
+
updatedAt: Date
|
|
98
|
+
translatedSunRequirements: 'Plein soleil' | 'Mi-ombre' | 'Ombre' | null
|
|
99
|
+
parent?: PlantI
|
|
100
|
+
children?: PlantI[]
|
|
101
|
+
PlantInventories?: PlantInventoryI[]
|
|
102
|
+
taggedItems?: TaggedItemI[]
|
|
103
|
+
/**
|
|
104
|
+
* List of selections that the plant is in.
|
|
105
|
+
* --------------------------------------
|
|
106
|
+
* Tends to be used for featured selections
|
|
107
|
+
*/
|
|
108
|
+
Selections?: SelectionI[]
|
|
109
|
+
tasks?: TaskI[]
|
|
110
|
+
Images?: ImageI[]
|
|
111
|
+
Member?: Partial<MemberI>
|
|
112
|
+
notes?: NoteI[]
|
|
113
|
+
/**
|
|
114
|
+
* Nitrogen-derived rotation group enum (virtual field on the Plant model).
|
|
115
|
+
* Source of truth for rotation classification — derived from
|
|
116
|
+
* `azoteNeedsKgPerHa` plus the regenerative override on `taxonFamily`.
|
|
117
|
+
*/
|
|
118
|
+
rotationGroup?: RotationGroup | null
|
|
119
|
+
/**
|
|
120
|
+
* French label of `rotationGroup`, ready for display.
|
|
121
|
+
*/
|
|
122
|
+
translatedRotationGroup?: string | null
|
|
123
|
+
taxonFamily?: TaxonFamilyI
|
|
124
|
+
seedingInfo: SeedingInfoI
|
|
125
|
+
/**
|
|
126
|
+
* Contextual search text for recommended plants
|
|
127
|
+
* Provides additional context when suggesting plants for specific garden zones
|
|
128
|
+
*/
|
|
129
|
+
contextualSearchText?: string
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface PlantInventoryI {
|
|
133
|
+
id: number
|
|
134
|
+
plantId: number
|
|
135
|
+
supplierName: string
|
|
136
|
+
plantName: string
|
|
137
|
+
supplierURL: string
|
|
138
|
+
imageLocation: string
|
|
139
|
+
description: string
|
|
140
|
+
inStock: boolean
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export type AvailableGrowingConditionsI = Record<string, string[]>
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* For each attribute filter key (e.g. "fruit.color"), the list of values that
|
|
147
|
+
* still yield at least one match under the current search + active filters.
|
|
148
|
+
* Consumed by the frontend to mark filter tags as "unavailable".
|
|
149
|
+
*/
|
|
150
|
+
export type AvailableAttributesI = Record<string, string[]>
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Which filter surface the frontend should render for the current search.
|
|
154
|
+
* - `specific`: family-specific filters (e.g. tomate attributes)
|
|
155
|
+
* - `general`: global growing-conditions filters
|
|
156
|
+
*
|
|
157
|
+
* Mutually exclusive by design — the two sets never appear together.
|
|
158
|
+
*/
|
|
159
|
+
export type PlantFiltersModeI = 'specific' | 'general'
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Backend-driven description of the filter UI for a given search response.
|
|
163
|
+
* The frontend is a dumb renderer: it shows the filters listed here with the
|
|
164
|
+
* given mode and marks tags whose value is not in `availability` as unavailable.
|
|
165
|
+
*/
|
|
166
|
+
export interface PlantFiltersUII {
|
|
167
|
+
mode: PlantFiltersModeI
|
|
168
|
+
filters: PlantFilterI[]
|
|
169
|
+
availability: Record<string, string[]>
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface GrowingConditionsConfigI {
|
|
173
|
+
filters: PlantFilterI[]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface GrowingConditionsFiltersI {
|
|
177
|
+
sunRequirements?: 'partialShade' | 'fullShade'
|
|
178
|
+
rotationGroup?: 'regenerative' | 'demanding' | 'moderately_demanding' | 'less_demanding'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface PlantSearchResultI {
|
|
182
|
+
plants: PlantModelI[]
|
|
183
|
+
selections: SelectionModelI[]
|
|
184
|
+
total?: number
|
|
185
|
+
familyName?: string
|
|
186
|
+
filtersUI?: PlantFiltersUII
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Response shape for `GET /selections/:slug/gardenZones/:id/rotation-suggestions`.
|
|
191
|
+
* - `advice`: short user-facing sentence explaining the recommendation
|
|
192
|
+
* - `Plants`: parent plants matching the recommended rotation group, with
|
|
193
|
+
* the avoided taxon families excluded. Empty when the zone has no usable
|
|
194
|
+
* history yet.
|
|
195
|
+
*/
|
|
196
|
+
export interface PlantRotationSuggestionsI {
|
|
197
|
+
targetYear: number
|
|
198
|
+
advice: string
|
|
199
|
+
Plants: PlantI[]
|
|
200
|
+
}
|
package/src/plantSelection.d.ts
CHANGED
package/src/taxonFamily.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { RotationGroup } from './rotationGroup'
|
|
2
|
-
|
|
3
|
-
export interface TaxonFamilyI {
|
|
4
|
-
id: number
|
|
5
|
-
slug: string
|
|
6
|
-
translation_key: string
|
|
7
|
-
name: string
|
|
8
|
-
latin_name: string | null
|
|
9
|
-
kingdom: string
|
|
10
|
-
description: string | null
|
|
11
|
-
rotation_group: RotationGroup | null
|
|
12
|
-
translated_rotation_group: string | null
|
|
13
|
-
createdAt?: Date
|
|
14
|
-
updatedAt?: Date
|
|
15
|
-
}
|
|
1
|
+
import type { RotationGroup } from './rotationGroup'
|
|
2
|
+
|
|
3
|
+
export interface TaxonFamilyI {
|
|
4
|
+
id: number
|
|
5
|
+
slug: string
|
|
6
|
+
translation_key: string
|
|
7
|
+
name: string
|
|
8
|
+
latin_name: string | null
|
|
9
|
+
kingdom: string
|
|
10
|
+
description: string | null
|
|
11
|
+
rotation_group: RotationGroup | null
|
|
12
|
+
translated_rotation_group: string | null
|
|
13
|
+
createdAt?: Date
|
|
14
|
+
updatedAt?: Date
|
|
15
|
+
}
|
package/src/users.api.d.ts
CHANGED
|
@@ -1,136 +1,134 @@
|
|
|
1
|
-
import type { DeviceDataI } from './device.d'
|
|
2
|
-
import type {
|
|
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' | '
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export type Response =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
+
}
|