@deallony/shared 1.2.79 → 1.2.81

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 (104) hide show
  1. package/dist/cjs/constants/events/EventBirthday.d.ts +2 -0
  2. package/dist/cjs/constants/events/EventBirthday.js +12 -0
  3. package/dist/cjs/constants/events/EventCustomization.d.ts +90 -0
  4. package/dist/cjs/constants/events/EventCustomization.js +72 -0
  5. package/dist/cjs/constants/events/EventDressCode.d.ts +65 -0
  6. package/dist/cjs/constants/events/EventDressCode.js +56 -0
  7. package/dist/cjs/constants/events/EventOccasion.d.ts +63 -0
  8. package/dist/cjs/constants/events/EventOccasion.js +242 -0
  9. package/dist/cjs/constants/events/EventPreview.d.ts +9 -0
  10. package/dist/cjs/constants/events/EventPreview.js +11 -1
  11. package/dist/cjs/constants/events/EventTemplate.d.ts +518 -10
  12. package/dist/cjs/constants/events/EventTemplate.js +81 -11
  13. package/dist/cjs/constants/events/EventTextPolicy.d.ts +7 -0
  14. package/dist/cjs/constants/events/EventTextPolicy.js +66 -0
  15. package/dist/cjs/constants/events/EventType.d.ts +273 -8
  16. package/dist/cjs/constants/events/EventType.js +38 -2
  17. package/dist/cjs/constants/events/EventVisibility.d.ts +19 -0
  18. package/dist/cjs/constants/events/EventVisibility.js +18 -0
  19. package/dist/cjs/constants/events/EventWedding.js +3 -0
  20. package/dist/cjs/constants/events/EventWeddingReligion.d.ts +72 -0
  21. package/dist/cjs/constants/events/EventWeddingReligion.js +126 -0
  22. package/dist/cjs/constants/events/index.d.ts +7 -0
  23. package/dist/cjs/constants/events/index.js +7 -0
  24. package/dist/cjs/constants/payments/PaymentStatus.d.ts +1 -1
  25. package/dist/cjs/constants/routes/app.d.ts +42 -0
  26. package/dist/cjs/constants/routes/app.js +14 -0
  27. package/dist/cjs/dto/events/EventBirthday.dto.d.ts +3 -0
  28. package/dist/cjs/dto/events/EventBirthday.dto.js +2 -0
  29. package/dist/cjs/dto/events/EventOccasion.dto.d.ts +3 -0
  30. package/dist/cjs/dto/events/EventOccasion.dto.js +2 -0
  31. package/dist/cjs/dto/events/index.d.ts +2 -0
  32. package/dist/cjs/dto/events/index.js +2 -0
  33. package/dist/cjs/schema/communication/ai-conversation.schema.d.ts +156 -0
  34. package/dist/cjs/schema/communication/ai-conversation.schema.js +57 -1
  35. package/dist/cjs/schema/communication/support-ticket.schema.d.ts +136 -0
  36. package/dist/cjs/schema/communication/support-ticket.schema.js +47 -2
  37. package/dist/cjs/schema/events/Event.schema.d.ts +399 -0
  38. package/dist/cjs/schema/events/Event.schema.js +67 -1
  39. package/dist/cjs/schema/events/EventBirthday.schema.d.ts +19 -0
  40. package/dist/cjs/schema/events/EventBirthday.schema.js +17 -0
  41. package/dist/cjs/schema/events/EventOccasion.schema.d.ts +22 -0
  42. package/dist/cjs/schema/events/EventOccasion.schema.js +31 -0
  43. package/dist/cjs/schema/events/EventRsvp.schema.d.ts +4 -0
  44. package/dist/cjs/schema/events/EventRsvp.schema.js +1 -0
  45. package/dist/cjs/schema/events/EventTimeline.schema.d.ts +5 -0
  46. package/dist/cjs/schema/events/EventTimeline.schema.js +14 -1
  47. package/dist/cjs/schema/events/EventWedding.schema.d.ts +32 -0
  48. package/dist/cjs/schema/events/EventWedding.schema.js +11 -0
  49. package/dist/cjs/schema/events/index.d.ts +2 -0
  50. package/dist/cjs/schema/events/index.js +2 -0
  51. package/dist/cjs/types/communication/Notification.d.ts +13 -2
  52. package/dist/cjs/types/communication/Notification.js +8 -0
  53. package/dist/cjs/types/events/Event.d.ts +18 -1
  54. package/dist/cjs/types/events/EventBirthday.d.ts +17 -0
  55. package/dist/cjs/types/events/EventBirthday.js +2 -0
  56. package/dist/cjs/types/events/EventCustomization.d.ts +20 -0
  57. package/dist/cjs/types/events/EventCustomization.js +2 -0
  58. package/dist/cjs/types/events/EventDressCode.d.ts +13 -0
  59. package/dist/cjs/types/events/EventDressCode.js +2 -0
  60. package/dist/cjs/types/events/EventOccasion.d.ts +24 -0
  61. package/dist/cjs/types/events/EventOccasion.js +2 -0
  62. package/dist/cjs/types/events/EventRsvp.d.ts +2 -0
  63. package/dist/cjs/types/events/EventTimeline.d.ts +2 -0
  64. package/dist/cjs/types/events/EventWedding.d.ts +7 -0
  65. package/dist/cjs/types/events/index.d.ts +4 -0
  66. package/dist/cjs/types/events/index.js +4 -0
  67. package/dist/cjs/types/index.d.ts +3 -0
  68. package/dist/cjs/types/index.js +5 -2
  69. package/dist/cjs/types/user/SavedSearch.d.ts +38 -0
  70. package/dist/cjs/types/user/SavedSearch.js +11 -0
  71. package/dist/esm/constants/events/EventBirthday.js +9 -0
  72. package/dist/esm/constants/events/EventCustomization.js +65 -0
  73. package/dist/esm/constants/events/EventDressCode.js +49 -0
  74. package/dist/esm/constants/events/EventOccasion.js +235 -0
  75. package/dist/esm/constants/events/EventPreview.js +9 -0
  76. package/dist/esm/constants/events/EventTemplate.js +78 -10
  77. package/dist/esm/constants/events/EventTextPolicy.js +61 -0
  78. package/dist/esm/constants/events/EventType.js +34 -1
  79. package/dist/esm/constants/events/EventVisibility.js +15 -0
  80. package/dist/esm/constants/events/EventWedding.js +3 -0
  81. package/dist/esm/constants/events/EventWeddingReligion.js +118 -0
  82. package/dist/esm/constants/events/index.js +7 -0
  83. package/dist/esm/constants/routes/app.js +14 -0
  84. package/dist/esm/dto/events/EventBirthday.dto.js +1 -0
  85. package/dist/esm/dto/events/EventOccasion.dto.js +1 -0
  86. package/dist/esm/dto/events/index.js +2 -0
  87. package/dist/esm/schema/communication/ai-conversation.schema.js +56 -0
  88. package/dist/esm/schema/communication/support-ticket.schema.js +46 -1
  89. package/dist/esm/schema/events/Event.schema.js +67 -1
  90. package/dist/esm/schema/events/EventBirthday.schema.js +14 -0
  91. package/dist/esm/schema/events/EventOccasion.schema.js +28 -0
  92. package/dist/esm/schema/events/EventRsvp.schema.js +1 -0
  93. package/dist/esm/schema/events/EventTimeline.schema.js +14 -1
  94. package/dist/esm/schema/events/EventWedding.schema.js +11 -0
  95. package/dist/esm/schema/events/index.js +2 -0
  96. package/dist/esm/types/communication/Notification.js +8 -0
  97. package/dist/esm/types/events/EventBirthday.js +1 -0
  98. package/dist/esm/types/events/EventCustomization.js +1 -0
  99. package/dist/esm/types/events/EventDressCode.js +1 -0
  100. package/dist/esm/types/events/EventOccasion.js +1 -0
  101. package/dist/esm/types/events/index.js +4 -0
  102. package/dist/esm/types/index.js +3 -0
  103. package/dist/esm/types/user/SavedSearch.js +8 -0
  104. package/package.json +1 -1
