ultimate-jekyll-manager 1.0.2 → 1.0.4

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 (30) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/CLAUDE.md +64 -1
  3. package/TODO.md +13 -0
  4. package/dist/assets/css/pages/admin/calendar/index.scss +212 -18
  5. package/dist/assets/js/pages/admin/calendar/calendar-core.js +535 -95
  6. package/dist/assets/js/pages/admin/calendar/calendar-events.js +631 -124
  7. package/dist/assets/js/pages/admin/calendar/calendar-renderer.js +238 -69
  8. package/dist/assets/js/pages/admin/calendar/campaign-preview.js +100 -0
  9. package/dist/assets/js/pages/admin/calendar/index.js +3 -16
  10. package/dist/assets/js/pages/contact/index.js +5 -1
  11. package/dist/defaults/dist/_includes/admin/sections/sidebar.json +0 -34
  12. package/dist/defaults/dist/_includes/admin/sections/topbar.json +0 -34
  13. package/dist/defaults/dist/_includes/themes/classy/backend/sections/topbar.html +1 -72
  14. package/dist/defaults/dist/_includes/themes/classy/frontend/sections/nav.html +7 -140
  15. package/dist/defaults/dist/_includes/themes/classy/global/sections/account.html +72 -0
  16. package/dist/defaults/dist/_layouts/blueprint/admin/calendar/index.html +442 -159
  17. package/dist/defaults/src/_includes/backend/sections/topbar.json +0 -34
  18. package/dist/defaults/src/_includes/frontend/sections/nav.json +0 -34
  19. package/dist/defaults/src/_includes/global/sections/account.json +36 -0
  20. package/package.json +2 -1
  21. package/dist/assets/js/pages/admin/notifications/index.js +0 -53
  22. package/dist/assets/js/pages/admin/notifications/new/index.js +0 -492
  23. package/dist/defaults/dist/_layouts/blueprint/admin/newsletters/index.html +0 -59
  24. package/dist/defaults/dist/_layouts/blueprint/admin/newsletters/new.html +0 -46
  25. package/dist/defaults/dist/_layouts/blueprint/admin/notifications/index.html +0 -103
  26. package/dist/defaults/dist/_layouts/blueprint/admin/notifications/new.html +0 -399
  27. package/dist/defaults/dist/pages/admin/newsletters/index.html +0 -7
  28. package/dist/defaults/dist/pages/admin/newsletters/new.html +0 -7
  29. package/dist/defaults/dist/pages/admin/notifications/index.html +0 -7
  30. package/dist/defaults/dist/pages/admin/notifications/new.html +0 -7
@@ -6,7 +6,7 @@ layout: themes/classy/admin/core/minimal-viewport-locked
6
6
  theme:
7
7
  header:
8
8
  title:
9
- content: "Calendar"
9
+ content: "Marketing Calendar"
10
10
  icon: "calendar-days"
11
11
  breadcrumbs:
12
12
  items:
@@ -14,11 +14,18 @@ theme:
14
14
  href: "/admin"
15
15
  - label: "Marketing"
16
16
  - label: "Calendar"
17
+ actions:
18
+ items:
19
+ - label: "Create Campaign"
20
+ icon: "plus"
21
+ color: "primary"
22
+ attributes:
23
+ - ["id", "btn-create-campaign"]
17
24
 
18
25
  ### REGULAR PAGES ###
19
26
  meta:
20
- title: "Calendar - Admin"
21
- description: "Marketing calendar for scheduling newsletters and notifications"
27
+ title: "Marketing Calendar - Admin"
28
+ description: "Schedule and manage marketing campaigns — emails and push notifications"
22
29
  breadcrumb: "Calendar"
23
30
 
24
31
  ### ICON PRE-RENDERING ###
@@ -28,7 +35,6 @@ prerender_icons:
28
35
  - name: "plus"
29
36
  - name: "pen"
30
37
  - name: "trash"
31
- - name: "grip-dots-vertical"
32
38
  - name: "clock"
33
39
  - name: "envelope"
34
40
  - name: "bell"
@@ -38,8 +44,22 @@ prerender_icons:
38
44
  - name: "calendar"
39
45
  - name: "grid-2"
40
46
  - name: "mobile"
