@deallony/shared 1.2.81 → 1.2.82

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.
Files changed (43) hide show
  1. package/dist/cjs/constants/events/EventGuests.d.ts +3 -0
  2. package/dist/cjs/constants/events/EventGuests.js +6 -0
  3. package/dist/cjs/constants/events/EventHost.d.ts +79 -0
  4. package/dist/cjs/constants/events/EventHost.js +155 -0
  5. package/dist/cjs/constants/events/index.d.ts +2 -0
  6. package/dist/cjs/constants/events/index.js +2 -0
  7. package/dist/cjs/constants/routes/app.d.ts +7 -0
  8. package/dist/cjs/constants/routes/app.js +2 -0
  9. package/dist/cjs/dto/events/EventHost.dto.d.ts +7 -0
  10. package/dist/cjs/dto/events/EventHost.dto.js +2 -0
  11. package/dist/cjs/dto/events/index.d.ts +1 -0
  12. package/dist/cjs/dto/events/index.js +1 -0
  13. package/dist/cjs/schema/communication/ai-conversation.schema.d.ts +2 -2
  14. package/dist/cjs/schema/communication/offer.schema.d.ts +3 -3
  15. package/dist/cjs/schema/events/Event.schema.d.ts +32 -0
  16. package/dist/cjs/schema/events/Event.schema.js +7 -0
  17. package/dist/cjs/schema/events/EventHost.schema.d.ts +36 -0
  18. package/dist/cjs/schema/events/EventHost.schema.js +44 -0
  19. package/dist/cjs/schema/events/index.d.ts +1 -0
  20. package/dist/cjs/schema/events/index.js +1 -0
  21. package/dist/cjs/types/communication/Notification.d.ts +8 -0
  22. package/dist/cjs/types/communication/Notification.js +9 -0
  23. package/dist/cjs/types/events/Event.d.ts +24 -0
  24. package/dist/cjs/types/events/EventHost.d.ts +29 -0
  25. package/dist/cjs/types/events/EventHost.js +2 -0
  26. package/dist/cjs/types/events/index.d.ts +1 -0
  27. package/dist/cjs/types/events/index.js +1 -0
  28. package/dist/cjs/types/index.d.ts +1 -0
  29. package/dist/cjs/types/index.js +3 -2
  30. package/dist/esm/constants/events/EventGuests.js +3 -0
  31. package/dist/esm/constants/events/EventHost.js +147 -0
  32. package/dist/esm/constants/events/index.js +2 -0
  33. package/dist/esm/constants/routes/app.js +2 -0
  34. package/dist/esm/dto/events/EventHost.dto.js +1 -0
  35. package/dist/esm/dto/events/index.js +1 -0
  36. package/dist/esm/schema/events/Event.schema.js +8 -1
  37. package/dist/esm/schema/events/EventHost.schema.js +41 -0
  38. package/dist/esm/schema/events/index.js +1 -0
  39. package/dist/esm/types/communication/Notification.js +9 -0
  40. package/dist/esm/types/events/EventHost.js +1 -0
  41. package/dist/esm/types/events/index.js +1 -0
  42. package/dist/esm/types/index.js +1 -0
  43. package/package.json +1 -1
