Skip to content

Commit c97a425

Browse files
committed
feat: Add schedule element button
Replaces the public toggle with a split button which allows to schedule the element for publication.
1 parent 5e63da0 commit c97a425

File tree

25 files changed

+224
-88
lines changed

25 files changed

+224
-88
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ if ENV["DB"] == "mysql" || ENV["DB"] == "mariadb"
1515
end
1616
gem "pg", "~> 1.0" if ENV["DB"] == "postgresql"
1717

18-
gem "alchemy_i18n", github: "AlchemyCMS/alchemy_i18n", branch: "main"
18+
gem "alchemy_i18n", github: "AlchemyCMS/alchemy_i18n", branch: "publish-button"
1919

2020
if ENV["ALCHEMY_STORAGE_ADAPTER"] == "active_storage"
2121
gem "ruby-vips"

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/dark-theme.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/light-theme.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/theme.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/images/alchemy/icons-sprite.svg

Lines changed: 1 addition & 1 deletion
Loading
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<alchemy-publish-element-button>
2+
<sl-button-group label="Example Button Group">
3+
<sl-tooltip
4+
content="<%= element.public? ? Alchemy.t(:hide_element) : Alchemy.t(:show_element) %>"
5+
<%= "disabled" if element.scheduled? || cannot?(:update, element) %>
6+
>
7+
<sl-button
8+
variant="<%= element.public? ? "default" : "primary" %>"
9+
type="submit"
10+
size="small"
11+
<%= "disabled" if element.scheduled? || cannot?(:update, element) %>
12+
<%= "outline" if element.public? %>
13+
pill
14+
>
15+
<alchemy-icon name="cloud-off" slot="prefix" size="1x"></alchemy-icon>
16+
<span><%= Alchemy.t(:hide, scope: "admin.elements.toolbar") %></span>
17+
</sl-button>
18+
</sl-tooltip>
19+
<sl-tooltip
20+
content="Schedule element"
21+
>
22+
<sl-button
23+
href="<%= alchemy.schedule_admin_element_path(element) %>"
24+
variant="<%= element.scheduled? ? "primary" : "default" %>"
25+
size="small"
26+
outline pill
27+
<%= "disabled" if cannot?(:update, element) %>
28+
>
29+
<alchemy-icon name="calendar-schedule" slot="suffix" size="1x"></alchemy-icon>
30+
</sl-button>
31+
</sl-tooltip>
32+
</sl-button-group>
33+
</alchemy-publish-element-button>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Alchemy
2+
module Admin
3+
class PublishElementButton < ViewComponent::Base
4+
delegate :alchemy, :cannot?, :render_icon, :link_to_dialog, to: :helpers
5+
6+
attr_reader :element
7+
8+
def initialize(element:)
9+
@element = element
10+
end
11+
end
12+
end
13+
end

app/controllers/alchemy/admin/elements_controller.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class ElementsController < Alchemy::Admin::BaseController
88
before_action :load_page_and_version, only: [:index, :new]
99
include Alchemy::Admin::Clipboard
1010

11-
before_action :load_element, only: [:update, :destroy, :collapse, :expand, :publish]
11+
before_action :load_element, only: [:schedule, :update, :destroy, :collapse, :expand, :publish]
1212
authorize_resource class: Alchemy::Element
1313

1414
def index
@@ -89,12 +89,22 @@ def destroy
8989
end
9090

9191
def publish
92-
@element.public = !@element.public?
92+
if schedule_element_params.present?
93+
@element.assign_attributes(schedule_element_params)
94+
else
95+
@element.public = !@element.public?
96+
end
9397
@element.save(validate: false)
94-
render json: {
95-
public: @element.public?,
96-
label: @element.public? ? Alchemy.t(:hide_element) : Alchemy.t(:show_element)
97-
}.merge(pagePublicationData(@element.page))
98+
99+
respond_to do |format|
100+
format.json do
101+
render json: {
102+
public: @element.public?,
103+
tooltip: @element.public? ? Alchemy.t(:hide_element) : Alchemy.t(:show_element)
104+
}.merge(pagePublicationData(@element.page))
105+
end
106+
format.turbo_stream
107+
end
98108
end
99109

100110
def order
@@ -216,6 +226,10 @@ def element_params
216226
def create_element_params
217227
params.require(:element).permit(:name, :page_version_id, :parent_element_id)
218228
end
229+
230+
def schedule_element_params
231+
params[:element].permit(:public_on, :public_until)
232+
end
219233
end
220234
end
221235
end

app/javascript/alchemy_admin/components/element_editor/publish_element_button.js

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,72 @@ import { patch } from "alchemy_admin/utils/ajax"
22
import { reloadPreview } from "alchemy_admin/components/preview_window"
33
import { growl } from "alchemy_admin/growler"
44
import { dispatchPageDirtyEvent } from "alchemy_admin/components/element_editor"
5+
import { openDialog } from "alchemy_admin/dialog"
56

67
export class PublishElementButton extends HTMLElement {
78
constructor() {
89
super()
910

10-
this.addEventListener("sl-change", this)
11+
this.button.addEventListener("click", this)
12+
this.scheduleButton.addEventListener("click", this)
1113
}
1214

1315
handleEvent(event) {
1416
const elementEditor = event.target.closest("alchemy-element-editor")
15-
if (elementEditor === this.elementEditor) {
17+
18+
if (event.target === this.scheduleButton) {
19+
// Open schedule dialog
20+
event.preventDefault()
21+
openDialog(this.scheduleButton.href, {
22+
size: "450x200",
23+
title: "Schedule Element Visibility"
24+
})
25+
} else if (elementEditor === this.elementEditor) {
26+
this.button.loading = true
1627
patch(Alchemy.routes.publish_admin_element_path(this.elementId))
1728
.then((response) => {
1829
const data = response.data
1930
this.elementEditor.published = data.public
20-
this.tooltip.setAttribute("content", data.label)
31+
this.button.setAttribute(
32+
"variant",
33+
data.public ? "default" : "primary"
34+
)
35+
if (data.public) {
36+
this.button.setAttribute("outline", true)
37+
} else {
38+
this.button.removeAttribute("outline")
39+
}
40+
this.tooltip.setAttribute("content", data.tooltip)
2141
reloadPreview()
2242
if (data.pageHasUnpublishedChanges) {
2343
dispatchPageDirtyEvent(data)
2444
}
2545
})
2646
.catch((error) => growl(error.message, "error"))
47+
.finally(() => {
48+
this.button.loading = false
49+
})
2750
}
2851
}
2952

3053
get elementEditor() {
3154
return this.closest("alchemy-element-editor")
3255
}
3356

57+
get button() {
58+
return this.querySelector("sl-button")
59+
}
60+
61+
get scheduleButton() {
62+
return this.querySelector("sl-button[href]")
63+
}
64+
65+
get buttonLabel() {
66+
return this.querySelector("sl-button span")
67+
}
68+
3469
get tooltip() {
35-
return this.closest("sl-tooltip")
70+
return this.querySelector("sl-tooltip")
3671
}
3772

3873
get elementId() {

0 commit comments

Comments
 (0)