41
- - name: "comment-sms"
42
- - name: "circle"
47
+ - name: "circle-check"
48
+ - name: "triangle-exclamation"
49
+ - name: "xmark"
50
+ - name: "rotate-right"
51
+ - name: "eye"
52
+ - name: "link"
53
+ - name: "tags"
54
+ - name: "users"
55
+ - name: "bullhorn"
56
+ - name: "code"
57
+ - name: "sliders"
58
+ - name: "arrow-up-right-from-square"
59
+ - name: "repeat"
60
+ - name: "wifi"
61
+ - name: "battery-full"
62
+ - name: "table-list"
43
63
  ---
44
64
 
45
65
  <!-- Calendar Root -->
@@ -51,241 +71,479 @@ prerender_icons:
51
71
  <div id="calendar-grid" class="flex-grow-1 overflow-hidden position-relative"></div>
52
72
  </div>
53
73
 
54
- <!-- Event Form Modal -->
55
- <div class="modal fade" id="calendar-event-modal" tabindex="-1" aria-labelledby="event-modal-title" aria-hidden="true">
74
+ <!-- Campaign Editor Modal -->
75
+ <div class="modal fade" id="campaign-editor-modal" tabindex="-1" aria-labelledby="campaign-modal-title" aria-hidden="true">
56
76
  <div class="modal-dialog modal-lg modal-dialog-scrollable">
57
77
  <div class="modal-content">
58
78
  <div class="modal-header">
59
- <h5 class="modal-title" id="event-modal-title">
79
+ <h5 class="modal-title" id="campaign-modal-title">
60
80
  {% uj_icon "plus", "fa-md me-2" %}
61
- <span id="event-modal-title-text">Create Event</span>
81
+ <span id="campaign-modal-title-text">Create Campaign</span>
62
82
  </h5>
63
83
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
64
84
  </div>
65
85
  <div class="modal-body">
66
- <form id="calendar-event-form" novalidate onsubmit="return false">
86
+ <form id="campaign-editor-form" novalidate onsubmit="return false">
67
87
 
68
- <!-- Event Type -->
88
+ <!-- Campaign Type -->
69
89
  <h6 class="mb-3">
70
- {% uj_icon "paper-plane", "fa-sm me-2 text-info" %}
71
- Event Type
90
+ {% uj_icon "bullhorn", "fa-sm me-2 text-info" %}
91
+ Campaign Type
72
92
  </h6>
73
93
  <div class="row mb-4">
74
94
  <div class="col-6">
75
- <input type="radio" class="btn-check" name="event.type" id="event-type-newsletter" value="newsletter" checked autocomplete="off">
76
- <label class="btn btn-outline-adaptive w-100 d-flex align-items-center justify-content-center" for="event-type-newsletter">
95
+ <input type="radio" class="btn-check" name="campaign.type" id="campaign-type-email" value="email" checked autocomplete="off">
96
+ <label class="btn btn-outline-adaptive w-100 d-flex align-items-center justify-content-center" for="campaign-type-email">
77
97
  {% uj_icon "envelope", "fa-sm me-2" %}
78
- Newsletter
98
+ Email
79
99
  </label>
80
100
  </div>
81
101
  <div class="col-6">
82
- <input type="radio" class="btn-check" name="event.type" id="event-type-notification" value="notification" autocomplete="off">
83
- <label class="btn btn-outline-adaptive w-100 d-flex align-items-center justify-content-center" for="event-type-notification">
102
+ <input type="radio" class="btn-check" name="campaign.type" id="campaign-type-push" value="push" autocomplete="off">
103
+ <label class="btn btn-outline-adaptive w-100 d-flex align-items-center justify-content-center" for="campaign-type-push">
84
104
  {% uj_icon "bell", "fa-sm me-2" %}
85
- Notification
105
+ Push Notification
86
106
  </label>
87
107
  </div>
88
108
  </div>
89
109
 
