fix: parity in behaviour for scripts, templates and posts
This commit is contained in:
@@ -885,6 +885,11 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
{:noreply, CodeEntityEditor.save_script(socket, &reload_shell/2, &append_output_entry/5)}
|
{:noreply, CodeEntityEditor.save_script(socket, &reload_shell/2, &append_output_entry/5)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("publish_script_editor", %{"id" => _script_id}, socket) do
|
||||||
|
{:noreply,
|
||||||
|
CodeEntityEditor.publish_script(socket, &reload_shell/2, &append_output_entry/5)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("run_script_editor", _params, socket) do
|
def handle_event("run_script_editor", _params, socket) do
|
||||||
{:noreply, CodeEntityEditor.run_script(socket, &reload_shell/2, &append_output_entry/5)}
|
{:noreply, CodeEntityEditor.run_script(socket, &reload_shell/2, &append_output_entry/5)}
|
||||||
end
|
end
|
||||||
@@ -905,6 +910,11 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
{:noreply, CodeEntityEditor.save_template(socket, &reload_shell/2, &append_output_entry/5)}
|
{:noreply, CodeEntityEditor.save_template(socket, &reload_shell/2, &append_output_entry/5)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("publish_template_editor", %{"id" => _template_id}, socket) do
|
||||||
|
{:noreply,
|
||||||
|
CodeEntityEditor.publish_template(socket, &reload_shell/2, &append_output_entry/5)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("validate_template_editor", _params, socket) do
|
def handle_event("validate_template_editor", _params, socket) do
|
||||||
{:noreply,
|
{:noreply,
|
||||||
CodeEntityEditor.validate_template(socket, &reload_shell/2, &append_output_entry/5)}
|
CodeEntityEditor.validate_template(socket, &reload_shell/2, &append_output_entry/5)}
|
||||||
|
|||||||
@@ -31,38 +31,12 @@ defmodule BDS.Desktop.ShellLive.CodeEntityEditor do
|
|||||||
|
|
||||||
@spec save_script(term(), term(), term()) :: term()
|
@spec save_script(term(), term(), term()) :: term()
|
||||||
def save_script(socket, reload, append_output) do
|
def save_script(socket, reload, append_output) do
|
||||||
%{id: script_id} = socket.assigns.current_tab
|
persist_script(socket, :save, reload, append_output)
|
||||||
|
end
|
||||||
|
|
||||||
case Scripts.get_script(script_id) do
|
@spec publish_script(term(), term(), term()) :: term()
|
||||||
nil ->
|
def publish_script(socket, reload, append_output) do
|
||||||
reload.(socket, socket.assigns.workbench)
|
persist_script(socket, :publish, reload, append_output)
|
||||||
|
|
||||||
%Script{} = script ->
|
|
||||||
draft = current_script_draft(socket.assigns, script)
|
|
||||||
|
|
||||||
case Scripting.validate(draft["content"] || "") do
|
|
||||||
:ok ->
|
|
||||||
case Scripts.update_script(script.id, script_attrs(draft)) do
|
|
||||||
{:ok, _updated} ->
|
|
||||||
socket
|
|
||||||
|> assign(
|
|
||||||
:script_editor_drafts,
|
|
||||||
Map.delete(socket.assigns.script_editor_drafts, script.id)
|
|
||||||
)
|
|
||||||
|> reload.(socket.assigns.workbench)
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
socket
|
|
||||||
|> append_output.(translated("Scripts"), inspect(reason), nil, "error")
|
|
||||||
|> reload.(socket.assigns.workbench)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
socket
|
|
||||||
|> append_output.(translated("Scripts"), inspect(reason), nil, "error")
|
|
||||||
|> reload.(socket.assigns.workbench)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_script(term(), term(), term()) :: term()
|
@spec check_script(term(), term(), term()) :: term()
|
||||||
@@ -148,33 +122,12 @@ defmodule BDS.Desktop.ShellLive.CodeEntityEditor do
|
|||||||
|
|
||||||
@spec save_template(term(), term(), term()) :: term()
|
@spec save_template(term(), term(), term()) :: term()
|
||||||
def save_template(socket, reload, append_output) do
|
def save_template(socket, reload, append_output) do
|
||||||
%{id: template_id} = socket.assigns.current_tab
|
persist_template(socket, :save, reload, append_output)
|
||||||
|
end
|
||||||
|
|
||||||
case Templates.get_template(template_id) do
|
@spec publish_template(term(), term(), term()) :: term()
|
||||||
nil ->
|
def publish_template(socket, reload, append_output) do
|
||||||
reload.(socket, socket.assigns.workbench)
|
persist_template(socket, :publish, reload, append_output)
|
||||||
|
|
||||||
%Template{} = template ->
|
|
||||||
draft = current_template_draft(socket.assigns, template)
|
|
||||||
|
|
||||||
with {:ok, %{valid: true}} <- MCP.validate_template(draft["content"] || ""),
|
|
||||||
{:ok, _updated} <- Templates.update_template(template.id, template_attrs(draft)) do
|
|
||||||
socket
|
|
||||||
|> assign(
|
|
||||||
:template_editor_drafts,
|
|
||||||
Map.delete(socket.assigns.template_editor_drafts, template.id)
|
|
||||||
)
|
|
||||||
|> reload.(socket.assigns.workbench)
|
|
||||||
else
|
|
||||||
{:ok, %{valid: false, errors: errors}} ->
|
|
||||||
append_output.(socket, translated("Templates"), Enum.join(errors, "; "), nil, "error")
|
|
||||||
|> reload.(socket.assigns.workbench)
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
append_output.(socket, translated("Templates"), inspect(reason), nil, "error")
|
|
||||||
|> reload.(socket.assigns.workbench)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec validate_template(term(), term(), term()) :: term()
|
@spec validate_template(term(), term(), term()) :: term()
|
||||||
@@ -239,6 +192,8 @@ defmodule BDS.Desktop.ShellLive.CodeEntityEditor do
|
|||||||
enabled: draft["enabled"],
|
enabled: draft["enabled"],
|
||||||
content: draft["content"],
|
content: draft["content"],
|
||||||
entrypoints: discover_entrypoints(draft["content"]),
|
entrypoints: discover_entrypoints(draft["content"]),
|
||||||
|
status: script.status || :draft,
|
||||||
|
can_publish?: script.status == :draft,
|
||||||
created_at: script.created_at,
|
created_at: script.created_at,
|
||||||
updated_at: script.updated_at
|
updated_at: script.updated_at
|
||||||
}
|
}
|
||||||
@@ -263,6 +218,8 @@ defmodule BDS.Desktop.ShellLive.CodeEntityEditor do
|
|||||||
kind: draft["kind"],
|
kind: draft["kind"],
|
||||||
enabled: draft["enabled"],
|
enabled: draft["enabled"],
|
||||||
content: draft["content"],
|
content: draft["content"],
|
||||||
|
status: template.status || :draft,
|
||||||
|
can_publish?: template.status == :draft,
|
||||||
created_at: template.created_at,
|
created_at: template.created_at,
|
||||||
updated_at: template.updated_at
|
updated_at: template.updated_at
|
||||||
}
|
}
|
||||||
@@ -271,6 +228,9 @@ defmodule BDS.Desktop.ShellLive.CodeEntityEditor do
|
|||||||
|
|
||||||
def build_template(_assigns), do: nil
|
def build_template(_assigns), do: nil
|
||||||
|
|
||||||
|
@spec status_label(term()) :: term()
|
||||||
|
def status_label(status), do: ShellData.dashboard_status_label(status)
|
||||||
|
|
||||||
@spec translated(term(), term()) :: term()
|
@spec translated(term(), term()) :: term()
|
||||||
def translated(text, bindings \\ %{}),
|
def translated(text, bindings \\ %{}),
|
||||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||||
@@ -342,6 +302,82 @@ defmodule BDS.Desktop.ShellLive.CodeEntityEditor do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp persist_script(socket, action, reload, append_output) do
|
||||||
|
%{id: script_id} = socket.assigns.current_tab
|
||||||
|
|
||||||
|
case Scripts.get_script(script_id) do
|
||||||
|
nil ->
|
||||||
|
reload.(socket, socket.assigns.workbench)
|
||||||
|
|
||||||
|
%Script{} = script ->
|
||||||
|
draft = current_script_draft(socket.assigns, script)
|
||||||
|
|
||||||
|
case Scripting.validate(draft["content"] || "") do
|
||||||
|
:ok ->
|
||||||
|
case Scripts.update_script(script.id, script_attrs(draft))
|
||||||
|
|> maybe_publish_script(script.id, action) do
|
||||||
|
{:ok, _updated} ->
|
||||||
|
socket
|
||||||
|
|> assign(
|
||||||
|
:script_editor_drafts,
|
||||||
|
Map.delete(socket.assigns.script_editor_drafts, script.id)
|
||||||
|
)
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
socket
|
||||||
|
|> append_output.(translated("Scripts"), inspect(reason), nil, "error")
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
socket
|
||||||
|
|> append_output.(translated("Scripts"), inspect(reason), nil, "error")
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp persist_template(socket, action, reload, append_output) do
|
||||||
|
%{id: template_id} = socket.assigns.current_tab
|
||||||
|
|
||||||
|
case Templates.get_template(template_id) do
|
||||||
|
nil ->
|
||||||
|
reload.(socket, socket.assigns.workbench)
|
||||||
|
|
||||||
|
%Template{} = template ->
|
||||||
|
draft = current_template_draft(socket.assigns, template)
|
||||||
|
|
||||||
|
with {:ok, %{valid: true}} <- MCP.validate_template(draft["content"] || ""),
|
||||||
|
{:ok, _updated} <-
|
||||||
|
Templates.update_template(template.id, template_attrs(draft))
|
||||||
|
|> maybe_publish_template(template.id, action) do
|
||||||
|
socket
|
||||||
|
|> assign(
|
||||||
|
:template_editor_drafts,
|
||||||
|
Map.delete(socket.assigns.template_editor_drafts, template.id)
|
||||||
|
)
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
else
|
||||||
|
{:ok, %{valid: false, errors: errors}} ->
|
||||||
|
append_output.(socket, translated("Templates"), Enum.join(errors, "; "), nil, "error")
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
append_output.(socket, translated("Templates"), inspect(reason), nil, "error")
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_publish_script({:ok, _script}, script_id, :publish), do: Scripts.publish_script(script_id)
|
||||||
|
defp maybe_publish_script(result, _script_id, _action), do: result
|
||||||
|
|
||||||
|
defp maybe_publish_template({:ok, _template}, template_id, :publish),
|
||||||
|
do: Templates.publish_template(template_id)
|
||||||
|
|
||||||
|
defp maybe_publish_template(result, _template_id, _action), do: result
|
||||||
|
|
||||||
defp normalize_template_kind("post"), do: :post
|
defp normalize_template_kind("post"), do: :post
|
||||||
defp normalize_template_kind("list"), do: :list
|
defp normalize_template_kind("list"), do: :list
|
||||||
defp normalize_template_kind("not-found"), do: :"not-found"
|
defp normalize_template_kind("not-found"), do: :"not-found"
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
<div class="scripts-view-shell" data-testid="script-editor">
|
<div class="scripts-view-shell editor" data-testid="script-editor">
|
||||||
<div class="editor-header scripts-header">
|
<div class="editor-header scripts-header">
|
||||||
<div class="editor-tabs"><div class="editor-tab active"><span class="editor-tab-title"><%= @script_editor.title %></span></div></div>
|
<div class="editor-tabs"><div class="editor-tab active"><span class="editor-tab-title"><%= @script_editor.title %></span></div></div>
|
||||||
<div class="editor-actions">
|
<div class="editor-actions">
|
||||||
<button class="scripts-save-button" type="button" phx-click="save_script_editor"><%= translated("Save") %></button>
|
<span class={[
|
||||||
<button class="scripts-run-button" type="button" phx-click="run_script_editor"><%= translated("Run") %></button>
|
"status-badge",
|
||||||
<button class="scripts-check-button" type="button" phx-click="check_script_editor"><%= translated("Check Syntax") %></button>
|
"status-#{@script_editor.status}"
|
||||||
|
]} data-testid="script-status-badge"><%= status_label(@script_editor.status) %></span>
|
||||||
|
<%= if @script_editor.can_publish? do %>
|
||||||
|
<button class="success" data-testid="script-publish-button" type="button" phx-click="publish_script_editor" phx-value-id={@script_editor.id}><%= translated("Publish") %></button>
|
||||||
|
<% end %>
|
||||||
|
<button class="secondary scripts-save-button" type="button" phx-click="save_script_editor"><%= translated("Save") %></button>
|
||||||
|
<button class="secondary scripts-run-button" type="button" phx-click="run_script_editor"><%= translated("Run") %></button>
|
||||||
|
<button class="secondary scripts-check-button" type="button" phx-click="check_script_editor"><%= translated("Check Syntax") %></button>
|
||||||
<button class="secondary danger" type="button" phx-click="delete_script_editor"><%= translated("Delete") %></button>
|
<button class="secondary danger" type="button" phx-click="delete_script_editor"><%= translated("Delete") %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<div class="templates-view-shell" data-testid="template-editor">
|
<div class="templates-view-shell editor" data-testid="template-editor">
|
||||||
<div class="editor-header templates-header">
|
<div class="editor-header templates-header">
|
||||||
<div class="editor-tabs"><div class="editor-tab active"><span class="editor-tab-title"><%= @template_editor.title %></span></div></div>
|
<div class="editor-tabs"><div class="editor-tab active"><span class="editor-tab-title"><%= @template_editor.title %></span></div></div>
|
||||||
<div class="editor-actions">
|
<div class="editor-actions">
|
||||||
<button class="templates-save-button" type="button" phx-click="save_template_editor"><%= translated("Save") %></button>
|
<span class={[
|
||||||
<button class="templates-validate-button" type="button" phx-click="validate_template_editor"><%= translated("Validate") %></button>
|
"status-badge",
|
||||||
|
"status-#{@template_editor.status}"
|
||||||
|
]} data-testid="template-status-badge"><%= status_label(@template_editor.status) %></span>
|
||||||
|
<%= if @template_editor.can_publish? do %>
|
||||||
|
<button class="success" data-testid="template-publish-button" type="button" phx-click="publish_template_editor" phx-value-id={@template_editor.id}><%= translated("Publish") %></button>
|
||||||
|
<% end %>
|
||||||
|
<button class="secondary templates-save-button" type="button" phx-click="save_template_editor"><%= translated("Save") %></button>
|
||||||
|
<button class="secondary templates-validate-button" type="button" phx-click="validate_template_editor"><%= translated("Validate") %></button>
|
||||||
<button class="secondary danger" type="button" phx-click="delete_template_editor"><%= translated("Delete") %></button>
|
<button class="secondary danger" type="button" phx-click="delete_template_editor"><%= translated("Delete") %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,7 +43,12 @@ defmodule BDS.Scripts do
|
|||||||
end
|
end
|
||||||
|
|
||||||
@spec get_script(String.t()) :: Script.t() | nil
|
@spec get_script(String.t()) :: Script.t() | nil
|
||||||
def get_script(script_id), do: Repo.get(Script, script_id)
|
def get_script(script_id) do
|
||||||
|
case Repo.get(Script, script_id) do
|
||||||
|
%Script{} = script -> hydrate_script_content(script)
|
||||||
|
nil -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec publish_script(String.t()) :: script_result() | {:error, :not_found}
|
@spec publish_script(String.t()) :: script_result() | {:error, :not_found}
|
||||||
def publish_script(script_id) do
|
def publish_script(script_id) do
|
||||||
@@ -91,7 +96,8 @@ defmodule BDS.Scripts do
|
|||||||
script.slug
|
script.slug
|
||||||
end
|
end
|
||||||
|
|
||||||
content_changed? = has_attr?(attrs, :content) and attr(attrs, :content) != script.content
|
content_changed? =
|
||||||
|
has_attr?(attrs, :content) and attr(attrs, :content) != effective_script_content(script)
|
||||||
now = Persistence.now_ms()
|
now = Persistence.now_ms()
|
||||||
|
|
||||||
updates =
|
updates =
|
||||||
@@ -294,6 +300,24 @@ defmodule BDS.Scripts do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp effective_script_content(%Script{} = script) do
|
||||||
|
case hydrate_script_content(script) do
|
||||||
|
%Script{content: content} when is_binary(content) -> content
|
||||||
|
_other -> ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hydrate_script_content(%Script{} = script) do
|
||||||
|
case script do
|
||||||
|
%Script{content: content} when is_binary(content) -> script
|
||||||
|
%Script{status: :published, file_path: file_path} when file_path not in [nil, ""] ->
|
||||||
|
%{script | content: published_script_body(script)}
|
||||||
|
|
||||||
|
_other ->
|
||||||
|
script
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp upsert_script_from_file(project_id, project, path) do
|
defp upsert_script_from_file(project_id, project, path) do
|
||||||
contents = File.read!(path)
|
contents = File.read!(path)
|
||||||
{:ok, %{fields: fields}} = Frontmatter.parse_document(contents)
|
{:ok, %{fields: fields}} = Frontmatter.parse_document(contents)
|
||||||
|
|||||||
@@ -43,7 +43,12 @@ defmodule BDS.Templates do
|
|||||||
end
|
end
|
||||||
|
|
||||||
@spec get_template(String.t()) :: Template.t() | nil
|
@spec get_template(String.t()) :: Template.t() | nil
|
||||||
def get_template(template_id), do: Repo.get(Template, template_id)
|
def get_template(template_id) do
|
||||||
|
case Repo.get(Template, template_id) do
|
||||||
|
%Template{} = template -> hydrate_template_content(template)
|
||||||
|
nil -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec publish_template(String.t()) :: template_result() | {:error, :not_found}
|
@spec publish_template(String.t()) :: template_result() | {:error, :not_found}
|
||||||
def publish_template(template_id) do
|
def publish_template(template_id) do
|
||||||
@@ -97,7 +102,7 @@ defmodule BDS.Templates do
|
|||||||
end
|
end
|
||||||
|
|
||||||
content_changed? =
|
content_changed? =
|
||||||
has_attr?(attrs, :content) and attr(attrs, :content) != template.content
|
has_attr?(attrs, :content) and attr(attrs, :content) != effective_template_content(template)
|
||||||
|
|
||||||
slug_changed? = next_slug != template.slug
|
slug_changed? = next_slug != template.slug
|
||||||
now = Persistence.now_ms()
|
now = Persistence.now_ms()
|
||||||
@@ -458,6 +463,24 @@ defmodule BDS.Templates do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp effective_template_content(%Template{} = template) do
|
||||||
|
case hydrate_template_content(template) do
|
||||||
|
%Template{content: content} when is_binary(content) -> content
|
||||||
|
_other -> ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hydrate_template_content(%Template{} = template) do
|
||||||
|
case template do
|
||||||
|
%Template{content: content} when is_binary(content) -> template
|
||||||
|
%Template{status: :published, file_path: file_path} when file_path not in [nil, ""] ->
|
||||||
|
%{template | content: published_template_body(template)}
|
||||||
|
|
||||||
|
_other ->
|
||||||
|
template
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp upsert_template_from_file(project_id, project, path) do
|
defp upsert_template_from_file(project_id, project, path) do
|
||||||
contents = File.read!(path)
|
contents = File.read!(path)
|
||||||
{:ok, %{fields: fields}} = Frontmatter.parse_document(contents)
|
{:ok, %{fields: fields}} = Frontmatter.parse_document(contents)
|
||||||
|
|||||||
@@ -899,7 +899,9 @@ button svg * {
|
|||||||
border-bottom: 1px solid var(--vscode-panel-border);
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor.editor {
|
.post-editor.editor,
|
||||||
|
.scripts-view-shell.editor,
|
||||||
|
.templates-view-shell.editor {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -907,7 +909,9 @@ button svg * {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-header {
|
.post-editor .editor-header,
|
||||||
|
.scripts-view-shell.editor .editor-header,
|
||||||
|
.templates-view-shell.editor .editor-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -918,14 +922,18 @@ button svg * {
|
|||||||
border-bottom: 1px solid var(--vscode-panel-border);
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-tabs {
|
.post-editor .editor-tabs,
|
||||||
|
.scripts-view-shell.editor .editor-tabs,
|
||||||
|
.templates-view-shell.editor .editor-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-tab {
|
.post-editor .editor-tab,
|
||||||
|
.scripts-view-shell.editor .editor-tab,
|
||||||
|
.templates-view-shell.editor .editor-tab {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
@@ -937,12 +945,16 @@ button svg * {
|
|||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-tab.active {
|
.post-editor .editor-tab.active,
|
||||||
|
.scripts-view-shell.editor .editor-tab.active,
|
||||||
|
.templates-view-shell.editor .editor-tab.active {
|
||||||
background-color: var(--vscode-tab-activeBackground);
|
background-color: var(--vscode-tab-activeBackground);
|
||||||
color: var(--vscode-tab-activeForeground);
|
color: var(--vscode-tab-activeForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-tab-title {
|
.post-editor .editor-tab-title,
|
||||||
|
.scripts-view-shell.editor .editor-tab-title,
|
||||||
|
.templates-view-shell.editor .editor-tab-title {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -960,7 +972,9 @@ button svg * {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-actions {
|
.post-editor .editor-actions,
|
||||||
|
.scripts-view-shell.editor .editor-actions,
|
||||||
|
.templates-view-shell.editor .editor-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -1047,7 +1061,9 @@ button svg * {
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .status-badge {
|
.post-editor .status-badge,
|
||||||
|
.scripts-view-shell.editor .status-badge,
|
||||||
|
.templates-view-shell.editor .status-badge {
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@@ -1055,17 +1071,23 @@ button svg * {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .status-badge.status-draft {
|
.post-editor .status-badge.status-draft,
|
||||||
|
.scripts-view-shell.editor .status-badge.status-draft,
|
||||||
|
.templates-view-shell.editor .status-badge.status-draft {
|
||||||
background-color: rgba(204, 167, 0, 0.2);
|
background-color: rgba(204, 167, 0, 0.2);
|
||||||
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .status-badge.status-published {
|
.post-editor .status-badge.status-published,
|
||||||
|
.scripts-view-shell.editor .status-badge.status-published,
|
||||||
|
.templates-view-shell.editor .status-badge.status-published {
|
||||||
background-color: rgba(115, 201, 145, 0.2);
|
background-color: rgba(115, 201, 145, 0.2);
|
||||||
color: var(--vscode-testing-iconPassed);
|
color: var(--vscode-testing-iconPassed);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .status-badge.status-archived {
|
.post-editor .status-badge.status-archived,
|
||||||
|
.scripts-view-shell.editor .status-badge.status-archived,
|
||||||
|
.templates-view-shell.editor .status-badge.status-archived {
|
||||||
background-color: rgba(133, 133, 133, 0.2);
|
background-color: rgba(133, 133, 133, 0.2);
|
||||||
color: var(--vscode-descriptionForeground);
|
color: var(--vscode-descriptionForeground);
|
||||||
}
|
}
|
||||||
@@ -1668,6 +1690,8 @@ button svg * {
|
|||||||
|
|
||||||
@media (max-width: 980px) {
|
@media (max-width: 980px) {
|
||||||
.post-editor .editor-header,
|
.post-editor .editor-header,
|
||||||
|
.scripts-view-shell.editor .editor-header,
|
||||||
|
.templates-view-shell.editor .editor-header,
|
||||||
.post-editor .metadata-toggle-header,
|
.post-editor .metadata-toggle-header,
|
||||||
.post-editor .editor-toolbar {
|
.post-editor .editor-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1686,7 +1710,9 @@ button svg * {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-toolbar-right,
|
.post-editor .editor-toolbar-right,
|
||||||
.post-editor .editor-actions {
|
.post-editor .editor-actions,
|
||||||
|
.scripts-view-shell.editor .editor-actions,
|
||||||
|
.templates-view-shell.editor .editor-actions {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2227,6 +2227,122 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
refute html =~ ~s(phx-value-mode="visual")
|
refute html =~ ~s(phx-value-mode="visual")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "script and template editors surface lifecycle state, load published file content, and allow publishing drafts",
|
||||||
|
%{project: project} do
|
||||||
|
{:ok, draft_script} =
|
||||||
|
Scripts.create_script(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Draft Utility",
|
||||||
|
kind: :utility,
|
||||||
|
content: "function main() return 'draft' end"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, published_script_seed} =
|
||||||
|
Scripts.create_script(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Published Utility",
|
||||||
|
kind: :utility,
|
||||||
|
content: "function main() return 'published script' end"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, _published_script} = Scripts.publish_script(published_script_seed.id)
|
||||||
|
published_script = Scripts.get_script(published_script_seed.id)
|
||||||
|
|
||||||
|
{:ok, draft_template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Draft Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "<article>draft template</article>"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, published_template_seed} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Published Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "<article>published template</article>"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, _published_template} = Templates.publish_template(published_template_seed.id)
|
||||||
|
published_template = Templates.get_template(published_template_seed.id)
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
published_script_html =
|
||||||
|
render_click(view, "pin_sidebar_item", %{
|
||||||
|
"route" => "scripts",
|
||||||
|
"id" => published_script.id,
|
||||||
|
"title" => published_script.title,
|
||||||
|
"subtitle" => "published"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert published_script_html =~ ~s(class="scripts-view-shell editor")
|
||||||
|
assert published_script_html =~ ~s(data-testid="script-editor")
|
||||||
|
assert published_script_html =~ ~s(data-testid="script-status-badge")
|
||||||
|
assert published_script_html =~ ~s(class="status-badge status-published")
|
||||||
|
assert published_script_html =~ ~s(class="secondary scripts-save-button")
|
||||||
|
assert published_script_html =~ ~s(class="secondary scripts-run-button")
|
||||||
|
assert published_script_html =~ ~s(class="secondary scripts-check-button")
|
||||||
|
assert published_script_html =~ "published"
|
||||||
|
|
||||||
|
assert published_script_html =~ "published script"
|
||||||
|
|
||||||
|
refute published_script_html =~ ~s(data-testid="script-publish-button")
|
||||||
|
|
||||||
|
published_template_html =
|
||||||
|
render_click(view, "pin_sidebar_item", %{
|
||||||
|
"route" => "templates",
|
||||||
|
"id" => published_template.id,
|
||||||
|
"title" => published_template.title,
|
||||||
|
"subtitle" => "published"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert published_template_html =~ ~s(class="templates-view-shell editor")
|
||||||
|
assert published_template_html =~ ~s(data-testid="template-editor")
|
||||||
|
assert published_template_html =~ ~s(data-testid="template-status-badge")
|
||||||
|
assert published_template_html =~ ~s(class="status-badge status-published")
|
||||||
|
assert published_template_html =~ ~s(class="secondary templates-save-button")
|
||||||
|
assert published_template_html =~ ~s(class="secondary templates-validate-button")
|
||||||
|
assert published_template_html =~ "published"
|
||||||
|
|
||||||
|
assert published_template_html =~ "published template"
|
||||||
|
|
||||||
|
refute published_template_html =~ ~s(data-testid="template-publish-button")
|
||||||
|
|
||||||
|
draft_script_html =
|
||||||
|
render_click(view, "pin_sidebar_item", %{
|
||||||
|
"route" => "scripts",
|
||||||
|
"id" => draft_script.id,
|
||||||
|
"title" => draft_script.title,
|
||||||
|
"subtitle" => "draft"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert draft_script_html =~ ~s(data-testid="script-publish-button")
|
||||||
|
assert draft_script_html =~ ~s(class="success")
|
||||||
|
draft_script_html = render_click(view, "publish_script_editor", %{"id" => draft_script.id})
|
||||||
|
assert Scripts.get_script(draft_script.id).status == :published
|
||||||
|
refute draft_script_html =~ ~s(data-testid="script-publish-button")
|
||||||
|
assert draft_script_html =~ ~s(data-testid="script-status-badge")
|
||||||
|
assert draft_script_html =~ "published"
|
||||||
|
|
||||||
|
draft_template_html =
|
||||||
|
render_click(view, "pin_sidebar_item", %{
|
||||||
|
"route" => "templates",
|
||||||
|
"id" => draft_template.id,
|
||||||
|
"title" => draft_template.title,
|
||||||
|
"subtitle" => "draft"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert draft_template_html =~ ~s(data-testid="template-publish-button")
|
||||||
|
assert draft_template_html =~ ~s(class="success")
|
||||||
|
draft_template_html = render_click(view, "publish_template_editor", %{"id" => draft_template.id})
|
||||||
|
assert Templates.get_template(draft_template.id).status == :published
|
||||||
|
refute draft_template_html =~ ~s(data-testid="template-publish-button")
|
||||||
|
assert draft_template_html =~ ~s(data-testid="template-status-badge")
|
||||||
|
assert draft_template_html =~ "published"
|
||||||
|
end
|
||||||
|
|
||||||
test "media tabs render a real editor and drive explicit save flows", %{
|
test "media tabs render a real editor and drive explicit save flows", %{
|
||||||
project: project,
|
project: project,
|
||||||
temp_dir: temp_dir
|
temp_dir: temp_dir
|
||||||
@@ -2505,7 +2621,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => script.slug
|
"subtitle" => script.slug
|
||||||
})
|
})
|
||||||
|
|
||||||
assert script_html =~ ~s(class="scripts-view-shell")
|
assert script_html =~ ~s(class="scripts-view-shell editor")
|
||||||
assert script_html =~ "scripts-monaco"
|
assert script_html =~ "scripts-monaco"
|
||||||
assert script_html =~ ~s(data-monaco-language="lua")
|
assert script_html =~ ~s(data-monaco-language="lua")
|
||||||
assert script_html =~ ~s(data-monaco-word-wrap="on")
|
assert script_html =~ ~s(data-monaco-word-wrap="on")
|
||||||
@@ -2520,7 +2636,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => template.slug
|
"subtitle" => template.slug
|
||||||
})
|
})
|
||||||
|
|
||||||
assert template_html =~ ~s(class="templates-view-shell")
|
assert template_html =~ ~s(class="templates-view-shell editor")
|
||||||
assert template_html =~ "templates-monaco"
|
assert template_html =~ "templates-monaco"
|
||||||
assert template_html =~ ~s(data-monaco-language="liquid")
|
assert template_html =~ ~s(data-monaco-language="liquid")
|
||||||
assert template_html =~ ~s(data-monaco-word-wrap="on")
|
assert template_html =~ ~s(data-monaco-word-wrap="on")
|
||||||
|
|||||||
Reference in New Issue
Block a user