sincerely 1.0.0 → 1.1.1

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/app/assets/config/sincerely.js +2 -0
  4. data/app/assets/images/sincerely/arrow_left.svg +1 -0
  5. data/app/assets/images/sincerely/check.svg +3 -0
  6. data/app/assets/images/sincerely/chevron_left.svg +1 -0
  7. data/app/assets/images/sincerely/clock.svg +1 -0
  8. data/app/assets/images/sincerely/code.svg +1 -0
  9. data/app/assets/images/sincerely/eye.svg +1 -0
  10. data/app/assets/images/sincerely/logout.svg +1 -0
  11. data/app/assets/images/sincerely/menu.svg +1 -0
  12. data/app/assets/images/sincerely/moon.svg +1 -0
  13. data/app/assets/images/sincerely/pencil.svg +4 -0
  14. data/app/assets/images/sincerely/plus.svg +3 -0
  15. data/app/assets/images/sincerely/progress_spinner.svg +3 -0
  16. data/app/assets/images/sincerely/spinner.svg +3 -0
  17. data/app/assets/images/sincerely/sun.svg +1 -0
  18. data/app/assets/images/sincerely/warning_circle.svg +4 -0
  19. data/app/assets/javascripts/sincerely/charts.js +115 -0
  20. data/app/assets/javascripts/sincerely/clickable_rows.js +29 -0
  21. data/app/assets/javascripts/sincerely/layout.js +104 -0
  22. data/app/assets/javascripts/sincerely/send_form.js +243 -0
  23. data/app/assets/javascripts/sincerely/template_form.js +44 -0
  24. data/app/assets/javascripts/sincerely/template_preview.js +12 -0
  25. data/app/assets/javascripts/sincerely/theme_init.js +9 -0
  26. data/app/assets/stylesheets/sincerely/charts.css +250 -0
  27. data/app/assets/stylesheets/sincerely/components.css +632 -0
  28. data/app/assets/stylesheets/sincerely/layout.css +233 -0
  29. data/app/assets/stylesheets/sincerely/navigation.css +198 -0
  30. data/app/assets/stylesheets/sincerely/send_form.css +287 -0
  31. data/app/assets/stylesheets/sincerely/template_form.css +74 -0
  32. data/app/assets/stylesheets/sincerely/template_preview.css +75 -0
  33. data/app/assets/stylesheets/sincerely/variables.css +88 -0
  34. data/app/controllers/sincerely/application_controller.rb +3 -72
  35. data/app/controllers/sincerely/concerns/authorization.rb +21 -0
  36. data/app/controllers/sincerely/concerns/delivery_metrics.rb +51 -0
  37. data/app/controllers/sincerely/concerns/event_filtering.rb +58 -0
  38. data/app/controllers/sincerely/concerns/event_timeline.rb +41 -0
  39. data/app/controllers/sincerely/concerns/notification_timeline.rb +73 -0
  40. data/app/controllers/sincerely/concerns/notifications.rb +47 -0
  41. data/app/controllers/sincerely/concerns/pagination.rb +39 -0
  42. data/app/controllers/sincerely/concerns/periods.rb +40 -0
  43. data/app/controllers/sincerely/dashboard_controller.rb +30 -88
  44. data/app/controllers/sincerely/delivery_events_controller.rb +13 -21
  45. data/app/controllers/sincerely/engagement_events_controller.rb +13 -22
  46. data/app/controllers/sincerely/notifications_controller.rb +68 -49
  47. data/app/controllers/sincerely/send_controller.rb +82 -67
  48. data/app/controllers/sincerely/templates_controller.rb +37 -8
  49. data/app/helpers/sincerely/application_helper.rb +22 -0
  50. data/app/helpers/sincerely/chart_helper.rb +79 -0
  51. data/app/views/layouts/sincerely/application.html.erb +24 -528
  52. data/app/views/sincerely/dashboard/index.html.erb +25 -256
  53. data/app/views/sincerely/delivery_events/index.html.erb +6 -38
  54. data/app/views/sincerely/engagement_events/index.html.erb +6 -38
  55. data/app/views/sincerely/notifications/index.html.erb +7 -23
  56. data/app/views/sincerely/notifications/show.html.erb +4 -4
  57. data/app/views/sincerely/send/new.html.erb +8 -522
  58. data/app/views/sincerely/shared/_event.html.erb +21 -0
  59. data/app/views/sincerely/shared/_line_chart.html.erb +38 -0
  60. data/app/views/sincerely/shared/_notification_type_filter.html.erb +9 -0
  61. data/app/views/sincerely/shared/_status_option.html.erb +1 -0
  62. data/app/views/sincerely/shared/_template_option.html.erb +3 -0
  63. data/app/views/sincerely/templates/_form.html.erb +8 -107
  64. data/app/views/sincerely/templates/edit.html.erb +2 -2
  65. data/app/views/sincerely/templates/index.html.erb +7 -15
  66. data/app/views/sincerely/templates/new.html.erb +2 -2
  67. data/app/views/sincerely/templates/preview.html.erb +7 -26
  68. data/app/views/sincerely/templates/show.html.erb +3 -3
  69. data/lib/config/sincerely_config.rb +2 -2
  70. data/lib/sincerely/delivery_systems/email_aws_ses.rb +53 -19
  71. data/lib/sincerely/engine.rb +4 -0
  72. data/lib/sincerely/mixins/notification_model.rb +8 -2
  73. data/lib/sincerely/services/process_delivery_event.rb +9 -7
  74. data/lib/sincerely/templates/notification_template.rb +1 -1
  75. data/lib/sincerely/version.rb +1 -1
  76. data/lib/sincerely.rb +1 -1
  77. metadata +66 -4
  78. data/sincerely.gemspec +0 -37