90
- <!-- Content -->
91
- <h6 class="mb-3">
92
- Content
93
- </h6>
110
+ <!-- Content: Edit / Preview tabs -->
111
+ <ul class="nav nav-tabs mb-3" id="campaign-content-tabs" role="tablist">
112
+ <li class="nav-item" role="presentation">
113
+ <button class="nav-link active" id="tab-edit" data-bs-toggle="tab" data-bs-target="#panel-edit" type="button" role="tab">
114
+ {% uj_icon "pen", "fa-sm me-1" %} Edit
115
+ </button>
116
+ </li>
117
+ <li class="nav-item" role="presentation">
118
+ <button class="nav-link" id="tab-preview" data-bs-toggle="tab" data-bs-target="#panel-preview" type="button" role="tab">
119
+ {% uj_icon "eye", "fa-sm me-1" %} Preview
120
+ </button>
121
+ </li>
122
+ </ul>
123
+
124
+ <div class="tab-content">
125
+ <!-- Edit Tab -->
126
+ <div class="tab-pane fade show active" id="panel-edit" role="tabpanel">
127
+ <div class="mb-3">
128
+ <label for="campaign-name" class="form-label">
129
+ Campaign Name <span class="text-danger">*</span>
130
+ </label>
131
+ <input type="text"
132
+ class="form-control"
133
+ id="campaign-name"
134
+ name="campaign.name"
135
+ placeholder="e.g. Spring Sale"
136
+ minlength="3"
137
+ maxlength="100"
138
+ required>
139
+ <div class="invalid-feedback">Campaign name is required (3-100 characters)</div>
140
+ </div>
94
141
 
142
+ <div class="mb-3">
143
+ <label for="campaign-subject" class="form-label">
144
+ Subject <span class="text-danger">*</span>
145
+ <small class="text-muted fw-normal ms-1" id="campaign-subject-hint">(email subject line)</small>
146
+ </label>
147
+ <input type="text"
148
+ class="form-control"
149
+ id="campaign-subject"
150
+ name="campaign.subject"
151
+ placeholder="e.g. 50% Off Premium!"
152
+ minlength="3"
153
+ maxlength="200"
154
+ required>
155
+ <div class="invalid-feedback">Subject is required</div>
156
+ </div>
157
+
158
+ <!-- Email-only fields -->
159
+ <div id="email-fields">
160
+ <div class="mb-3">
161
+ <label for="campaign-preheader" class="form-label">
162
+ Preheader
163
+ <small class="text-muted fw-normal ms-1">(preview text)</small>
164
+ </label>
165
+ <input type="text"
166
+ class="form-control"
167
+ id="campaign-preheader"
168
+ name="campaign.preheader"
169
+ placeholder="Brief preview text shown in inbox"
170
+ maxlength="200">
171
+ </div>
172
+
173
+ <div class="mb-3">
174
+ <label for="campaign-content" class="form-label">
175
+ Content
176
+ <small class="text-muted fw-normal ms-1">(markdown)</small>
177
+ </label>
178
+ <textarea class="form-control font-monospace"
179
+ id="campaign-content"
180
+ name="campaign.content"
181
+ rows="6"
182
+ placeholder="# Hello&#10;&#10;Check out our **sale**!"></textarea>
183
+ </div>
184
+
185
+ <div class="row mb-3">
186
+ <div class="col-md-6">
187
+ <label for="campaign-template" class="form-label">Template</label>
188
+ <select class="form-select" id="campaign-template" name="campaign.template">
189
+ <option value="default" selected>Default</option>
190
+ <option value="main/basic/card">Card</option>
191
+ <option value="main/basic/plain">Plain</option>
192
+ </select>
193
+ </div>
194
+ <div class="col-md-6">
195
+ <label for="campaign-sender" class="form-label">Sender</label>
196
+ <select class="form-select" id="campaign-sender" name="campaign.sender">
197
+ <option value="marketing" selected>Marketing</option>
198
+ <option value="newsletter">Newsletter</option>
199
+ <option value="hello">Hello</option>
200
+ <option value="orders">Orders</option>
201
+ </select>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <!-- Push-only fields -->
207
+ <div id="push-fields" class="d-none">
208
+ <div class="mb-3">
209
+ <label for="campaign-icon" class="form-label">
210
+ Icon URL
211
+ </label>
212
+ <input type="url"
213
+ class="form-control"
214
+ id="campaign-icon"
215
+ name="campaign.icon"
216
+ placeholder="https://example.com/icon.png"
217
+ pattern="https?://.+"
218
+ title="URL must start with http:// or https://">
219
+ <div class="invalid-feedback">Please provide a valid icon URL</div>
220
+ </div>
221
+
222
+ <div class="mb-3">
223
+ <label for="campaign-click-action" class="form-label">
224
+ Click Action URL
225
+ </label>
226
+ <input type="url"
227
+ class="form-control"
228
+ id="campaign-click-action"
229
+ name="campaign.clickAction"
230
+ placeholder="https://example.com/landing"
231
+ pattern="https?://.+"
232
+ title="URL must start with http:// or https://">
233
+ <div class="invalid-feedback">Please provide a valid URL</div>
234
+ </div>
235
+
236
+ <div class="mb-3">
237
+ <label for="campaign-push-tags" class="form-label">
238
+ {% uj_icon "tags", "fa-sm me-1 text-info" %}
239
+ Filter by Tags
240
+ <small class="text-muted fw-normal ms-1">(comma-separated)</small>
241
+ </label>
242
+ <input type="text"
243
+ class="form-control"
244
+ id="campaign-push-tags"
245
+ name="campaign.filters.tags"
246
+ placeholder="e.g. premium, active">
247
+ </div>
248
+
249
+ <div class="mb-3">
250
+ <label for="campaign-push-owner" class="form-label">
251
+ Owner UID
252
+ <small class="text-muted fw-normal ms-1">(optional, for testing)</small>
253
+ </label>
254
+ <input type="text"
255
+ class="form-control"
256
+ id="campaign-push-owner"
257
+ name="campaign.filters.owner"
258
+ placeholder="User UID for targeted test send">
259
+ </div>
260
+ </div>
261
+ </div>
262
+
263
+ <!-- Preview Tab -->
264
+ <div class="tab-pane fade" id="panel-preview" role="tabpanel">
265
+ <div id="campaign-preview-container">
266
+ <div class="text-center text-muted py-4">
267
+ <div class="spinner-border spinner-border-sm me-2" role="status"></div>
268
+ Loading preview...
269
+ </div>
270
+ </div>
271
+ </div>
272
+ </div>
273
+
274
+ <!-- Discount Code -->
95
275
  <div class="mb-3">