@@ -1,7 +1,40 @@
1
+ /** Groups on the «ما نوع المناسبة؟» step */
2
+ export const EventTypeGroups = {
3
+ CELEBRATIONS: { value: 'celebrations', name: { en: 'Weddings & engagements', ar: 'أفراح' } },
4
+ FAMILY: { value: 'family', name: { en: 'Family & kids', ar: 'العائلة والأطفال' } },
5
+ MILESTONES: { value: 'milestones', name: { en: 'Milestones & occasions', ar: 'إنجازات ومناسبات' } },
6
+ BUSINESS: { value: 'business', name: { en: 'Business & general', ar: 'أعمال وعامة' } },
7
+ };
8
+ /**
9
+ * Every invitation type. `details` says where its details are stored:
10
+ * wedding = event.wedding (couple), occasion = event.occasion (generic, see EventOccasionTypeConfigs).
11
+ */
1
12
  export const EventTypes = {
2
- WEDDING: { value: 'wedding', name: { en: 'Wedding', ar: 'زفاف' } },
13
+ WEDDING: { value: 'wedding', name: { en: 'Wedding', ar: 'زفاف' }, group: 'celebrations', details: 'wedding' },
14
+ ENGAGEMENT: { value: 'engagement', name: { en: 'Engagement', ar: 'حفلة خطبة' }, group: 'celebrations', details: 'wedding' },
15
+ MARRIAGE: { value: 'marriage', name: { en: 'Marriage contract', ar: 'حفل زواج' }, group: 'celebrations', details: 'wedding' },
16
+ PROPOSAL: { value: 'proposal', name: { en: 'Proposal', ar: 'حفل Proposal' }, group: 'celebrations', details: 'occasion' },
17
+ BACHELOR: { value: 'bachelor', name: { en: 'Bachelor party', ar: 'حفل توديع عزوبية' }, group: 'celebrations', details: 'occasion' },
18
+ BIRTHDAY: { value: 'birthday', name: { en: 'Birthday', ar: 'عيد ميلاد' }, group: 'family', details: 'occasion' },
19
+ NEWBORN: { value: 'newborn', name: { en: 'New baby', ar: 'مباركة مولود' }, group: 'family', details: 'occasion' },
20
+ GENDER_REVEAL: { value: 'gender_reveal', name: { en: 'Gender reveal', ar: 'احتفال جنس المولود' }, group: 'family', details: 'occasion' },
21
+ CIRCUMCISION: { value: 'circumcision', name: { en: 'Circumcision', ar: 'حفل طهور' }, group: 'family', details: 'occasion' },
22
+ GRADUATION: { value: 'graduation', name: { en: 'Graduation', ar: 'حفل تخرج' }, group: 'milestones', details: 'occasion' },
23
+ HAJJ: { value: 'hajj', name: { en: 'Hajj return', ar: 'حفلة حج' }, group: 'milestones', details: 'occasion' },
24
+ MOTHERS_DAY: { value: 'mothers_day', name: { en: "Mother's Day", ar: 'حفل عيد الأم' }, group: 'milestones', details: 'occasion' },
25
+ VALENTINE: { value: 'valentine', name: { en: "Valentine's Day", ar: 'حفل لعيد الحب' }, group: 'milestones', details: 'occasion' },
26
+ OPENING: { value: 'opening', name: { en: 'Business opening', ar: 'افتتاح عمل تجاري' }, group: 'business', details: 'occasion' },
27
+ GENERAL: { value: 'general', name: { en: 'General party', ar: 'حفلة عامة' }, group: 'business', details: 'occasion' },
3
28
  };
29
+ export const EventTypeValues = Object.values(EventTypes).map((t) => t.value);
4
30
  export const EventTypeOptions = Object.values(EventTypes).map((type) => ({
5
31
  value: type.value,
6
32
  name: type.name,
33
+ group: type.group,
34
+ details: type.details,
7
35
  }));