@@ -0,0 +1,12 @@
1
+ document.querySelectorAll('.preview-tab').forEach(function(tab) {
2
+ tab.addEventListener('click', function() {
3
+ var selected = tab.dataset.tab;
4
+
5
+ document.querySelectorAll('.preview-tab').forEach(function(t) {
6
+ t.classList.toggle('active', t.dataset.tab === selected);
7
+ });
8
+
9
+ document.getElementById('html-preview').classList.toggle('preview-frame--hidden', selected !== 'html');
10
+ document.getElementById('text-preview').classList.toggle('preview-frame--hidden', selected !== 'text');
11
+ });
12
+ });
@@ -0,0 +1,9 @@
1
+ (function() {
2
+ var theme = localStorage.getItem('sincerely-theme');
3
+ if (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
4
+ theme = 'dark';
5
+ }
6
+ if (theme) {
7
+ document.documentElement.setAttribute('data-theme', theme);
8
+ }
9
+ })();
@@ -0,0 +1,250 @@
1
+ /* Donut Chart */
2
+ .donut-chart-container {
3
+ display: flex;
4
+ flex-direction: column;
5
+ align-items: center;
6
+ gap: 1.5rem;
7
+ padding: 1rem 0;
8
+ }
9
+
10
+ .donut-chart-wrapper {
11
+ position: relative;
12
+ width: 300px;
13
+ height: 300px;
14
+ flex-shrink: 0;
15
+ }
16
+
17
+ .donut-chart {
18
+ width: 100%;
19
+ height: 100%;
20
+ transform: rotate(-90deg);
21
+ }
22
+
23
+ .donut-segment {
24
+ fill: none;
25
+ stroke-width: 32;
26
+ stroke-linecap: butt;
27
+ transition: opacity 0.2s, filter 0.2s;
28
+ cursor: pointer;
29
+ }
30
+
31
+ .donut-segment.dimmed {
32
+ opacity: 0.3;
33
+ filter: grayscale(60%);
34
+ }
35
+
36
+ .donut-segment.highlighted {
37
+ filter: brightness(1.1);
38
+ }
39
+
40
+ .donut-center {
41
+ position: absolute;
42
+ top: 50%;
43
+ left: 50%;
44
+ transform: translate(-50%, -50%);
45
+ text-align: center;
46
+ pointer-events: none;
47
+ }
48
+
49
+ .donut-center-value {
50
+ font-size: 2rem;
51
+ font-weight: 700;
52
+ color: var(--text-primary);
53
+ line-height: 1;
54
+ }
55
+
56
+ .donut-center-label {
57
+ font-size: 0.75rem;
58
+ color: var(--text-secondary);
59
+ margin-top: 0.25rem;
60
+ }
61
+
62
+ .donut-legend {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ justify-content: center;
66
+ gap: 0.5rem;
67
+ width: 100%;
68
+ }
69
+
70
+ .donut-legend-item {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 0.5rem;
74
+ padding: 0.375rem 0.625rem;
75
+ border-radius: 0.375rem;
76
+ cursor: pointer;
77
+ transition: background-color 0.15s, opacity 0.2s;
78
+ }
79
+
80
+ .donut-legend-item:hover {
81
+ background: var(--gray-100);
82
+ }
83
+
84
+ .donut-legend-item.dimmed {
85
+ opacity: 0.4;
86
+ }
87
+
88
+ .donut-legend-item.highlighted {
89
+ background: var(--gray-100);
90
+ }
91
+
92
+ .donut-legend-color {
93
+ width: 10px;
94
+ height: 10px;
95
+ border-radius: 50%;
96
+ flex-shrink: 0;
97
+ }
98
+
99
+ .donut-legend-text {
100
+ font-size: 0.8125rem;
101
+ font-weight: 500;
102
+ color: var(--text-primary);
103
+ }
104
+
105
+ .donut-legend-count {
106
+ font-size: 0.8125rem;
107
+ font-weight: 600;
108
+ color: var(--text-secondary);
109
+ }
110
+
111
+ .donut-empty {
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ height: 200px;
116
+ color: var(--text-secondary);
117
+ font-size: 0.875rem;
118
+ }
119
+
120
+ /* Line Chart */
121
+ .line-chart-container {
122
+ padding: 1rem 0;
123
+ }
124
+
125
+ .line-chart-wrapper {
126
+ position: relative;
127
+ width: 100%;
128
+ height: 280px;
129
+ }
130
+
131
+ .line-chart {
132
+ width: 100%;
133
+ height: 100%;
134
+ }
135
+
136
+ .line-chart-line {
137
+ fill: none;
138
+ stroke-width: 2.5;
139
+ stroke-linecap: round;
140
+ stroke-linejoin: round;
141
+ transition: opacity 0.2s, stroke-width 0.2s;
142
+ cursor: pointer;
143
+ }
144
+
145
+ .line-chart-line.dimmed {
146
+ opacity: 0.15;
147
+ }
148
+
149
+ .line-chart-line.highlighted {
150
+ stroke-width: 4;
151
+ }
152
+
153
+ .line-chart-area {
154
+ opacity: 0.1;
155
+ transition: opacity 0.2s;
156
+ }
157
+
158
+ .line-chart-area.dimmed {
159
+ opacity: 0.02;
160
+ }
161
+
162
+ .line-chart-area.highlighted {
163
+ opacity: 0.2;
164
+ }
165
+
166
+ .line-chart-axis {
167
+ stroke: var(--border-color);
168
+ stroke-width: 1;
169
+ }
170
+
171
+ .line-chart-grid {
172
+ stroke: var(--border-color);
173
+ stroke-width: 0.5;
174
+ opacity: 0.5;
175
+ }
176
+
177
+ .line-chart-label {
178
+ font-size: 0.625rem;
179
+ fill: var(--text-secondary);
180
+ }
181
+
182
+ .line-chart-label-y {
183
+ text-anchor: end;
184
+ }
185
+
186
+ .line-chart-label-x {
187
+ text-anchor: middle;
188
+ }
189
+
190
+ .line-chart-legend {
191
+ display: flex;
192
+ flex-wrap: wrap;
193
+ justify-content: center;
194
+ gap: 0.5rem;
195
+ margin-top: 1rem;
196
+ }
197
+
198
+ .line-chart-legend-item {
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 0.5rem;
202
+ padding: 0.375rem 0.625rem;
203
+ border-radius: 0.375rem;
204
+ cursor: pointer;
205
+ transition: background-color 0.15s, opacity 0.2s;
206
+ }
207
+
208
+ .line-chart-legend-item:hover {
209
+ background: var(--gray-100);
210
+ }
211
+
212
+ .line-chart-legend-item.dimmed {
213
+ opacity: 0.4;
214
+ }
215
+
216
+ .line-chart-legend-item.highlighted {
217
+ background: var(--gray-100);
218
+ }
219
+
220
+ .line-chart-empty {
221
+ display: flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ height: 280px;
225
+ color: var(--text-secondary);
226
+ font-size: 0.875rem;
227
+ }
228
+
229
+ /* Responsive */
230
+ @media (max-width: 768px) {
231
+ .donut-chart-wrapper {
232
+ width: 220px;
233
+ height: 220px;
234
+ }
235
+
236
+ .line-chart-wrapper {
237
+ height: 200px;
238
+ }
239
+ }
240
+
241
+ @media (max-width: 480px) {
242
+ .donut-chart-wrapper {
243
+ width: 180px;
244
+ height: 180px;
245
+ }
246
+
247
+ .donut-center-value {
248
+ font-size: 1.5rem;
249
+ }
250
+ }