diff --git a/lib/outlook/internal_tree.ex b/lib/outlook/internal_tree.ex
index b70485f..88cf60a 100644
--- a/lib/outlook/internal_tree.ex
+++ b/lib/outlook/internal_tree.ex
@@ -9,9 +9,9 @@ defmodule Outlook.InternalTree do
|> Html.to_html()
end
- def render_html_preview(tree) do
+ def render_html_preview(tree, target \\ "1") do
tree
- |> Html.to_html_preview("1")
+ |> Html.to_html_preview(target)
end
require Logger
diff --git a/lib/outlook/translations.ex b/lib/outlook/translations.ex
index b94a777..08f773d 100644
--- a/lib/outlook/translations.ex
+++ b/lib/outlook/translations.ex
@@ -35,7 +35,10 @@ defmodule Outlook.Translations do
** (Ecto.NoResultsError)
"""
- def get_translation!(id), do: Repo.get!(Translation, id)
+ def get_translation!(id) do
+ Repo.get!(Translation, id)
+ |> Repo.preload([:article])
+ end
@doc """
Creates a translation.
diff --git a/lib/outlook/translations/basic.ex b/lib/outlook/translations/basic.ex
new file mode 100644
index 0000000..68fec6c
--- /dev/null
+++ b/lib/outlook/translations/basic.ex
@@ -0,0 +1,25 @@
+defmodule Outlook.Translations.Basic do
+
+ alias Outlook.InternalTree.InternalNode
+ alias Outlook.InternalTree.TranslationUnit
+
+ def internal_tree_to_tunit_map(tree) do
+ collect_translation_units(tree)
+ |> Enum.map(fn tunit -> {tunit.uuid, tunit} end)
+ |> Enum.into(%{})
+ end
+
+ defp collect_translation_units([%InternalNode{type: :element} = node | rest]) do
+ collect_translation_units(node.content) ++ collect_translation_units(rest)
+ end
+
+ defp collect_translation_units([%TranslationUnit{} = tunit | rest]) do
+ [tunit | collect_translation_units(rest)]
+ end
+
+ defp collect_translation_units([_|rest]) do
+ [] ++ collect_translation_units(rest)
+ end
+
+ defp collect_translation_units([]), do: []
+end
diff --git a/lib/outlook/translations/translation.ex b/lib/outlook/translations/translation.ex
index 3d9fd16..c3e2b33 100644
--- a/lib/outlook/translations/translation.ex
+++ b/lib/outlook/translations/translation.ex
@@ -24,7 +24,7 @@ defmodule Outlook.Translations.Translation do
def changeset(translation, attrs) do
translation
|> cast(attrs, [:lang, :title, :teaser, :date, :public, :unauthorized, :article_id])
- |> cast(attrs, [:content], force_changes: true)
+ |> cast(attrs, [:content])
|> validate_required([:lang, :title, :content, :date, :public, :unauthorized, :article_id])
|> unique_constraint([:lang, :article_id],
message: "translation for this language already exists",
diff --git a/lib/outlook_web.ex b/lib/outlook_web.ex
index 1c7a24a..24df2b1 100644
--- a/lib/outlook_web.ex
+++ b/lib/outlook_web.ex
@@ -87,6 +87,7 @@ defmodule OutlookWeb do
# Core UI components and translation
import OutlookWeb.CoreComponents
import OutlookWeb.HtmlTreeComponent
+ import OutlookWeb.TunitEditorComponent
import OutlookWeb.Gettext
# Shortcut for generating JS commands
diff --git a/lib/outlook_web/components/tunit_editor_component.ex b/lib/outlook_web/components/tunit_editor_component.ex
new file mode 100644
index 0000000..0270b2e
--- /dev/null
+++ b/lib/outlook_web/components/tunit_editor_component.ex
@@ -0,0 +1,51 @@
+defmodule OutlookWeb.TunitEditorComponent do
+
+ use Phoenix.Component
+
+ import OutlookWeb.CoreComponents
+ # alias Phoenix.LiveView.JS
+
+ defp statuses do
+ [ {:untranslated, "bg-red-800 col-span-3 disabled:border-gray-600"},
+ {:passable, "bg-amber-500 col-span-2 disabled:border-gray-600"},
+ {:done, "bg-green-700 disabled:border-gray-600"} ]
+ end
+
+ attr :current_tunit, :any
+ attr :target, :string
+
+ def tunit_editor(assigns) do
+ assigns = unless assigns.current_tunit do
+ assigns
+ |> assign(
+ current_tunit: %Outlook.InternalTree.TranslationUnit{},
+ disabled: true
+ )
+ else
+ assigns |> assign(disabled: false)
+ end
+ ~H"""
+
+ <%!--
+ <%= @current_tunit.content |> raw %>
+
--%>
+
+
+
+
+ <.status_button class={class} status={status} target={@target} disabled={@current_tunit.status == status}>
+
+
+ """
+ end
+
+ defp status_button(assigns) do
+ ~H"""
+ <.link phx-click="tunit_status" phx-value-status={@status} phx-target={@target}>
+ <.button class={@class} title="select translation status" disabled={@disabled}><%= @status |> to_string %>
+
+ """
+ end
+end
diff --git a/lib/outlook_web/live/article_live/index.html.heex b/lib/outlook_web/live/article_live/index.html.heex
index e3b4d21..530619b 100644
--- a/lib/outlook_web/live/article_live/index.html.heex
+++ b/lib/outlook_web/live/article_live/index.html.heex
@@ -15,6 +15,7 @@
<.link navigate={~p"/articles/#{article}"}>Show
<.link patch={~p"/articles/#{article}/edit"}>Edit
+ <.link navigate={~p"/translations/new?article_id=#{article}"}>New Translation
<:action :let={article}>
<.link phx-click={JS.push("delete", value: %{id: article.id})} data-confirm="Are you sure?">
diff --git a/lib/outlook_web/live/article_live/new_components.ex b/lib/outlook_web/live/article_live/new_components.ex
index c67a420..635a339 100644
--- a/lib/outlook_web/live/article_live/new_components.ex
+++ b/lib/outlook_web/live/article_live/new_components.ex
@@ -47,4 +47,12 @@ defmodule OutlookWeb.ArticleLive.NewComponents do
<.button phx-click="approve_translation_units">Continue
"""
end
+
+ def final_form(assigns) do
+ ~H"""
+ Final Form
+
+ <.button phx-click="save">Save
+ """
+ end
end
diff --git a/lib/outlook_web/live/article_live/show.html.heex b/lib/outlook_web/live/article_live/show.html.heex
index e3e0d07..dc5a745 100644
--- a/lib/outlook_web/live/article_live/show.html.heex
+++ b/lib/outlook_web/live/article_live/show.html.heex
@@ -18,6 +18,8 @@
<%= InternalTree.render_html(@article.content) |> raw %>
+<.link navigate={~p"/translations/new?article_id=#{@article.id}"}>New Translation
+
<.back navigate={~p"/articles"}>Back to articles
<.modal :if={@live_action == :edit} id="article-modal" show on_cancel={JS.patch(~p"/articles/#{@article}")}>
diff --git a/lib/outlook_web/live/translation_live/edit.ex b/lib/outlook_web/live/translation_live/edit.ex
new file mode 100644
index 0000000..e6fe131
--- /dev/null
+++ b/lib/outlook_web/live/translation_live/edit.ex
@@ -0,0 +1,28 @@
+defmodule OutlookWeb.TranslationLive.Edit do
+ use OutlookWeb, :live_view
+
+ alias Outlook.Articles
+ alias Outlook.Translations
+
+ @impl true
+ def render(assigns) do
+ ~H"""
+ <.live_component
+ module={OutlookWeb.TranslationLive.FormComponent}
+ id={:new}
+ title="New Translation"
+ action={@live_action}
+ translation={@translation}
+ translation_content={@translation_content}
+ navigate={~p"/translations"}
+ />
+ """
+ end
+
+ @impl true
+ def mount(%{"id" => id} = _params, _session, socket) do
+ socket = socket
+ |> assign_new(:translation, fn -> Translations.get_translation!(id) end)
+ {:ok, assign_new(socket, :translation_content, fn -> socket.assigns.translation.content end)}
+ end
+end
diff --git a/lib/outlook_web/live/translation_live/form_component.ex b/lib/outlook_web/live/translation_live/form_component.ex
index a08bbe7..b8e914e 100644
--- a/lib/outlook_web/live/translation_live/form_component.ex
+++ b/lib/outlook_web/live/translation_live/form_component.ex
@@ -1,36 +1,45 @@
defmodule OutlookWeb.TranslationLive.FormComponent do
use OutlookWeb, :live_component
- alias Outlook.Translations
+ alias Outlook.{Translations,InternalTree}
+ alias Outlook.InternalTree.TranslationUnit
@impl true
def render(assigns) do
~H"""
-
- <.header>
- <%= @title %>
- <:subtitle>Use this form to manage translation records in your database.
-
+
+
+ <.header>
+ <%= @title %>
+ <:subtitle>Use this form to manage translation records in your database.
+
- <.simple_form
- :let={f}
- for={@changeset}
- id="translation-form"
- phx-target={@myself}
- phx-change="validate"
- phx-submit="save"
- >
- <.input field={{f, :lang}} type="text" label="lang" />
- <.input field={{f, :title}} type="text" label="title" />
- <.input field={{f, :teaser}} type="text" label="teaser" />
- <.input field={{f, :content}} type="text" label="content" />
- <.input field={{f, :date}} type="datetime-local" label="date" />
- <.input field={{f, :public}} type="checkbox" label="public" />
- <.input field={{f, :unauthorized}} type="checkbox" label="unauthorized" />
- <:actions>
- <.button phx-disable-with="Saving...">Save Translation
-
-
+ <.simple_form
+ :let={f}
+ for={@changeset}
+ id="translation-form"
+ phx-target={@myself}
+ phx-change="validate"
+ phx-submit="save"
+ >
+ <.input field={{f, :article_id}} type="hidden" />
+ <.input field={{f, :lang}} type="select" label="lang"
+ options={Application.get_env(:outlook,:deepl)[:target_langs]} />
+ <.input field={{f, :title}} type="text" label="title" />
+ <.input field={{f, :teaser}} type="textarea" label="teaser" class="h-28" />
+ <%!-- <.input field={{f, :content}} type="text" label="content" /> --%>
+ <.input field={{f, :date}} type="datetime-local" label="date" />
+ <.input field={{f, :public}} type="checkbox" label="public" />
+ <.input field={{f, :unauthorized}} type="checkbox" label="unauthorized" />
+ <:actions>
+ <.button phx-disable-with="Saving...">Save Translation
+
+
+ <.tunit_editor current_tunit={@current_tunit} target={@myself} />
+
+
+ <%= InternalTree.render_html_preview(@translation.article.content, @myself) |> raw %>
+
"""
end
@@ -56,9 +65,41 @@ defmodule OutlookWeb.TranslationLive.FormComponent do
end
def handle_event("save", %{"translation" => translation_params}, socket) do
+ socket = socket
+ |> update_translation_with_current_tunit()
+ translation_params = translation_params
+ |> Map.put("content", socket.assigns.translation_content)
save_translation(socket, socket.assigns.action, translation_params)
end
+ def handle_event("tunit_status", %{"status" => status}, socket) do
+ tunit = %TranslationUnit{socket.assigns.current_tunit | status: String.to_atom(status)}
+ {:noreply, socket |> assign(:current_tunit, tunit)}
+ end
+
+ def handle_event("select_current_tunit", %{"uuid" => uuid}, socket) do
+ {:noreply,
+ socket
+ |> update_translation_with_current_tunit
+ |> assign(:current_tunit, socket.assigns.translation_content[uuid])}
+ end
+
+ def handle_event("update_current_tunit", %{"content" => content}, socket) do
+ tunit = %TranslationUnit{socket.assigns.current_tunit | content: content}
+ {:noreply, socket |> assign(:current_tunit, tunit)}
+ end
+
+ defp update_translation_with_current_tunit(socket) do
+ translation_content = if socket.assigns.current_tunit do
+ socket.assigns.translation_content
+ |> Map.put(socket.assigns.current_tunit.uuid, socket.assigns.current_tunit)
+ else
+ socket.assigns.translation_content
+ end
+ socket
+ |> assign(:translation_content, translation_content)
+ end
+
defp save_translation(socket, :edit, translation_params) do
case Translations.update_translation(socket.assigns.translation, translation_params) do
{:ok, _translation} ->
diff --git a/lib/outlook_web/live/translation_live/index.html.heex b/lib/outlook_web/live/translation_live/index.html.heex
index e0379a1..edcf9f0 100644
--- a/lib/outlook_web/live/translation_live/index.html.heex
+++ b/lib/outlook_web/live/translation_live/index.html.heex
@@ -11,7 +11,7 @@
<:col :let={translation} label="Lang"><%= translation.lang %>
<:col :let={translation} label="Title"><%= translation.title %>
<:col :let={translation} label="Teaser"><%= translation.teaser %>
- <:col :let={translation} label="Content"><%= translation.content %>
+ <%!-- <:col :let={translation} label="Content"><%= translation.content %> --%>
<:col :let={translation} label="Date"><%= translation.date %>
<:col :let={translation} label="Public"><%= translation.public %>
<:col :let={translation} label="Unauthorized"><%= translation.unauthorized %>
@@ -19,7 +19,7 @@
<.link navigate={~p"/translations/#{translation}"}>Show
- <.link patch={~p"/translations/#{translation}/edit"}>Edit
+ <.link navigate={~p"/translations/#{translation}/edit"}>Edit
<:action :let={translation}>
<.link phx-click={JS.push("delete", value: %{id: translation.id})} data-confirm="Are you sure?">
diff --git a/lib/outlook_web/live/translation_live/new.ex b/lib/outlook_web/live/translation_live/new.ex
new file mode 100644
index 0000000..600b6d4
--- /dev/null
+++ b/lib/outlook_web/live/translation_live/new.ex
@@ -0,0 +1,38 @@
+defmodule OutlookWeb.TranslationLive.New do
+ use OutlookWeb, :live_view
+
+ alias Outlook.Articles
+ alias Outlook.Translations.{Translation,Basic}
+
+ @impl true
+ def render(assigns) do
+ ~H"""
+ <.live_component
+ module={OutlookWeb.TranslationLive.FormComponent}
+ id={:new}
+ title="New Translation"
+ action={@live_action}
+ translation={@translation}
+ translation_content={@translation_content}
+ navigate={~p"/translations"}
+ />
+ """
+ end
+
+ @impl true
+ def mount(%{"article_id" => article_id} = _params, _session, socket) do
+ socket = socket
+ |> assign_new(:translation, fn ->
+ %Translation{
+ article_id: article_id,
+ article: get_article(article_id)
+ }
+ end)
+ {:ok, assign_new(socket, :translation_content, fn ->
+ Basic.internal_tree_to_tunit_map(socket.assigns.translation.article.content) end)}
+ end
+
+ defp get_article(article_id) do
+ Articles.get_article!(article_id)
+ end
+end
diff --git a/lib/outlook_web/live/translation_live/show.html.heex b/lib/outlook_web/live/translation_live/show.html.heex
index 8703f90..4efb2ab 100644
--- a/lib/outlook_web/live/translation_live/show.html.heex
+++ b/lib/outlook_web/live/translation_live/show.html.heex
@@ -2,7 +2,7 @@
Translation <%= @translation.id %>
<:subtitle>This is a translation record from your database.
<:actions>
- <.link patch={~p"/translations/#{@translation}/show/edit"} phx-click={JS.push_focus()}>
+ <.link navigate={~p"/translations/#{@translation}/edit"} phx-click={JS.push_focus()}>
<.button>Edit translation
@@ -12,21 +12,10 @@
<:item title="Lang"><%= @translation.lang %>
<:item title="Title"><%= @translation.title %>
<:item title="Teaser"><%= @translation.teaser %>
- <:item title="Content"><%= @translation.content %>
+ <%!-- <:item title="Content"><%= @translation.content %> --%>
<:item title="Date"><%= @translation.date %>
<:item title="Public"><%= @translation.public %>
<:item title="Unauthorized"><%= @translation.unauthorized %>
<.back navigate={~p"/translations"}>Back to translations
-
-<.modal :if={@live_action == :edit} id="translation-modal" show on_cancel={JS.patch(~p"/translations/#{@translation}")}>
- <.live_component
- module={OutlookWeb.TranslationLive.FormComponent}
- id={@translation.id}
- title={@page_title}
- action={@live_action}
- translation={@translation}
- navigate={~p"/translations/#{@translation}"}
- />
-
diff --git a/lib/outlook_web/router.ex b/lib/outlook_web/router.ex
index c37de82..297abaa 100644
--- a/lib/outlook_web/router.ex
+++ b/lib/outlook_web/router.ex
@@ -86,11 +86,12 @@ defmodule OutlookWeb.Router do
live "/articles/:id/show/edit", ArticleLive.Show, :edit
live "/translations", TranslationLive.Index, :index
- live "/translations/new", TranslationLive.Index, :new
- live "/translations/:id/edit", TranslationLive.Index, :edit
+
+ live "/translations/new", TranslationLive.New, :new
+
+ live "/translations/:id/edit", TranslationLive.Edit, :edit
live "/translations/:id", TranslationLive.Show, :show
- live "/translations/:id/show/edit", TranslationLive.Show, :edit
live "/deepl_accounts", DeeplAccountLive.Index, :index
live "/deepl_accounts/new", DeeplAccountLive.Index, :new