studio-engine 0.37.0 → 0.39.0

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,91 @@
1
+ <%# admin/emails/:key — one email, previewed live.
2
+
3
+ A bare content wrapper like the index, so it renders inside each host's
4
+ layout. Lifted from turf-monster's admin/emails/show (the prior art this
5
+ work folds into the engine) and given the banner half: the same iframe over
6
+ the real rendered email, plus what artwork is riding on top of it and where
7
+ that artwork came from. %>
8
+ <% content_for(:title) { @entry.label } %>
9
+ <%
10
+ source = Studio::EmailCatalog.source(@entry.key)
11
+ banner_url = Studio::EmailCatalog.preview_url(@entry.key)
12
+ aspect = Studio::EmailCatalog::ASPECT_RATIO
13
+
14
+ badge_class, badge_label =
15
+ case source
16
+ when :app then ["badge bg-success/10 text-success border-success/30", "#{Studio.app_name}'s own"]
17
+ when :default then ["badge bg-inset text-muted border-subtle", "Inherited default"]
18
+ else ["badge bg-warning/10 text-warning border-warning/30", "No image"]
19
+ end
20
+ %>
21
+ <div class="max-w-5xl mx-auto px-4 pb-16">
22
+ <header class="pt-8 pb-5">
23
+ <%= link_to "← All emails", admin_emails_path,
24
+ class: "text-sm text-muted hover:text-heading underline underline-offset-2" %>
25
+ <h1 class="text-3xl font-bold text-heading mt-2"><%= @entry.label %></h1>
26
+
27
+ <div class="flex flex-wrap items-center gap-2 mt-3">
28
+ <span class="badge <%= @entry.marketing? ? "bg-primary/10 text-primary border-primary/30" : "bg-inset text-muted border-subtle" %> whitespace-nowrap">
29
+ <%= @entry.type.to_s.titleize %>
30
+ </span>
31
+ <span class="<%= badge_class %> whitespace-nowrap"><%= badge_label %></span>
32
+ <span class="font-mono text-2xs text-muted"><%= @entry.key %></span>
33
+ </div>
34
+
35
+ <% if @entry.description.present? %>
36
+ <p class="text-body max-w-2xl mt-3"><%= @entry.description %></p>
37
+ <% end %>
38
+
39
+ <% if @subject.present? %>
40
+ <p class="text-sm text-muted mt-2">
41
+ Subject: <strong class="text-heading"><%= @subject %></strong>
42
+ </p>
43
+ <% end %>
44
+ </header>
45
+
46
+ <%# The banner riding on top of this email, and where it came from. %>
47
+ <section class="card p-4 mb-6">
48
+ <p class="label-upper mb-3">Banner</p>
49
+ <div class="rounded-lg overflow-hidden border border-subtle max-w-md"
50
+ style="aspect-ratio: <%= aspect %>; background: linear-gradient(135deg, var(--color-primary-700), var(--color-primary-900));">
51
+ <% if banner_url %>
52
+ <%= image_tag banner_url, class: "w-full h-full object-cover",
53
+ alt: "#{@entry.label} email banner" %>
54
+ <% else %>
55
+ <div class="w-full h-full flex items-center justify-center text-xs text-white/80 text-center px-4">
56
+ This email sends without a banner.
57
+ </div>
58
+ <% end %>
59
+ </div>
60
+ <p class="text-sm text-muted mt-3">
61
+ <% if @uploads_available %>
62
+ Change it on the <%= link_to "emails list", admin_emails_path, class: "underline underline-offset-2" %>.
63
+ <% else %>
64
+ <%= Studio.app_name %> has no object storage configured, so this image can be
65
+ viewed but not replaced yet.
66
+ <% end %>
67
+ </p>
68
+ </section>
69
+
70
+ <%# The email itself. An iframe because the response IS an email document —
71
+ its own <html>, its own table layout, and no business inheriting the admin
72
+ page's stylesheet. %>
73
+ <section class="card p-3 bg-inset">
74
+ <p class="label-upper mb-3 px-1">Preview</p>
75
+ <% if @entry.previewable? %>
76
+ <iframe src="<%= admin_email_raw_path(@entry.key) %>"
77
+ style="width:100%;height:780px;border:0;border-radius:8px;background:#ffffff;"
78
+ title="<%= @entry.label %> preview"></iframe>
79
+ <% else %>
80
+ <div class="px-3 pb-4">
81
+ <p class="text-sm text-body">
82
+ No preview is registered for this email. Pass a <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">preview:</code>
83
+ callable when registering it — anything that returns a Mail — and it renders here.
84
+ </p>
85
+ </div>
86
+ <% end %>
87
+ <% if @preview_error.present? %>
88
+ <p class="text-2xs text-danger mt-2 px-1 font-mono break-all"><%= @preview_error %></p>
89
+ <% end %>
90
+ </section>
91
+ </div>
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.37.0"
2
+ VERSION = "0.39.0"
3
3
  end
data/lib/studio.rb CHANGED
@@ -558,10 +558,17 @@ module Studio
558
558
  # Studio::EmailImage.resolved_url — so an app is branded on day one whether
559
559
  # or not it draws the page.
560
560
  if Studio.draw_admin_emails_routes
561
- get "admin/emails", to: "studio/emails#index", as: :admin_emails
562
- patch "admin/emails/:key", to: "studio/emails#update", as: :admin_email,
561
+ get "admin/emails", to: "studio/emails#index", as: :admin_emails
562
+ # /raw is drawn BEFORE /:key so "raw" is never captured as a key.
563
+ get "admin/emails/:key/raw", to: "studio/emails#raw", as: :admin_email_raw,
563
564
  constraints: { key: /[a-z0-9_]+/ }
564
- delete "admin/emails/:key", to: "studio/emails#destroy",
565
+ get "admin/emails/:key", to: "studio/emails#show", as: :admin_email,
566
+ constraints: { key: /[a-z0-9_]+/ }
567
+ # Same path, same helper (admin_email_path) — a named route only needs
568
+ # to be declared once per name, and these share the show route's URL.
569
+ patch "admin/emails/:key", to: "studio/emails#update",
570
+ constraints: { key: /[a-z0-9_]+/ }
571
+ delete "admin/emails/:key", to: "studio/emails#destroy",
565
572
  constraints: { key: /[a-z0-9_]+/ }
566
573
  end
567
574
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.37.0
4
+ version: 0.39.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie
@@ -223,6 +223,7 @@ files:
223
223
  - app/models/studio/model_page.rb
224
224
  - app/models/theme_setting.rb
225
225
  - app/services/google_oauth_validator.rb
226
+ - app/services/studio/email_catalog.rb
226
227
  - app/services/studio/email_image.rb
227
228
  - app/views/components/_admin_dropdown.html.erb
228
229
  - app/views/components/_avatar.html.erb
@@ -276,6 +277,7 @@ files:
276
277
  - app/views/studio/email_images/index.html.erb
277
278
  - app/views/studio/emails/_row.html.erb
278
279
  - app/views/studio/emails/index.html.erb
280
+ - app/views/studio/emails/show.html.erb
279
281
  - app/views/studio/links/confirm.html.erb
280
282
  - app/views/studio/local_emails/index.html.erb
281
283
  - app/views/studio/modals/_crop_photo.html.erb