@@ -0,0 +1,3 @@
1
+ /** Limits of the expected guest count asked when creating an event */
2
+ export declare const EVENT_EXPECTED_GUESTS_MIN = 1;
3
+ export declare const EVENT_EXPECTED_GUESTS_MAX = 100000;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EVENT_EXPECTED_GUESTS_MAX = exports.EVENT_EXPECTED_GUESTS_MIN = void 0;
4
+ /** Limits of the expected guest count asked when creating an event */
5
+ exports.EVENT_EXPECTED_GUESTS_MIN = 1;
6
+ exports.EVENT_EXPECTED_GUESTS_MAX = 100000;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * People of an event other than its creator: who they are (a slot of the event type),
3
+ * what they may do (role) and the permission map shared by the backend check and the app.
4
+ */
5
+ type Text = {
6
+ en: string;
7
+ ar: string;
8
+ };
9
+ /** organiser: full control like the owner; guests: the event's guests only */
10
+ export declare const EventHostRoleValues: readonly ["organiser", "guests"];
11
+ export type EventHostRole = (typeof EventHostRoleValues)[number];
12
+ export declare const EventHostRole: {
13
+ readonly ORGANISER: "organiser";
14
+ readonly GUESTS: "guests";
15
+ };
16
+ export declare const EventHostRoleNames: Record<EventHostRole, Text>;
17
+ export declare const EventHostRoleHints: Record<EventHostRole, Text>;
18
+ export declare const EventHostRoleOptions: {
19
+ value: "organiser" | "guests";
20
+ name: Text;
21
+ hint: Text;
22
+ }[];
23
+ /** pending: waiting for the person to sign in and accept; active: accepted */
24
+ export declare const EventHostStatusValues: readonly ["pending", "active", "declined", "left", "removed"];
25
+ export type EventHostStatus = (typeof EventHostStatusValues)[number];
26
+ export declare const EventHostStatus: {
27
+ readonly PENDING: "pending";
28
+ readonly ACTIVE: "active";
29
+ readonly DECLINED: "declined";
30
+ readonly LEFT: "left";
31
+ readonly REMOVED: "removed";
32
+ };
33
+ export declare const EventHostStatusNames: Record<EventHostStatus, Text>;
34
+ /** Statuses that still hold the number on the event (one row per number) */
35
+ export declare const EventHostOpenStatusValues: EventHostStatus[];
36
+ /** What the signed-in user is on an event */
37
+ export declare const EventAccessRoleValues: readonly ["owner", "organiser", "guests"];
38
+ export type EventAccessRole = (typeof EventAccessRoleValues)[number];
39
+ export declare const EventPermissionValues: readonly ["event.view", "event.edit", "event.publish", "event.delete", "guests.manage", "hosts.manage"];
40
+ export type EventPermission = (typeof EventPermissionValues)[number];
41
+ export declare const EventRolePermissions: Record<EventAccessRole, readonly EventPermission[]>;
42
+ export declare const hasEventPermission: (role: EventAccessRole | null | undefined, permission: EventPermission) => boolean;
43
+ export declare const EventAccessRoleNames: Record<EventAccessRole, Text>;
44
+ /** Old single-number fields a slot is kept in sync with */
45
+ export type EventPeopleSlotMirror = "wedding.bride_phone" | "wedding.groom_phone" | "occasion.phone";
46
+ /** A suggested person of an event type: one optional phone field in the form */
47
+ export type EventPeopleSlot = {
48
+ key: string;
49
+ label: Text;
50
+ /** Role they get unless the owner changes it */
51
+ role: EventHostRole;
52
+ /** The existing form field that already holds this number */
53
+ mirrors?: EventPeopleSlotMirror;
54
+ /** Hidden from them until the event ends when the type's details.surprise is "yes" */
55
+ surprise_when_details_surprise?: boolean;
56
+ };
57
+ /**
58
+ * Suggested people of each event type. The person the event is for manages guests;
59
+ * whoever organises or invites gets full control. Anyone else is added by hand.
60
+ */
61
+ export declare const EventPeopleSlots: Record<string, EventPeopleSlot[]>;
62
+ /** Suggested people of a type (a type without its own list gets the organiser only) */
63
+ export declare const findEventPeopleSlots: (eventType: string | null | undefined) => EventPeopleSlot[];
64
+ export declare const findEventPeopleSlot: (eventType: string | null | undefined, key: string | null | undefined) => EventPeopleSlot | undefined;
65
+ /** Answers to «من أنت في هذه المناسبة؟» besides the type's slots */
66
+ export declare const EventCreatorRoleExtra: {
67
+ readonly COMPANY: "company";
68
+ readonly OTHER: "other";
69
+ };
70
+ export declare const EventCreatorRoleExtraNames: Record<(typeof EventCreatorRoleExtra)[keyof typeof EventCreatorRoleExtra], Text>;
71
+ /** Label of the person a host row (or the creator) is: slot label, or the free text */
72
+ export declare const eventPersonLabel: (eventType: string | null | undefined, slotKey: string | null | undefined, fallback?: string | null) => Text | null;
73
+ export declare const EVENT_HOST_LABEL_MAX = 60;
74
+ /**
75
+ * One spelling for a phone number so numbers typed differently still match:
76
+ * digits only with a leading +, 00 → +, and no 0 right after a known country code.
77
+ */
78
+ export declare const normalizeEventPhone: (value: string | null | undefined) => string | null;
79
+ export {};
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeEventPhone = exports.EVENT_HOST_LABEL_MAX = exports.eventPersonLabel = exports.EventCreatorRoleExtraNames = exports.EventCreatorRoleExtra = exports.findEventPeopleSlot = exports.findEventPeopleSlots = exports.EventPeopleSlots = exports.EventAccessRoleNames = exports.hasEventPermission = exports.EventRolePermissions = exports.EventPermissionValues = exports.EventAccessRoleValues = exports.EventHostOpenStatusValues = exports.EventHostStatusNames = exports.EventHostStatus = exports.EventHostStatusValues = exports.EventHostRoleOptions = exports.EventHostRoleHints = exports.EventHostRoleNames = exports.EventHostRole = exports.EventHostRoleValues = void 0;
4
+ /** organiser: full control like the owner; guests: the event's guests only */
5
+ exports.EventHostRoleValues = ["organiser", "guests"];
6
+ exports.EventHostRole = {
7
+ ORGANISER: "organiser",
8
+ GUESTS: "guests",
9
+ };
10
+ exports.EventHostRoleNames = {
11
+ organiser: { ar: "منظم", en: "Organiser" },
12
+ guests: { ar: "إدارة المدعوين", en: "Manages guests" },
13
+ };
14
+ exports.EventHostRoleHints = {
15
+ organiser: { ar: "يدير المناسبة كاملة", en: "Manages the whole event" },
16
+ guests: { ar: "يدير المدعوين ويرسل الدعوات ويتابع الردود", en: "Manages guests, sends invitations and follows replies" },
17
+ };
18
+ exports.EventHostRoleOptions = exports.EventHostRoleValues.map((value) => ({
19
+ value,
20
+ name: exports.EventHostRoleNames[value],
21
+ hint: exports.EventHostRoleHints[value],
22
+ }));
23
+ /** pending: waiting for the person to sign in and accept; active: accepted */
24
+ exports.EventHostStatusValues = ["pending", "active", "declined", "left", "removed"];
25
+ exports.EventHostStatus = {
26
+ PENDING: "pending",
27
+ ACTIVE: "active",
28
+ DECLINED: "declined",
29
+ LEFT: "left",
30
+ REMOVED: "removed",
31
+ };
32
+ exports.EventHostStatusNames = {
33
+ pending: { ar: "بانتظار القبول", en: "Waiting to accept" },
34
+ active: { ar: "انضم", en: "Joined" },
35
+ declined: { ar: "رفض", en: "Declined" },
36
+ left: { ar: "غادر", en: "Left" },
37
+ removed: { ar: "أزيل", en: "Removed" },
38
+ };
39
+ /** Statuses that still hold the number on the event (one row per number) */
40
+ exports.EventHostOpenStatusValues = ["pending", "active"];
41
+ /** What the signed-in user is on an event */
42
+ exports.EventAccessRoleValues = ["owner", "organiser", "guests"];
43
+ exports.EventPermissionValues = [
44
+ /** See the event (drafts included) and its invitation link */
45
+ "event.view",
46
+ /** Details, names, date, timeline, dress code, design, photos, visibility, preview */
47
+ "event.edit",
48
+ /** Publish (pay), unpublish */
49
+ "event.publish",
50
+ "event.delete",
51
+ /** Guests: add, edit, remove, send links, read status, replies, check-in, guest reports */
52
+ "guests.manage",
53
+ /** Add/remove people of the event and change their role */
54
+ "hosts.manage",
55
+ ];
56
+ exports.EventRolePermissions = {
57
+ owner: exports.EventPermissionValues,
58
+ organiser: exports.EventPermissionValues,
59
+ guests: ["event.view", "guests.manage"],
60
+ };
61
+ const hasEventPermission = (role, permission) => !!role && exports.EventRolePermissions[role].includes(permission);
62
+ exports.hasEventPermission = hasEventPermission;
63
+ exports.EventAccessRoleNames = {
64
+ owner: { ar: "المالك", en: "Owner" },
65
+ organiser: exports.EventHostRoleNames.organiser,
66
+ guests: exports.EventHostRoleNames.guests,
67
+ };
68
+ const slot = (key, ar, en, role, extra = {}) => ({ key, label: { ar, en }, role, ...extra });
69
+ const organiser = (ar = "المنظم", en = "Organiser") => slot("organiser", ar, en, "organiser", { mirrors: "occasion.phone" });
70
+ const weddingPeople = [
71
+ slot("bride", "العروس", "Bride", "guests", { mirrors: "wedding.bride_phone" }),
72
+ slot("groom", "العريس", "Groom", "guests", { mirrors: "wedding.groom_phone" }),
73
+ slot("organiser", "المنظم", "Organiser", "organiser"),
74
+ ];
75
+ /**
76
+ * Suggested people of each event type. The person the event is for manages guests;
77
+ * whoever organises or invites gets full control. Anyone else is added by hand.
78
+ */
79
+ exports.EventPeopleSlots = {
80
+ wedding: weddingPeople,
81
+ engagement: weddingPeople,
82
+ marriage: weddingPeople,
83
+ proposal: [
84
+ slot("proposer", "من سيطلب", "Who is proposing", "organiser"),
85
+ slot("partner", "الشريك", "Partner", "guests", { surprise_when_details_surprise: true }),
86
+ organiser(),
87
+ ],
88
+ bachelor: [slot("celebrant", "العروس أو العريس", "Bride or groom", "guests"), organiser()],
89
+ birthday: [slot("celebrant", "صاحب عيد الميلاد", "Birthday person", "guests"), organiser("من يدعو", "Who is inviting")],
90
+ newborn: [slot("mother", "الأم", "Mother", "organiser", { mirrors: "occasion.phone" }), slot("father", "الأب", "Father", "organiser")],
91
+ gender_reveal: [slot("mother", "الأم", "Mother", "organiser", { mirrors: "occasion.phone" }), slot("father", "الأب", "Father", "organiser")],
92
+ circumcision: [slot("mother", "الأم", "Mother", "organiser", { mirrors: "occasion.phone" }), slot("father", "الأب", "Father", "organiser")],
93
+ graduation: [slot("graduate", "الخريج", "Graduate", "guests"), organiser("من يدعو", "Who is inviting")],
94
+ hajj: [
95
+ slot("pilgrim", "الحاج أو الحاجة", "Pilgrim", "guests"),
96
+ slot("pilgrim2", "الحاج الثاني", "Second pilgrim", "guests"),
97
+ organiser("من يدعو", "Who is inviting"),
98
+ ],
99
+ mothers_day: [slot("mother", "الأم", "Mother", "guests"), organiser("من يدعو", "Who is inviting")],
100
+ valentine: [
101
+ slot("first", "الشخص الأول", "First person", "guests"),
102
+ slot("second", "الشخص الثاني", "Second person", "guests"),
103
+ organiser("من يدعو", "Who is inviting"),
104
+ ],
105
+ opening: [slot("shop_owner", "صاحب المحل", "Business owner", "organiser", { mirrors: "occasion.phone" })],
106
+ general: [organiser()],
107
+ };
108
+ const DEFAULT_PEOPLE = [organiser()];
109
+ /** Suggested people of a type (a type without its own list gets the organiser only) */
110
+ const findEventPeopleSlots = (eventType) => (eventType && exports.EventPeopleSlots[eventType]) || DEFAULT_PEOPLE;
111
+ exports.findEventPeopleSlots = findEventPeopleSlots;
112
+ const findEventPeopleSlot = (eventType, key) => key ? (0, exports.findEventPeopleSlots)(eventType).find((s) => s.key === key) : undefined;
113
+ exports.findEventPeopleSlot = findEventPeopleSlot;
114
+ /** Answers to «من أنت في هذه المناسبة؟» besides the type's slots */
115
+ exports.EventCreatorRoleExtra = {
116
+ COMPANY: "company",
117
+ OTHER: "other",
118
+ };
119
+ exports.EventCreatorRoleExtraNames = {
120
+ company: { ar: "شركة", en: "A company" },
121
+ other: { ar: "شخص آخر", en: "Someone else" },
122
+ };
123
+ /** Label of the person a host row (or the creator) is: slot label, or the free text */
124
+ const eventPersonLabel = (eventType, slotKey, fallback) => {
125
+ const s = (0, exports.findEventPeopleSlot)(eventType, slotKey);
126
+ if (s)
127
+ return s.label;
128
+ if (slotKey === "company" || slotKey === "other")
129
+ return exports.EventCreatorRoleExtraNames[slotKey];
130
+ return fallback ? { ar: fallback, en: fallback } : null;
131
+ };
132
+ exports.eventPersonLabel = eventPersonLabel;
133
+ exports.EVENT_HOST_LABEL_MAX = 60;
134
+ /**
135
+ * One spelling for a phone number so numbers typed differently still match:
136
+ * digits only with a leading +, 00 → +, and no 0 right after a known country code.
137
+ */
138
+ const normalizeEventPhone = (value) => {
139
+ if (!value)
140
+ return null;
141
+ let digits = String(value).trim().replace(/[^\d+]/g, "");
142
+ if (digits.startsWith("00"))
143
+ digits = "+" + digits.slice(2);
144
+ if (!digits.startsWith("+"))
145
+ digits = "+" + digits.replace(/\+/g, "");
146
+ digits = "+" + digits.slice(1).replace(/\+/g, "");
147
+ for (const code of ["+963", "+971", "+965", "+961", "+90", "+966", "+962", "+974"]) {
148
+ if (digits.startsWith(code + "0")) {
149
+ digits = code + digits.slice(code.length + 1);
150
+ break;
151
+ }
152
+ }
153
+ return digits.length > 4 ? digits : null;
154
+ };
155
+ exports.normalizeEventPhone = normalizeEventPhone;
@@ -13,3 +13,5 @@ export * from "./EventVisibility";
13
13
  export * from "./EventWeddingReligion";
