Skip to content

Commit d7ce119

Browse files
committed
Add datetime preview selector for time-travel preview of scheduled elements
Alchemy now supports previewing pages as they will appear at a specific point in time. This allows content editors to verify that scheduled elements will publish and unpublish correctly. The feature adds a preview_at attribute to Alchemy::Current which is used globally by the Publishable concern to determine element visibility. When a datetime is selected in the toolbar, both the page preview iframe and the elements sidebar reload with time-filtered content. The published scope and public? method on all publishable records (elements, page versions) respect this setting, falling back to Time.current when not set. Signed-off-by: Thomas von Deyen <vondeyen@blish.cloud>
1 parent 54eeb08 commit d7ce119

File tree

18 files changed

+256
-9
lines changed

18 files changed

+256
-9
lines changed

app/assets/builds/alchemy/admin.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/assets/builds/alchemy/alchemy_admin.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/assets/builds/alchemy/alchemy_admin.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module Alchemy
2+
module Admin
3+
class PreviewTimeSelect < ViewComponent::Base
4+
erb_template <<~ERB
5+
<div id="preview_time_select">
6+
<div class="select_with_label">
7+
<sl-tooltip content="<%= tooltip %>" placement="top-start">
8+
<alchemy-icon name="calendar-schedule"></alchemy-icon>
9+
<alchemy-auto-submit>
10+
<form action="<%= url %>" method="get">
11+
<%= select_tag("alchemy_preview_time",
12+
options_for_select(preview_times, selected),
13+
include_blank: Alchemy.t(:now),
14+
disabled:) %>
15+
</form>
16+
<alchemy-auto-submit>
17+
</sl-tooltip>
18+
</div>
19+
<div class="toolbar_spacer"></div>
20+
</div>
21+
ERB
22+
23+
def initialize(page_version, url:, selected: nil)
24+
@page_version = page_version
25+
@url = url
26+
@selected = selected
27+
@disabled = preview_times.none?
28+
end
29+
30+
private
31+
32+
attr_reader :page_version, :selected, :url, :disabled
33+
34+
def preview_times
35+
@_preview_times ||= begin
36+
now = Time.current
37+
elements = page_version.elements
38+
future_public_on = elements.where("public_on > ?", now).pluck(:public_on)
39+
future_public_until = elements.where("public_until > ?", now).pluck(:public_until)
40+
times = (future_public_on | future_public_until)
41+
times.sort!
42+
times.map { |time| [l(time, format: :"alchemy.element_date"), time.iso8601] }
43+
end
44+
end
45+
46+
def tooltip
47+
if disabled
48+
Alchemy.t(:no_future_publication_dates)
49+
else
50+
Alchemy.t(:preview_time)
51+
end
52+
end
53+
end
54+
end
55+
end

app/controllers/alchemy/admin/elements_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ElementsController < Alchemy::Admin::BaseController
77

88
before_action :load_page_and_version, only: [:index, :new]
99
include Alchemy::Admin::Clipboard
10+
include Alchemy::Admin::PreviewTime
1011

1112
before_action :load_element, only: [:update, :destroy, :collapse, :expand, :publish]
1213
authorize_resource class: Alchemy::Element

app/controllers/alchemy/admin/pages_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Admin
55
class PagesController < ResourcesController
66
include OnPageLayout::CallbacksRunner
77
include Alchemy::Admin::Clipboard
8+
include Alchemy::Admin::PreviewTime
89

910
helper "alchemy/pages"
1011

app/javascript/alchemy_admin/components/datepicker.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ class Datepicker extends AlchemyHTMLElement {
2222
}
2323

2424
this.flatpickr = flatpickr(this.inputField, this.flatpickrOptions)
25+
this.inputField.addEventListener("keydown", (e) => {
26+
if (e.key === "Escape" || e.key === "Delete" || e.key === "Backspace") {
27+
this.flatpickr.clear()
28+
this.updatePreview("")
29+
}
30+
})
2531
}
2632

2733
disconnected() {

app/models/concerns/alchemy/publishable.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def already_public_for?(at: Current.preview_time)
6060
# @param at [DateTime] (Time.current)
6161
# @returns Boolean
6262
def still_public_for?(at: Current.preview_time)
63-
public_until.nil? || public_until >= at
63+
public_until.nil? || public_until > at
6464
end
6565
end
6666
end

app/stylesheets/alchemy/admin/toolbar.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@
8080
margin-left: var(--spacing-1);
8181
}
8282

83+
#preview_time_select {
84+
display: inline-flex;
85+
86+
select {
87+
width: var(--select-x-large-width);
88+
}
89+
}
90+
8391
#overlay_toolbar {
8492
@extend %gradiated-toolbar;
8593
}

app/views/alchemy/admin/elements/_schedule.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
<alchemy-message type="info">
88
<% if element.public? %>
99
<p><%= sanitize t(".visibility_status.public_html") %></p>
10-
<% if element.scheduled? %>
10+
<% if element.scheduled? && element.public_until.present? %>
1111
<%= sanitize t(".public_scheduled_html",
1212
public_until: l(element.public_until, format: :"alchemy.element_date")) %>
13-
<% elsif element.public_until.blank? %>
13+
<% else %>
1414
<%= t(".public_no_schedule") %>
1515
<% end %>
1616
<% else %>

0 commit comments

Comments
 (0)