96
- <label for="event-title" class="form-label">
97
- Title <span class="text-danger">*</span>
276
+ <label for="campaign-discount-code" class="form-label">
277
+ Discount Code
278
+ <small class="text-muted fw-normal ms-1">(optional)</small>
98
279
  </label>
99
280
  <input type="text"
100
281
  class="form-control"
101
- id="event-title"
102
- name="event.title"
103
- placeholder="Enter event title"
104
- minlength="3"
105
- maxlength="100"
106
- required>
107
- <div class="invalid-feedback">Title is required (3-100 characters)</div>
282
+ id="campaign-discount-code"
283
+ name="campaign.discountCode"
284
+ placeholder="e.g. UPGRADE15">
108
285
  </div>
109
286
 
110
- <div class="mb-4">
111
- <label for="event-body" class="form-label">
112
- Body
287
+ <!-- Test Mode -->
288
+ <div class="form-check mb-4">
289
+ <input class="form-check-input"
290
+ type="checkbox"
291
+ id="campaign-test"
292
+ name="campaign.test">
293
+ <label class="form-check-label" for="campaign-test">
294
+ Test mode
113
295
  </label>
114
- <textarea class="form-control"
115
- id="event-body"
116
- name="event.data.body"
117
- rows="4"
118
- placeholder="Enter event content or description"></textarea>
296
+ <small class="d-block text-muted mt-1">
297
+ Send only to admin/test recipients instead of the full audience. Use this to preview the campaign before going live.
298
+ </small>
119
299
  </div>
120
300
 
121
301
  <!-- Schedule -->
122
- <h6 class="mb-3">
302
+ <h6 class="mb-3 mt-4">
123
303
  {% uj_icon "clock", "fa-sm me-2 text-info" %}
124
304
  Schedule
125
305
  </h6>
126
306
 
127
307
  <div class="row mb-3">
128
- <div class="col-md-4">
129
- <label for="event-date" class="form-label">
308
+ <div class="col-md-5">
309
+ <label for="campaign-date" class="form-label">
130
310
  Date <span class="text-danger">*</span>
