@deallony/shared 1.2.79 → 1.2.80

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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Owner customization of an event's invitation page (colors, name font, sections, texts, music).
3
+ * Every field is optional: an empty value keeps the chosen template's own look.
4
+ */
5
+ export type EventCustomizationPalette = {
6
+ value: string;
7
+ name: {
8
+ en: string;
9
+ ar: string;
10
+ };
11
+ /** Page background */
12
+ background: string;
13
+ /** Cards and panels on top of the background */
14
+ surface: string;
15
+ /** Running text */
16
+ text: string;
17
+ /** Names, headings and buttons */
18
+ accent: string;
19
+ /** True when the background is dark, so templates pick their light-on-dark variants */
20
+ is_dark: boolean;
21
+ };
22
+ export declare const EventCustomizationPalettes: EventCustomizationPalette[];
23
+ export type EventCustomizationFont = {
24
+ value: string;
25
+ name: {
26
+ en: string;
27
+ ar: string;
28
+ };
29
+ /** CSS font-family, loaded from Google Fonts by the event site */
30
+ family: string;
31
+ /** Google Fonts css2 family parameter */
32
+ google: string;
33
+ };
34
+ export declare const EventCustomizationFonts: EventCustomizationFont[];
35
+ export type EventCustomizationTrack = {
36
+ value: string;
37
+ name: {
38
+ en: string;
39
+ ar: string;
40
+ };
41
+ /** Path on the event site */
42
+ src: string;
43
+ };
44
+ /** Music the owner can pick; add a track here once its file is on the event site under /music */
45
+ export declare const EventCustomizationTracks: EventCustomizationTrack[];
46
+ export declare const EventCustomizationSection: {
47
+ readonly COUNTDOWN: "countdown";
48
+ readonly TIMELINE: "timeline";
49
+ readonly MAP: "map";
50
+ readonly GALLERY: "gallery";
51
+ readonly RSVP: "rsvp";
52
+ readonly MUSIC: "music";
53
+ };
54
+ export type EventCustomizationSectionKey = (typeof EventCustomizationSection)[keyof typeof EventCustomizationSection];
55
+ export declare const EventCustomizationSectionOptions: {
56
+ value: EventCustomizationSectionKey;
57
+ name: {
58
+ en: string;
59
+ ar: string;
60
+ };
61
+ }[];
62
+ export declare const EVENT_CUSTOMIZATION_TEXT_MAX = 160;
63
+ export declare const EventCustomizationPaletteValues: [string, ...string[]];
64
+ export declare const EventCustomizationFontValues: [string, ...string[]];
65
+ export declare const EventCustomizationTrackValues: [string, ...string[]];
66
+ export declare const EventCustomizationSectionValues: [EventCustomizationSectionKey, ...EventCustomizationSectionKey[]];
67
+ export declare const findEventCustomizationPalette: (value: string | null | undefined) => EventCustomizationPalette | null;
68
+ export declare const findEventCustomizationFont: (value: string | null | undefined) => EventCustomizationFont | null;
69
+ export declare const findEventCustomizationTrack: (value: string | null | undefined) => EventCustomizationTrack | null;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * Owner customization of an event's invitation page (colors, name font, sections, texts, music).
4
+ * Every field is optional: an empty value keeps the chosen template's own look.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.findEventCustomizationTrack = exports.findEventCustomizationFont = exports.findEventCustomizationPalette = exports.EventCustomizationSectionValues = exports.EventCustomizationTrackValues = exports.EventCustomizationFontValues = exports.EventCustomizationPaletteValues = exports.EVENT_CUSTOMIZATION_TEXT_MAX = exports.EventCustomizationSectionOptions = exports.EventCustomizationSection = exports.EventCustomizationTracks = exports.EventCustomizationFonts = exports.EventCustomizationPalettes = void 0;
8
+ exports.EventCustomizationPalettes = [
9
+ { value: "night", name: { en: "Night", ar: "ليلي" }, background: "#0f0c18", surface: "#1b1628", text: "#efe8f6", accent: "#d6b36a", is_dark: true },
10
+ { value: "ivory", name: { en: "Ivory", ar: "عاجي" }, background: "#fbf6ee", surface: "#ffffff", text: "#2a2118", accent: "#9c6b3c", is_dark: false },
11
+ { value: "olive", name: { en: "Olive", ar: "زيتي" }, background: "#eef2ea", surface: "#f8faf5", text: "#1f2a1c", accent: "#4f6b3a", is_dark: false },
12
+ { value: "rose", name: { en: "Rose", ar: "وردي" }, background: "#fff4f5", surface: "#ffffff", text: "#3a1d24", accent: "#b0476a", is_dark: false },
13
+ { value: "ocean", name: { en: "Ocean", ar: "بحري" }, background: "#0d1b26", surface: "#132836", text: "#e3eef5", accent: "#7fc4d8", is_dark: true },
14
+ ];
15
+ exports.EventCustomizationFonts = [
16
+ { value: "amiri", name: { en: "Amiri", ar: "أميري" }, family: "Amiri", google: "Amiri:wght@400;700" },
17
+ { value: "aref-ruqaa", name: { en: "Ruqaa", ar: "رقعة" }, family: "Aref Ruqaa", google: "Aref+Ruqaa:wght@400;700" },
18
+ { value: "reem-kufi", name: { en: "Kufi", ar: "كوفي" }, family: "Reem Kufi", google: "Reem+Kufi:wght@400;600" },
19
+ { value: "tajawal", name: { en: "Tajawal", ar: "تجوال" }, family: "Tajawal", google: "Tajawal:wght@400;700" },
20
+ ];
21
+ /** Music the owner can pick; add a track here once its file is on the event site under /music */
22
+ exports.EventCustomizationTracks = [
23
+ { value: "wedding-theme", name: { en: "Wedding theme", ar: "لحن الزفاف" }, src: "/music/wedding-theme.mp3" },
24
+ ];
25
+ exports.EventCustomizationSection = {
26
+ COUNTDOWN: "countdown",
27
+ TIMELINE: "timeline",
28
+ MAP: "map",
29
+ GALLERY: "gallery",
30
+ RSVP: "rsvp",
31
+ MUSIC: "music",
32
+ };
33
+ exports.EventCustomizationSectionOptions = [
34
+ { value: "countdown", name: { en: "Countdown", ar: "العد التنازلي" } },
35
+ { value: "timeline", name: { en: "Program", ar: "برنامج الحفل" } },
36
+ { value: "map", name: { en: "Location", ar: "الموقع والخريطة" } },
37
+ { value: "gallery", name: { en: "Photos", ar: "الصور" } },
38
+ { value: "rsvp", name: { en: "RSVP", ar: "تأكيد الحضور" } },
39
+ { value: "music", name: { en: "Music", ar: "الموسيقى" } },
40
+ ];
41
+ exports.EVENT_CUSTOMIZATION_TEXT_MAX = 160;
42
+ exports.EventCustomizationPaletteValues = exports.EventCustomizationPalettes.map((p) => p.value);
43
+ exports.EventCustomizationFontValues = exports.EventCustomizationFonts.map((f) => f.value);
44
+ exports.EventCustomizationTrackValues = exports.EventCustomizationTracks.map((t) => t.value);
45
+ exports.EventCustomizationSectionValues = exports.EventCustomizationSectionOptions.map((s) => s.value);
46
+ const findEventCustomizationPalette = (value) => { var _a; return (_a = exports.EventCustomizationPalettes.find((p) => p.value === value)) !== null && _a !== void 0 ? _a : null; };
47
+ exports.findEventCustomizationPalette = findEventCustomizationPalette;
48
+ const findEventCustomizationFont = (value) => { var _a; return (_a = exports.EventCustomizationFonts.find((f) => f.value === value)) !== null && _a !== void 0 ? _a : null; };
49
+ exports.findEventCustomizationFont = findEventCustomizationFont;
50
+ const findEventCustomizationTrack = (value) => { var _a; return (_a = exports.EventCustomizationTracks.find((t) => t.value === value)) !== null && _a !== void 0 ? _a : null; };
51
+ exports.findEventCustomizationTrack = findEventCustomizationTrack;
@@ -1,7 +1,10 @@
1
+ import type { IEventCustomization } from "../../types/events/EventCustomization";
1
2
  /** Messages between the app (WebView / iframe) and the event site's /preview/:token page */
