feat: step 5 claimed done
This commit is contained in:
@@ -514,14 +514,10 @@ defmodule BDS.Desktop.ShellCommands do
|
||||
|
||||
defp normalize_translation_validation(report) do
|
||||
%{
|
||||
summary: %{
|
||||
missing_count: length(report.missing),
|
||||
orphan_count: length(report.orphan_files),
|
||||
do_not_translate_count: length(report.do_not_translate_posts)
|
||||
},
|
||||
missing: Enum.map(report.missing, &stringify_map/1),
|
||||
orphan_files: report.orphan_files,
|
||||
do_not_translate_posts: report.do_not_translate_posts
|
||||
checked_database_row_count: report.checked_database_row_count,
|
||||
checked_filesystem_file_count: report.checked_filesystem_file_count,
|
||||
invalid_database_rows: Enum.map(report.invalid_database_rows, &stringify_map/1),
|
||||
invalid_filesystem_files: Enum.map(report.invalid_filesystem_files, &stringify_map/1)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -532,11 +528,10 @@ defmodule BDS.Desktop.ShellCommands do
|
||||
project_id: project_id,
|
||||
route: "translation_validation",
|
||||
title: "Translation Validation",
|
||||
subtitle: "Published posts checked against required blog languages",
|
||||
subtitle: "Database rows and translation files checked for invalid state",
|
||||
editorMeta: [
|
||||
%{label: "Missing", value: Integer.to_string(length(report.missing))},
|
||||
%{label: "Orphan Files", value: Integer.to_string(length(report.orphan_files))},
|
||||
%{label: "Skipped", value: Integer.to_string(length(report.do_not_translate_posts))}
|
||||
%{label: "Invalid DB", value: Integer.to_string(length(report.invalid_database_rows))},
|
||||
%{label: "Invalid Files", value: Integer.to_string(length(report.invalid_filesystem_files))}
|
||||
],
|
||||
payload: normalize_translation_validation(report)
|
||||
}
|
||||
|
||||
@@ -97,7 +97,9 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> assign(:script_editor_drafts, %{})
|
||||
|> assign(:template_editor_drafts, %{})
|
||||
|> assign(:chat_editor_inputs, %{})
|
||||
|> assign(:chat_model_selectors_open, %{})
|
||||
|> assign(:misc_editor_selected_pairs, %{})
|
||||
|> assign(:misc_editor_git_selected_files, %{})
|
||||
|> assign(:metadata_diff_active_tabs, %{})
|
||||
|> assign(:metadata_diff_field_filters, %{})
|
||||
|> assign(:shell_overlay, nil)
|
||||
@@ -683,6 +685,14 @@ defmodule BDS.Desktop.ShellLive do
|
||||
{:noreply, ChatEditor.update_input(socket, message, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("toggle_chat_model_selector", _params, socket) do
|
||||
{:noreply, ChatEditor.toggle_model_selector(socket, &reload_shell/2)}
|
||||
end
|
||||
|
||||
def handle_event("select_chat_model", %{"model" => model_id}, socket) do
|
||||
{:noreply, ChatEditor.set_model(socket, model_id, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
|
||||
def handle_event("send_chat_editor_message", _params, socket) do
|
||||
{:noreply, ChatEditor.send_message(socket, &reload_shell/2, &append_output_entry/5)}
|
||||
end
|
||||
@@ -701,6 +711,17 @@ defmodule BDS.Desktop.ShellLive do
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("fix_translation_validation", _params, socket) do
|
||||
case MiscEditor.fix_translation_validation(socket, &append_output_entry/5) do
|
||||
{:rerun, next_socket} -> {:noreply, apply_shell_command(next_socket, "validate_translations")}
|
||||
{:socket, next_socket} -> {:noreply, next_socket}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("select_git_diff_file", %{"path" => path}, socket) do
|
||||
{:noreply, socket |> MiscEditor.select_git_diff_file(path) |> assign_misc_editor()}
|
||||
end
|
||||
|
||||
def handle_event("toggle_duplicate_pair", %{"pair-id" => pair_id}, socket) do
|
||||
{:noreply, MiscEditor.toggle_duplicate(socket, pair_id, &reload_shell/2)}
|
||||
end
|
||||
|
||||
@@ -13,6 +13,31 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
assign(socket, :chat_editor, build(socket.assigns))
|
||||
end
|
||||
|
||||
def toggle_model_selector(socket, reload) do
|
||||
%{id: conversation_id} = socket.assigns.current_tab
|
||||
current = Map.get(socket.assigns.chat_model_selectors_open, conversation_id, false)
|
||||
|
||||
socket
|
||||
|> assign(:chat_model_selectors_open, Map.put(socket.assigns.chat_model_selectors_open, conversation_id, not current))
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
|
||||
def set_model(socket, model_id, reload, append_output) do
|
||||
%{id: conversation_id} = socket.assigns.current_tab
|
||||
|
||||
case AI.set_conversation_model(conversation_id, model_id) do
|
||||
{:ok, _conversation} ->
|
||||
socket
|
||||
|> assign(:chat_model_selectors_open, Map.put(socket.assigns.chat_model_selectors_open, conversation_id, false))
|
||||
|> reload.(socket.assigns.workbench)
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Chat"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
|
||||
def update_input(socket, value, reload) do
|
||||
%{id: conversation_id} = socket.assigns.current_tab
|
||||
|
||||
@@ -53,10 +78,12 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
%ChatConversation{} = conversation ->
|
||||
%{
|
||||
id: conversation.id,
|
||||
title: conversation.title || translated("New Chat"),
|
||||
title: conversation.title || translated("chat.newChat"),
|
||||
model: conversation.model,
|
||||
available_models: AI.available_chat_models(conversation.model),
|
||||
model_selector_open?: Map.get(assigns.chat_model_selectors_open, conversation.id, false),
|
||||
input: Map.get(assigns.chat_editor_inputs, conversation.id, ""),
|
||||
messages: AI.list_chat_messages(conversation.id),
|
||||
messages: build_entries(AI.list_chat_messages(conversation.id)),
|
||||
offline?: Map.get(assigns, :offline_mode, true)
|
||||
}
|
||||
end
|
||||
@@ -64,5 +91,89 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
|
||||
def build(_assigns), do: nil
|
||||
|
||||
def message_role_label(:user), do: translated("chat.role.you")
|
||||
def message_role_label(_role), do: translated("chat.role.assistant")
|
||||
|
||||
def tool_call_name(tool_call) when is_map(tool_call) do
|
||||
Map.get(tool_call, "name") || Map.get(tool_call, :name) || "tool"
|
||||
end
|
||||
|
||||
def tool_surface_type(surface), do: Map.get(surface, :type, "json")
|
||||
|
||||
defp build_entries(messages) do
|
||||
{entries, current_entry} =
|
||||
Enum.reduce(messages, {[], nil}, fn message, {entries, current_entry} ->
|
||||
case message.role do
|
||||
:tool ->
|
||||
if current_entry && current_entry.role == :assistant do
|
||||
{entries, append_tool_surface(current_entry, message)}
|
||||
else
|
||||
{entries, current_entry}
|
||||
end
|
||||
|
||||
:system ->
|
||||
{entries, current_entry}
|
||||
|
||||
_other ->
|
||||
entries = finalize_entry(entries, current_entry)
|
||||
{entries, start_entry(message)}
|
||||
end
|
||||
end)
|
||||
|
||||
entries
|
||||
|> finalize_entry(current_entry)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
defp finalize_entry(entries, nil), do: entries
|
||||
defp finalize_entry(entries, entry), do: [entry | entries]
|
||||
|
||||
defp start_entry(message) do
|
||||
%{
|
||||
id: message.id,
|
||||
role: message.role,
|
||||
content: message.content || "",
|
||||
tool_markers: normalize_tool_calls(message.tool_calls),
|
||||
tool_surfaces: []
|
||||
}
|
||||
end
|
||||
|
||||
defp append_tool_surface(entry, message) do
|
||||
case normalize_tool_surface(message.content) do
|
||||
nil -> entry
|
||||
surface -> update_in(entry.tool_surfaces, &(&1 ++ [surface]))
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_tool_calls(tool_calls) when is_list(tool_calls) do
|
||||
Enum.map(tool_calls, fn tool_call ->
|
||||
%{
|
||||
name: tool_call_name(tool_call),
|
||||
arguments: Map.get(tool_call, "arguments") || Map.get(tool_call, :arguments) || Map.get(tool_call, "args") || Map.get(tool_call, :args) || %{}
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
defp normalize_tool_calls(_tool_calls), do: []
|
||||
|
||||
defp normalize_tool_surface(content) when is_binary(content) do
|
||||
case Jason.decode(content) do
|
||||
{:ok, %{"type" => type} = decoded} ->
|
||||
%{
|
||||
type: type,
|
||||
title: decoded["title"],
|
||||
columns: List.wrap(decoded["columns"]),
|
||||
rows: Enum.map(List.wrap(decoded["rows"]), &List.wrap/1),
|
||||
fields: List.wrap(decoded["fields"]),
|
||||
data: decoded
|
||||
}
|
||||
|
||||
_other ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_tool_surface(_content), do: nil
|
||||
|
||||
def translated(text, bindings \\ %{}), do: ShellData.translate(text, bindings, Process.get(:bds_ui_locale))
|
||||
end
|
||||
|
||||
@@ -1,37 +1,113 @@
|
||||
<div class="chat-panel" data-testid="chat-editor">
|
||||
<div class="chat-panel-header">
|
||||
<div class="chat-panel-title"><%= @chat_editor.title %></div>
|
||||
|
||||
<div class="chat-panel-header-actions">
|
||||
<button
|
||||
class="chat-model-selector-button"
|
||||
type="button"
|
||||
phx-click="toggle_chat_model_selector"
|
||||
data-testid="chat-model-selector-button"
|
||||
>
|
||||
<span><%= @chat_editor.model || translated("chat.newChat") %></span>
|
||||
<span class="chat-model-selector-caret">▾</span>
|
||||
</button>
|
||||
|
||||
<%= if @chat_editor.model_selector_open? and @chat_editor.available_models != [] do %>
|
||||
<div class="chat-model-selector-menu">
|
||||
<%= for model <- @chat_editor.available_models do %>
|
||||
<button
|
||||
class={[
|
||||
"chat-model-selector-option",
|
||||
if(model.id == @chat_editor.model, do: "active")
|
||||
]}
|
||||
type="button"
|
||||
phx-click="select_chat_model"
|
||||
phx-value-model={model.id}
|
||||
>
|
||||
<%= model.name %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-messages">
|
||||
<%= if Enum.empty?(@chat_editor.messages) do %>
|
||||
<div class="chat-welcome">
|
||||
<div class="chat-welcome-icon">🤖</div>
|
||||
<h2><%= translated("New Chat") %></h2>
|
||||
<p><%= translated("Ask the assistant about the active project.") %></p>
|
||||
<h2><%= translated("chat.welcomeTitle") %></h2>
|
||||
<p><%= translated("chat.welcomeDescription") %></p>
|
||||
<ul>
|
||||
<li><%= translated("Search posts and media") %></li>
|
||||
<li><%= translated("Inspect metadata") %></li>
|
||||
<li><%= translated("Open related tabs") %></li>
|
||||
<li><%= translated("Review generated output") %></li>
|
||||
<li><%= translated("Navigate settings") %></li>
|
||||
<li><%= translated("chat.welcomeTipSearch") %></li>
|
||||
<li><%= translated("chat.welcomeTipChart") %></li>
|
||||
<li><%= translated("chat.welcomeTipTable") %></li>
|
||||
<li><%= translated("chat.welcomeTipMetadata") %></li>
|
||||
<li><%= translated("chat.welcomeTipTabs") %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= for message <- @chat_editor.messages do %>
|
||||
<div class={["chat-message", to_string(message.role || "assistant")]}>
|
||||
<div class="chat-message-content">
|
||||
<div class="chat-message-header"><span class="chat-message-role"><%= String.capitalize(to_string(message.role || "assistant")) %></span></div>
|
||||
<div class="chat-message-header"><span class="chat-message-role"><%= message_role_label(message.role) %></span></div>
|
||||
|
||||
<%= if message.tool_markers != [] do %>
|
||||
<div class="chat-tool-markers">
|
||||
<%= for tool_call <- message.tool_markers do %>
|
||||
<div class="chat-tool-marker" data-testid="chat-tool-marker">
|
||||
<span class="chat-tool-marker-name"><%= tool_call_name(tool_call) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="chat-message-text"><%= message.content || "" %></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= for surface <- message.tool_surfaces do %>
|
||||
<article class="chat-tool-surface" data-testid="chat-tool-surface">
|
||||
<%= if surface.title do %>
|
||||
<h3><%= surface.title %></h3>
|
||||
<% end %>
|
||||
|
||||
<%= case tool_surface_type(surface) do %>
|
||||
<% "table" -> %>
|
||||
<div class="chat-tool-surface-table-wrap">
|
||||
<table class="chat-tool-surface-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<%= for column <- surface.columns do %>
|
||||
<th><%= column %></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= for row <- surface.rows do %>
|
||||
<tr>
|
||||
<%= for value <- row do %>
|
||||
<td><%= value %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<% _other -> %>
|
||||
<pre class="chat-tool-surface-json"><%= Jason.encode!(surface.data, pretty: true) %></pre>
|
||||
<% end %>
|
||||
</article>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="chat-input-container">
|
||||
<form class="chat-input-wrapper" phx-change="change_chat_editor_input">
|
||||
<textarea class="chat-input chat-surface-input" name="message" rows="1" placeholder={translated("Send a message")}><%= @chat_editor.input %></textarea>
|
||||
<textarea class="chat-input chat-surface-input" name="message" rows="1" placeholder={translated("chat.inputPlaceholder")}><%= @chat_editor.input %></textarea>
|
||||
<button class="chat-send-button" type="button" phx-click="send_chat_editor_message" disabled={String.trim(@chat_editor.input || "") == "" or @chat_editor.offline?}>↑</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,11 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.{Embeddings, Generation, Git}
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.{Embeddings, Generation, Git, Posts, Repo}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Settings.Setting
|
||||
|
||||
embed_templates "misc_editor_html/*"
|
||||
|
||||
@@ -114,6 +117,37 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
end
|
||||
end
|
||||
|
||||
def fix_translation_validation(socket, append_output) do
|
||||
report =
|
||||
socket.assigns
|
||||
|> meta()
|
||||
|> Map.get(:payload, %{})
|
||||
|> normalize_translation_validation_report()
|
||||
|
||||
{:ok, result} = Posts.fix_invalid_translations(report)
|
||||
|
||||
{:rerun,
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("Translation Validation"),
|
||||
translated("translationValidation.toast.fixSuccess", %{
|
||||
dbRows: result.deleted_database_rows,
|
||||
files: result.deleted_files,
|
||||
flushed: result.flushed_translations
|
||||
})
|
||||
)}
|
||||
rescue
|
||||
error -> {:socket, append_output.(socket, translated("Translation Validation"), inspect(error), nil, "error")}
|
||||
end
|
||||
|
||||
def select_git_diff_file(socket, file_path) do
|
||||
assign(
|
||||
socket,
|
||||
:misc_editor_git_selected_files,
|
||||
Map.put(socket.assigns.misc_editor_git_selected_files, socket.assigns.current_tab.id, file_path)
|
||||
)
|
||||
end
|
||||
|
||||
def metadata_diff_repair_request(socket, field, direction) do
|
||||
meta = meta(socket.assigns)
|
||||
payload = Map.get(meta, :payload, %{})
|
||||
@@ -245,14 +279,23 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
end
|
||||
|
||||
defp build_translation_validation(meta, payload) do
|
||||
report = normalize_translation_validation_report(payload)
|
||||
|
||||
%{
|
||||
kind: :translation_validation,
|
||||
title: Map.get(meta, :title, translated("Translation Validation")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
summary: Map.get(payload, :summary, %{}),
|
||||
missing: Map.get(payload, :missing, []),
|
||||
orphan_files: Map.get(payload, :orphan_files, []),
|
||||
do_not_translate_posts: Map.get(payload, :do_not_translate_posts, [])
|
||||
summary: %{},
|
||||
summary_text:
|
||||
translated("translationValidation.summary", %{
|
||||
dbRows: report.checked_database_row_count,
|
||||
files: report.checked_filesystem_file_count,
|
||||
invalidDb: length(report.invalid_database_rows),
|
||||
invalidFiles: length(report.invalid_filesystem_files)
|
||||
}),
|
||||
invalid_database_rows: report.invalid_database_rows,
|
||||
invalid_filesystem_files: report.invalid_filesystem_files,
|
||||
can_fix?: report.invalid_database_rows != [] or report.invalid_filesystem_files != []
|
||||
}
|
||||
end
|
||||
|
||||
@@ -270,29 +313,93 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
end
|
||||
|
||||
defp build_git_diff(assigns, meta) do
|
||||
diff_text =
|
||||
case Git.diff(assigns.projects.active_project_id) do
|
||||
{:ok, %{staged_diff: staged, unstaged_diff: unstaged}} ->
|
||||
[
|
||||
"# Staged Changes\n\n",
|
||||
if(String.trim(staged) == "", do: translated("No staged changes"), else: staged),
|
||||
"\n\n# Working Tree\n\n",
|
||||
if(String.trim(unstaged) == "", do: translated("No unstaged changes"), else: unstaged)
|
||||
]
|
||||
|> IO.iodata_to_binary()
|
||||
project_id = assigns.projects.active_project_id
|
||||
selected_files = Map.get(assigns, :misc_editor_git_selected_files, %{})
|
||||
|
||||
{:error, reason} -> inspect(reason)
|
||||
{files, diff, error_message} =
|
||||
case Git.status(project_id) do
|
||||
{:ok, %{files: files}} ->
|
||||
file_paths = files |> Enum.map(&Map.get(&1, :path)) |> Enum.reject(&is_nil/1) |> Enum.uniq() |> Enum.sort()
|
||||
selected_file_path = select_git_diff_path(assigns.current_tab.id, file_paths, selected_files)
|
||||
|
||||
diff =
|
||||
case selected_file_path do
|
||||
nil ->
|
||||
empty_git_diff(project_id)
|
||||
|
||||
file_path ->
|
||||
case Git.get_diff_content(project_id, file_path) do
|
||||
{:ok, diff} -> diff
|
||||
{:error, reason} -> Map.merge(empty_git_diff(project_id), %{file_path: file_path, error: inspect(reason)})
|
||||
end
|
||||
end
|
||||
|
||||
{file_paths, diff, nil}
|
||||
|
||||
{:error, reason} ->
|
||||
{[], empty_git_diff(project_id), inspect(reason)}
|
||||
end
|
||||
|
||||
preferences = git_diff_preferences()
|
||||
|
||||
%{
|
||||
kind: :git_diff,
|
||||
title: Map.get(meta, :title, translated("Git Diff")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
diff_text: diff_text,
|
||||
summary: %{}
|
||||
summary: %{},
|
||||
files: files,
|
||||
selected_file_path: diff.file_path,
|
||||
active_diff: Map.put(diff, :language, git_diff_language(diff.file_path)),
|
||||
preferences: preferences,
|
||||
empty_message: error_message || translated("No unstaged changes")
|
||||
}
|
||||
end
|
||||
|
||||
def translation_issue_label(issue) do
|
||||
case issue_value(issue, :issue) do
|
||||
"same-language-as-canonical" -> translated("translationValidation.issue.sameLanguage")
|
||||
"do-not-translate-has-translations" -> translated("translationValidation.issue.doNotTranslate")
|
||||
"content-in-database" -> translated("translationValidation.issue.contentInDatabase")
|
||||
_other -> translated("translationValidation.issue.missingSource")
|
||||
end
|
||||
end
|
||||
|
||||
def translation_issue_languages(issue) do
|
||||
canonical_language = issue_value(issue, :canonical_language)
|
||||
translation_language = issue_value(issue, :translation_language)
|
||||
|
||||
if canonical_language in [nil, ""] do
|
||||
translation_language
|
||||
else
|
||||
translated("translationValidation.languagesWithCanonical", %{
|
||||
canonical: canonical_language,
|
||||
translation: translation_language
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def translation_issue_value(issue, key), do: issue_value(issue, key)
|
||||
|
||||
def git_diff_language(nil), do: "plaintext"
|
||||
|
||||
def git_diff_language(file_path) do
|
||||
case file_path |> Path.extname() |> String.downcase() do
|
||||
".md" -> "markdown"
|
||||
".markdown" -> "markdown"
|
||||
".mdx" -> "markdown"
|
||||
".ts" -> "typescript"
|
||||
".tsx" -> "typescript"
|
||||
".js" -> "javascript"
|
||||
".jsx" -> "javascript"
|
||||
".json" -> "json"
|
||||
".css" -> "css"
|
||||
".html" -> "html"
|
||||
".yml" -> "yaml"
|
||||
".yaml" -> "yaml"
|
||||
_other -> "plaintext"
|
||||
end
|
||||
end
|
||||
|
||||
defp meta(assigns) do
|
||||
Map.get(assigns.tab_meta, {assigns.current_tab.type, assigns.current_tab.id}, %{})
|
||||
end
|
||||
@@ -496,4 +603,60 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
defp diff_name(diff) do
|
||||
Map.get(diff, :field) || Map.get(diff, "field") || Map.get(diff, :name) || Map.get(diff, "name") || "value"
|
||||
end
|
||||
|
||||
defp normalize_translation_validation_report(payload) when is_map(payload) do
|
||||
%{
|
||||
checked_database_row_count: issue_value(payload, :checked_database_row_count) || 0,
|
||||
checked_filesystem_file_count: issue_value(payload, :checked_filesystem_file_count) || 0,
|
||||
invalid_database_rows:
|
||||
payload
|
||||
|> issue_value(:invalid_database_rows)
|
||||
|> List.wrap(),
|
||||
invalid_filesystem_files:
|
||||
payload
|
||||
|> issue_value(:invalid_filesystem_files)
|
||||
|> List.wrap()
|
||||
}
|
||||
end
|
||||
|
||||
defp normalize_translation_validation_report(_payload) do
|
||||
%{
|
||||
checked_database_row_count: 0,
|
||||
checked_filesystem_file_count: 0,
|
||||
invalid_database_rows: [],
|
||||
invalid_filesystem_files: []
|
||||
}
|
||||
end
|
||||
|
||||
defp issue_value(issue, key) when is_map(issue) do
|
||||
Map.get(issue, key) || Map.get(issue, Atom.to_string(key))
|
||||
end
|
||||
|
||||
defp issue_value(_issue, _key), do: nil
|
||||
|
||||
defp select_git_diff_path(tab_id, files, selected_files) do
|
||||
selected = Map.get(selected_files, tab_id)
|
||||
|
||||
if selected in files do
|
||||
selected
|
||||
else
|
||||
List.first(files)
|
||||
end
|
||||
end
|
||||
|
||||
defp empty_git_diff(file_path) do
|
||||
%{file_path: file_path, original: "", modified: "", error: nil}
|
||||
end
|
||||
|
||||
defp git_diff_preferences do
|
||||
%{
|
||||
view_style: get_global_setting("ui.git_diff_view_style") || "inline",
|
||||
word_wrap: get_global_setting("ui.git_diff_word_wrap") == "true",
|
||||
hide_unchanged_regions: get_global_setting("ui.git_diff_hide_unchanged_regions") == "true"
|
||||
}
|
||||
end
|
||||
|
||||
defp get_global_setting(key) do
|
||||
Repo.one(from setting in Setting, where: setting.key == ^key, select: setting.value)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -181,10 +181,79 @@
|
||||
</div>
|
||||
|
||||
<% :translation_validation -> %>
|
||||
<div class="misc-columns">
|
||||
<section class="misc-card"><h3><%= translated("Missing") %></h3><ul><%= for issue <- @misc_editor.missing do %><li><%= inspect(issue) %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= translated("Orphan Files") %></h3><ul><%= for file <- @misc_editor.orphan_files do %><li><%= file %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= translated("Do Not Translate") %></h3><ul><%= for post <- @misc_editor.do_not_translate_posts do %><li><%= inspect(post) %></li><% end %></ul></section>
|
||||
<div class="translation-validation-view">
|
||||
<section class="translation-validation-summary">
|
||||
<p><%= @misc_editor.summary_text %></p>
|
||||
</section>
|
||||
|
||||
<section class="translation-validation-section">
|
||||
<h3><%= translated("translationValidation.databaseTitle") %></h3>
|
||||
|
||||
<%= if @misc_editor.invalid_database_rows == [] do %>
|
||||
<p class="translation-validation-empty"><%= translated("translationValidation.noneDatabase") %></p>
|
||||
<% else %>
|
||||
<div class="translation-validation-list">
|
||||
<%= for issue <- @misc_editor.invalid_database_rows do %>
|
||||
<article class="translation-validation-card translation-validation-card-db" data-testid="translation-validation-card">
|
||||
<p class="translation-validation-card-title"><%= translation_issue_label(issue) %></p>
|
||||
<dl class="translation-validation-card-meta">
|
||||
<dt><%= translated("translationValidation.field.translationFor") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :translation_for) %></dd>
|
||||
<%= if translation_issue_value(issue, :translation_id) do %>
|
||||
<dt><%= translated("translationValidation.field.translationId") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :translation_id) %></dd>
|
||||
<% end %>
|
||||
<%= if translation_issue_value(issue, :title) do %>
|
||||
<dt><%= translated("translationValidation.field.title") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :title) %></dd>
|
||||
<% end %>
|
||||
<dt><%= translated("translationValidation.field.languages") %></dt>
|
||||
<dd><%= translation_issue_languages(issue) %></dd>
|
||||
<%= if translation_issue_value(issue, :file_path) do %>
|
||||
<dt><%= translated("translationValidation.field.filePath") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :file_path) %></dd>
|
||||
<% end %>
|
||||
</dl>
|
||||
</article>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<section class="translation-validation-section">
|
||||
<h3><%= translated("translationValidation.filesystemTitle") %></h3>
|
||||
|
||||
<%= if @misc_editor.invalid_filesystem_files == [] do %>
|
||||
<p class="translation-validation-empty"><%= translated("translationValidation.noneFilesystem") %></p>
|
||||
<% else %>
|
||||
<div class="translation-validation-list">
|
||||
<%= for issue <- @misc_editor.invalid_filesystem_files do %>
|
||||
<article class="translation-validation-card translation-validation-card-file" data-testid="translation-validation-card">
|
||||
<p class="translation-validation-card-title"><%= translation_issue_label(issue) %></p>
|
||||
<dl class="translation-validation-card-meta">
|
||||
<dt><%= translated("translationValidation.field.translationFor") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :translation_for) %></dd>
|
||||
<%= if translation_issue_value(issue, :title) do %>
|
||||
<dt><%= translated("translationValidation.field.title") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :title) %></dd>
|
||||
<% end %>
|
||||
<dt><%= translated("translationValidation.field.languages") %></dt>
|
||||
<dd><%= translation_issue_languages(issue) %></dd>
|
||||
<%= if translation_issue_value(issue, :file_path) do %>
|
||||
<dt><%= translated("translationValidation.field.filePath") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :file_path) %></dd>
|
||||
<% end %>
|
||||
</dl>
|
||||
</article>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<div class="translation-validation-actions">
|
||||
<button class="secondary" type="button" phx-click="rerun_misc_editor" data-testid="translation-validation-revalidate"><%= translated("translationValidation.revalidate") %></button>
|
||||
<button class="primary" type="button" phx-click="fix_translation_validation" data-testid="translation-validation-fix" disabled={not @misc_editor.can_fix?}><%= translated("translationValidation.fix") %></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% :find_duplicates -> %>
|
||||
@@ -202,7 +271,35 @@
|
||||
</div>
|
||||
|
||||
<% :git_diff -> %>
|
||||
<div class="misc-card misc-code-card"><pre><code><%= @misc_editor.diff_text %></code></pre></div>
|
||||
<div class="git-diff-view">
|
||||
<%= if @misc_editor.files == [] do %>
|
||||
<p class="git-diff-empty"><%= @misc_editor.empty_message %></p>
|
||||
<% else %>
|
||||
<form class="git-diff-toolbar" phx-change="select_git_diff_file">
|
||||
<label for="git-diff-file-select"><%= translated("gitDiff.changedFiles") %></label>
|
||||
<select id="git-diff-file-select" data-testid="git-diff-file-select" name="path">
|
||||
<%= for file_path <- @misc_editor.files do %>
|
||||
<option value={file_path} selected={file_path == @misc_editor.selected_file_path}><%= file_path %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<div
|
||||
id={"git-diff-editor-#{String.replace(@misc_editor.active_diff.file_path || "working-tree", ~r/[^a-zA-Z0-9_-]+/, "-")}"}
|
||||
class="git-diff-editor"
|
||||
phx-hook="MonacoDiffEditor"
|
||||
data-monaco-diff-language={@misc_editor.active_diff.language}
|
||||
data-monaco-diff-file-path={@misc_editor.active_diff.file_path}
|
||||
data-monaco-diff-view-style={@misc_editor.preferences.view_style}
|
||||
data-monaco-diff-word-wrap={if(@misc_editor.preferences.word_wrap, do: "on", else: "off")}
|
||||
data-monaco-diff-hide-unchanged={if(@misc_editor.preferences.hide_unchanged_regions, do: "true", else: "false")}
|
||||
>
|
||||
<textarea class="monaco-diff-original" hidden><%= @misc_editor.active_diff.original %></textarea>
|
||||
<textarea class="monaco-diff-modified" hidden><%= @misc_editor.active_diff.modified %></textarea>
|
||||
<div class="monaco-diff-editor-instance"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user