131
311
  </label>
132
312
  <input type="date"
133
313
  class="form-control"
134
- id="event-date"
135
- name="event.date"
314
+ id="campaign-date"
315
+ name="campaign.date"
136
316
  required>
137
317
  <div class="invalid-feedback">Date is required</div>
138
318
  </div>
139
319
  <div class="col-md-4">
140
- <label for="event-time" class="form-label">
320
+ <label for="campaign-time" class="form-label">
141
321
  Time <span class="text-danger">*</span>
142
322
  </label>
143
323
  <input type="time"
144
324
  class="form-control"
145
- id="event-time"
146
- name="event.time"
325
+ id="campaign-time"
326
+ name="campaign.time"
147
327
  value="09:00"
148
328
  required>
149
329
  <div class="invalid-feedback">Time is required</div>
150
330
  </div>
151
- <div class="col-md-4">
152
- <label for="event-duration" class="form-label">
153
- Duration (min)
154
- </label>
155
- <input type="number"
156
- class="form-control"
157
- id="event-duration"
158
- name="event.duration"
159
- value="60"
160
- min="5"
161
- max="1440"
162
- step="5">
331
+ <div class="col-md-3 d-flex align-items-end">
332
+ <button type="button" class="btn btn-sm btn-outline-primary w-100" id="btn-send-now">
333
+ {% uj_icon "paper-plane", "fa-sm me-1" %}
334
+ Send Now
335
+ </button>
163
336
  </div>
164
337
  </div>
165
338
 
166
- <!-- Targeting -->
339
+ <!-- Recurrence -->
167
340
  <h6 class="mb-3 mt-4">
168
- {% uj_icon "users", "fa-sm me-2 text-success" %}
169
- Target Audience
341
+ {% uj_icon "repeat", "fa-sm me-2 text-info" %}
342
+ Recurrence
343
+ <small class="text-muted fw-normal ms-1">(optional)</small>
170
344
  </h6>
171
345
 
172
- <div class="mb-3">
173
- <div class="form-check">
174
- <input class="form-check-input"
175
- type="radio"
176
- name="event.data.audience.type"
177
- id="audience-all"
178
- value="all"
179
- checked>
180
- <label class="form-check-label" for="audience-all">
181
- All Users
182
- </label>
346
+ <div class="form-check mb-3">
347
+ <input class="form-check-input"
348
+ type="checkbox"
349
+ id="campaign-recurring"
350
+ name="campaign.recurring">
351
+ <label class="form-check-label" for="campaign-recurring">
352
+ Make this a recurring campaign
353
+ </label>
354
+ </div>
355
+
356
+ <div id="recurrence-fields" class="d-none">
357
+ <div class="row mb-3">
358
+ <div class="col-md-4">
359
+ <label for="campaign-recurrence-pattern" class="form-label">Pattern</label>
360
+ <select class="form-select" id="campaign-recurrence-pattern" name="campaign.recurrence.pattern">
361
+ <option value="daily">Daily</option>
362
+ <option value="weekly">Weekly</option>
363
+ <option value="monthly" selected>Monthly</option>
364
+ <option value="quarterly">Quarterly</option>
365
+ <option value="yearly">Yearly</option>
366
+ </select>
367
+ </div>
368
+ <div class="col-md-4">
369
+ <label for="campaign-recurrence-hour" class="form-label">Hour (UTC)</label>
370
+ <input type="number"
371
+ class="form-control"
372
+ id="campaign-recurrence-hour"
373
+ name="campaign.recurrence.hour"
374
+ value="14"
375
+ min="0"
376
+ max="23">
377
+ </div>
378
+ <div class="col-md-4">
379
+ <label for="campaign-recurrence-day" class="form-label">
380
+ Day
381
+ <small class="text-muted fw-normal ms-1" id="recurrence-day-hint">(of month)</small>
382
+ </label>
383
+ <input type="number"
384
+ class="form-control"
385
+ id="campaign-recurrence-day"
386
+ name="campaign.recurrence.day"
387
+ value="1"
388
+ min="0"
389
+ max="31">
390
+ </div>
183
391
  </div>
