Compare commits
10 Commits
7990b74bf0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2111cfa1d1 | |||
| c8d1639afa | |||
| 43e11178db | |||
| 4828a68bcd | |||
| 2b25c13095 | |||
| 357bcae450 | |||
| 698da7f8b7 | |||
| d53eceb7a0 | |||
| 888e7575f0 | |||
| 8a0e2f22c1 |
@ -6,6 +6,8 @@ let TranslationFormHook = {
|
|||||||
this.tunit_editor = this.el.querySelector("#tunit-editor-content")
|
this.tunit_editor = this.el.querySelector("#tunit-editor-content")
|
||||||
this.save_edit_button = this.el.querySelector("#save-edit-button")
|
this.save_edit_button = this.el.querySelector("#save-edit-button")
|
||||||
this.save_publish_button = this.el.querySelector("#save-publish-button")
|
this.save_publish_button = this.el.querySelector("#save-publish-button")
|
||||||
|
let article_preview_links = document.querySelectorAll(".article-preview a")
|
||||||
|
article_preview_links.forEach(el => el.addEventListener('click', e => e.preventDefault()))
|
||||||
},
|
},
|
||||||
keyupHandler(e) {
|
keyupHandler(e) {
|
||||||
var push_event = true
|
var push_event = true
|
||||||
|
|||||||
@ -12,6 +12,7 @@ defmodule Outlook.Articles.Article do
|
|||||||
field :language, :string, default: "EN"
|
field :language, :string, default: "EN"
|
||||||
field :title, :string
|
field :title, :string
|
||||||
field :url, :string
|
field :url, :string
|
||||||
|
field :remarks, :string
|
||||||
belongs_to :author, Author
|
belongs_to :author, Author
|
||||||
has_many :translations, Translation, on_delete: :delete_all
|
has_many :translations, Translation, on_delete: :delete_all
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ defmodule Outlook.Articles.Article do
|
|||||||
@doc false
|
@doc false
|
||||||
def changeset(article, attrs) do
|
def changeset(article, attrs) do
|
||||||
article
|
article
|
||||||
|> cast(attrs, [:title, :content, :url, :language, :date, :author_id])
|
|> cast(attrs, [:title, :content, :url, :language, :date, :author_id, :remarks])
|
||||||
|> validate_required([:title, :content, :url, :language, :date, :author_id])
|
|> validate_required([:title, :content, :url, :language, :date, :author_id])
|
||||||
|> foreign_key_constraint(:author_id)
|
|> foreign_key_constraint(:author_id)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -56,6 +56,7 @@ defmodule Outlook.InternalTree do
|
|||||||
|
|
||||||
def render_public_content(tree, translation, language) do
|
def render_public_content(tree, translation, language) do
|
||||||
Translation.render_translation(tree, translation)
|
Translation.render_translation(tree, translation)
|
||||||
|
|> garnish(%{})
|
||||||
|> Html.render_doc()
|
|> Html.render_doc()
|
||||||
|> Hyphenation.hyphenate(language)
|
|> Hyphenation.hyphenate(language)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -10,7 +10,7 @@ defmodule Outlook.InternalTree.RawInternalBasic do
|
|||||||
|
|
||||||
@splitmarker "@@translationunit@@"
|
@splitmarker "@@translationunit@@"
|
||||||
@nonperiodmarker "@@nonperiod@@"
|
@nonperiodmarker "@@nonperiod@@"
|
||||||
@void_elements ~w(img br hr)
|
@void_elements ~w(img br hr iframe)
|
||||||
|
|
||||||
def set_split_markers([ %InternalNode{type: :text} = textnode | rest ]) do
|
def set_split_markers([ %InternalNode{type: :text} = textnode | rest ]) do
|
||||||
[ %InternalNode{textnode |
|
[ %InternalNode{textnode |
|
||||||
@ -18,7 +18,7 @@ defmodule Outlook.InternalTree.RawInternalBasic do
|
|||||||
|> String.replace(~r/\.\.\.+/u, "…")
|
|> String.replace(~r/\.\.\.+/u, "…")
|
||||||
|> String.replace(~r/([[:upper:]])\./u, "\\1#{@nonperiodmarker}")
|
|> String.replace(~r/([[:upper:]])\./u, "\\1#{@nonperiodmarker}")
|
||||||
|> String.replace(~r/(\d)\.(\d)/u, "\\1#{@nonperiodmarker}\\2")
|
|> String.replace(~r/(\d)\.(\d)/u, "\\1#{@nonperiodmarker}\\2")
|
||||||
|> String.replace(~r|([.?!]["'”]?\s*)|u, "\\1#{@splitmarker}")
|
|> String.replace(~r|([.?!]["'”’]?\s*)|u, "\\1#{@splitmarker}")
|
||||||
|> String.replace(@nonperiodmarker, ".")
|
|> String.replace(@nonperiodmarker, ".")
|
||||||
} | set_split_markers(rest) ]
|
} | set_split_markers(rest) ]
|
||||||
end
|
end
|
||||||
|
|||||||
@ -52,7 +52,7 @@ defmodule Outlook.InternalTree.TunitModifications do
|
|||||||
end
|
end
|
||||||
[%InternalNode{node| content: content} | apply_modifier(rest, modifier, tu_ids)]
|
[%InternalNode{node| content: content} | apply_modifier(rest, modifier, tu_ids)]
|
||||||
end
|
end
|
||||||
def apply_modifier([node, rest], modifier, tu_ids), do: [node | apply_modifier(rest, modifier, tu_ids)]
|
def apply_modifier([node | rest], modifier, tu_ids), do: [node | apply_modifier(rest, modifier, tu_ids)]
|
||||||
def apply_modifier([],_, _), do: []
|
def apply_modifier([],_, _), do: []
|
||||||
|
|
||||||
def process_tunit_list(tunits, modifier, tu_ids) do
|
def process_tunit_list(tunits, modifier, tu_ids) do
|
||||||
|
|||||||
@ -15,6 +15,7 @@ defmodule Outlook.Translations.Translation do
|
|||||||
field :public_content, :string
|
field :public_content, :string
|
||||||
field :title, :string
|
field :title, :string
|
||||||
field :unauthorized, :boolean, default: false
|
field :unauthorized, :boolean, default: false
|
||||||
|
field :remarks, :string
|
||||||
belongs_to :user, User
|
belongs_to :user, User
|
||||||
belongs_to :article, Article
|
belongs_to :article, Article
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ defmodule Outlook.Translations.Translation do
|
|||||||
@doc false
|
@doc false
|
||||||
def changeset(translation, attrs) do
|
def changeset(translation, attrs) do
|
||||||
translation
|
translation
|
||||||
|> cast(attrs, [:language, :title, :teaser, :date, :public, :unauthorized, :article_id, :public_content])
|
|> cast(attrs, [:language, :title, :teaser, :date, :public, :unauthorized, :article_id, :public_content, :remarks])
|
||||||
|> cast(attrs, [:content])
|
|> cast(attrs, [:content])
|
||||||
|> validate_required([:language, :title, :content, :date, :public, :unauthorized, :article_id])
|
|> validate_required([:language, :title, :content, :date, :public, :unauthorized, :article_id])
|
||||||
|> unique_constraint([:language, :article_id],
|
|> unique_constraint([:language, :article_id],
|
||||||
|
|||||||
@ -9,7 +9,7 @@ defmodule OutlookWeb.DarkModeComponent do
|
|||||||
|
|
||||||
def dark_mode_widget(assigns) do
|
def dark_mode_widget(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div id="dark-mode-widget" class="absolute flex w-full justify-between p-0" phx-hook="dark_mode_widget">
|
<div id="dark-mode-widget" class="relative flex w-full justify-between p-0" phx-hook="dark_mode_widget">
|
||||||
<div class="flex"></div>
|
<div class="flex"></div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<button class="p-2" type="button">
|
<button class="p-2" type="button">
|
||||||
|
|||||||
@ -3,6 +3,7 @@ defmodule OutlookWeb.HtmlTreeComponent do
|
|||||||
use Phoenix.Component
|
use Phoenix.Component
|
||||||
# use OutlookWeb, :html
|
# use OutlookWeb, :html
|
||||||
import OutlookWeb.CoreComponents
|
import OutlookWeb.CoreComponents
|
||||||
|
import OutlookWeb.ViewHelpers
|
||||||
|
|
||||||
alias Phoenix.LiveView.JS
|
alias Phoenix.LiveView.JS
|
||||||
|
|
||||||
@ -15,10 +16,14 @@ defmodule OutlookWeb.HtmlTreeComponent do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def attributes(assigns) do
|
def attributes(assigns) do
|
||||||
~H" <%= @name %>="<%= @value %>""
|
~H" <%= @name %>="<%= elipsed_text(@value, 16) %>""
|
||||||
end
|
end
|
||||||
|
|
||||||
def tnode(%{node: %{status: _}} = assigns), do: ~H"<%= String.slice(@node.content, 0..20) %><%= if String.length(@node.content) > 20 do %>...<% end %><br>"
|
def tnode(%{node: %{status: _}} = assigns) do
|
||||||
|
~H"""
|
||||||
|
<span title={@node.content} {@node.eph.attributes}><%= elipsed_text(@node.content, 30) %></span><br>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
def tnode(assigns) when assigns.node.type == :element do
|
def tnode(assigns) when assigns.node.type == :element do
|
||||||
~H"""
|
~H"""
|
||||||
@ -32,7 +37,7 @@ defmodule OutlookWeb.HtmlTreeComponent do
|
|||||||
|
|
||||||
def tnode(assigns) when assigns.node.type == :text do
|
def tnode(assigns) when assigns.node.type == :text do
|
||||||
~H"""
|
~H"""
|
||||||
"<%= String.slice(@node.content, 0..35) %><%= if String.length(@node.content) > 35 do %>..."<% end %><br>
|
"<%= elipsed_text(@node.content, 30) %><br>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<.dark_mode_widget />
|
<.dark_mode_widget />
|
||||||
</header>
|
</header>
|
||||||
<main class="px-2 py-4 sm:px-6 lg:px-8">
|
<main class="px-2 sm:px-6 lg:px-8">
|
||||||
<div class="mx-auto max-w-xl">
|
<div class="mx-auto max-w-xl">
|
||||||
<.flash kind={:info} title="Success!" flash={@flash} />
|
<.flash kind={:info} title="Success!" flash={@flash} />
|
||||||
<.flash kind={:error} title="Error!" flash={@flash} />
|
<.flash kind={:error} title="Error!" flash={@flash} />
|
||||||
|
|||||||
@ -26,6 +26,7 @@ defmodule OutlookWeb.ArticleLive.FormComponent do
|
|||||||
<.input field={{f, :language}} type="select" label="language"
|
<.input field={{f, :language}} type="select" label="language"
|
||||||
options={Application.get_env(:outlook,:deepl)[:source_langs]} />
|
options={Application.get_env(:outlook,:deepl)[:source_langs]} />
|
||||||
<.input field={{f, :date}} type="datetime-local" label="date" />
|
<.input field={{f, :date}} type="datetime-local" label="date" />
|
||||||
|
<.input field={{f, :remarks}} type="textarea" label="remarks" class="h-28" />
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Saving...">Save Article</.button>
|
<.button phx-disable-with="Saving...">Save Article</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
|
|||||||
@ -45,7 +45,7 @@ defmodule OutlookWeb.ArticleLive.NewComponents do
|
|||||||
~H"""
|
~H"""
|
||||||
<div>Review Translation Units</div>
|
<div>Review Translation Units</div>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<div id="html-tree" class="w-96 overflow-auto whitespace-nowrap">
|
<div id="html-tree" class="article w-96 overflow-auto whitespace-nowrap">
|
||||||
<.render_tree tree={@raw_internal_tree} ></.render_tree>
|
<.render_tree tree={@raw_internal_tree} ></.render_tree>
|
||||||
</div>
|
</div>
|
||||||
<div id="partition-preview" class="article show-boundary overflow-auto h-full">
|
<div id="partition-preview" class="article show-boundary overflow-auto h-full">
|
||||||
|
|||||||
@ -40,6 +40,10 @@
|
|||||||
<a href="#" class="hide-link" phx-click={JS.remove_class("show-boundary", to: ".article")}>hide boundaries</a>
|
<a href="#" class="hide-link" phx-click={JS.remove_class("show-boundary", to: ".article")}>hide boundaries</a>
|
||||||
<.render_doc tree={@article_content} />
|
<.render_doc tree={@article_content} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="my-4">
|
||||||
|
<div>Remarks:</div>
|
||||||
|
<%= @article.remarks |> tidy_raw %>
|
||||||
|
</div>
|
||||||
<div class="h-10" />
|
<div class="h-10" />
|
||||||
|
|
||||||
<.link class="text-sm font-semibold" navigate={~p"/translations/new?article_id=#{@article.id}"}>New Translation</.link>
|
<.link class="text-sm font-semibold" navigate={~p"/translations/new?article_id=#{@article.id}"}>New Translation</.link>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ defmodule OutlookWeb.TranslationLive.FormComponent do
|
|||||||
<:subtitle>Use this form to manage translation records in your database.</:subtitle>
|
<:subtitle>Use this form to manage translation records in your database.</:subtitle>
|
||||||
</.header>
|
</.header>
|
||||||
|
|
||||||
|
<div phx-click={JS.toggle(to: ".more-fields")} class="cursor-pointer">more/less fields</div>
|
||||||
<.simple_form
|
<.simple_form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={@changeset}
|
for={@changeset}
|
||||||
@ -23,15 +24,20 @@ defmodule OutlookWeb.TranslationLive.FormComponent do
|
|||||||
phx-submit="save"
|
phx-submit="save"
|
||||||
>
|
>
|
||||||
<.input field={{f, :article_id}} type="hidden" />
|
<.input field={{f, :article_id}} type="hidden" />
|
||||||
|
<div class="more-fields">
|
||||||
<.input field={{f, :language}} type="select" label="language"
|
<.input field={{f, :language}} type="select" label="language"
|
||||||
options={Application.get_env(:outlook,:deepl)[:target_langs]} />
|
options={Application.get_env(:outlook,:deepl)[:target_langs]} />
|
||||||
|
</div>
|
||||||
<.input field={{f, :title}} type="text" label="title" />
|
<.input field={{f, :title}} type="text" label="title" />
|
||||||
|
<div class="more-fields">
|
||||||
<.input field={{f, :teaser}} type="textarea" label="teaser" class="h-28" />
|
<.input field={{f, :teaser}} type="textarea" label="teaser" class="h-28" />
|
||||||
<.input field={{f, :date}} type="datetime-local" label="date" />
|
<.input field={{f, :date}} type="datetime-local" label="date" />
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<.input field={{f, :public}} type="checkbox" label="public" />
|
<.input field={{f, :public}} type="checkbox" label="public" />
|
||||||
<.input field={{f, :unauthorized}} type="checkbox" label="unauthorized" />
|
<.input field={{f, :unauthorized}} type="checkbox" label="unauthorized" />
|
||||||
</div>
|
</div>
|
||||||
|
<.input field={{f, :remarks}} type="textarea" label="remarks" class="h-28" />
|
||||||
|
</div>
|
||||||
<input type="hidden" id="continue_edit" name="continue_edit" value="false" />
|
<input type="hidden" id="continue_edit" name="continue_edit" value="false" />
|
||||||
<input type="hidden" id="publish" name="publish" value="false" />
|
<input type="hidden" id="publish" name="publish" value="false" />
|
||||||
<:actions>
|
<:actions>
|
||||||
@ -45,7 +51,7 @@ defmodule OutlookWeb.TranslationLive.FormComponent do
|
|||||||
</.simple_form>
|
</.simple_form>
|
||||||
<.tunit_editor current_tunit={@current_tunit} target={@myself} />
|
<.tunit_editor current_tunit={@current_tunit} target={@myself} />
|
||||||
</div>
|
</div>
|
||||||
<div class="article basis-1/2 max-h-screen overflow-auto">
|
<div class="article article-preview basis-1/2 max-h-screen overflow-auto">
|
||||||
<.button phx-disable-with="Translating..." phx-click="translate-deepl" phx-target={@myself}
|
<.button phx-disable-with="Translating..." phx-click="translate-deepl" phx-target={@myself}
|
||||||
data-confirm-not="Are you sure? All previously translated text will be lost.">Translate with Deepl</.button>
|
data-confirm-not="Are you sure? All previously translated text will be lost.">Translate with Deepl</.button>
|
||||||
<progress :if={@deepl_progress} max="100" value={@deepl_progress} />
|
<progress :if={@deepl_progress} max="100" value={@deepl_progress} />
|
||||||
|
|||||||
@ -11,11 +11,12 @@
|
|||||||
<.list>
|
<.list>
|
||||||
<:item title="Language"><%= @translation.language %></:item>
|
<:item title="Language"><%= @translation.language %></:item>
|
||||||
<:item title="Title"><%= @translation.title %></:item>
|
<:item title="Title"><%= @translation.title %></:item>
|
||||||
<:item title="Teaser"><%= @translation.teaser %></:item>
|
<:item title="Teaser"><%= @translation.teaser |> tidy_raw %></:item>
|
||||||
<%!-- <:item title="Content"><%= @translation.content %></:item> --%>
|
<%!-- <:item title="Content"><%= @translation.content %></:item> --%>
|
||||||
<:item title="Date"><%= @translation.date %></:item>
|
<:item title="Date"><%= @translation.date %></:item>
|
||||||
<:item title="Public"><%= @translation.public %></:item>
|
<:item title="Public"><%= @translation.public %></:item>
|
||||||
<:item title="Unauthorized"><%= @translation.unauthorized %></:item>
|
<:item title="Unauthorized"><%= @translation.unauthorized %></:item>
|
||||||
|
<:item title="Remarks"><%= @translation.remarks |> tidy_raw %></:item>
|
||||||
</.list>
|
</.list>
|
||||||
|
|
||||||
<div class="article show_status">
|
<div class="article show_status">
|
||||||
|
|||||||
@ -18,4 +18,13 @@ defmodule OutlookWeb.ViewHelpers do
|
|||||||
def strip_links(html) do
|
def strip_links(html) do
|
||||||
raise "Yet to be implemented!"
|
raise "Yet to be implemented!"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def elipsed_text(text, length) do
|
||||||
|
if String.length(text) < length do
|
||||||
|
text
|
||||||
|
else
|
||||||
|
part_length = (length - 3) / 2 |> trunc()
|
||||||
|
"#{String.slice(text, 0..part_length)} … #{String.slice(text, -part_length..-1)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Outlook.Repo.Migrations.AddRemarksToTranslations do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:translations) do
|
||||||
|
add :remarks, :text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Outlook.Repo.Migrations.AddRemarksToArticles do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:articles) do
|
||||||
|
add :remarks, :text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user