2
3
  export declare const EventPreviewMessageType: {
3
4
  readonly SET_TEMPLATE: "deallony:set-template";
4
5
  readonly READY: "deallony:preview-ready";
6
+ /** Live customization while the owner edits it, before it is saved */
7
+ readonly SET_CUSTOMIZATION: "deallony:set-customization";
5
8
  };
6
9
  export type EventPreviewMessage = {
7
10
  type: typeof EventPreviewMessageType.SET_TEMPLATE;
@@ -9,6 +12,9 @@ export type EventPreviewMessage = {
9
12
  } | {
10
13
  type: typeof EventPreviewMessageType.READY;
11
14
  template: string | null;
15
+ } | {
16
+ type: typeof EventPreviewMessageType.SET_CUSTOMIZATION;
17
+ customization: IEventCustomization | null;
12
18
  };
13
19
  /** Query parameter that picks the template on the preview page's first paint */
14
20
  export declare const EventPreviewTemplateParam = "template";
@@ -5,6 +5,8 @@ exports.DEFAULT_WEDDING_TEMPLATE = exports.EventPreviewTemplateParam = exports.E
5
5
  exports.EventPreviewMessageType = {
6
6
  SET_TEMPLATE: "deallony:set-template",
7
7
  READY: "deallony:preview-ready",
8
+ /** Live customization while the owner edits it, before it is saved */
9
+ SET_CUSTOMIZATION: "deallony:set-customization",
8
10
  };
9
11
  /** Query parameter that picks the template on the preview page's first paint */
10
12
  exports.EventPreviewTemplateParam = "template";
@@ -6,3 +6,4 @@ export * from "./EventTimeline";
6
6
  export * from "./EventInvitationScanType";
7
7
  export * from "./EventPricing";
8
8
  export * from "./EventPreview";
9
+ export * from "./EventCustomization";
@@ -22,3 +22,4 @@ __exportStar(require("./EventTimeline"), exports);
22
22
  __exportStar(require("./EventInvitationScanType"), exports);
23
23
  __exportStar(require("./EventPricing"), exports);
24
24
  __exportStar(require("./EventPreview"), exports);
25
+ __exportStar(require("./EventCustomization"), exports);
@@ -24,7 +24,7 @@ export declare const PaymentStatuses: {
24
24
  export declare const PaymentStatusValues: [string, ...string[]];
25
25
  export type PaymentStatus = (typeof PaymentStatusValues)[number];
26
26
  export declare const PaymentStatusOptions: {
27
- value: "pending" | "approved" | "rejected";
27
+ value: "pending" | "rejected" | "approved";
28
28
  name: {
29
29
  readonly en: "Pending";
30
30
  readonly ar: "قيد الانتظار";
@@ -58,6 +58,7 @@ export declare const PotentialUserConversationEventSchema: z.ZodObject<{
58
58
  }, z.core.$strip>;
59
59
  export declare const AiConversationStatusSchema: z.ZodEnum<{
60
60
  starting: "starting";
61
+ awaiting_approval: "awaiting_approval";
61
62
  waiting_for_reply: "waiting_for_reply";
62
63
  chatting: "chatting";
63
64
  interested: "interested";
@@ -79,6 +80,7 @@ export declare const AiConversationListSchema: z.ZodObject<{
79
80
  limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
80
81
  statuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
81
82
  starting: "starting";
83
+ awaiting_approval: "awaiting_approval";
82
84
  waiting_for_reply: "waiting_for_reply";
83
85
  chatting: "chatting";
84
86
  interested: "interested";
@@ -118,6 +120,8 @@ export type AiConversationListItem = {
118
120
  sender: 'user' | 'ai' | 'human';
119
121
  created_at: string;
120
122
  } | null;
123
+ /** AI drafts of this conversation waiting for an admin */
124
+ pending_draft_count?: number;
121
125
  /** Filled in by the marketplace from the potential user record. */
122
126
  potential_user?: {
123
127
  id: number;
@@ -134,8 +138,108 @@ export type AiConversationListResult = {
134
138
  /** Counts over all conversations matching purpose and search, not only this page. */
135
139
  status_counts: Record<AiConversationStatus, number>;
136
140
  active_count: number;
141
+ /** AI drafts waiting for an admin, over all conversations */
142
+ pending_approval_count?: number;
137
143
  };
138
144
  export type ConversationLanguage = z.infer<typeof ConversationLanguageSchema>;
139
145
  export type StartAiConversation = z.infer<typeof StartAiConversationSchema>;
140
146
  export type ConversationEventType = z.infer<typeof ConversationEventTypeSchema>;
141
147
  export type PotentialUserConversationEvent = z.infer<typeof PotentialUserConversationEventSchema>;
148
+ /** One switch for all AI conversations: when on, AI messages wait for an admin */
149
+ export declare const AiApprovalSettingsSchema: z.ZodObject<{
150
+ enabled: z.ZodBoolean;
151
+ }, z.core.$strip>;
152
+ /**
153
+ * pending: waiting for an admin. sent: approved and sent. rejected: thrown away by an admin.
154
+ * outdated: the customer wrote again, so a newer draft replaced it. failed: approved but WhatsApp refused it.
155
+ */
156
+ export declare const AiDraftStatusSchema: z.ZodEnum<{
157
+ failed: "failed";
158
+ pending: "pending";
159
+ sent: "sent";
160
+ rejected: "rejected";
161
+ outdated: "outdated";
162
+ }>;
163
+ /** opening: first messages of a new chat. reply: answer to the customer. opt_out: goodbye after a stop request */
164
+ export declare const AiDraftKindSchema: z.ZodEnum<{
165
+ opening: "opening";
166
+ reply: "reply";
167
+ opt_out: "opt_out";
168
+ }>;
169
+ export declare const AiDraftListSchema: z.ZodObject<{
170
+ page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
171
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
172
+ statuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
173
+ failed: "failed";
174
+ pending: "pending";
175
+ sent: "sent";
176
+ rejected: "rejected";
177
+ outdated: "outdated";
178
+ }>>>;
179
+ conversation_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
180
+ }, z.core.$strip>;
181
+ export declare const AiDraftDecisionSchema: z.ZodObject<{
182
+ draft_id: z.ZodCoercedNumber<unknown>;
183
+ action: z.ZodEnum<{
184
+ approve: "approve";
185
+ reject: "reject";
186
+ }>;
187
+ replies: z.ZodOptional<z.ZodArray<z.ZodString>>;
188
+ }, z.core.$strip>;
189
+ export declare const AiDraftApproveAllSchema: z.ZodObject<{
190
+ draft_ids: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
191
+ }, z.core.$strip>;
192
+ export type AiApprovalSettings = z.infer<typeof AiApprovalSettingsSchema>;
193
+ export type AiDraftStatus = z.infer<typeof AiDraftStatusSchema>;
194
+ export type AiDraftKind = z.infer<typeof AiDraftKindSchema>;
195
+ export type AiDraftList = z.infer<typeof AiDraftListSchema>;
196
+ export type AiDraftDecision = z.infer<typeof AiDraftDecisionSchema>;
197
+ export type AiDraftApproveAll = z.infer<typeof AiDraftApproveAllSchema>;
198
+ export type AiDraftItem = {
199
+ id: number;
200
+ conversation_id: number;
201
+ kind: AiDraftKind;
202
+ status: AiDraftStatus;
203
+ replies: string[];
204
+ /** Status change the AI asked for; applied only when the draft is sent */
205
+ event: ConversationEventType | null;
206
+ summary: string | null;
207
+ /** Support ticket the AI asked to open; opened only when the draft is sent */
208
+ ticket: {
209
+ title: string;
210
+ description: string;
211
+ category: string;
212
+ priority: string;
213
+ } | null;
214
+ error: string | null;
215
+ created_at: string;
216
+ decided_at: string | null;
217
+ conversation: {
218
+ id: number;
219
+ potential_user_id: number | null;
220
+ phone: string;
221
+ purpose: AiConversationPurpose;
222
+ subject_type: string;
223
+ status: AiConversationStatus;
224
+ /** Latest messages for context, oldest first */
225
+ recent_messages: Array<{
226
+ direction: 'inbound' | 'outbound';
227
+ sender: 'user' | 'ai' | 'human';
228
+ content: string;
229
+ created_at: string;
230
+ }>;
231
+ };
232
+ /** Filled in by the marketplace from the potential user record. */
233
+ potential_user?: {
234
+ id: number;
235
+ name: string | null;
236
+ } | null;
237
+ };
238
+ export type AiDraftListResult = {
239
+ items: AiDraftItem[];
240
+ page: number;
241
+ limit: number;
242
+ total: number;
243
+ total_pages: number;
244
+ pending_count: number;
245
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AiConversationListSchema = exports.AI_CONVERSATION_ACTIVE_STATUSES = exports.AiConversationPurposeSchema = exports.AiConversationStatusSchema = exports.PotentialUserConversationEventSchema = exports.ConversationEventTypeSchema = exports.StartAiConversationSchema = exports.ConversationLanguageSchema = exports.MarketingSubjectTypeSchema = void 0;
3
+ exports.AiDraftApproveAllSchema = exports.AiDraftDecisionSchema = exports.AiDraftListSchema = exports.AiDraftKindSchema = exports.AiDraftStatusSchema = exports.AiApprovalSettingsSchema = exports.AiConversationListSchema = exports.AI_CONVERSATION_ACTIVE_STATUSES = exports.AiConversationPurposeSchema = exports.AiConversationStatusSchema = exports.PotentialUserConversationEventSchema = exports.ConversationEventTypeSchema = exports.StartAiConversationSchema = exports.ConversationLanguageSchema = exports.MarketingSubjectTypeSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  exports.MarketingSubjectTypeSchema = zod_1.z.enum([
6
6
  'motors',
@@ -41,6 +41,8 @@ exports.PotentialUserConversationEventSchema = zod_1.z.object({
41
41
  });
42
42
  exports.AiConversationStatusSchema = zod_1.z.enum([
43
43
  'starting',
44
+ /** The opening is written and waits for an admin to approve it */
45
+ 'awaiting_approval',
44
46
  'waiting_for_reply',
45
47
  'chatting',
46
48
  'interested',
@@ -55,6 +57,7 @@ exports.AiConversationPurposeSchema = zod_1.z.enum(['marketing', 'support']);
55
57
  /** Statuses of a conversation that is still open with the customer. */
56
58
  exports.AI_CONVERSATION_ACTIVE_STATUSES = [
57
59
  'starting',
60
+ 'awaiting_approval',
58
61
  'waiting_for_reply',
59
62
  'chatting',
60
63
  'interested',
@@ -68,3 +71,31 @@ exports.AiConversationListSchema = zod_1.z.object({
68
71
  purpose: exports.AiConversationPurposeSchema.optional(),
69
72
  search: zod_1.z.string().trim().max(100).optional(),
70
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
+ exports.AiApprovalSettingsSchema = zod_1.z.object({
77
+ enabled: zod_1.z.boolean(),
78
+ });
79
+ /**
80
+ * pending: waiting for an admin. sent: approved and sent. rejected: thrown away by an admin.
81
+ * outdated: the customer wrote again, so a newer draft replaced it. failed: approved but WhatsApp refused it.
82
+ */
83
+ exports.AiDraftStatusSchema = zod_1.z.enum(['pending', 'sent', 'rejected', 'outdated', 'failed']);
84
+ /** opening: first messages of a new chat. reply: answer to the customer. opt_out: goodbye after a stop request */
85
+ exports.AiDraftKindSchema = zod_1.z.enum(['opening', 'reply', 'opt_out']);
86
+ exports.AiDraftListSchema = zod_1.z.object({
87
+ page: zod_1.z.coerce.number().int().positive().default(1),
88
+ limit: zod_1.z.coerce.number().int().min(1).max(100).default(20),
89
+ statuses: zod_1.z.array(exports.AiDraftStatusSchema).max(5).optional(),
90
+ conversation_id: zod_1.z.coerce.number().int().positive().optional(),
91
+ });
92
+ exports.AiDraftDecisionSchema = zod_1.z.object({
93
+ draft_id: zod_1.z.coerce.number().int().positive(),
94
+ action: zod_1.z.enum(['approve', 'reject']),
95
+ /** Edited messages to send instead of the AI's; only with approve */
96
+ replies: zod_1.z.array(zod_1.z.string().trim().min(1).max(4000)).min(1).max(4).optional(),
97
+ });
98
+ exports.AiDraftApproveAllSchema = zod_1.z.object({
99
+ /** Approve only these drafts; all pending drafts when left out */
100
+ draft_ids: zod_1.z.array(zod_1.z.coerce.number().int().positive()).max(100).optional(),
101
+ });
@@ -1,4 +1,25 @@
1
1
  import { z } from "zod";
2
+ export declare const eventCustomizationSchema: z.ZodObject<{
3
+ palette: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
4
+ [x: string]: string;
5
+ }>>>;
6
+ font: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
7
+ [x: string]: string;
8
+ }>>>;
9
+ sections: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodEnum<{
10
+ countdown: "countdown";
11
+ timeline: "timeline";
12
+ map: "map";
13
+ gallery: "gallery";
14
+ rsvp: "rsvp";
15
+ music: "music";
16
+ }> & z.core.$partial, z.ZodBoolean>>>;
17
+ welcome_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
18
+ rsvp_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
19
+ music: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
20
+ [x: string]: string;
21
+ }>>>;
22
+ }, z.core.$strip>;
2
23
  export declare const eventBaseSchema: z.ZodObject<{
3
24
  lat: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
4
25
  long: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -35,6 +56,27 @@ export declare const eventBaseSchema: z.ZodObject<{
35
56
  qrcode: "qrcode";
36
57
  barcode: "barcode";
37
58
  }>>;
59
+ customization: z.ZodOptional<z.ZodNullable<z.ZodObject<{
60
+ palette: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
61
+ [x: string]: string;
62
+ }>>>;
63
+ font: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
64
+ [x: string]: string;
65
+ }>>>;
66
+ sections: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodEnum<{
67
+ countdown: "countdown";
68
+ timeline: "timeline";
69
+ map: "map";
70
+ gallery: "gallery";
71
+ rsvp: "rsvp";
72
+ music: "music";
73
+ }> & z.core.$partial, z.ZodBoolean>>>;
74
+ welcome_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
75
+ rsvp_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
76
+ music: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
77
+ [x: string]: string;
78
+ }>>>;
79
+ }, z.core.$strip>>>;
38
80
  }, z.core.$strip>;
39
81
  export declare const createEventSchema: z.ZodObject<{
40
82
  lat: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -72,6 +114,27 @@ export declare const createEventSchema: z.ZodObject<{
72
114
  qrcode: "qrcode";
73
115
  barcode: "barcode";
74
116
  }>>;
117
+ customization: z.ZodOptional<z.ZodNullable<z.ZodObject<{
118
+ palette: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
119
+ [x: string]: string;
120
+ }>>>;
121
+ font: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
122
+ [x: string]: string;
123
+ }>>>;
124
+ sections: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodEnum<{
125
+ countdown: "countdown";
126
+ timeline: "timeline";
127
+ map: "map";
128
+ gallery: "gallery";
129
+ rsvp: "rsvp";
130
+ music: "music";
131
+ }> & z.core.$partial, z.ZodBoolean>>>;
132
+ welcome_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
133
+ rsvp_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
134
+ music: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
135
+ [x: string]: string;
136
+ }>>>;
137
+ }, z.core.$strip>>>;
75
138
  }, z.core.$strip>;
76
139
  export declare const updateEventSchema: z.ZodObject<{
77
140
  lat: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -109,6 +172,27 @@ export declare const updateEventSchema: z.ZodObject<{
109
172
  qrcode: "qrcode";
110
173
  barcode: "barcode";
111
174
  }>>;
175
+ customization: z.ZodOptional<z.ZodNullable<z.ZodObject<{
176
+ palette: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
177
+ [x: string]: string;
178
+ }>>>;
179
+ font: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
180
+ [x: string]: string;
181
+ }>>>;
182
+ sections: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodEnum<{
183
+ countdown: "countdown";
184
+ timeline: "timeline";
185
+ map: "map";
186
+ gallery: "gallery";
187
+ rsvp: "rsvp";
188
+ music: "music";
189
+ }> & z.core.$partial, z.ZodBoolean>>>;
190
+ welcome_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
191
+ rsvp_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
192
+ music: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
193
+ [x: string]: string;
194
+ }>>>;
195
+ }, z.core.$strip>>>;
112
196
  id: z.ZodCoercedNumber<unknown>;
113
197
  }, z.core.$strip>;
114
198
  export declare const saveEventSchema: z.ZodObject<{
@@ -147,6 +231,27 @@ export declare const saveEventSchema: z.ZodObject<{
147
231
  qrcode: "qrcode";
148
232
  barcode: "barcode";
149
233
  }>>;
234
+ customization: z.ZodOptional<z.ZodNullable<z.ZodObject<{
235
+ palette: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
236
+ [x: string]: string;
237
+ }>>>;
238
+ font: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
239
+ [x: string]: string;
240
+ }>>>;
241
+ sections: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodEnum<{
242
+ countdown: "countdown";
243
+ timeline: "timeline";
244
+ map: "map";
245
+ gallery: "gallery";
246
+ rsvp: "rsvp";
247
+ music: "music";
248
+ }> & z.core.$partial, z.ZodBoolean>>>;
249
+ welcome_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
250
+ rsvp_message: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string | null, string>>>>;
251
+ music: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
252
+ [x: string]: string;
253
+ }>>>;
254
+ }, z.core.$strip>>>;
150
255
  id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