14
14
  export * from "./EventTextPolicy";
15
15
  export * from "./EventDressCode";
16
+ export * from "./EventHost";
17
+ export * from "./EventGuests";
@@ -29,3 +29,5 @@ __exportStar(require("./EventVisibility"), exports);
29
29
  __exportStar(require("./EventWeddingReligion"), exports);
30
30
  __exportStar(require("./EventTextPolicy"), exports);
31
31
  __exportStar(require("./EventDressCode"), exports);
32
+ __exportStar(require("./EventHost"), exports);
33
+ __exportStar(require("./EventGuests"), exports);
@@ -513,6 +513,13 @@ export declare const APP_ROUTES: {
513
513
  id: string | number;
514
514
  };
515
515
  };
516
+ /** People of the event (owner, organisers, guest managers) */
517
+ readonly hosts: (id: number | string) => {
518
+ pathname: string;
519
+ params: {
520
+ id: string | number;
521
+ };
522
+ };
516
523
  };
517
524
  };
518
525
  export type SharedAppRoutes = typeof APP_ROUTES;
@@ -211,5 +211,7 @@ exports.APP_ROUTES = {
211
211
  publish: (id) => ({ pathname: "/event/publish/[id]", params: { id } }),
212
212
  edit: (id) => ({ pathname: "/event/new", params: { id } }),
213
213
  details: (id) => ({ pathname: "/event/details/[id]", params: { id } }),
214
+ /** People of the event (owner, organisers, guest managers) */
215
+ hosts: (id) => ({ pathname: "/event/hosts/[id]", params: { id } }),
214
216
  },
215
217
  };
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ import { addEventHostSchema, eventHostIdSchema, eventHostsListSchema, eventPersonInputSchema, updateEventHostSchema } from '../../schema/events/EventHost.schema';
3
+ export type EventPersonInputDto = z.infer<typeof eventPersonInputSchema>;
4
+ export type AddEventHostDto = z.infer<typeof addEventHostSchema>;
5
+ export type UpdateEventHostDto = z.infer<typeof updateEventHostSchema>;
6
+ export type EventHostIdDto = z.infer<typeof eventHostIdSchema>;
7
+ export type EventHostsListDto = z.infer<typeof eventHostsListSchema>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -5,3 +5,4 @@ export * from './EventWedding.dto';
5
5
  export * from './EventBirthday.dto';
6
6
  export * from './EventOccasion.dto';
7
7
  export * from './EventVenue.dto';
8
+ export * from './EventHost.dto';
@@ -21,3 +21,4 @@ __exportStar(require("./EventWedding.dto"), exports);
21
21
  __exportStar(require("./EventBirthday.dto"), exports);
22
22
  __exportStar(require("./EventOccasion.dto"), exports);
23
23
  __exportStar(require("./EventVenue.dto"), exports);