36
+ export const findEventType = (value) => { var _a; return (_a = EventTypeOptions.find((t) => t.value === (value || 'wedding'))) !== null && _a !== void 0 ? _a : null; };
37
+ /** Types whose details are a couple stored in event.wedding (events without a type are weddings) */
38
+ export const isCoupleEventType = (value) => { var _a, _b; return ((_b = (_a = findEventType(value)) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : 'wedding') === 'wedding'; };
39
+ /** Types whose details are stored in event.occasion */
40
+ export const isOccasionEventType = (value) => { var _a; return ((_a = findEventType(value)) === null || _a === void 0 ? void 0 : _a.details) === 'occasion'; };
@@ -0,0 +1,15 @@
1
+ /** Who can find an event: private = only people with its link, public = can be listed and indexed */
2
+ export const EventVisibilityValues = ["private", "public"];
3
+ export const EventVisibility = {
4
+ PRIVATE: "private",
5
+ PUBLIC: "public",
6
+ };
7
+ export const DEFAULT_EVENT_VISIBILITY = "private";
8
+ export const EventVisibilityNames = {
9
+ private: { en: "Private", ar: "خاصة" },
10
+ public: { en: "Public", ar: "عامة" },
11
+ };
12
+ export const EventVisibilityOptions = EventVisibilityValues.map((value) => ({
13
+ value,
14
+ name: EventVisibilityNames[value],
15
+ }));
@@ -8,4 +8,7 @@ export const emptyEventWedding = {
8
8
  last_groom_name: null,
9
9
  bride_phone: null,
10
10
  groom_phone: null,
11
+ religion: 'muslim',
12
+ verse_key: null,
13
+ custom_verse: null,
11
14
  };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Invitation style of a wedding (also used by engagement and marriage, which share event.wedding).
3
+ * muslim = the Islamic opening, Quran verse and dua (the original templates).
4
+ * general = social congratulations only: names, place, date, program. Suits Christian weddings.
5
+ *
6
+ * Content rule (owner decision 2026-11-04): no Christian religious symbols, creeds, Bible verses or rites
7
+ * may appear in any invitation. The general style therefore carries no religious text at all.
8
+ */
9
+ export const EventWeddingReligionValues = ["muslim", "general"];
10
+ export const EventWeddingReligion = {
11
+ MUSLIM: "muslim",
12
+ GENERAL: "general",
13
+ };
14
+ export const DEFAULT_EVENT_WEDDING_RELIGION = "muslim";
15
+ export const EventWeddingReligionNames = {
16
+ muslim: {
17
+ en: "Islamic",
18
+ ar: "إسلامي",
19
+ hint_en: "Bismillah, a Quran verse and a dua.",
20
+ hint_ar: "البسملة وآية قرآنية ودعاء للعروسين.",
21
+ },
22
+ general: {
23
+ en: "General",
24
+ ar: "عام",
25
+ hint_en: "Social congratulations only, with no religious text.",
26
+ hint_ar: "تهنئة اجتماعية فقط بدون نصوص دينية.",
27
+ },
28
+ };
29
+ export const EventWeddingReligionOptions = EventWeddingReligionValues.map((value) => ({
30
+ value,
31
+ name: EventWeddingReligionNames[value],
32
+ }));
33
+ export const EventWeddingVerses = [
34
+ {
35
+ value: "rum-21",
36
+ religion: "muslim",
37
+ text: "﴿ وَمِنْ آيَاتِهِ أَنْ خَلَقَ لَكُمْ مِنْ أَنْفُسِكُمْ أَزْوَاجًا لِتَسْكُنُوا إِلَيْهَا وَجَعَلَ بَيْنَكُمْ مَوَدَّةً وَرَحْمَةً ﴾",
38
+ reference: "سورة الروم - آية ٢١",
39
+ label: { en: "Ar-Rum 21", ar: "سورة الروم - آية ٢١" },
40
+ },
41
+ {
42
+ value: "furqan-74",
43
+ religion: "muslim",
44
+ text: "﴿ رَبَّنَا هَبْ لَنَا مِنْ أَزْوَاجِنَا وَذُرِّيَّاتِنَا قُرَّةَ أَعْيُنٍ وَاجْعَلْنَا لِلْمُتَّقِينَ إِمَامًا ﴾",
45
+ reference: "سورة الفرقان - آية ٧٤",
46
+ label: { en: "Al-Furqan 74", ar: "سورة الفرقان - آية ٧٤" },
47
+ },
48
+ {
49
+ value: "rum-mawadda",
50
+ religion: "muslim",
51
+ text: "﴿ وَجَعَلَ بَيْنَكُم مَّوَدَّةً وَرَحْمَةً ﴾",
52
+ reference: "سورة الروم",
53
+ label: { en: "Mawadda wa rahma", ar: "وجعل بينكم مودة ورحمة" },
54
+ },
55
+ {
56
+ value: "together",
57
+ religion: "general",
58
+ text: "«معاً نبدأ أجمل حكايات العمر»",
59
+ reference: null,
60
+ label: { en: "Together we begin", ar: "معاً نبدأ أجمل حكايات العمر" },
61
+ },
62
+ {
63
+ value: "love-loyalty",
64
+ religion: "general",
65
+ text: "«بالحب والوفاء نبدأ رحلتنا، وبحضوركم تكتمل فرحتنا»",
66
+ reference: null,
67
+ label: { en: "Love and loyalty", ar: "بالحب والوفاء نبدأ رحلتنا" },
68
+ },
69
+ {
70
+ value: "night-of-life",
71
+ religion: "general",
72
+ text: "«ليلة العمر لا تكتمل إلا بوجود من نحب»",
73
+ reference: null,
74
+ label: { en: "Night of a lifetime", ar: "ليلة العمر لا تكتمل إلا بوجود من نحب" },
75
+ },
76
+ ];
77
+ /** General style only: the owner writes the couple's words by hand (event.wedding.custom_verse) */
78
+ export const EVENT_WEDDING_CUSTOM_VERSE = "custom";
79
+ export const EVENT_WEDDING_CUSTOM_VERSE_MAX = 160;
80
+ export const EventWeddingVerseValues = [
81
+ EVENT_WEDDING_CUSTOM_VERSE,
82
+ ...EventWeddingVerses.map((v) => v.value),
83
+ ];
84
+ /** The hand-written text applies only to the general style */
85
+ export const eventWeddingCustomVerse = (verseKey, religion, customVerse) => verseKey === EVENT_WEDDING_CUSTOM_VERSE && normalizeEventWeddingReligion(religion) === "general"
86
+ ? (customVerse === null || customVerse === void 0 ? void 0 : customVerse.trim()) || null
87
+ : null;
88
+ export const EventWeddingReligionTextsMap = {
89
+ muslim: {
90
+ opening: "بسم الله الرحمن الرحيم",
91
+ verse_title: "مقتطف قرآني مبارك",
92
+ blessing: "اللهم بارك لهما وبارك عليهما واجمع بينهما في خير، واجعل أيامهما مودة ورحمة وسكينة.",
93
+ blessing_title: "دعاء",
94
+ symbol: "۞",
95
+ contract_line: "على سنّة الله ورسوله",
96
+ },
97
+ general: {
98
+ opening: null,
99
+ verse_title: "كلمة افتتاحية",
100
+ blessing: "نتمنى لهما حياة مليئة بالحب والسعادة، وأياماً عامرة بالفرح والوفاق.",
101
+ blessing_title: "أمنية",
102
+ symbol: "⚭",
103
+ contract_line: null,
104
+ },
105
+ };
106
+ /** Ready-made program item offered for the general style */
107
+ export const EVENT_WEDDING_CEREMONY_TIMELINE_TITLE = "مراسم الزواج";
108
+ export const normalizeEventWeddingReligion = (value) => EventWeddingReligionValues.includes(value !== null && value !== void 0 ? value : "")
109
+ ? value
110
+ : DEFAULT_EVENT_WEDDING_RELIGION;
111
+ export const eventWeddingVersesFor = (religion) => EventWeddingVerses.filter((v) => v.religion === normalizeEventWeddingReligion(religion));
112
+ /** The saved verse when it belongs to the style, otherwise the style's first one */
113
+ export const findEventWeddingVerse = (value, religion) => {
114
+ var _a;
115
+ const list = eventWeddingVersesFor(religion);
116
+ return (_a = list.find((v) => v.value === value)) !== null && _a !== void 0 ? _a : list[0];
117
+ };
118
+ export const eventWeddingReligionTexts = (religion) => EventWeddingReligionTextsMap[normalizeEventWeddingReligion(religion)];
@@ -1,8 +1,15 @@
1
1
  export * from "./Event.js";
2
2
  export * from "./EventType.js";
3
3
  export * from "./EventWedding.js";
4
+ export * from "./EventBirthday.js";
5
+ export * from "./EventOccasion.js";
4
6
  export * from "./EventTemplate.js";
5
7
  export * from "./EventTimeline.js";
6
8
  export * from "./EventInvitationScanType.js";
7
9
  export * from "./EventPricing.js";
8
10
  export * from "./EventPreview.js";
11
+ export * from "./EventCustomization.js";
12
+ export * from "./EventVisibility.js";
13
+ export * from "./EventWeddingReligion.js";
14
+ export * from "./EventTextPolicy.js";
15
+ export * from "./EventDressCode.js";
@@ -7,6 +7,12 @@ export const APP_ROUTES = {
7
7
  list: () => ({ pathname: "/notifications" }),
8
8
  settings: () => ({ pathname: "/notifications/settings" }),
9
9
  },
10
+ savedSearches: {
11
+ /** The user's saved ad searches ("بحثي المحفوظ") */
12
+ list: () => ({ pathname: "/saved-searches" }),
13
+ /** Opens a saved search's results with its new ads marked, and marks them seen */
14
+ open: (id) => ({ pathname: "/saved-searches/[id]", params: { id: String(id) } }),
15
+ },
10
16
  favorites: {
11
17
  compare: (ad_ids) => ({
12
18
  pathname: "/favorites/compare",
@@ -181,6 +187,10 @@ export const APP_ROUTES = {
181
187
  details: (id) => ({ pathname: "/company/details/[id]", params: { id } }),
182
188
  /** Public profile with the company's ads */
183
189
  profile: (id) => ({ pathname: "/company/profile/[id]", params: { id } }),
190
+ /** Every ad published under the company, for its members */
191
+ ads: (id) => ({ pathname: "/company/ads/[id]", params: { id } }),
192
+ /** This week's most active companies (opened from the home strip) */
193
+ ranking: () => ({ pathname: "/company/ranking" }),
184
194
  },
185
195
  venue: {
186
196
  new: (companyId) => ({ pathname: "/venue/new", params: { company_id: companyId } }),
@@ -189,8 +199,12 @@ export const APP_ROUTES = {
189
199
  },
190
200
  event: {
191
201
  new: (venueId) => venueId ? { pathname: "/event/new", params: { venue_id: venueId } } : { pathname: "/event/new", params: { personal: "1" } },
202
+ /** A new personal event of one type (wedding, birthday), chosen on the start page */
203
+ newOfType: (eventType) => ({ pathname: "/event/new", params: { personal: "1", event_type: eventType } }),
192
204
  start: () => ({ pathname: "/event/start" }),
193
205
  mine: () => ({ pathname: "/event/mine" }),
206
+ /** Upcoming public events (optionally of one venue) */
207
+ upcoming: (venueId) => venueId ? { pathname: "/event/upcoming", params: { venue_id: venueId } } : { pathname: "/event/upcoming" },
194
208
  publish: (id) => ({ pathname: "/event/publish/[id]", params: { id } }),
195
209
  edit: (id) => ({ pathname: "/event/new", params: { id } }),
196
210
  details: (id) => ({ pathname: "/event/details/[id]", params: { id } }),
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -2,4 +2,6 @@ export * from './Event.dto.js';
2
2
  export * from './EventRsvp.dto.js';
3
3
  export * from './EventTimeline.dto.js';
4
4
  export * from './EventWedding.dto.js';
5
+ export * from './EventBirthday.dto.js';
6
+ export * from './EventOccasion.dto.js';
5
7
  export * from './EventVenue.dto.js';
@@ -38,6 +38,10 @@ export const PotentialUserConversationEventSchema = z.object({
38
38
  });
39
39
  export const AiConversationStatusSchema = z.enum([
40
40
  'starting',
41
+ /** The opening is written and waits for an admin to approve it */
42
+ 'awaiting_approval',
43
+ /** Waits for the sending hours to open: the opening, an approved draft or the AI's reply goes out then */
44
+ 'scheduled',
41
45
  'waiting_for_reply',
42
46
  'chatting',
43
47
  'interested',
@@ -52,6 +56,8 @@ export const AiConversationPurposeSchema = z.enum(['marketing', 'support']);
52
56
  /** Statuses of a conversation that is still open with the customer. */
53
57
  export const AI_CONVERSATION_ACTIVE_STATUSES = [
54
58
  'starting',
59
+ 'awaiting_approval',
60
+ 'scheduled',
55
61
  'waiting_for_reply',
56
62
  'chatting',
57
63
  'interested',
@@ -65,3 +71,53 @@ export const AiConversationListSchema = z.object({
65
71
  purpose: AiConversationPurposeSchema.optional(),
66
72
  search: z.string().trim().max(100).optional(),
67
73
  });
74
+ /* ---------- Admin approval before AI messages are sent ---------- */
75
+ /** One switch for all AI conversations: when on, AI messages wait for an admin */
76
+ export const AiApprovalSettingsSchema = z.object({
77
+ enabled: z.boolean(),
78
+ });
79
+ /**
80
+ * pending: waiting for an admin. scheduled: ready, goes out when the sending hours open (send_after).
81
+ * sent: approved and sent. rejected: thrown away by an admin.
82
+ * outdated: the customer wrote again, so a newer draft replaced it. failed: approved but WhatsApp refused it.
83
+ */
84
+ export const AiDraftStatusSchema = z.enum(['pending', 'scheduled', 'sent', 'rejected', 'outdated', 'failed']);
85
+ /** opening: first messages of a new chat. reply: answer to the customer. opt_out: goodbye after a stop request */
86
+ export const AiDraftKindSchema = z.enum(['opening', 'reply', 'opt_out']);
87
+ export const AiDraftListSchema = z.object({
88
+ page: z.coerce.number().int().positive().default(1),
89
+ limit: z.coerce.number().int().min(1).max(100).default(20),
90
+ statuses: z.array(AiDraftStatusSchema).max(5).optional(),
91
+ conversation_id: z.coerce.number().int().positive().optional(),
92
+ });
93
+ export const AiDraftDecisionSchema = z.object({
94
+ draft_id: z.coerce.number().int().positive(),
95
+ action: z.enum(['approve', 'reject']),
96
+ /** Edited messages to send instead of the AI's; only with approve */
97
+ replies: z.array(z.string().trim().min(1).max(4000)).min(1).max(4).optional(),
98
+ });
99
+ /** Sends a scheduled draft right away, ignoring the sending hours */
100
+ export const AiDraftSendNowSchema = z.object({
101
+ draft_id: z.coerce.number().int().positive(),
102
+ });
103
+ export const AiDraftApproveAllSchema = z.object({
104
+ /** Approve only these drafts; all pending drafts when left out */
105
+ draft_ids: z.array(z.coerce.number().int().positive()).max(100).optional(),
106
+ });
107
+ /* ---------- Sending hours: AI marketing messages go out only between start and end ---------- */
108
+ const ClockTimeSchema = z
109
+ .string()
110
+ .trim()
111
+ .regex(/^([01]\d|2[0-3]):[0-5]\d$/, 'Use HH:MM, 24-hour clock');
112
+ /**
113
+ * One setting for all AI marketing conversations. Times are on each customer's own clock,
114
+ * picked from the phone code (+963 Syria, +971 UAE); other numbers use default_timezone.
115
+ * end before start means the hours cross midnight (for example 20:00 to 02:00).
116
+ */
117
+ export const AiSendWindowSettingsSchema = z.object({
118
+ enabled: z.boolean(),
119
+ start: ClockTimeSchema,
120
+ end: ClockTimeSchema,
121
+ /** IANA time zone, for example Asia/Damascus */
122
+ default_timezone: z.string().trim().min(1).max(60),
123
+ });
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  export const SupportTicketStatusSchema = z.enum(['open', 'assigned', 'in_progress', 'waiting_customer', 'resolved', 'closed', 'reopened']);
3
3
  export const SupportTicketPrioritySchema = z.enum(['low', 'normal', 'high', 'urgent']);
4
- export const SupportTicketCategorySchema = z.enum(['technical_issue', 'account', 'advertisement', 'payment', 'complaint', 'other']);
4
+ export const SupportTicketCategorySchema = z.enum(['technical_issue', 'account', 'advertisement', 'payment', 'complaint', 'kyc', 'event', 'login', 'other']);
5
+ export const SupportTicketSourceSchema = z.enum(['admin', 'ai', 'app', 'guest', 'report']);
5
6
  export const SupportTicketCreateSchema = z.object({
6
7
  title: z.string().trim().min(4).max(180),
7
8
  description: z.string().trim().min(5).max(5000),
@@ -26,7 +27,51 @@ export const SupportTicketListSchema = z.object({
26
27
  status: SupportTicketStatusSchema.optional(),
27
28
  priority: SupportTicketPrioritySchema.optional(),
28
29
  search: z.string().trim().max(100).optional(),
30
+ source: SupportTicketSourceSchema.optional(),
31
+ screen: z.string().trim().max(40).optional(),
29
32
  });
30
33
  export const SupportTicketStatusUpdateSchema = SupportTicketIdSchema.extend({ status: SupportTicketStatusSchema });
31
34
  export const SupportTicketAssignmentSchema = SupportTicketIdSchema.extend({ assigned_to: z.number().int().positive() });
32
35
  export const SupportTicketReplySchema = SupportTicketIdSchema.extend({ message: z.string().trim().min(1).max(4000) });
36
+ // In-app "report a problem" button (create event, create ad, KYC, login)
37
+ export const SupportReportScreenSchema = z.enum(['event.new', 'ad.new', 'kyc', 'auth.login']);
38
+ export const SupportReportReasonSchema = z.enum(['blocked', 'error_message', 'slow', 'other']);
39
+ export const SupportReportAttachmentSchema = z.object({
40
+ url: z.string().trim().url().max(500),
41
+ id: z.number().int().positive().nullable().optional(),
42
+ mimetype: z.string().max(80).nullable().optional(),
43
+ width: z.number().nonnegative().nullable().optional(),
44
+ height: z.number().nonnegative().nullable().optional(),
45
+ });
46
+ export const SupportReportRequestLogSchema = z.object({
47
+ at: z.string(),
48
+ method: z.string().max(10),
49
+ path: z.string().max(300),
50
+ status: z.number().int().nullable(),
51
+ ms: z.number().nullable().optional(),
52
+ message: z.string().max(300).nullable().optional(),
53
+ });
54
+ export const SupportReportErrorLogSchema = z.object({
55
+ at: z.string(),
56
+ source: z.string().max(40),
57
+ message: z.string().max(500),
58
+ });
59
+ export const SupportReportContextSchema = z.object({
60
+ screen: z.object({ route: z.string().max(200), step: z.union([z.string(), z.number()]).nullable().optional(), ids: z.record(z.string(), z.union([z.string(), z.number(), z.null()])).optional(), error_fields: z.array(z.string()).optional() }).optional(),
61
+ device: z.record(z.string(), z.any()).optional(),
62
+ app: z.record(z.string(), z.any()).optional(),
63
+ requests: z.array(SupportReportRequestLogSchema).max(50).optional(),
64
+ errors: z.array(SupportReportErrorLogSchema).max(50).optional(),
65
+ }).passthrough();
66
+ export const SupportReportCreateSchema = z.object({
67
+ description: z.string().trim().min(5).max(5000),
68
+ screen: SupportReportScreenSchema,
69
+ reason: SupportReportReasonSchema.default('other'),
70
+ phone: z.string().trim().min(8).max(30).nullable().optional(),
71
+ device_id: z.string().trim().max(80).nullable().optional(),
72
+ platform: z.string().trim().max(20).nullable().optional(),
73
+ app_version: z.string().trim().max(60).nullable().optional(),
74
+ ad_id: z.number().int().positive().nullable().optional(),
75
+ context: SupportReportContextSchema.nullable().optional(),
76
+ attachments: z.array(SupportReportAttachmentSchema).max(3).optional(),
77
+ });
@@ -1,7 +1,55 @@
1
1
  import { z } from "zod";
2
2
  import { saveEventTimelineSchema } from "./EventTimeline.schema.js";
3
3
  import { saveEventWeddingSchema } from "./EventWedding.schema.js";
4
- import { EventInvitationScanTypeValues, EventPublishPaymentMethodValues, } from "../../constants/events/index.js";
4
+ import { saveEventBirthdaySchema } from "./EventBirthday.schema.js";
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";
7
+ import { BLOCKED_RELIGIOUS_TEXT_MESSAGE, hasBlockedReligiousText } from "../../constants/events/EventTextPolicy.js";
8
+ const customizationText = z
9
+ .string()
10
+ .trim()
11
+ .max(EVENT_CUSTOMIZATION_TEXT_MAX)
12
+ .refine((v) => !hasBlockedReligiousText(v), { message: BLOCKED_RELIGIOUS_TEXT_MESSAGE.ar })
13
+ .transform((v) => (v.length ? v : null))
14
+ .nullable()
15
+ .optional();
16
+ export const eventCustomizationSchema = z.object({
17
+ palette: z.enum(EventCustomizationPaletteValues).nullable().optional(),
18
+ font: z.enum(EventCustomizationFontValues).nullable().optional(),
19
+ sections: z
20
+ .partialRecord(z.enum(EventCustomizationSectionValues), z.boolean())
21
+ .nullable()
22
+ .optional(),
23
+ welcome_message: customizationText,
24
+ rsvp_message: customizationText,
25
+ music: z.enum(EventCustomizationTrackValues).nullable().optional(),
26
+ opening: z.enum(EventCustomizationOpeningValues).nullable().optional(),
27
+ auto_scroll: z.boolean().nullable().optional(),
28
+ });
29
+ export const eventDressCodeSchema = z.object({
30
+ style: z.enum(EventDressCodeStyleValues).nullable().optional(),
31
+ /** Colours guests are asked to wear (any number) */
32
+ colors: z
33
+ .array(z.enum(EventDressCodeColorValues))
34
+ .transform((v) => Array.from(new Set(v)))
35
+ .nullable()
36
+ .optional(),
37
+ /** Colours guests should not wear (any number) */
38
+ avoid_colors: z
39
+ .array(z.enum(EventDressCodeColorValues))
40
+ .transform((v) => Array.from(new Set(v)))
41
+ .nullable()
42
+ .optional(),
43
+ color_mode: z.enum(EventDressCodeColorModeValues).nullable().optional(),
44
+ note: z
45
+ .string()
46
+ .trim()
47
+ .max(EVENT_DRESS_CODE_NOTE_MAX)
48
+ .refine((v) => !hasBlockedReligiousText(v), { message: BLOCKED_RELIGIOUS_TEXT_MESSAGE.ar })
49
+ .transform((v) => (v.length ? v : null))
50
+ .nullable()
51
+ .optional(),
52
+ });
5
53
  export const eventBaseSchema = z.object({
6
54
  lat: z.coerce.number().optional().nullable(),
7
55
  long: z.coerce.number().optional().nullable(),
@@ -10,6 +58,10 @@ export const eventBaseSchema = z.object({
10
58
  is_published: z.boolean().optional(),
11
59
  slug: z.string().trim().min(1).max(255),
12
60
  wedding: saveEventWeddingSchema,
61
+ /** @deprecated birthdays are saved in `occasion` since 2026-11 */
62
+ birthday: saveEventBirthdaySchema.nullable().optional(),
63
+ /** Details of occasion types (birthday, graduation, newborn, ...) */
64
+ occasion: saveEventOccasionSchema.nullable().optional(),
13
65
  timeline: saveEventTimelineSchema,
14
66
  event_type: z.string().trim().max(255).nullable().optional(),
15
67
  event_template: z.string().trim().max(255).nullable().optional(),
@@ -17,6 +69,9 @@ export const eventBaseSchema = z.object({
17
69
  venue_name: z.string().trim().max(255).nullable().optional(),
18
70
  location_label: z.string().trim().nullable().optional(),
19
71
  invitation_scan_type: z.enum(EventInvitationScanTypeValues).optional(),
72
+ customization: eventCustomizationSchema.nullable().optional(),
73
+ dress_code: eventDressCodeSchema.nullable().optional(),
74
+ visibility: z.enum(EventVisibilityValues).optional(),
20
75
  });
21
76
  export const createEventSchema = eventBaseSchema;
22
77
  export const updateEventSchema = eventBaseSchema.extend({
@@ -35,7 +90,18 @@ export const eventIdSchema = z.object({
35
90
  });
36
91
  export const getEventsFilterSchema = z.object({
37
92
  is_published: z.boolean().optional(),
93
+ visibility: z.enum(EventVisibilityValues).optional(),
38
94
  search: z.string().trim().optional(),
39
95
  page: z.coerce.number().int().positive().optional(),
40
96
  limit: z.coerce.number().int().positive().max(100).optional(),
41
97
  });
98
+ export const setEventVisibilitySchema = z.object({
99
+ id: z.coerce.number().int().positive(),
100
+ visibility: z.enum(EventVisibilityValues),
101
+ });
102
+ export const publicEventsFilterSchema = z.object({
103
+ venue_id: z.coerce.number().int().positive().optional(),
104
+ company_id: z.coerce.number().int().positive().optional(),
105
+ page: z.coerce.number().int().positive().optional(),
106
+ limit: z.coerce.number().int().positive().max(50).optional(),
107
+ });
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ import { internationalPhoneSchema } from "../common/Phone.schema.js";
3
+ export const EVENT_BIRTHDAY_AGE_MAX = 120;
4
+ export const eventBirthdayBaseSchema = z.object({
5
+ event_id: z.coerce.number().int().positive().optional(),
6
+ cover: z.record(z.string(), z.any()).nullable().optional(),
7
+ celebrant_name: z.string().trim().max(255).nullable().optional(),
8
+ age: z.coerce.number().int().min(1).max(EVENT_BIRTHDAY_AGE_MAX).nullable().optional(),
9
+ host_name: z.string().trim().max(255).nullable().optional(),
10
+ phone: internationalPhoneSchema.nullable().optional(),
11
+ });
12
+ export const saveEventBirthdaySchema = eventBirthdayBaseSchema.extend({
13
+ id: z.coerce.number().int().positive().optional(),
14
+ });
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import { internationalPhoneSchema } from "../common/Phone.schema.js";
3
+ import { EVENT_OCCASION_LONG_TEXT_MAX, EVENT_OCCASION_TEXT_MAX } from "../../constants/events/EventOccasion.js";
4
+ import { EVENT_TEMPLATE_MAX_PHOTOS } from "../../constants/events/EventTemplate.js";
5
+ import { BLOCKED_RELIGIOUS_TEXT_MESSAGE, hasBlockedReligiousText } from "../../constants/events/EventTextPolicy.js";
6
+ const allowedText = (max) => z.string().trim().max(max).refine((v) => !hasBlockedReligiousText(v), { message: BLOCKED_RELIGIOUS_TEXT_MESSAGE.ar });
7
+ const detailValue = z.union([
8
+ allowedText(EVENT_OCCASION_LONG_TEXT_MAX),
9
+ z.number(),
10
+ z.boolean(),
11
+ z.null(),
12
+ ]);
13
+ export const eventOccasionBaseSchema = z.object({
14
+ event_id: z.coerce.number().int().positive().optional(),
15
+ primary_name: z.string().trim().max(EVENT_OCCASION_TEXT_MAX).nullable().optional(),
16
+ secondary_name: z.string().trim().max(EVENT_OCCASION_TEXT_MAX).nullable().optional(),
17
+ host_name: z.string().trim().max(EVENT_OCCASION_TEXT_MAX).nullable().optional(),
18
+ phone: internationalPhoneSchema.nullable().optional(),
19
+ cover: z.record(z.string(), z.any()).nullable().optional(),
20
+ /** Extra pictures after the cover, for designs with several photos */
21
+ /** By slot: an empty slot is null so the next pictures keep their place */
22
+ photos: z.array(z.record(z.string(), z.any()).nullable()).max(EVENT_TEMPLATE_MAX_PHOTOS - 1).nullable().optional(),
23
+ /** Keys come from the type's config; the API keeps only known keys */
24
+ details: z.record(z.string().max(64), detailValue).nullable().optional(),
25
+ });
26
+ export const saveEventOccasionSchema = eventOccasionBaseSchema.extend({
27
+ id: z.coerce.number().int().positive().optional(),
28
+ });
@@ -12,6 +12,7 @@ export const eventRsvpBaseSchema = z.object({
12
12
  attendance_status: z.enum(attendanceStatusValues),
13
13
  guest_count: z.coerce.number().int().positive().max(100).default(1),
14
14
  message: z.string().trim().max(5000).nullable().optional(),
15
+ answers: z.record(z.string().max(64), z.union([z.string().max(100), z.number(), z.boolean()])).nullable().optional(),
15
16
  responded_at: z.iso.datetime().optional(),
16
17
  });
17
18
  export const createEventRsvpSchema = eventRsvpBaseSchema;
@@ -1,7 +1,20 @@
1
1
  import { z } from 'zod';
2
+ import { BLOCKED_RELIGIOUS_TEXT_MESSAGE, hasBlockedReligiousText } from '../../constants/events/EventTextPolicy.js';
2
3
  export const timelinePayloadSchema = z.object({
3
- title: z.string().trim().min(1).max(255),
4
+ title: z
5
+ .string()
6
+ .trim()
7
+ .min(1)
8
+ .max(255)
9
+ .refine((v) => !hasBlockedReligiousText(v), { message: BLOCKED_RELIGIOUS_TEXT_MESSAGE.ar }),
4
10
  start_time: z.string().trim().regex(/^([01]\d|2[0-3]):([0-5]\d)$/),
11
+ place: z
12
+ .string()
13
+ .trim()
14
+ .max(255)
15
+ .refine((v) => !hasBlockedReligiousText(v), { message: BLOCKED_RELIGIOUS_TEXT_MESSAGE.ar })
16
+ .nullable()
17
+ .optional(),
5
18
  });
6
19
  export const eventTimelineBaseSchema = z.object({
7
20
  event_id: z.coerce.number().int().positive().optional(),
@@ -1,5 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { internationalPhoneSchema } from "../common/Phone.schema.js";
3
+ import { EVENT_WEDDING_CUSTOM_VERSE_MAX, EventWeddingReligionValues, EventWeddingVerseValues, } from "../../constants/events/EventWeddingReligion.js";
4
+ import { BLOCKED_RELIGIOUS_TEXT_MESSAGE, hasBlockedReligiousText } from "../../constants/events/EventTextPolicy.js";
3
5
  export const eventWeddingBaseSchema = z.object({
4
6
  event_id: z.coerce.number().int().positive().optional(),
5
7
  cover: z.record(z.string(), z.any()).nullable().optional(),
@@ -9,6 +11,15 @@ export const eventWeddingBaseSchema = z.object({
9
11
  last_groom_name: z.string().trim().max(255).nullable().optional(),
10
12
  bride_phone: internationalPhoneSchema.nullable().optional(),
11
13
  groom_phone: internationalPhoneSchema.nullable().optional(),
14
+ religion: z.enum(EventWeddingReligionValues).nullable().optional(),
15
+ verse_key: z.enum(EventWeddingVerseValues).nullable().optional(),
16
+ custom_verse: z
17
+ .string()
18
+ .trim()
19
+ .max(EVENT_WEDDING_CUSTOM_VERSE_MAX)
20
+ .refine((v) => !hasBlockedReligiousText(v), { message: BLOCKED_RELIGIOUS_TEXT_MESSAGE.ar })
21
+ .nullable()
22
+ .optional(),
12
23
  });
13
24
  export const createEventWeddingSchema = eventWeddingBaseSchema;
14
25
  export const updateEventWeddingSchema = eventWeddingBaseSchema.extend({
@@ -2,5 +2,7 @@ export * from "./Event.schema.js";
2
2
  export * from "./EventRsvp.schema.js";
3
3
  export * from "./EventTimeline.schema.js";
4
4
  export * from "./EventWedding.schema.js";
5
+ export * from "./EventBirthday.schema.js";
6
+ export * from "./EventOccasion.schema.js";
5
7
  export * from "./EventVenue.schema.js";
6
8
  export { internationalPhoneSchema } from "../common/Phone.schema.js";
@@ -15,6 +15,10 @@ export var NotificationTypeEnum;
15
15
  NotificationTypeEnum["POINTS_EARNED"] = "points_earned";
16
16
  /** Sent by Deallony from the dashboard */
17
17
  NotificationTypeEnum["DEALLONY_NEWS"] = "deallony_news";
18
+ /** A guest opened their personal invitation or replied to it */
19
+ NotificationTypeEnum["EVENT_GUESTS"] = "event_guests";
20
+ /** New ads match one of the user's saved searches */
21
+ NotificationTypeEnum["SAVED_SEARCH"] = "saved_search";
18
22
  })(NotificationTypeEnum || (NotificationTypeEnum = {}));
19
23
  /** How the centre groups types for its filter chips and settings */
20
24
  export var NotificationCategoryEnum;
@@ -23,6 +27,8 @@ export var NotificationCategoryEnum;
23
27
  NotificationCategoryEnum["MY_ADS"] = "my_ads";
24
28
  NotificationCategoryEnum["POINTS"] = "points";
25
29
  NotificationCategoryEnum["DEALLONY"] = "deallony";
30
+ NotificationCategoryEnum["EVENTS"] = "events";
31
+ NotificationCategoryEnum["SAVED_SEARCHES"] = "saved_searches";
26
32
  })(NotificationCategoryEnum || (NotificationCategoryEnum = {}));
27
33
  export const NOTIFICATION_CATEGORY = {
28
34
  [NotificationTypeEnum.FAVORITE_PRICE_DROP]: NotificationCategoryEnum.FAVORITES,
@@ -34,4 +40,6 @@ export const NOTIFICATION_CATEGORY = {
34
40
  [NotificationTypeEnum.AD_SAVED]: NotificationCategoryEnum.MY_ADS,
35
41
  [NotificationTypeEnum.POINTS_EARNED]: NotificationCategoryEnum.POINTS,
36
42
  [NotificationTypeEnum.DEALLONY_NEWS]: NotificationCategoryEnum.DEALLONY,
43
+ [NotificationTypeEnum.EVENT_GUESTS]: NotificationCategoryEnum.EVENTS,
44
+ [NotificationTypeEnum.SAVED_SEARCH]: NotificationCategoryEnum.SAVED_SEARCHES,
37
45
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};