heartraite 1.0.192 → 1.0.194

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.
@@ -4,6 +4,10 @@ export declare enum CloudFunction {
4
4
  AUTHENTICATE_BANKID = "bankid/authenticate",
5
5
  COLLECT_BANKID = "bankid/collect",
6
6
  SUBMIT_FEEDBACK = "feedback/submit-feedback",
7
+ EXPRESS_CONSULTATION_INTEREST = "consultation/express-interest",
8
+ GET_MY_CONSULTATION_INTEREST = "consultation/my-interest",
9
+ GET_CONSULTATION_INTERESTS = "consultation/admin-interests",
10
+ UPDATE_CONSULTATION_INTEREST = "consultation/admin-update-interest",
7
11
  GET_CA = "ca/get-ca",
8
12
  SUBMIT_ANSWER = "ca/submit-answer",
9
13
  SEND_EVENT_FEEDBACK = "event/send-event-feedback",
@@ -8,6 +8,10 @@ var CloudFunction;
8
8
  CloudFunction["AUTHENTICATE_BANKID"] = "bankid/authenticate";
9
9
  CloudFunction["COLLECT_BANKID"] = "bankid/collect";
10
10
  CloudFunction["SUBMIT_FEEDBACK"] = "feedback/submit-feedback";
11
+ CloudFunction["EXPRESS_CONSULTATION_INTEREST"] = "consultation/express-interest";
12
+ CloudFunction["GET_MY_CONSULTATION_INTEREST"] = "consultation/my-interest";
13
+ CloudFunction["GET_CONSULTATION_INTERESTS"] = "consultation/admin-interests";
14
+ CloudFunction["UPDATE_CONSULTATION_INTEREST"] = "consultation/admin-update-interest";
11
15
  CloudFunction["GET_CA"] = "ca/get-ca";
12
16
  CloudFunction["SUBMIT_ANSWER"] = "ca/submit-answer";
13
17
  CloudFunction["SEND_EVENT_FEEDBACK"] = "event/send-event-feedback";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * State machine for a user's expression of interest in a 1:1
3
+ * consultation with a credentialed professional (psychologist,
4
+ * therapist, etc.). v1 ships the "interest list" MVP where the
5
+ * team manually follows up by email; v2 will add real booking +
6
+ * payment, at which point the later states get exercised.
7
+ *
8
+ * PENDING → user expressed interest, team hasn't reached
9
+ * out yet. Initial state on submit.
10
+ * FOLLOWED_UP → team emailed the user. v1 manual; v2 may be
11
+ * automated after a Stripe charge.
12
+ * BOOKED → consultation session scheduled (v2 only — v1
13
+ * tracks this only via team-internal notes).
14
+ * COMPLETED → session happened.
15
+ * DECLINED → user backed out / team declined the request.
16
+ *
17
+ * v1 only relies on the PENDING → FOLLOWED_UP transition driven
18
+ * by an admin action. Future states are reserved so the type
19
+ * shape doesn't change when the booking flow lands.
20
+ */
21
+ export declare enum ConsultationInterestStatus {
22
+ PENDING = "PENDING",
23
+ FOLLOWED_UP = "FOLLOWED_UP",
24
+ BOOKED = "BOOKED",
25
+ COMPLETED = "COMPLETED",
26
+ DECLINED = "DECLINED"
27
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConsultationInterestStatus = void 0;
4
+ /**
5
+ * State machine for a user's expression of interest in a 1:1
6
+ * consultation with a credentialed professional (psychologist,
7
+ * therapist, etc.). v1 ships the "interest list" MVP where the
8
+ * team manually follows up by email; v2 will add real booking +
9
+ * payment, at which point the later states get exercised.
10
+ *
11
+ * PENDING → user expressed interest, team hasn't reached
12
+ * out yet. Initial state on submit.
13
+ * FOLLOWED_UP → team emailed the user. v1 manual; v2 may be
14
+ * automated after a Stripe charge.
15
+ * BOOKED → consultation session scheduled (v2 only — v1
16
+ * tracks this only via team-internal notes).
17
+ * COMPLETED → session happened.
18
+ * DECLINED → user backed out / team declined the request.
19
+ *
20
+ * v1 only relies on the PENDING → FOLLOWED_UP transition driven
21
+ * by an admin action. Future states are reserved so the type
22
+ * shape doesn't change when the booking flow lands.
23
+ */
24
+ var ConsultationInterestStatus;
25
+ (function (ConsultationInterestStatus) {
26
+ ConsultationInterestStatus["PENDING"] = "PENDING";
27
+ ConsultationInterestStatus["FOLLOWED_UP"] = "FOLLOWED_UP";
28
+ ConsultationInterestStatus["BOOKED"] = "BOOKED";
29
+ ConsultationInterestStatus["COMPLETED"] = "COMPLETED";
30
+ ConsultationInterestStatus["DECLINED"] = "DECLINED";
31
+ })(ConsultationInterestStatus || (exports.ConsultationInterestStatus = ConsultationInterestStatus = {}));
@@ -14,7 +14,8 @@ export declare enum FirestoreCollection {
14
14
  PLS_INPUTS = "pls_inputs",
15
15
  PLS_ASSESSMENTS = "pls_assessments",
16
16
  PLS_SCORES = "pls_scores",
17
- PENDING_USERS = "pending_users"
17
+ PENDING_USERS = "pending_users",
18
+ CONSULTATION_INTERESTS = "consultation_interests"
18
19
  }
19
20
  export declare enum ConversationCollection {
20
21
  MESSAGES = "messages"
@@ -19,6 +19,7 @@ var FirestoreCollection;
19
19
  FirestoreCollection["PLS_ASSESSMENTS"] = "pls_assessments";
20
20
  FirestoreCollection["PLS_SCORES"] = "pls_scores";
21
21
  FirestoreCollection["PENDING_USERS"] = "pending_users";
22
+ FirestoreCollection["CONSULTATION_INTERESTS"] = "consultation_interests";
22
23
  })(FirestoreCollection || (exports.FirestoreCollection = FirestoreCollection = {}));
23
24
  var ConversationCollection;
24
25
  (function (ConversationCollection) {
@@ -1,6 +1,7 @@
1
1
  export * from "./app-route.enum";
2
2
  export * from "./ca.enum";
3
3
  export * from "./cloudfunction.enum";
4
+ export * from "./consultation.enum";
4
5
  export * from "./country.enum";
5
6
  export * from "./evaluation.enum";
6
7
  export * from "./environment.enum";
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./app-route.enum"), exports);
18
18
  __exportStar(require("./ca.enum"), exports);
19
19
  __exportStar(require("./cloudfunction.enum"), exports);
20
+ __exportStar(require("./consultation.enum"), exports);
20
21
  __exportStar(require("./country.enum"), exports);
21
22
  __exportStar(require("./evaluation.enum"), exports);
22
23
  __exportStar(require("./environment.enum"), exports);
@@ -1,6 +1,6 @@
1
1
  import { CloudFunction } from "../enum";
2
- import { AddImagesRequest, AuthenticateBankIDRequest, CollectBankIDRequest, CreateBillingPortalRequest, CreateCheckoutSessionRequest, CreateLikeRequest, CreatePLSInputRequest, CreatePLSSubmissionRequest, CreateReportRequest, DeleteUserRequest, GetCARequest, GetLikesRequest, GetMatchableUserRequest, GetMatchCheckRequest, GetMatchesRequest, GetAdvancedMatchRequest, GetMessagesRequest, GetPLSSubmissionRequest, GetProductsRequest, GetSelfAwarenessRequest, GetUserRequest, HandleLastMessageSeenRequest, HandleLikeSeenRequest, HandleMatchSeenRequest, RegisterOnboardingRequest, RegisterUserRequest, RemoveMatchRequest, SendMessageRequest, SubmitAnswerRequest, SubmitCategoryFeedbackRequest, SubmitPLSCategoryFeedbackRequest, SubmitPLSDemographicsRequest, UpdatePLSAnswerRequest, UpdateQuestionsRequest, UpdateUserRequest, SubmitFeedbackRequest, SyncAffiliateRequest, JoinEventRequest, GetEventRequest, GetEventMatchRequest, SendEventFeedbackRequest, GetEventParticipantsRequest } from "./request.types";
3
- import { AddImagesResponse, AuthenticateBankIDResponse, CollectBankIDResponse, CreateBillingPortalResponse, CreateCheckoutSessionResponse, CreateLikeResponse, CreatePLSInputResponse, CreatePLSSubmissionResponse, CreateReportResponse, DeleteUserResponse, GetCAResponse, GetLikesResponse, GetMatchableUserResponse, GetMatchableUsersResponse, GetMatchCheckResponse, GetMatchesResponse, GetAdvancedMatchResponse, GetMessagesResponse, GetPLSSubmissionResponse, GetProductsResponse, GetSelfAwarenessReponse, GetUserResponse, HandleLastMessageSeenResponse, HandleLikeSeenResponse, HandleMatchSeenResponse, RegisterOnboardingResponse, RegisterUserResponse, RemoveMatchResponse, SendMessageResponse, SubmitAnswerResponse, SubmitCategoryFeedbackResponse, SubmitPLSCategoryFeedbackResponse, SubmitPLSDemographicsResponse, UpdatePLSAnswerResponse, UpdateQuestionsResponse, UpdateUserResponse, SubmitFeedbackResponse, SyncAffiliateResponse, GetUserEventsResponse, JoinEventResponse, GetEventResponse, GetEventMatchResponse, SendEventFeedbackResponse, GetEventParticipantsResponse } from "./response.types";
2
+ import { AddImagesRequest, AuthenticateBankIDRequest, CollectBankIDRequest, CreateBillingPortalRequest, CreateCheckoutSessionRequest, CreateLikeRequest, CreatePLSInputRequest, CreatePLSSubmissionRequest, CreateReportRequest, DeleteUserRequest, GetCARequest, GetLikesRequest, GetMatchableUserRequest, GetMatchCheckRequest, GetMatchesRequest, GetAdvancedMatchRequest, GetMessagesRequest, GetPLSSubmissionRequest, GetProductsRequest, GetSelfAwarenessRequest, GetUserRequest, HandleLastMessageSeenRequest, HandleLikeSeenRequest, HandleMatchSeenRequest, RegisterOnboardingRequest, RegisterUserRequest, RemoveMatchRequest, SendMessageRequest, SubmitAnswerRequest, SubmitCategoryFeedbackRequest, SubmitPLSCategoryFeedbackRequest, SubmitPLSDemographicsRequest, UpdatePLSAnswerRequest, UpdateQuestionsRequest, UpdateUserRequest, SubmitFeedbackRequest, ExpressConsultationInterestRequest, GetMyConsultationInterestRequest, GetConsultationInterestsRequest, UpdateConsultationInterestRequest, SyncAffiliateRequest, JoinEventRequest, GetEventRequest, GetEventMatchRequest, SendEventFeedbackRequest, GetEventParticipantsRequest } from "./request.types";
3
+ import { AddImagesResponse, AuthenticateBankIDResponse, CollectBankIDResponse, CreateBillingPortalResponse, CreateCheckoutSessionResponse, CreateLikeResponse, CreatePLSInputResponse, CreatePLSSubmissionResponse, CreateReportResponse, DeleteUserResponse, GetCAResponse, GetLikesResponse, GetMatchableUserResponse, GetMatchableUsersResponse, GetMatchCheckResponse, GetMatchesResponse, GetAdvancedMatchResponse, GetMessagesResponse, GetPLSSubmissionResponse, GetProductsResponse, GetSelfAwarenessReponse, GetUserResponse, HandleLastMessageSeenResponse, HandleLikeSeenResponse, HandleMatchSeenResponse, RegisterOnboardingResponse, RegisterUserResponse, RemoveMatchResponse, SendMessageResponse, SubmitAnswerResponse, SubmitCategoryFeedbackResponse, SubmitPLSCategoryFeedbackResponse, SubmitPLSDemographicsResponse, UpdatePLSAnswerResponse, UpdateQuestionsResponse, UpdateUserResponse, SubmitFeedbackResponse, ExpressConsultationInterestResponse, GetMyConsultationInterestResponse, GetConsultationInterestsResponse, UpdateConsultationInterestResponse, SyncAffiliateResponse, GetUserEventsResponse, JoinEventResponse, GetEventResponse, GetEventMatchResponse, SendEventFeedbackResponse, GetEventParticipantsResponse } from "./response.types";
4
4
  /**
5
5
  * @deprecated This function is deprecated.
6
6
  * Use `CloudFunctionTypes` inside application code instead.
@@ -63,6 +63,22 @@ export type CloudFunctionTypes = {
63
63
  payload: SubmitFeedbackRequest;
64
64
  response: SubmitFeedbackResponse;
65
65
  };
66
+ [CloudFunction.EXPRESS_CONSULTATION_INTEREST]: {
67
+ payload: ExpressConsultationInterestRequest;
68
+ response: ExpressConsultationInterestResponse;
69
+ };
70
+ [CloudFunction.GET_MY_CONSULTATION_INTEREST]: {
71
+ payload: GetMyConsultationInterestRequest;
72
+ response: GetMyConsultationInterestResponse;
73
+ };
74
+ [CloudFunction.GET_CONSULTATION_INTERESTS]: {
75
+ payload: GetConsultationInterestsRequest;
76
+ response: GetConsultationInterestsResponse;
77
+ };
78
+ [CloudFunction.UPDATE_CONSULTATION_INTEREST]: {
79
+ payload: UpdateConsultationInterestRequest;
80
+ response: UpdateConsultationInterestResponse;
81
+ };
66
82
  [CloudFunction.CREATE_LIKE]: {
67
83
  payload: CreateLikeRequest;
68
84
  response: CreateLikeResponse;
@@ -0,0 +1,62 @@
1
+ import { ConsultationInterestStatus } from "../enum";
2
+ /**
3
+ * A user's expression of interest in a 1:1 consultation with a
4
+ * credentialed professional. v1 surface is "Bohm-samtal" — a video
5
+ * session with Fredric Bohm, recorded as an interest-list entry the
6
+ * team manually follows up on. The domain name stays generic
7
+ * (`Consultation`) so subsequent professionals can land here
8
+ * without a schema rename.
9
+ *
10
+ * `expertId` scopes the record to a specific professional. v1 has
11
+ * exactly one value (`"bohm"`); future additions (other
12
+ * psychologists, therapists, sex therapists, financial planners,
13
+ * etc.) plug in without a schema migration. Idempotency at the
14
+ * backend is `(userId, expertId)` — a user can express interest in
15
+ * multiple experts independently, but only one pending record per
16
+ * (user, expert) pair at any time.
17
+ *
18
+ * The slug format (e.g. `"bohm"`) reads cleanly in logs / admin
19
+ * dashboards. Future v2 may move expert metadata (name, photo,
20
+ * pricing, credentials) into DatoCMS and key entries by their CMS
21
+ * slug — the runtime contract stays the same string.
22
+ */
23
+ export type ConsultationInterest = {
24
+ id: string;
25
+ userId: string;
26
+ expertId: string;
27
+ submittedAt: string;
28
+ status: ConsultationInterestStatus;
29
+ /**
30
+ * Set by the admin endpoint when the team reaches out by email.
31
+ * Stays `undefined` while the record is `PENDING`.
32
+ */
33
+ followUpSentAt?: string;
34
+ /**
35
+ * Internal team notes — never surfaced to the user. Used during
36
+ * the manual follow-up phase to track context ("emailed 2026-05-
37
+ * 14, awaiting reply", "user confirmed availability X/Y/Z", etc).
38
+ */
39
+ notes?: string;
40
+ };
41
+ /**
42
+ * The user-facing summary returned from
43
+ * `/consultation/my-interest`. Replaces the AsyncStorage flag in
44
+ * youand-app — the screen reads this on mount to decide whether
45
+ * to show the "Anmäl intresse" CTA or the post-submit "Tack — vi
46
+ * hör av oss" confirmation state.
47
+ *
48
+ * Scoped to a single `expertId` (the one the client asked about) —
49
+ * a user with interest in multiple experts gets multiple summaries
50
+ * via multiple calls. v1 only ever asks about `"bohm"`, so the app
51
+ * still has a single CTA flow.
52
+ *
53
+ * Carries just enough for the UI to render the two states; the
54
+ * full `ConsultationInterest` record (notes, internal status
55
+ * machinery) stays admin-only.
56
+ */
57
+ export type ConsultationInterestSummary = {
58
+ expertId: string;
59
+ hasInterest: boolean;
60
+ submittedAt: string | null;
61
+ status: ConsultationInterestStatus | null;
62
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,6 +2,7 @@ export * from "./am.types";
2
2
  export * from "./assessment.types";
3
3
  export * from "./ca.types";
4
4
  export * from "./cloud-function-types";
5
+ export * from "./consultation.types";
5
6
  export * from "./dq.types";
6
7
  export * from "./error.types";
7
8
  export * from "./event.types";
@@ -18,6 +18,7 @@ __exportStar(require("./am.types"), exports);
18
18
  __exportStar(require("./assessment.types"), exports);
19
19
  __exportStar(require("./ca.types"), exports);
20
20
  __exportStar(require("./cloud-function-types"), exports);
21
+ __exportStar(require("./consultation.types"), exports);
21
22
  __exportStar(require("./dq.types"), exports);
22
23
  __exportStar(require("./error.types"), exports);
23
24
  __exportStar(require("./event.types"), exports);
@@ -1,4 +1,4 @@
1
- import { DislikeReason, FeedbackAgreement, FeedbackType, Gender, LikeType, Market, ProviderType, ReportReason, Theme, UserDeletionReason } from "../enum";
1
+ import { ConsultationInterestStatus, DislikeReason, FeedbackAgreement, FeedbackType, Gender, LikeType, Market, ProviderType, ReportReason, Theme, UserDeletionReason } from "../enum";
2
2
  import { DeepPartial } from "./helper.types";
3
3
  import { Answer, Demographics } from "./submission.types";
4
4
  import { DatingPreferences, UserConsents, UserDevice, UserNotifications, UserProfile, UserSettings } from "./user.types";
@@ -59,6 +59,33 @@ export type SubmitFeedbackRequest = {
59
59
  context?: string;
60
60
  rating?: number;
61
61
  };
62
+ export type ExpressConsultationInterestRequest = {
63
+ expertId: string;
64
+ };
65
+ export type GetMyConsultationInterestRequest = {
66
+ expertId: string;
67
+ };
68
+ export type GetConsultationInterestsRequest = {
69
+ /**
70
+ * Optional status filter. Omit to fetch every record. Most
71
+ * common use is `PENDING` for the team's outreach worklist.
72
+ */
73
+ status?: ConsultationInterestStatus;
74
+ /**
75
+ * Optional expert filter. Omit for the cross-expert worklist;
76
+ * pass to scope a single professional's outreach queue.
77
+ */
78
+ expertId?: string;
79
+ };
80
+ export type UpdateConsultationInterestRequest = {
81
+ id: string;
82
+ status: ConsultationInterestStatus;
83
+ /**
84
+ * Internal team-only notes. Trim before sending; empty / unset
85
+ * leaves the previous value in place.
86
+ */
87
+ notes?: string;
88
+ };
62
89
  export type HandleLastMessageSeenRequest = {
63
90
  conversationId: string;
64
91
  };
@@ -1,3 +1,4 @@
1
+ import { ConsultationInterest, ConsultationInterestSummary } from "./consultation.types";
1
2
  import { Submission, CategoryEvaluation } from "./submission.types";
2
3
  import { Event, FullParticipant, FullParticipantWithCS } from "./event.types";
3
4
  import { UserMatch } from "./match.types";
@@ -46,6 +47,10 @@ export type GetEventParticipantsResponse = {
46
47
  eventMatchIds: string[];
47
48
  };
48
49
  export type SubmitFeedbackResponse = undefined;
50
+ export type ExpressConsultationInterestResponse = ConsultationInterestSummary;
51
+ export type GetMyConsultationInterestResponse = ConsultationInterestSummary;
52
+ export type GetConsultationInterestsResponse = ConsultationInterest[];
53
+ export type UpdateConsultationInterestResponse = ConsultationInterest;
49
54
  export type HandleLastMessageSeenResponse = undefined;
50
55
  export type SendMessageResponse = Message;
51
56
  export type GetMessagesResponse = Message[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heartraite",
3
- "version": "1.0.192",
3
+ "version": "1.0.194",
4
4
  "description": "Heartraite npm package for common functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -4,6 +4,10 @@ export enum CloudFunction {
4
4
  AUTHENTICATE_BANKID = "bankid/authenticate",
5
5
  COLLECT_BANKID = "bankid/collect",
6
6
  SUBMIT_FEEDBACK = "feedback/submit-feedback",
7
+ EXPRESS_CONSULTATION_INTEREST = "consultation/express-interest",
8
+ GET_MY_CONSULTATION_INTEREST = "consultation/my-interest",
9
+ GET_CONSULTATION_INTERESTS = "consultation/admin-interests",
10
+ UPDATE_CONSULTATION_INTEREST = "consultation/admin-update-interest",
7
11
  GET_CA = "ca/get-ca",
8
12
  SUBMIT_ANSWER = "ca/submit-answer",
9
13
  SEND_EVENT_FEEDBACK = "event/send-event-feedback",
@@ -0,0 +1,27 @@
1
+ /**
2
+ * State machine for a user's expression of interest in a 1:1
3
+ * consultation with a credentialed professional (psychologist,
4
+ * therapist, etc.). v1 ships the "interest list" MVP where the
5
+ * team manually follows up by email; v2 will add real booking +
6
+ * payment, at which point the later states get exercised.
7
+ *
8
+ * PENDING → user expressed interest, team hasn't reached
9
+ * out yet. Initial state on submit.
10
+ * FOLLOWED_UP → team emailed the user. v1 manual; v2 may be
11
+ * automated after a Stripe charge.
12
+ * BOOKED → consultation session scheduled (v2 only — v1
13
+ * tracks this only via team-internal notes).
14
+ * COMPLETED → session happened.
15
+ * DECLINED → user backed out / team declined the request.
16
+ *
17
+ * v1 only relies on the PENDING → FOLLOWED_UP transition driven
18
+ * by an admin action. Future states are reserved so the type
19
+ * shape doesn't change when the booking flow lands.
20
+ */
21
+ export enum ConsultationInterestStatus {
22
+ PENDING = "PENDING",
23
+ FOLLOWED_UP = "FOLLOWED_UP",
24
+ BOOKED = "BOOKED",
25
+ COMPLETED = "COMPLETED",
26
+ DECLINED = "DECLINED",
27
+ }
@@ -15,6 +15,7 @@ export enum FirestoreCollection {
15
15
  PLS_ASSESSMENTS = "pls_assessments",
16
16
  PLS_SCORES = "pls_scores",
17
17
  PENDING_USERS = "pending_users",
18
+ CONSULTATION_INTERESTS = "consultation_interests",
18
19
  }
19
20
 
20
21
  export enum ConversationCollection {
package/src/enum/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./app-route.enum";
2
2
  export * from "./ca.enum";
3
3
  export * from "./cloudfunction.enum";
4
+ export * from "./consultation.enum";
4
5
  export * from "./country.enum";
5
6
  export * from "./evaluation.enum";
6
7
  export * from "./environment.enum";
@@ -36,6 +36,10 @@ import {
36
36
  UpdateQuestionsRequest,
37
37
  UpdateUserRequest,
38
38
  SubmitFeedbackRequest,
39
+ ExpressConsultationInterestRequest,
40
+ GetMyConsultationInterestRequest,
41
+ GetConsultationInterestsRequest,
42
+ UpdateConsultationInterestRequest,
39
43
  SyncAffiliateRequest,
40
44
  JoinEventRequest,
41
45
  GetEventRequest,
@@ -81,6 +85,10 @@ import {
81
85
  UpdateQuestionsResponse,
82
86
  UpdateUserResponse,
83
87
  SubmitFeedbackResponse,
88
+ ExpressConsultationInterestResponse,
89
+ GetMyConsultationInterestResponse,
90
+ GetConsultationInterestsResponse,
91
+ UpdateConsultationInterestResponse,
84
92
  SyncAffiliateResponse,
85
93
  GetUserEventsResponse,
86
94
  JoinEventResponse,
@@ -163,6 +171,24 @@ export type CloudFunctionTypes = {
163
171
  response: SubmitFeedbackResponse;
164
172
  };
165
173
 
174
+ // consultation
175
+ [CloudFunction.EXPRESS_CONSULTATION_INTEREST]: {
176
+ payload: ExpressConsultationInterestRequest;
177
+ response: ExpressConsultationInterestResponse;
178
+ };
179
+ [CloudFunction.GET_MY_CONSULTATION_INTEREST]: {
180
+ payload: GetMyConsultationInterestRequest;
181
+ response: GetMyConsultationInterestResponse;
182
+ };
183
+ [CloudFunction.GET_CONSULTATION_INTERESTS]: {
184
+ payload: GetConsultationInterestsRequest;
185
+ response: GetConsultationInterestsResponse;
186
+ };
187
+ [CloudFunction.UPDATE_CONSULTATION_INTEREST]: {
188
+ payload: UpdateConsultationInterestRequest;
189
+ response: UpdateConsultationInterestResponse;
190
+ };
191
+
166
192
  // like
167
193
  [CloudFunction.CREATE_LIKE]: {
168
194
  payload: CreateLikeRequest;
@@ -0,0 +1,64 @@
1
+ import { ConsultationInterestStatus } from "../enum";
2
+
3
+ /**
4
+ * A user's expression of interest in a 1:1 consultation with a
5
+ * credentialed professional. v1 surface is "Bohm-samtal" — a video
6
+ * session with Fredric Bohm, recorded as an interest-list entry the
7
+ * team manually follows up on. The domain name stays generic
8
+ * (`Consultation`) so subsequent professionals can land here
9
+ * without a schema rename.
10
+ *
11
+ * `expertId` scopes the record to a specific professional. v1 has
12
+ * exactly one value (`"bohm"`); future additions (other
13
+ * psychologists, therapists, sex therapists, financial planners,
14
+ * etc.) plug in without a schema migration. Idempotency at the
15
+ * backend is `(userId, expertId)` — a user can express interest in
16
+ * multiple experts independently, but only one pending record per
17
+ * (user, expert) pair at any time.
18
+ *
19
+ * The slug format (e.g. `"bohm"`) reads cleanly in logs / admin
20
+ * dashboards. Future v2 may move expert metadata (name, photo,
21
+ * pricing, credentials) into DatoCMS and key entries by their CMS
22
+ * slug — the runtime contract stays the same string.
23
+ */
24
+ export type ConsultationInterest = {
25
+ id: string;
26
+ userId: string;
27
+ expertId: string;
28
+ submittedAt: string;
29
+ status: ConsultationInterestStatus;
30
+ /**
31
+ * Set by the admin endpoint when the team reaches out by email.
32
+ * Stays `undefined` while the record is `PENDING`.
33
+ */
34
+ followUpSentAt?: string;
35
+ /**
36
+ * Internal team notes — never surfaced to the user. Used during
37
+ * the manual follow-up phase to track context ("emailed 2026-05-
38
+ * 14, awaiting reply", "user confirmed availability X/Y/Z", etc).
39
+ */
40
+ notes?: string;
41
+ };
42
+
43
+ /**
44
+ * The user-facing summary returned from
45
+ * `/consultation/my-interest`. Replaces the AsyncStorage flag in
46
+ * youand-app — the screen reads this on mount to decide whether
47
+ * to show the "Anmäl intresse" CTA or the post-submit "Tack — vi
48
+ * hör av oss" confirmation state.
49
+ *
50
+ * Scoped to a single `expertId` (the one the client asked about) —
51
+ * a user with interest in multiple experts gets multiple summaries
52
+ * via multiple calls. v1 only ever asks about `"bohm"`, so the app
53
+ * still has a single CTA flow.
54
+ *
55
+ * Carries just enough for the UI to render the two states; the
56
+ * full `ConsultationInterest` record (notes, internal status
57
+ * machinery) stays admin-only.
58
+ */
59
+ export type ConsultationInterestSummary = {
60
+ expertId: string;
61
+ hasInterest: boolean;
62
+ submittedAt: string | null;
63
+ status: ConsultationInterestStatus | null;
64
+ };
@@ -2,6 +2,7 @@ export * from "./am.types";
2
2
  export * from "./assessment.types";
3
3
  export * from "./ca.types";
4
4
  export * from "./cloud-function-types";
5
+ export * from "./consultation.types";
5
6
  export * from "./dq.types";
6
7
  export * from "./error.types";
7
8
  export * from "./event.types";
@@ -1,4 +1,5 @@
1
1
  import {
2
+ ConsultationInterestStatus,
2
3
  DislikeReason,
3
4
  FeedbackAgreement,
4
5
  FeedbackType,
@@ -87,6 +88,46 @@ export type SubmitFeedbackRequest = {
87
88
  rating?: number;
88
89
  };
89
90
 
91
+ // consultation — 1:1 video sessions with credentialed professionals
92
+ //
93
+ // v1 endpoints are CAGuard'd from the user-facing side (express +
94
+ // my-interest, gated on completed CA) and AdminGuard'd from the
95
+ // team side (admin-interests + admin-update-interest). userId
96
+ // always comes from the authed token, never the client. `expertId`
97
+ // scopes the request to a specific professional — v1 only sends
98
+ // `"bohm"` but the field is required so the schema is stable when
99
+ // future experts land.
100
+ export type ExpressConsultationInterestRequest = {
101
+ expertId: string;
102
+ };
103
+
104
+ export type GetMyConsultationInterestRequest = {
105
+ expertId: string;
106
+ };
107
+
108
+ export type GetConsultationInterestsRequest = {
109
+ /**
110
+ * Optional status filter. Omit to fetch every record. Most
111
+ * common use is `PENDING` for the team's outreach worklist.
112
+ */
113
+ status?: ConsultationInterestStatus;
114
+ /**
115
+ * Optional expert filter. Omit for the cross-expert worklist;
116
+ * pass to scope a single professional's outreach queue.
117
+ */
118
+ expertId?: string;
119
+ };
120
+
121
+ export type UpdateConsultationInterestRequest = {
122
+ id: string;
123
+ status: ConsultationInterestStatus;
124
+ /**
125
+ * Internal team-only notes. Trim before sending; empty / unset
126
+ * leaves the previous value in place.
127
+ */
128
+ notes?: string;
129
+ };
130
+
90
131
  // message
91
132
  export type HandleLastMessageSeenRequest = {
92
133
  conversationId: string;
@@ -1,3 +1,7 @@
1
+ import {
2
+ ConsultationInterest,
3
+ ConsultationInterestSummary,
4
+ } from "./consultation.types";
1
5
  import { Submission, CategoryEvaluation } from "./submission.types";
2
6
  import { Event, FullParticipant, FullParticipantWithCS } from "./event.types";
3
7
  import { UserMatch } from "./match.types";
@@ -60,6 +64,18 @@ export type GetEventParticipantsResponse = {
60
64
  // feedback
61
65
  export type SubmitFeedbackResponse = undefined;
62
66
 
67
+ // consultation
68
+ //
69
+ // `Express` returns the freshly-recorded summary so the app can
70
+ // flip into the "Tack — vi hör av oss" state without a follow-up
71
+ // fetch. `GetMyConsultationInterest` returns the same summary;
72
+ // admin endpoints return the full record(s) with internal notes
73
+ // + audit fields.
74
+ export type ExpressConsultationInterestResponse = ConsultationInterestSummary;
75
+ export type GetMyConsultationInterestResponse = ConsultationInterestSummary;
76
+ export type GetConsultationInterestsResponse = ConsultationInterest[];
77
+ export type UpdateConsultationInterestResponse = ConsultationInterest;
78
+
63
79
  // message
64
80
  export type HandleLastMessageSeenResponse = undefined;
65
81
  export type SendMessageResponse = Message;