184
- <div class="form-check mt-2">
185
- <input class="form-check-input"
186
- type="radio"
187
- name="event.data.audience.type"
188
- id="audience-segment"
189
- value="segment">
190
- <label class="form-check-label" for="audience-segment">
191
- User Segment
192
- </label>
392
+
393
+ <div class="row mb-3 d-none" id="recurrence-month-row">
394
+ <div class="col-md-4">
395
+ <label for="campaign-recurrence-month" class="form-label">Month</label>
396
+ <select class="form-select" id="campaign-recurrence-month" name="campaign.recurrence.month">
397
+ <option value="1">January</option>
398
+ <option value="2">February</option>
399
+ <option value="3">March</option>
400
+ <option value="4">April</option>
401
+ <option value="5">May</option>
402
+ <option value="6">June</option>
403
+ <option value="7">July</option>
404
+ <option value="8">August</option>
405
+ <option value="9">September</option>
406
+ <option value="10">October</option>
407
+ <option value="11">November</option>
408
+ <option value="12">December</option>
409
+ </select>
410
+ </div>
193
411
  </div>
194
- <div class="form-check mt-2">
195
- <input class="form-check-input"
196
- type="radio"
197
- name="event.data.audience.type"
198
- id="audience-test"
199
- value="test">
200
- <label class="form-check-label" for="audience-test">
201
- Test Users Only
202
- </label>
412
+
413
+ <div class="alert alert-info small mb-3" id="recurrence-info">
414
+ This campaign will repeat automatically. Editing changes all future sends.
203
415
  </div>
204
416
  </div>
205
417
 
206
- <!-- Channels -->
418
+ <!-- Targeting -->
207
419
  <h6 class="mb-3 mt-4">
208
- Delivery Channels
420
+ {% uj_icon "users", "fa-sm me-2 text-success" %}
421
+ Targeting
209
422
  </h6>
210
423
 
211
- <div class="row mb-4">
212
- <div class="col-6 col-md-3">
213
- <div class="form-check">
214
- <input class="form-check-input"
215
- type="checkbox"
216
- id="channel-push"
217
- name="event.data.channels.push"
218
- checked>
219
- <label class="form-check-label" for="channel-push">
220
- {% uj_icon "mobile", "fa-sm me-1 text-primary" %}
221
- Push
222
- </label>
223
- </div>
224
- </div>
225
- <div class="col-6 col-md-3">
226
- <div class="form-check">
227
- <input class="form-check-input"
228
- type="checkbox"
229
- id="channel-email"
230
- name="event.data.channels.email">
231
- <label class="form-check-label" for="channel-email">
232
- {% uj_icon "envelope", "fa-sm me-1 text-info" %}
233
- Email
234
- </label>
235
- </div>
236
- </div>
237
- <div class="col-6 col-md-3">
424
+ <div class="mb-3" id="email-targeting">
425
+ <label class="form-label">Providers</label>
426
+ <small class="d-block text-muted mb-2">Which services to send through. Leave unchecked to use all enabled providers.</small>
427
+ <div class="d-flex gap-3">
238
428
  <div class="form-check">
239
429
  <input class="form-check-input"
240
430
  type="checkbox"
241
- id="channel-sms"
242
- name="event.data.channels.sms">
243
- <label class="form-check-label" for="channel-sms">
244
- {% uj_icon "comment-sms", "fa-sm me-1 text-success" %}
245
- SMS
431
+ id="campaign-provider-sendgrid"
432
+ name="campaign.providers.sendgrid">
433
+ <label class="form-check-label" for="campaign-provider-sendgrid">
434
+ SendGrid
246
435
  </label>
247
436
  </div>
248
- </div>
249
- <div class="col-6 col-md-3">
250
437
  <div class="form-check">
251
438
  <input class="form-check-input"
252
439
  type="checkbox"
253
- id="channel-inapp"
254
- name="event.data.channels.inapp">
255
- <label class="form-check-label" for="channel-inapp">
256
- {% uj_icon "bell", "fa-sm me-1 text-warning" %}
257
- In-App
440
+ id="campaign-provider-beehiiv"
441
+ name="campaign.providers.beehiiv">
442
+ <label class="form-check-label" for="campaign-provider-beehiiv">
443
+ Beehiiv
258
444
  </label>