151
256
  }, z.core.$strip>;
152
257
  export declare const publishEventSchema: z.ZodObject<{
@@ -1,10 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getEventsFilterSchema = exports.eventIdSchema = exports.publishEventSchema = exports.saveEventSchema = exports.updateEventSchema = exports.createEventSchema = exports.eventBaseSchema = void 0;
3
+ exports.getEventsFilterSchema = exports.eventIdSchema = exports.publishEventSchema = exports.saveEventSchema = exports.updateEventSchema = exports.createEventSchema = exports.eventBaseSchema = exports.eventCustomizationSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const EventTimeline_schema_1 = require("./EventTimeline.schema");
6
6
  const EventWedding_schema_1 = require("./EventWedding.schema");
7
7
  const events_1 = require("../../constants/events");
8
+ const customizationText = zod_1.z
9
+ .string()
10
+ .trim()
11
+ .max(events_1.EVENT_CUSTOMIZATION_TEXT_MAX)
12
+ .transform((v) => (v.length ? v : null))
13
+ .nullable()
14
+ .optional();
15
+ exports.eventCustomizationSchema = zod_1.z.object({
16
+ palette: zod_1.z.enum(events_1.EventCustomizationPaletteValues).nullable().optional(),
17
+ font: zod_1.z.enum(events_1.EventCustomizationFontValues).nullable().optional(),
18
+ sections: zod_1.z
19
+ .partialRecord(zod_1.z.enum(events_1.EventCustomizationSectionValues), zod_1.z.boolean())
20
+ .nullable()
21
+ .optional(),
22
+ welcome_message: customizationText,
23
+ rsvp_message: customizationText,
24
+ music: zod_1.z.enum(events_1.EventCustomizationTrackValues).nullable().optional(),
25
+ });
8
26
  exports.eventBaseSchema = zod_1.z.object({
9
27
  lat: zod_1.z.coerce.number().optional().nullable(),
10
28
  long: zod_1.z.coerce.number().optional().nullable(),
@@ -20,6 +38,7 @@ exports.eventBaseSchema = zod_1.z.object({
20
38
  venue_name: zod_1.z.string().trim().max(255).nullable().optional(),
21
39
  location_label: zod_1.z.string().trim().nullable().optional(),
22
40
  invitation_scan_type: zod_1.z.enum(events_1.EventInvitationScanTypeValues).optional(),
41
+ customization: exports.eventCustomizationSchema.nullable().optional(),
23
42
  });
24
43
  exports.createEventSchema = exports.eventBaseSchema;
25
44
  exports.updateEventSchema = exports.eventBaseSchema.extend({
@@ -2,6 +2,8 @@ import type { IEventTimeline } from "./EventTimeline";
2
2
  import type { IEventRsvp } from "./EventRsvp";
3
3
  import type { IEventWedding } from "./EventWedding";
4
4
  import type { IEventVenue } from "./EventVenue";
5
+ import type { IEventCustomization } from "./EventCustomization";
6
+ export type { IEventCustomization };
5
7
  import type { IUser } from "../user/User";
6
8
  import { EventInvitationScanType } from "../../constants/events";
7
9
  export type IEvent = {
@@ -15,6 +17,8 @@ export type IEvent = {
15
17
  event_template: string | null;
16
18
  slug: string;
17
19
  invitation_scan_type: EventInvitationScanType;
20
+ /** Owner customization of the invitation page; null keeps the template look */
21
+ customization?: IEventCustomization | null;
18
22
  wedding?: IEventWedding | null;
19
23
  timeline?: IEventTimeline | null;
20
24
  rsvps?: IEventRsvp[];
@@ -0,0 +1,16 @@
1
+ import type { EventCustomizationSectionKey } from "../../constants/events/EventCustomization";
2
+ /** Owner choices for the invitation page; anything left empty keeps the template's own look */
3
+ export type IEventCustomization = {
4
+ /** EventCustomizationPalettes value */
5
+ palette?: string | null;
6
+ /** EventCustomizationFonts value, used for names and headings */
7
+ font?: string | null;
8
+ /** Sections the owner hid or showed; a missing key means shown */
9
+ sections?: Partial<Record<EventCustomizationSectionKey, boolean>> | null;
10
+ /** Replaces the template's welcome line */
11
+ welcome_message?: string | null;
12
+ /** Replaces the template's RSVP invitation line */
13
+ rsvp_message?: string | null;
14
+ /** EventCustomizationTracks value */
15
+ music?: string | null;
16
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,3 +2,4 @@ export * from './Event';
2
2
  export * from './EventRsvp';
3
3
  export * from './EventTimeline';
4
4
  export * from './EventWedding';
5
+ export * from './EventCustomization';
@@ -18,3 +18,4 @@ __exportStar(require("./Event"), exports);
18
18
  __exportStar(require("./EventRsvp"), exports);
19
19
  __exportStar(require("./EventTimeline"), exports);
20
20
  __exportStar(require("./EventWedding"), exports);
21
+ __exportStar(require("./EventCustomization"), exports);
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Owner customization of an event's invitation page (colors, name font, sections, texts, music).
3
+ * Every field is optional: an empty value keeps the chosen template's own look.
4
+ */
5
+ export const EventCustomizationPalettes = [
6
+ { value: "night", name: { en: "Night", ar: "ليلي" }, background: "#0f0c18", surface: "#1b1628", text: "#efe8f6", accent: "#d6b36a", is_dark: true },
7
+ { value: "ivory", name: { en: "Ivory", ar: "عاجي" }, background: "#fbf6ee", surface: "#ffffff", text: "#2a2118", accent: "#9c6b3c", is_dark: false },
8
+ { value: "olive", name: { en: "Olive", ar: "زيتي" }, background: "#eef2ea", surface: "#f8faf5", text: "#1f2a1c", accent: "#4f6b3a", is_dark: false },
9
+ { value: "rose", name: { en: "Rose", ar: "وردي" }, background: "#fff4f5", surface: "#ffffff", text: "#3a1d24", accent: "#b0476a", is_dark: false },
10
+ { value: "ocean", name: { en: "Ocean", ar: "بحري" }, background: "#0d1b26", surface: "#132836", text: "#e3eef5", accent: "#7fc4d8", is_dark: true },
11
+ ];
12
+ export const EventCustomizationFonts = [
13
+ { value: "amiri", name: { en: "Amiri", ar: "أميري" }, family: "Amiri", google: "Amiri:wght@400;700" },
14
+ { value: "aref-ruqaa", name: { en: "Ruqaa", ar: "رقعة" }, family: "Aref Ruqaa", google: "Aref+Ruqaa:wght@400;700" },
15
+ { value: "reem-kufi", name: { en: "Kufi", ar: "كوفي" }, family: "Reem Kufi", google: "Reem+Kufi:wght@400;600" },
16
+ { value: "tajawal", name: { en: "Tajawal", ar: "تجوال" }, family: "Tajawal", google: "Tajawal:wght@400;700" },
17
+ ];
18
+ /** Music the owner can pick; add a track here once its file is on the event site under /music */
19
+ export const EventCustomizationTracks = [
20
+ { value: "wedding-theme", name: { en: "Wedding theme", ar: "لحن الزفاف" }, src: "/music/wedding-theme.mp3" },
21
+ ];
22
+ export const EventCustomizationSection = {
23
+ COUNTDOWN: "countdown",
24
+ TIMELINE: "timeline",
25
+ MAP: "map",
26
+ GALLERY: "gallery",
27
+ RSVP: "rsvp",
28
+ MUSIC: "music",
29
+ };
30
+ export const EventCustomizationSectionOptions = [
31
+ { value: "countdown", name: { en: "Countdown", ar: "العد التنازلي" } },
32
+ { value: "timeline", name: { en: "Program", ar: "برنامج الحفل" } },
33
+ { value: "map", name: { en: "Location", ar: "الموقع والخريطة" } },
34
+ { value: "gallery", name: { en: "Photos", ar: "الصور" } },
35
+ { value: "rsvp", name: { en: "RSVP", ar: "تأكيد الحضور" } },
36
+ { value: "music", name: { en: "Music", ar: "الموسيقى" } },
37
+ ];
38
+ export const EVENT_CUSTOMIZATION_TEXT_MAX = 160;
39
+ export const EventCustomizationPaletteValues = EventCustomizationPalettes.map((p) => p.value);
40
+ export const EventCustomizationFontValues = EventCustomizationFonts.map((f) => f.value);
41
+ export const EventCustomizationTrackValues = EventCustomizationTracks.map((t) => t.value);
42
+ export const EventCustomizationSectionValues = EventCustomizationSectionOptions.map((s) => s.value);
43
+ export const findEventCustomizationPalette = (value) => { var _a; return (_a = EventCustomizationPalettes.find((p) => p.value === value)) !== null && _a !== void 0 ? _a : null; };
44
+ export const findEventCustomizationFont = (value) => { var _a; return (_a = EventCustomizationFonts.find((f) => f.value === value)) !== null && _a !== void 0 ? _a : null; };
45
+ export const findEventCustomizationTrack = (value) => { var _a; return (_a = EventCustomizationTracks.find((t) => t.value === value)) !== null && _a !== void 0 ? _a : null; };
@@ -2,6 +2,8 @@
2
2
  export const EventPreviewMessageType = {
3
3
  SET_TEMPLATE: "deallony:set-template",
4
4
  READY: "deallony:preview-ready",
5
+ /** Live customization while the owner edits it, before it is saved */
6
+ SET_CUSTOMIZATION: "deallony:set-customization",
5
7
  };
6
8
  /** Query parameter that picks the template on the preview page's first paint */
7
9
  export const EventPreviewTemplateParam = "template";
@@ -6,3 +6,4 @@ export * from "./EventTimeline.js";
6
6
  export * from "./EventInvitationScanType.js";
7
7
  export * from "./EventPricing.js";
8
8
  export * from "./EventPreview.js";
9
+ export * from "./EventCustomization.js";
@@ -38,6 +38,8 @@ 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',
41
43
  'waiting_for_reply',
42
44
  'chatting',
43
45
  'interested',
@@ -52,6 +54,7 @@ export const AiConversationPurposeSchema = z.enum(['marketing', 'support']);
52
54
  /** Statuses of a conversation that is still open with the customer. */
53
55
  export const AI_CONVERSATION_ACTIVE_STATUSES = [
54
56
  'starting',
57
+ 'awaiting_approval',
55
58
  'waiting_for_reply',
56
59
  'chatting',
57
60
  'interested',
@@ -65,3 +68,31 @@ export const AiConversationListSchema = z.object({
65
68
  purpose: AiConversationPurposeSchema.optional(),
66
69
  search: z.string().trim().max(100).optional(),
67
70
  });
71
+ /* ---------- Admin approval before AI messages are sent ---------- */
72
+ /** One switch for all AI conversations: when on, AI messages wait for an admin */
73
+ export const AiApprovalSettingsSchema = z.object({
74
+ enabled: z.boolean(),
75
+ });
76
+ /**
77
+ * pending: waiting for an admin. sent: approved and sent. rejected: thrown away by an admin.
78
+ * outdated: the customer wrote again, so a newer draft replaced it. failed: approved but WhatsApp refused it.
79
+ */
80
+ export const AiDraftStatusSchema = z.enum(['pending', 'sent', 'rejected', 'outdated', 'failed']);
81
+ /** opening: first messages of a new chat. reply: answer to the customer. opt_out: goodbye after a stop request */
82
+ export const AiDraftKindSchema = z.enum(['opening', 'reply', 'opt_out']);
83
+ export const AiDraftListSchema = z.object({
84
+ page: z.coerce.number().int().positive().default(1),
85
+ limit: z.coerce.number().int().min(1).max(100).default(20),
86
+ statuses: z.array(AiDraftStatusSchema).max(5).optional(),
87
+ conversation_id: z.coerce.number().int().positive().optional(),
88
+ });
89
+ export const AiDraftDecisionSchema = z.object({
90
+ draft_id: z.coerce.number().int().positive(),
91
+ action: z.enum(['approve', 'reject']),
92
+ /** Edited messages to send instead of the AI's; only with approve */
93
+ replies: z.array(z.string().trim().min(1).max(4000)).min(1).max(4).optional(),
94
+ });
95
+ export const AiDraftApproveAllSchema = z.object({
96
+ /** Approve only these drafts; all pending drafts when left out */
97
+ draft_ids: z.array(z.coerce.number().int().positive()).max(100).optional(),
98
+ });
@@ -1,7 +1,25 @@
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 { EVENT_CUSTOMIZATION_TEXT_MAX, EventCustomizationFontValues, EventCustomizationPaletteValues, EventCustomizationSectionValues, EventCustomizationTrackValues, EventInvitationScanTypeValues, EventPublishPaymentMethodValues, } from "../../constants/events/index.js";
5
+ const customizationText = z
6
+ .string()
7
+ .trim()
8
+ .max(EVENT_CUSTOMIZATION_TEXT_MAX)
9
+ .transform((v) => (v.length ? v : null))
10
+ .nullable()
11
+ .optional();
12
+ export const eventCustomizationSchema = z.object({
13
+ palette: z.enum(EventCustomizationPaletteValues).nullable().optional(),
14
+ font: z.enum(EventCustomizationFontValues).nullable().optional(),
15
+ sections: z
16
+ .partialRecord(z.enum(EventCustomizationSectionValues), z.boolean())
17
+ .nullable()
18
+ .optional(),
19
+ welcome_message: customizationText,
20
+ rsvp_message: customizationText,
21
+ music: z.enum(EventCustomizationTrackValues).nullable().optional(),
22
+ });
5
23
  export const eventBaseSchema = z.object({
6
24
  lat: z.coerce.number().optional().nullable(),
7
25
  long: z.coerce.number().optional().nullable(),
@@ -17,6 +35,7 @@ export const eventBaseSchema = z.object({
17
35
  venue_name: z.string().trim().max(255).nullable().optional(),
18
36
  location_label: z.string().trim().nullable().optional(),
19
37
  invitation_scan_type: z.enum(EventInvitationScanTypeValues).optional(),
38
+ customization: eventCustomizationSchema.nullable().optional(),
20
39
  });
21
40
  export const createEventSchema = eventBaseSchema;
22
41
  export const updateEventSchema = eventBaseSchema.extend({
@@ -0,0 +1 @@
1
+ export {};
@@ -2,3 +2,4 @@ export * from './Event.js';
2
2
  export * from './EventRsvp.js';
3
3
  export * from './EventTimeline.js';
4
4
  export * from './EventWedding.js';
5
+ export * from './EventCustomization.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deallony/shared",
3
- "version": "1.2.79",
3
+ "version": "1.2.80",
4
4
  "private": false,
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",