24
+ __exportStar(require("./EventHost.dto"), exports);
@@ -173,9 +173,9 @@ export declare const AiApprovalSettingsSchema: z.ZodObject<{
173
173
  * outdated: the customer wrote again, so a newer draft replaced it. failed: approved but WhatsApp refused it.
174
174
  */
175
175
  export declare const AiDraftStatusSchema: z.ZodEnum<{
176
+ pending: "pending";
176
177
  scheduled: "scheduled";
177
178
  failed: "failed";
178
- pending: "pending";
179
179
  sent: "sent";
180
180
  rejected: "rejected";
181
181
  outdated: "outdated";
@@ -190,9 +190,9 @@ export declare const AiDraftListSchema: z.ZodObject<{
190
190
  page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
191
191
  limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
192
192
  statuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
193
+ pending: "pending";
193
194
  scheduled: "scheduled";
194
195
  failed: "failed";
195
- pending: "pending";
196
196
  sent: "sent";
197
197
  rejected: "rejected";
198
198
  outdated: "outdated";
@@ -1,9 +1,9 @@
1
1
  import { z } from "zod";
2
2
  export declare const offerStatusSchema: z.ZodEnum<{
3
3
  pending: "pending";
4
+ declined: "declined";
4
5
  accepted: "accepted";
5
6
  countered: "countered";
6
- declined: "declined";
7
7
  }>;
8
8
  /** PRICE_OFFER payload. Replies (accept, counter, decline) point at the offer they answer. */
9
9
  export declare const priceOfferPayloadSchema: z.ZodObject<{
@@ -17,9 +17,9 @@ export declare const priceOfferPayloadSchema: z.ZodObject<{
17
17
  }, z.core.$strip>;
18
18
  status: z.ZodOptional<z.ZodEnum<{
19
19
  pending: "pending";
20
+ declined: "declined";
20
21
  accepted: "accepted";
21
22
  countered: "countered";
22
- declined: "declined";
23
23
  }>>;
24
24
  reply_to_message_id: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
25
25
  note: z.ZodNullable<z.ZodOptional<z.ZodString>>;
@@ -36,9 +36,9 @@ export declare const rentOfferPayloadSchema: z.ZodObject<{
36
36
  }, z.core.$strip>;
37
37
  status: z.ZodOptional<z.ZodEnum<{
38
38
  pending: "pending";
39
+ declined: "declined";
39
40
  accepted: "accepted";
40
41
  countered: "countered";
41
- declined: "declined";
42
42
  }>>;
43
43
  reply_to_message_id: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
44
44
  note: z.ZodNullable<z.ZodOptional<z.ZodString>>;
@@ -109,6 +109,7 @@ export declare const eventBaseSchema: z.ZodObject<{
109
109
  venue_id: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
110
110
  venue_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
111
111
  location_label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
112
+ expected_guests: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
112
113
  invitation_scan_type: z.ZodOptional<z.ZodEnum<{
113
114
  none: "none";
114
115
  qrcode: "qrcode";
@@ -168,6 +169,13 @@ export declare const eventBaseSchema: z.ZodObject<{
168
169
  private: "private";
169
170
  public: "public";
170
171
  }>>;
172
+ creator_role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
173
+ people: z.ZodOptional<z.ZodArray<z.ZodObject<{
174
+ slot: z.ZodString;
175
+ phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
176
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
177
+ surprise: z.ZodOptional<z.ZodBoolean>;
178
+ }, z.core.$strip>>>;
171
179
  }, z.core.$strip>;
172
180
  export declare const createEventSchema: z.ZodObject<{
173
181
  lat: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -229,6 +237,7 @@ export declare const createEventSchema: z.ZodObject<{
229
237
  venue_id: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
230
238
  venue_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
231
239
  location_label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
240
+ expected_guests: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
232
241
  invitation_scan_type: z.ZodOptional<z.ZodEnum<{
233
242
  none: "none";
234
243
  qrcode: "qrcode";
@@ -288,6 +297,13 @@ export declare const createEventSchema: z.ZodObject<{
288
297
  private: "private";
289
298
  public: "public";
290
299
  }>>;
300
+ creator_role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
301
+ people: z.ZodOptional<z.ZodArray<z.ZodObject<{
302
+ slot: z.ZodString;
303
+ phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
304
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
305
+ surprise: z.ZodOptional<z.ZodBoolean>;
306
+ }, z.core.$strip>>>;
291
307
  }, z.core.$strip>;
292
308
  export declare const updateEventSchema: z.ZodObject<{
293
309
  lat: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -349,6 +365,7 @@ export declare const updateEventSchema: z.ZodObject<{
349
365
  venue_id: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
350
366
  venue_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
351
367
  location_label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
368
+ expected_guests: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
352
369
  invitation_scan_type: z.ZodOptional<z.ZodEnum<{
353
370
  none: "none";
354
371
  qrcode: "qrcode";
@@ -408,6 +425,13 @@ export declare const updateEventSchema: z.ZodObject<{
408
425
  private: "private";
409
426
  public: "public";
410
427
  }>>;
428
+ creator_role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
429
+ people: z.ZodOptional<z.ZodArray<z.ZodObject<{
430
+ slot: z.ZodString;
431
+ phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
432
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
433
+ surprise: z.ZodOptional<z.ZodBoolean>;
434
+ }, z.core.$strip>>>;
411
435
  id: z.ZodCoercedNumber<unknown>;
412
436
  }, z.core.$strip>;
413
437
  export declare const saveEventSchema: z.ZodObject<{
@@ -470,6 +494,7 @@ export declare const saveEventSchema: z.ZodObject<{
470
494
  venue_id: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
471
495
  venue_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
472
496
  location_label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
497
+ expected_guests: z.ZodOptional<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
473
498
  invitation_scan_type: z.ZodOptional<z.ZodEnum<{
474
499
  none: "none";
475
500
  qrcode: "qrcode";
@@ -529,6 +554,13 @@ export declare const saveEventSchema: z.ZodObject<{
529
554
  private: "private";
530
555
  public: "public";
531
556
  }>>;
557
+ creator_role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
558
+ people: z.ZodOptional<z.ZodArray<z.ZodObject<{
559
+ slot: z.ZodString;
560
+ phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
561
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
562
+ surprise: z.ZodOptional<z.ZodBoolean>;
563
+ }, z.core.$strip>>>;
532
564
  id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
533
565
  }, z.core.$strip>;
534
566
  export declare const publishEventSchema: z.ZodObject<{
@@ -6,6 +6,7 @@ const EventTimeline_schema_1 = require("./EventTimeline.schema");
6
6
  const EventWedding_schema_1 = require("./EventWedding.schema");
7
7
  const EventBirthday_schema_1 = require("./EventBirthday.schema");
8
8
  const EventOccasion_schema_1 = require("./EventOccasion.schema");
9
+ const EventHost_schema_1 = require("./EventHost.schema");
9
10
  const events_1 = require("../../constants/events");
10
11
  const EventTextPolicy_1 = require("../../constants/events/EventTextPolicy");
11
12
  const customizationText = zod_1.z
@@ -71,10 +72,16 @@ exports.eventBaseSchema = zod_1.z.object({
71
72
  venue_id: zod_1.z.coerce.number().int().positive().nullable().optional(),
72
73
  venue_name: zod_1.z.string().trim().max(255).nullable().optional(),
73
74
  location_label: zod_1.z.string().trim().nullable().optional(),
75
+ /** Expected number of guests; the app asks for it on every new event (old events may have none) */
76
+ expected_guests: zod_1.z.coerce.number().int().min(events_1.EVENT_EXPECTED_GUESTS_MIN).max(events_1.EVENT_EXPECTED_GUESTS_MAX).nullable().optional(),
74
77
  invitation_scan_type: zod_1.z.enum(events_1.EventInvitationScanTypeValues).optional(),
75
78
  customization: exports.eventCustomizationSchema.nullable().optional(),
76
79
  dress_code: exports.eventDressCodeSchema.nullable().optional(),
77
80
  visibility: zod_1.z.enum(events_1.EventVisibilityValues).optional(),
81
+ /** «من أنت في هذه المناسبة؟»: a slot key of the type, "company" or "other" */
82
+ creator_role: zod_1.z.string().trim().max(40).nullable().optional(),
83
+ /** Suggested people of the type (CONSTANTS.EVENTS.EventPeopleSlots) without their own field */
84
+ people: zod_1.z.array(EventHost_schema_1.eventPersonInputSchema).max(20).optional(),
78
85
  });
79
86
  exports.createEventSchema = exports.eventBaseSchema;
80
87
  exports.updateEventSchema = exports.eventBaseSchema.extend({
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ /** One suggested person in the create/edit form; an empty phone removes them */
3
+ export declare const eventPersonInputSchema: z.ZodObject<{
4
+ slot: z.ZodString;
5
+ phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
6
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
7
+ surprise: z.ZodOptional<z.ZodBoolean>;
8
+ }, z.core.$strip>;
9
+ /** Add someone to an event by hand */
10
+ export declare const addEventHostSchema: z.ZodObject<{
11
+ event_id: z.ZodCoercedNumber<unknown>;
12
+ phone: z.ZodString;
13
+ role: z.ZodEnum<{
14
+ organiser: "organiser";
15
+ guests: "guests";
16
+ }>;
17
+ label: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
18
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
19
+ surprise: z.ZodOptional<z.ZodBoolean>;
20
+ }, z.core.$strip>;
21
+ export declare const updateEventHostSchema: z.ZodObject<{
22
+ host_id: z.ZodCoercedNumber<unknown>;
23
+ role: z.ZodOptional<z.ZodEnum<{
24
+ organiser: "organiser";
25
+ guests: "guests";
26
+ }>>;
27
+ label: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
28
+ name: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
29
+ surprise: z.ZodOptional<z.ZodBoolean>;
30
+ }, z.core.$strip>;
31
+ export declare const eventHostIdSchema: z.ZodObject<{
32
+ host_id: z.ZodCoercedNumber<unknown>;
33
+ }, z.core.$strip>;
34
+ export declare const eventHostsListSchema: z.ZodObject<{
35
+ event_id: z.ZodCoercedNumber<unknown>;
36
+ }, z.core.$strip>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eventHostsListSchema = exports.eventHostIdSchema = exports.updateEventHostSchema = exports.addEventHostSchema = exports.eventPersonInputSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const Phone_schema_1 = require("../common/Phone.schema");
6
+ const EventHost_1 = require("../../constants/events/EventHost");
7
+ const hostName = zod_1.z
8
+ .string()
9
+ .trim()
10
+ .max(EventHost_1.EVENT_HOST_LABEL_MAX)
11
+ .transform((v) => (v.length ? v : null))
12
+ .nullable()
13
+ .optional();
14
+ /** One suggested person in the create/edit form; an empty phone removes them */
15
+ exports.eventPersonInputSchema = zod_1.z.object({
16
+ slot: zod_1.z.string().trim().min(1).max(40),
17
+ phone: Phone_schema_1.internationalPhoneSchema.nullable().optional(),
18
+ name: hostName,
19
+ /** Don't tell them or give access before the event ends */
20
+ surprise: zod_1.z.boolean().optional(),
21
+ });
22
+ /** Add someone to an event by hand */
23
+ exports.addEventHostSchema = zod_1.z.object({
24
+ event_id: zod_1.z.coerce.number().int().positive(),
25
+ phone: Phone_schema_1.internationalPhoneSchema,
26
+ role: zod_1.z.enum(EventHost_1.EventHostRoleValues),
27
+ /** e.g. «أم العروس» */
28
+ label: hostName,
29
+ name: hostName,
30
+ surprise: zod_1.z.boolean().optional(),
31
+ });
32
+ exports.updateEventHostSchema = zod_1.z.object({
33
+ host_id: zod_1.z.coerce.number().int().positive(),
34
+ role: zod_1.z.enum(EventHost_1.EventHostRoleValues).optional(),
35
+ label: hostName,
36
+ name: hostName,
37
+ surprise: zod_1.z.boolean().optional(),
38
+ });
39
+ exports.eventHostIdSchema = zod_1.z.object({
40
+ host_id: zod_1.z.coerce.number().int().positive(),
41
+ });
42
+ exports.eventHostsListSchema = zod_1.z.object({
43
+ event_id: zod_1.z.coerce.number().int().positive(),
44
+ });
@@ -6,3 +6,4 @@ export * from "./EventBirthday.schema";
6
6
  export * from "./EventOccasion.schema";
7
7
  export * from "./EventVenue.schema";
8
8
  export { internationalPhoneSchema } from "../common/Phone.schema";
9
+ export * from "./EventHost.schema";
@@ -24,3 +24,4 @@ __exportStar(require("./EventOccasion.schema"), exports);
24
24
  __exportStar(require("./EventVenue.schema"), exports);
25
25
  var Phone_schema_1 = require("../common/Phone.schema");
26
26
  Object.defineProperty(exports, "internationalPhoneSchema", { enumerable: true, get: function () { return Phone_schema_1.internationalPhoneSchema; } });
27
+ __exportStar(require("./EventHost.schema"), exports);
@@ -17,6 +17,12 @@ export declare enum NotificationTypeEnum {
17
17
  DEALLONY_NEWS = "deallony_news",
18
18
  /** A guest opened their personal invitation or replied to it */
19
19
  EVENT_GUESTS = "event_guests",
20
+ /** Someone added the user to an event (to accept) */
21
+ EVENT_HOST_ADDED = "event_host_added",
22
+ /** A person the owner added accepted and joined */
23
+ EVENT_HOST_JOINED = "event_host_joined",
24
+ /** The user no longer has access to an event */
25
+ EVENT_HOST_REMOVED = "event_host_removed",
20
26
  /** New ads match one of the user's saved searches */
21
27
  SAVED_SEARCH = "saved_search"
22
28
  }
@@ -51,6 +57,8 @@ export type INotificationData = {
51
57
  /** EVENT_GUESTS: the event and the guest it is about */
52
58
  event_id?: number;
53
59
  rsvp_id?: number;
60
+ /** EVENT_HOST_*: the host row it is about */
61
+ host_id?: number;
54
62
  /** SAVED_SEARCH: the saved search the new ads (ad_ids, count) match */
55
63
  saved_search_id?: number;
56
64
  };
@@ -20,6 +20,12 @@ var NotificationTypeEnum;
20
20
  NotificationTypeEnum["DEALLONY_NEWS"] = "deallony_news";
21
21
  /** A guest opened their personal invitation or replied to it */
22
22
  NotificationTypeEnum["EVENT_GUESTS"] = "event_guests";
23
+ /** Someone added the user to an event (to accept) */
24
+ NotificationTypeEnum["EVENT_HOST_ADDED"] = "event_host_added";
25
+ /** A person the owner added accepted and joined */
26
+ NotificationTypeEnum["EVENT_HOST_JOINED"] = "event_host_joined";
27
+ /** The user no longer has access to an event */
28
+ NotificationTypeEnum["EVENT_HOST_REMOVED"] = "event_host_removed";
23
29
  /** New ads match one of the user's saved searches */
24
30
  NotificationTypeEnum["SAVED_SEARCH"] = "saved_search";
25
31
  })(NotificationTypeEnum || (exports.NotificationTypeEnum = NotificationTypeEnum = {}));
@@ -44,5 +50,8 @@ exports.NOTIFICATION_CATEGORY = {
44
50
  [NotificationTypeEnum.POINTS_EARNED]: NotificationCategoryEnum.POINTS,
45
51
  [NotificationTypeEnum.DEALLONY_NEWS]: NotificationCategoryEnum.DEALLONY,
46
52
  [NotificationTypeEnum.EVENT_GUESTS]: NotificationCategoryEnum.EVENTS,
53
+ [NotificationTypeEnum.EVENT_HOST_ADDED]: NotificationCategoryEnum.EVENTS,
54
+ [NotificationTypeEnum.EVENT_HOST_JOINED]: NotificationCategoryEnum.EVENTS,
55
+ [NotificationTypeEnum.EVENT_HOST_REMOVED]: NotificationCategoryEnum.EVENTS,
47
56
  [NotificationTypeEnum.SAVED_SEARCH]: NotificationCategoryEnum.SAVED_SEARCHES,
48
57
  };
@@ -6,9 +6,11 @@ import type { IEventOccasion } from "./EventOccasion";
6
6
  import type { IEventVenue } from "./EventVenue";
7
7
  import type { IEventCustomization } from "./EventCustomization";
8
8
  import type { IEventDressCode } from "./EventDressCode";
9
+ import type { IEventHost } from "./EventHost";
9
10
  export type { IEventCustomization };
10
11
  import type { IUser } from "../user/User";
11
12
  import { EventInvitationScanType, EventVisibility } from "../../constants/events";
13
+ import type { EventAccessRole, EventPermission } from "../../constants/events/EventHost";
12
14
  export type IEvent = {
13
15
  id?: number;
14
16
  lat: number | null;
@@ -37,6 +39,8 @@ export type IEvent = {
37
39
  venue?: IEventVenue | null;
38
40
  venue_name?: string | null;
39
41
  location_label?: string | null;
42
+ /** Expected number of guests; null for events created before it was asked */
43
+ expected_guests?: number | null;
40
44
  user_id?: number | null;
41
45
  user?: IUser | null;
42
46
  payment_id?: number | null;
@@ -48,4 +52,24 @@ export type IEvent = {
48
52
  preview_url?: string | null;
49
53
  /** Couple's secret guest-management page; returned to the owner only */
50
54
  manage_url?: string | null;
55
+ /** Who created it: a slot key of the type (bride, groom, graduate...), "company" or "other"; null for old events */
56
+ creator_role?: string | null;
57
+ /** What the signed-in user is on this event (owner, organiser or guests); null when they have no access yet */
58
+ my_role?: EventAccessRole | null;
59
+ /** What the signed-in user may do (CONSTANTS.EVENTS.EventRolePermissions of my_role) */
60
+ my_permissions?: EventPermission[];
61
+ /** The signed-in user's own host row, when they are not the owner (pending rows included) */
62
+ my_host?: IEventHost | null;
63
+ /** The type's suggested people as the form shows them; only for those who manage people */
64
+ people?: {
65
+ slot: string;
66
+ phone: string | null;
67
+ name: string | null;
68
+ surprise: boolean;
69
+ status: string;
70
+ }[];
71
+ /** Owner's display name, for people the event was shared with */
72
+ owner_name?: string | null;
73
+ /** Who saved last (owner or a host), for «آخر تعديل» */
74
+ updated_by_user_id?: number | null;
51
75
  };
@@ -0,0 +1,29 @@
1
+ import type { EventHostRole, EventHostStatus } from "../../constants/events/EventHost";
2
+ /** Someone other than the creator who can open and manage an event in the app */
3
+ export type IEventHost = {
4
+ id: number;
5
+ event_id: number;
6
+ /** Normalized number; only people who can manage hosts get it */
7
+ phone?: string | null;
8
+ user_id?: number | null;
9
+ /** The account's name once linked */
10
+ user_name?: string | null;
11
+ role: EventHostRole;
12
+ /** Slot key of the event type (CONSTANTS.EVENTS.EventPeopleSlots) or "manual" */
13
+ source: string;
14
+ /** Free text for people added by hand (e.g. «أم العروس»); slots use their own label */
15
+ label: string | null;
16
+ /** Name typed by the owner */
17
+ name: string | null;
18
+ status: EventHostStatus;
19
+ /** Surprise: they get no notice or access until the event ends */
20
+ surprise: boolean;
21
+ /** Computed: when a surprise person gets access (the event's end); null when not hidden */
22
+ hidden_until?: string | Date | null;
23
+ /** Computed: still hidden from them now */
24
+ is_hidden?: boolean;
25
+ added_by_user_id: number | null;
26
+ linked_at: string | Date | null;
27
+ accepted_at: string | Date | null;
28
+ created_at?: string | Date;
29
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -6,3 +6,4 @@ export * from './EventBirthday';
6
6
  export * from './EventOccasion';
7
7
  export * from './EventCustomization';
8
8
  export * from "./EventDressCode";
9
+ export * from './EventHost';
@@ -22,3 +22,4 @@ __exportStar(require("./EventBirthday"), exports);
22
22
  __exportStar(require("./EventOccasion"), exports);
23
23
  __exportStar(require("./EventCustomization"), exports);
24
24
  __exportStar(require("./EventDressCode"), exports);
25
+ __exportStar(require("./EventHost"), exports);
@@ -45,6 +45,7 @@ export * as EventWedding from './events/EventWedding';
45
45
  export * as EventBirthday from './events/EventBirthday';
46
46
  export * as EventOccasion from './events/EventOccasion';
47
47
  export * as EventVenue from './events/EventVenue';
48
+ export * as EventHost from './events/EventHost';
48
49
  export * as User from "./user/User";
49
50
  export * as Role from "./user/Role";
50
51
  export * as Permission from "./user/Permission";
@@ -36,8 +36,8 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Permission = exports.Role = exports.User = exports.EventVenue = exports.EventOccasion = exports.EventBirthday = exports.EventWedding = exports.EventTimeline = exports.EventRsvp = exports.Event = exports.District = exports.City = exports.AdminBoundary = exports.WorkType = exports.UsedProductCondition = exports.UsedProductAge = exports.Unit = exports.Transmission = exports.RegionalSpec = exports.PublisherType = exports.PropertyStatus = exports.PriceType = exports.OfferType = exports.LicenseType = exports.FuelType = exports.Feature = exports.Facility = exports.Currency = exports.Color = exports.CategoryWorkType = exports.Category = exports.CarBody = exports.UsedProduct = exports.Service = exports.PropertySubType = exports.Property = exports.MotorTrim = exports.Motor = exports.AdPromotion = exports.Addresses = exports.AdDailyViews = exports.AdLocation = exports.Ad = exports.FilterParams = exports.ModuleFetchEndpoints = exports.Config = exports.AccessType = exports.Status = exports.Response = exports.LocalizedName = void 0;
40
- exports.Payment = exports.Analysis = exports.GeminiApiKey = exports.Google = exports.Kyc = exports.SocialMediaProfile = exports.SocialMediaPlatform = exports.Image = exports.Media = exports.File = exports.Notification = exports.NotificationToken = exports.MessageTemplate = exports.Device = exports.Chat = exports.AppStartScreen = exports.MetaData = exports.Company = exports.PotentialUser = exports.Loyalty = exports.SavedSearch = exports.Favorite = exports.Wallet = void 0;
39
+ exports.Role = exports.User = exports.EventHost = exports.EventVenue = exports.EventOccasion = exports.EventBirthday = exports.EventWedding = exports.EventTimeline = exports.EventRsvp = exports.Event = exports.District = exports.City = exports.AdminBoundary = exports.WorkType = exports.UsedProductCondition = exports.UsedProductAge = exports.Unit = exports.Transmission = exports.RegionalSpec = exports.PublisherType = exports.PropertyStatus = exports.PriceType = exports.OfferType = exports.LicenseType = exports.FuelType = exports.Feature = exports.Facility = exports.Currency = exports.Color = exports.CategoryWorkType = exports.Category = exports.CarBody = exports.UsedProduct = exports.Service = exports.PropertySubType = exports.Property = exports.MotorTrim = exports.Motor = exports.AdPromotion = exports.Addresses = exports.AdDailyViews = exports.AdLocation = exports.Ad = exports.FilterParams = exports.ModuleFetchEndpoints = exports.Config = exports.AccessType = exports.Status = exports.Response = exports.LocalizedName = void 0;
40
+ exports.Payment = exports.Analysis = exports.GeminiApiKey = exports.Google = exports.Kyc = exports.SocialMediaProfile = exports.SocialMediaPlatform = exports.Image = exports.Media = exports.File = exports.Notification = exports.NotificationToken = exports.MessageTemplate = exports.Device = exports.Chat = exports.AppStartScreen = exports.MetaData = exports.Company = exports.PotentialUser = exports.Loyalty = exports.SavedSearch = exports.Favorite = exports.Wallet = exports.Permission = void 0;
41
41
  // common
42
42
  exports.LocalizedName = __importStar(require("./common/LocalizedName"));
43
43
  exports.Response = __importStar(require("./common/Response"));
@@ -90,6 +90,7 @@ exports.EventWedding = __importStar(require("./events/EventWedding"));
90
90
  exports.EventBirthday = __importStar(require("./events/EventBirthday"));
91
91
  exports.EventOccasion = __importStar(require("./events/EventOccasion"));
92
92
  exports.EventVenue = __importStar(require("./events/EventVenue"));
93
+ exports.EventHost = __importStar(require("./events/EventHost"));
93
94
  // user
94
95
  exports.User = __importStar(require("./user/User"));
95
96
  exports.Role = __importStar(require("./user/Role"));
@@ -0,0 +1,3 @@
1
+ /** Limits of the expected guest count asked when creating an event */
2
+ export const EVENT_EXPECTED_GUESTS_MIN = 1;
3
+ export const EVENT_EXPECTED_GUESTS_MAX = 100000;
@@ -0,0 +1,147 @@
1
+ /** organiser: full control like the owner; guests: the event's guests only */
2
+ export const EventHostRoleValues = ["organiser", "guests"];
3
+ export const EventHostRole = {
4
+ ORGANISER: "organiser",
5
+ GUESTS: "guests",
6
+ };
7
+ export const EventHostRoleNames = {
8
+ organiser: { ar: "منظم", en: "Organiser" },
9
+ guests: { ar: "إدارة المدعوين", en: "Manages guests" },
10
+ };
11
+ export const EventHostRoleHints = {
12
+ organiser: { ar: "يدير المناسبة كاملة", en: "Manages the whole event" },
13
+ guests: { ar: "يدير المدعوين ويرسل الدعوات ويتابع الردود", en: "Manages guests, sends invitations and follows replies" },
14
+ };
15
+ export const EventHostRoleOptions = EventHostRoleValues.map((value) => ({
16
+ value,
17
+ name: EventHostRoleNames[value],
18
+ hint: EventHostRoleHints[value],
19
+ }));
20
+ /** pending: waiting for the person to sign in and accept; active: accepted */
21
+ export const EventHostStatusValues = ["pending", "active", "declined", "left", "removed"];
22
+ export const EventHostStatus = {
23
+ PENDING: "pending",
24
+ ACTIVE: "active",
25
+ DECLINED: "declined",
26
+ LEFT: "left",
27
+ REMOVED: "removed",
28
+ };
29
+ export const EventHostStatusNames = {
30
+ pending: { ar: "بانتظار القبول", en: "Waiting to accept" },
31
+ active: { ar: "انضم", en: "Joined" },
32
+ declined: { ar: "رفض", en: "Declined" },
33
+ left: { ar: "غادر", en: "Left" },
34
+ removed: { ar: "أزيل", en: "Removed" },
35
+ };
36
+ /** Statuses that still hold the number on the event (one row per number) */
37
+ export const EventHostOpenStatusValues = ["pending", "active"];
38
+ /** What the signed-in user is on an event */
39
+ export const EventAccessRoleValues = ["owner", "organiser", "guests"];
40
+ export const EventPermissionValues = [
41
+ /** See the event (drafts included) and its invitation link */
42
+ "event.view",
43
+ /** Details, names, date, timeline, dress code, design, photos, visibility, preview */
44
+ "event.edit",
45
+ /** Publish (pay), unpublish */
46
+ "event.publish",
47
+ "event.delete",
48
+ /** Guests: add, edit, remove, send links, read status, replies, check-in, guest reports */
49
+ "guests.manage",
50
+ /** Add/remove people of the event and change their role */
51
+ "hosts.manage",
52
+ ];
53
+ export const EventRolePermissions = {
54
+ owner: EventPermissionValues,
55
+ organiser: EventPermissionValues,
56
+ guests: ["event.view", "guests.manage"],
57
+ };
58
+ export const hasEventPermission = (role, permission) => !!role && EventRolePermissions[role].includes(permission);
59
+ export const EventAccessRoleNames = {
60
+ owner: { ar: "المالك", en: "Owner" },
61
+ organiser: EventHostRoleNames.organiser,
62
+ guests: EventHostRoleNames.guests,
63
+ };
64
+ const slot = (key, ar, en, role, extra = {}) => ({ key, label: { ar, en }, role, ...extra });
65
+ const organiser = (ar = "المنظم", en = "Organiser") => slot("organiser", ar, en, "organiser", { mirrors: "occasion.phone" });
66
+ const weddingPeople = [
67
+ slot("bride", "العروس", "Bride", "guests", { mirrors: "wedding.bride_phone" }),
68
+ slot("groom", "العريس", "Groom", "guests", { mirrors: "wedding.groom_phone" }),
69
+ slot("organiser", "المنظم", "Organiser", "organiser"),
70
+ ];
71
+ /**
72
+ * Suggested people of each event type. The person the event is for manages guests;
73
+ * whoever organises or invites gets full control. Anyone else is added by hand.
74
+ */
75
+ export const EventPeopleSlots = {
76
+ wedding: weddingPeople,
77
+ engagement: weddingPeople,
78
+ marriage: weddingPeople,
79
+ proposal: [
80
+ slot("proposer", "من سيطلب", "Who is proposing", "organiser"),
81
+ slot("partner", "الشريك", "Partner", "guests", { surprise_when_details_surprise: true }),
82
+ organiser(),
83
+ ],
84
+ bachelor: [slot("celebrant", "العروس أو العريس", "Bride or groom", "guests"), organiser()],
85
+ birthday: [slot("celebrant", "صاحب عيد الميلاد", "Birthday person", "guests"), organiser("من يدعو", "Who is inviting")],
86
+ newborn: [slot("mother", "الأم", "Mother", "organiser", { mirrors: "occasion.phone" }), slot("father", "الأب", "Father", "organiser")],
87
+ gender_reveal: [slot("mother", "الأم", "Mother", "organiser", { mirrors: "occasion.phone" }), slot("father", "الأب", "Father", "organiser")],
88
+ circumcision: [slot("mother", "الأم", "Mother", "organiser", { mirrors: "occasion.phone" }), slot("father", "الأب", "Father", "organiser")],
89
+ graduation: [slot("graduate", "الخريج", "Graduate", "guests"), organiser("من يدعو", "Who is inviting")],
90
+ hajj: [
91
+ slot("pilgrim", "الحاج أو الحاجة", "Pilgrim", "guests"),
92
+ slot("pilgrim2", "الحاج الثاني", "Second pilgrim", "guests"),
93
+ organiser("من يدعو", "Who is inviting"),
94
+ ],
95
+ mothers_day: [slot("mother", "الأم", "Mother", "guests"), organiser("من يدعو", "Who is inviting")],
96
+ valentine: [
97
+ slot("first", "الشخص الأول", "First person", "guests"),
98
+ slot("second", "الشخص الثاني", "Second person", "guests"),
99
+ organiser("من يدعو", "Who is inviting"),
100
+ ],
101
+ opening: [slot("shop_owner", "صاحب المحل", "Business owner", "organiser", { mirrors: "occasion.phone" })],
102
+ general: [organiser()],
103
+ };
104
+ const DEFAULT_PEOPLE = [organiser()];
105
+ /** Suggested people of a type (a type without its own list gets the organiser only) */
106
+ export const findEventPeopleSlots = (eventType) => (eventType && EventPeopleSlots[eventType]) || DEFAULT_PEOPLE;
107
+ export const findEventPeopleSlot = (eventType, key) => key ? findEventPeopleSlots(eventType).find((s) => s.key === key) : undefined;
108
+ /** Answers to «من أنت في هذه المناسبة؟» besides the type's slots */
109
+ export const EventCreatorRoleExtra = {
110
+ COMPANY: "company",
111
+ OTHER: "other",
112
+ };
113
+ export const EventCreatorRoleExtraNames = {
114
+ company: { ar: "شركة", en: "A company" },
115
+ other: { ar: "شخص آخر", en: "Someone else" },
116
+ };
117
+ /** Label of the person a host row (or the creator) is: slot label, or the free text */
118
+ export const eventPersonLabel = (eventType, slotKey, fallback) => {
119
+ const s = findEventPeopleSlot(eventType, slotKey);
120
+ if (s)
121
+ return s.label;
122
+ if (slotKey === "company" || slotKey === "other")
123
+ return EventCreatorRoleExtraNames[slotKey];
124
+ return fallback ? { ar: fallback, en: fallback } : null;
125
+ };
126
+ export const EVENT_HOST_LABEL_MAX = 60;
127
+ /**
128
+ * One spelling for a phone number so numbers typed differently still match:
129
+ * digits only with a leading +, 00 → +, and no 0 right after a known country code.
130
+ */
131
+ export const normalizeEventPhone = (value) => {
132
+ if (!value)
133
+ return null;
134
+ let digits = String(value).trim().replace(/[^\d+]/g, "");
135
+ if (digits.startsWith("00"))
136
+ digits = "+" + digits.slice(2);
137
+ if (!digits.startsWith("+"))
138
+ digits = "+" + digits.replace(/\+/g, "");
139
+ digits = "+" + digits.slice(1).replace(/\+/g, "");
140
+ for (const code of ["+963", "+971", "+965", "+961", "+90", "+966", "+962", "+974"]) {
141
+ if (digits.startsWith(code + "0")) {
142
+ digits = code + digits.slice(code.length + 1);
143
+ break;
144
+ }
145
+ }
146
+ return digits.length > 4 ? digits : null;
147
+ };
@@ -13,3 +13,5 @@ export * from "./EventVisibility.js";
13
13
  export * from "./EventWeddingReligion.js";
14
14
  export * from "./EventTextPolicy.js";
15
15
  export * from "./EventDressCode.js";
16
+ export * from "./EventHost.js";
17
+ export * from "./EventGuests.js";
@@ -208,5 +208,7 @@ export const APP_ROUTES = {
208
208
  publish: (id) => ({ pathname: "/event/publish/[id]", params: { id } }),
209
209
  edit: (id) => ({ pathname: "/event/new", params: { id } }),
210
210
  details: (id) => ({ pathname: "/event/details/[id]", params: { id } }),
211
+ /** People of the event (owner, organisers, guest managers) */
212
+ hosts: (id) => ({ pathname: "/event/hosts/[id]", params: { id } }),
211
213
  },
212
214
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -5,3 +5,4 @@ export * from './EventWedding.dto.js';
5
5
  export * from './EventBirthday.dto.js';
6
6
  export * from './EventOccasion.dto.js';
7
7
  export * from './EventVenue.dto.js';
8
+ export * from './EventHost.dto.js';
@@ -3,7 +3,8 @@ import { saveEventTimelineSchema } from "./EventTimeline.schema.js";
3
3
  import { saveEventWeddingSchema } from "./EventWedding.schema.js";
4
4
  import { saveEventBirthdaySchema } from "./EventBirthday.schema.js";
5
5
  import { saveEventOccasionSchema } from "./EventOccasion.schema.js";
6
- import { EVENT_CUSTOMIZATION_TEXT_MAX, EVENT_DRESS_CODE_NOTE_MAX, EventDressCodeColorModeValues, EventDressCodeColorValues, EventDressCodeStyleValues, EventCustomizationFontValues, EventCustomizationOpeningValues, EventCustomizationPaletteValues, EventCustomizationSectionValues, EventCustomizationTrackValues, EventInvitationScanTypeValues, EventPublishPaymentMethodValues, EventVisibilityValues, } from "../../constants/events/index.js";
6
+ import { eventPersonInputSchema } from "./EventHost.schema.js";
7
+ import { EVENT_CUSTOMIZATION_TEXT_MAX, EVENT_DRESS_CODE_NOTE_MAX, EVENT_EXPECTED_GUESTS_MAX, EVENT_EXPECTED_GUESTS_MIN, EventDressCodeColorModeValues, EventDressCodeColorValues, EventDressCodeStyleValues, EventCustomizationFontValues, EventCustomizationOpeningValues, EventCustomizationPaletteValues, EventCustomizationSectionValues, EventCustomizationTrackValues, EventInvitationScanTypeValues, EventPublishPaymentMethodValues, EventVisibilityValues, } from "../../constants/events/index.js";
7
8
  import { BLOCKED_RELIGIOUS_TEXT_MESSAGE, hasBlockedReligiousText } from "../../constants/events/EventTextPolicy.js";
8
9
  const customizationText = z
9
10
  .string()
@@ -68,10 +69,16 @@ export const eventBaseSchema = z.object({
68
69
  venue_id: z.coerce.number().int().positive().nullable().optional(),
69
70
  venue_name: z.string().trim().max(255).nullable().optional(),
70
71
  location_label: z.string().trim().nullable().optional(),
72
+ /** Expected number of guests; the app asks for it on every new event (old events may have none) */
73
+ expected_guests: z.coerce.number().int().min(EVENT_EXPECTED_GUESTS_MIN).max(EVENT_EXPECTED_GUESTS_MAX).nullable().optional(),
71
74
  invitation_scan_type: z.enum(EventInvitationScanTypeValues).optional(),
72
75
  customization: eventCustomizationSchema.nullable().optional(),
73
76
  dress_code: eventDressCodeSchema.nullable().optional(),
74
77
  visibility: z.enum(EventVisibilityValues).optional(),
78
+ /** «من أنت في هذه المناسبة؟»: a slot key of the type, "company" or "other" */
79
+ creator_role: z.string().trim().max(40).nullable().optional(),
80
+ /** Suggested people of the type (CONSTANTS.EVENTS.EventPeopleSlots) without their own field */
81
+ people: z.array(eventPersonInputSchema).max(20).optional(),
75
82
  });
76
83
  export const createEventSchema = eventBaseSchema;
77
84
  export const updateEventSchema = eventBaseSchema.extend({
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ import { internationalPhoneSchema } from "../common/Phone.schema.js";
3
+ import { EVENT_HOST_LABEL_MAX, EventHostRoleValues } from "../../constants/events/EventHost.js";
4
+ const hostName = z
5
+ .string()
6
+ .trim()
7
+ .max(EVENT_HOST_LABEL_MAX)
8
+ .transform((v) => (v.length ? v : null))
9
+ .nullable()
10
+ .optional();
11
+ /** One suggested person in the create/edit form; an empty phone removes them */
12
+ export const eventPersonInputSchema = z.object({
13
+ slot: z.string().trim().min(1).max(40),
14
+ phone: internationalPhoneSchema.nullable().optional(),
15
+ name: hostName,
16
+ /** Don't tell them or give access before the event ends */
17
+ surprise: z.boolean().optional(),
18
+ });
19
+ /** Add someone to an event by hand */
20
+ export const addEventHostSchema = z.object({
21
+ event_id: z.coerce.number().int().positive(),
22
+ phone: internationalPhoneSchema,
23
+ role: z.enum(EventHostRoleValues),
24
+ /** e.g. «أم العروس» */
25
+ label: hostName,
26
+ name: hostName,
27
+ surprise: z.boolean().optional(),
28
+ });
29
+ export const updateEventHostSchema = z.object({
30
+ host_id: z.coerce.number().int().positive(),
31
+ role: z.enum(EventHostRoleValues).optional(),
32
+ label: hostName,
33
+ name: hostName,
34
+ surprise: z.boolean().optional(),
35
+ });
36
+ export const eventHostIdSchema = z.object({
37
+ host_id: z.coerce.number().int().positive(),
38
+ });
39
+ export const eventHostsListSchema = z.object({
40
+ event_id: z.coerce.number().int().positive(),
41
+ });
@@ -6,3 +6,4 @@ export * from "./EventBirthday.schema.js";
6
6
  export * from "./EventOccasion.schema.js";
7
7
  export * from "./EventVenue.schema.js";
8
8
  export { internationalPhoneSchema } from "../common/Phone.schema.js";
9
+ export * from "./EventHost.schema.js";
@@ -17,6 +17,12 @@ export var NotificationTypeEnum;
17
17
  NotificationTypeEnum["DEALLONY_NEWS"] = "deallony_news";
18
18
  /** A guest opened their personal invitation or replied to it */
19
19
  NotificationTypeEnum["EVENT_GUESTS"] = "event_guests";
20
+ /** Someone added the user to an event (to accept) */
21
+ NotificationTypeEnum["EVENT_HOST_ADDED"] = "event_host_added";
22
+ /** A person the owner added accepted and joined */
23
+ NotificationTypeEnum["EVENT_HOST_JOINED"] = "event_host_joined";
24
+ /** The user no longer has access to an event */
25
+ NotificationTypeEnum["EVENT_HOST_REMOVED"] = "event_host_removed";
20
26
  /** New ads match one of the user's saved searches */
21
27
  NotificationTypeEnum["SAVED_SEARCH"] = "saved_search";
22
28
  })(NotificationTypeEnum || (NotificationTypeEnum = {}));
@@ -41,5 +47,8 @@ export const NOTIFICATION_CATEGORY = {
41
47
  [NotificationTypeEnum.POINTS_EARNED]: NotificationCategoryEnum.POINTS,
42
48
  [NotificationTypeEnum.DEALLONY_NEWS]: NotificationCategoryEnum.DEALLONY,
43
49
  [NotificationTypeEnum.EVENT_GUESTS]: NotificationCategoryEnum.EVENTS,
50
+ [NotificationTypeEnum.EVENT_HOST_ADDED]: NotificationCategoryEnum.EVENTS,
51
+ [NotificationTypeEnum.EVENT_HOST_JOINED]: NotificationCategoryEnum.EVENTS,
52
+ [NotificationTypeEnum.EVENT_HOST_REMOVED]: NotificationCategoryEnum.EVENTS,
44
53
  [NotificationTypeEnum.SAVED_SEARCH]: NotificationCategoryEnum.SAVED_SEARCHES,
45
54
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -6,3 +6,4 @@ export * from './EventBirthday.js';
6
6
  export * from './EventOccasion.js';
7
7
  export * from './EventCustomization.js';
8
8
  export * from "./EventDressCode.js";
9
+ export * from './EventHost.js';
@@ -53,6 +53,7 @@ export * as EventWedding from './events/EventWedding.js';
53
53
  export * as EventBirthday from './events/EventBirthday.js';
54
54
  export * as EventOccasion from './events/EventOccasion.js';
55
55
  export * as EventVenue from './events/EventVenue.js';
56
+ export * as EventHost from './events/EventHost.js';
56
57
  // user
57
58
  export * as User from "./user/User.js";
58
59
  export * as Role from "./user/Role.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deallony/shared",
3
- "version": "1.2.81",
3
+ "version": "1.2.82",
4
4
  "private": false,
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",