259
445
  </div>
260
446
  </div>
261
447
  </div>
262
448
 
263
- <!-- Status & Color -->
264
- <h6 class="mb-3">
265
- Settings
449
+ <div class="form-check mb-2">
450
+ <input class="form-check-input"
451
+ type="checkbox"
452
+ id="campaign-all"
453
+ name="campaign.all">
454
+ <label class="form-check-label" for="campaign-all">
455
+ Target all contacts
456
+ <small class="text-muted fw-normal ms-1">(SendGrid only — overrides lists/segments)</small>
457
+ </label>
458
+ </div>
459
+
460
+ <div class="mb-3">
461
+ <label for="campaign-lists" class="form-label">
462
+ Lists
463
+ <small class="text-muted fw-normal ms-1">(SendGrid list IDs, comma-separated — empty = brand default list)</small>
464
+ </label>
465
+ <input type="text"
466
+ class="form-control"
467
+ id="campaign-lists"
468
+ name="campaign.lists"
469
+ placeholder="Leave empty for default brand list">
470
+ </div>
471
+
472
+ <div class="mb-3">
473
+ <label for="campaign-segments" class="form-label">
474
+ Segments
475
+ <small class="text-muted fw-normal ms-1">(comma-separated IDs)</small>
476
+ </label>
477
+ <input type="text"
478
+ class="form-control"
479
+ id="campaign-segments"
480
+ name="campaign.segments"
481
+ placeholder="e.g. seg_abc123, seg_def456">
482
+ </div>
483
+
484
+ <div class="mb-3">
485
+ <label for="campaign-exclude-segments" class="form-label">
486
+ Exclude Segments
487
+ <small class="text-muted fw-normal ms-1">(comma-separated IDs)</small>
488
+ </label>
489
+ <input type="text"
490
+ class="form-control"
491
+ id="campaign-exclude-segments"
492
+ name="campaign.excludeSegments"
493
+ placeholder="e.g. seg_unsubscribed">
494
+ </div>
495
+
496
+ <!-- Advanced Settings (collapsible) -->
497
+ <h6 class="mb-3 mt-4">
498
+ <a class="text-decoration-none" data-bs-toggle="collapse" href="#advanced-settings" role="button" aria-expanded="false">
499
+ {% uj_icon "sliders", "fa-sm me-2 text-info" %}
500
+ Advanced Settings
501
+ <small class="text-muted fw-normal ms-1">(optional)</small>
502
+ </a>
266
503
  </h6>
267
504
 
268
- <div class="row mb-3">
269
- <div class="col-md-6">
270
- <label for="event-status" class="form-label">Status</label>
271
- <select class="form-select" id="event-status" name="event.status">
272
- <option value="draft">Draft</option>
273
- <option value="scheduled">Scheduled</option>
274
- </select>
505
+ <div class="collapse" id="advanced-settings">
506
+ <div class="row mb-3">
507
+ <div class="col-md-6">
508
+ <label for="campaign-group" class="form-label">ASM Group</label>
509
+ <input type="text"
510
+ class="form-control"
511
+ id="campaign-group"
512
+ name="campaign.group"
513
+ placeholder="e.g. marketing">
514
+ </div>
515
+ <div class="col-md-6">
516
+ <label for="campaign-categories" class="form-label">
517
+ Categories
518
+ <small class="text-muted fw-normal ms-1">(comma-separated)</small>
519
+ </label>
520
+ <input type="text"
521
+ class="form-control"
522
+ id="campaign-categories"
523
+ name="campaign.categories"
524
+ placeholder="e.g. marketing, q1-launch">
525
+ </div>
275
526
  </div>
276
- <div class="col-md-6">
277
- <label class="form-label">Color</label>
278
- <div class="d-flex gap-2 flex-wrap align-items-center" id="color-swatches">
279
- <button type="button" class="color-swatch active" data-color="#4CAF50" style="background-color: #4CAF50;" title="Green"></button>
280
- <button type="button" class="color-swatch" data-color="#2196F3" style="background-color: #2196F3;" title="Blue"></button>
281
- <button type="button" class="color-swatch" data-color="#FF9800" style="background-color: #FF9800;" title="Orange"></button>
282
- <button type="button" class="color-swatch" data-color="#F44336" style="background-color: #F44336;" title="Red"></button>
283
- <button type="button" class="color-swatch" data-color="#9C27B0" style="background-color: #9C27B0;" title="Purple"></button>
284
- <button type="button" class="color-swatch" data-color="#00BCD4" style="background-color: #00BCD4;" title="Cyan"></button>
285
- <button type="button" class="color-swatch" data-color="#795548" style="background-color: #795548;" title="Brown"></button>
286
- <button type="button" class="color-swatch" data-color="#607D8B" style="background-color: #607D8B;" title="Gray"></button>
527
+
528
+ <div class="mb-3">
529
+ <label class="form-label">UTM Overrides</label>
530
+ <div class="row g-2" id="utm-fields">
531
+ <div class="col-md-6">
532
+ <input type="text" class="form-control form-control-sm" name="campaign.utm.utm_source" placeholder="utm_source">
533
+ </div>
534
+ <div class="col-md-6">
535
+ <input type="text" class="form-control form-control-sm" name="campaign.utm.utm_medium" placeholder="utm_medium">
536
+ </div>
537
+ <div class="col-md-6">
538
+ <input type="text" class="form-control form-control-sm" name="campaign.utm.utm_campaign" placeholder="utm_campaign">
539
+ </div>
540
+ <div class="col-md-6">
541
+ <input type="text" class="form-control form-control-sm" name="campaign.utm.utm_term" placeholder="utm_term">
542
+ </div>
543
+ <div class="col-md-6">
544
+ <input type="text" class="form-control form-control-sm" name="campaign.utm.utm_content" placeholder="utm_content">
545
+ </div>
287
546
  </div>
288
- <input type="hidden" name="event.color" id="event-color" value="#4CAF50">
289
547
  </div>
290
548
  </div>
291
549
 
@@ -293,19 +551,44 @@ prerender_icons:
293
551
  </div>
294
552
  <div class="modal-footer d-flex justify-content-between">
295
553
  <div>
296
- <button type="button" class="btn btn-outline-danger d-none" id="btn-delete-event">
554
+ <button type="button" class="btn btn-outline-danger d-none" id="btn-delete-campaign">
297
555
  {% uj_icon "trash", "fa-sm me-1" %}
298
556
  Delete
299
557
  </button>
300
558
  </div>
301
559
  <div class="d-flex gap-2">
302
560
  <button type="button" class="btn btn-outline-adaptive" data-bs-dismiss="modal">Cancel</button>
303
- <button type="submit" form="calendar-event-form" class="btn btn-adaptive">
561
+ <button type="submit" form="campaign-editor-form" class="btn btn-adaptive" id="btn-save-campaign">
304
562
  {% uj_icon "paper-plane", "fa-sm me-1" %}
305
- <span class="button-text">Save Event</span>
563
+ <span class="button-text">Save Campaign</span>
306
564
  </button>
307
565
  </div>
308
566
  </div>
309
567
  </div>
310
568
  </div>
311
569
  </div>
570
+
571
+ <!-- Campaign Results Modal (read-only for sent/failed) -->
572
+ <div class="modal fade" id="campaign-results-modal" tabindex="-1" aria-labelledby="campaign-results-title" aria-hidden="true">
573
+ <div class="modal-dialog modal-lg modal-dialog-scrollable">
574
+ <div class="modal-content">
575
+ <div class="modal-header">
576
+ <h5 class="modal-title" id="campaign-results-title">
577
+ {% uj_icon "eye", "fa-md me-2" %}
578
+ <span id="campaign-results-title-text">Campaign Details</span>
579
+ </h5>
580
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
581
+ </div>
582
+ <div class="modal-body" id="campaign-results-body">
583
+ <!-- Populated dynamically -->
584
+ </div>
585
+ <div class="modal-footer">
586
+ <button type="button" class="btn btn-outline-danger d-none" id="btn-retry-campaign">
587
+ {% uj_icon "rotate-right", "fa-sm me-1" %}
588
+ Retry (Create New)
589
+ </button>
590
+ <button type="button" class="btn btn-outline-adaptive" data-bs-dismiss="modal">Close</button>
591
+ </div>
592
+ </div>
593
+ </div>
594
+ </div>