diff --git a/core/assets/css/app.css b/core/assets/css/app.css index 25a2f807d..946e624c7 100644 --- a/core/assets/css/app.css +++ b/core/assets/css/app.css @@ -222,17 +222,17 @@ trix-editor, trix-editor h1, .wysiwig h1 { - @apply text-title2 font-title2 mb-8; + @apply text-title3 font-title3 mb-8; } trix-editor h2, .wysiwig h2 { - @apply text-title3 font-title3 mb-8; + @apply text-title4 font-title4 mb-8; } trix-editor h3, .wysiwig h3 { - @apply text-title4 font-title4 mb-8; + @apply text-title5 font-title5 mb-8; } trix-editor strong, diff --git a/core/assets/js/feldspar_app.js b/core/assets/js/feldspar_app.js index 8cf14c428..2b8a2962e 100644 --- a/core/assets/js/feldspar_app.js +++ b/core/assets/js/feldspar_app.js @@ -25,6 +25,6 @@ export const FeldsparApp = { }, handleMessage(e) { - this.pushEvent("app_event", e.data); + this.pushEvent("feldspar_event", e.data); }, }; diff --git a/core/config/dev.exs b/core/config/dev.exs index e1526ab02..33c6e92ed 100644 --- a/core/config/dev.exs +++ b/core/config/dev.exs @@ -85,14 +85,6 @@ config :core, config :core, :s3, bucket: "eylixir" -config :core, - :data_donation_storage_backend, - fake: Systems.Storage.FakeBackend, - s3: Systems.Storage.AWS.Backend, - azure: Systems.Storage.Azure.Backend, - centerdata: Systems.Storage.Centerdata.Backend, - yoda: Systems.Storage.YodaBackend - # For Minio (local S3) config :ex_aws, access_key_id: "my_access_key", diff --git a/core/config/runtime.exs b/core/config/runtime.exs index b681f1ea8..801f34963 100644 --- a/core/config/runtime.exs +++ b/core/config/runtime.exs @@ -26,15 +26,6 @@ if config_env() == :prod do port: String.to_integer(System.get_env("HTTP_PORT", "8000")) ] - # Port - - config :core, - :data_donation_storage_backend, - s3: Systems.Storage.AWS.Backend, - azure: Systems.Storage.Azure.Backend, - centerdata: Systems.Storage.Centerdata.Backend, - yoda: Systems.Storage.YodaBackend - # MAILGUN if mailgun_api_key = System.get_env("MAILGUN_API_KEY") do diff --git a/core/frameworks/fabric.ex b/core/frameworks/fabric.ex index cfb66aee2..4ae221877 100644 --- a/core/frameworks/fabric.ex +++ b/core/frameworks/fabric.ex @@ -1,29 +1,129 @@ defmodule Fabric do - alias Fabric.LiveView - alias Fabric.LiveComponent + @type assigns :: map() + @type composition_id :: atom() + @type composition :: child() | element() + @type child :: %{module: module(), params: map()} + @type element :: map() | binary() | number() + + @callback compose(id :: composition_id(), a :: assigns()) :: composition() | nil + + defmacro __using__(_opts) do + quote do + @behaviour Fabric + + import Fabric + import Fabric.Html + + require Logger + + def compose_element(%Phoenix.LiveView.Socket{assigns: assigns} = socket, element_id) + when is_atom(element_id) do + %Phoenix.LiveView.Socket{socket | assigns: compose_element(assigns, element_id)} + end + + def compose_element(%{} = assigns, element_id) when is_atom(element_id) do + element = compose(element_id, assigns) + Phoenix.Component.assign(assigns, element_id, element) + end + + def compose_child(context, child_id, opts \\ []) + + def compose_child(%Phoenix.LiveView.Socket{assigns: assigns} = socket, child_id, opts) + when is_atom(child_id) do + %Phoenix.LiveView.Socket{socket | assigns: compose_child(assigns, child_id)} + end + + def compose_child(%{fabric: fabric} = assigns, child_id, only: update) + when is_atom(child_id) do + if exists?(fabric, child_id) do + compose_child(assigns, child_id) + else + assigns + end + end + + def compose_child(%{fabric: fabric} = assigns, child_id, _opts) when is_atom(child_id) do + fabric = + if child = prepare_child(fabric, child_id, compose(child_id, assigns)) do + add_child(fabric, child) + else + remove_child(fabric, child_id) + end + + Phoenix.Component.assign(assigns, fabric: fabric) + end + + def compose(_id, _assigns) do + Logger.error("compose/2 not implemented") + nil + end + + defoverridable compose: 2 + + def element(_id, _assigns) do + Logger.error("element/2 not implemented") + nil + end + + defoverridable element: 2 + end + end + + # Prepare + + def prepare_child(context, child_id, %{module: module, params: params}) do + prepare_child(context, child_id, module, params) + end + + def prepare_child(_context, _child_id, _), do: nil def prepare_child( - %Phoenix.LiveView.Socket{assigns: %{fabric: fabric}}, + %Phoenix.LiveView.Socket{assigns: assigns}, child_id, module, params ) do + prepare_child(assigns, child_id, module, params) + end + + def prepare_child(%{fabric: fabric}, child_id, module, params) do prepare_child(fabric, child_id, module, params) end def prepare_child(%Fabric.Model{self: self}, child_id, module, params) do - child_ref = %LiveComponent.RefModel{id: child_id, module: module} - child_fabric = %Fabric.Model{parent: self, self: child_ref, children: []} + child_ref = %Fabric.LiveComponent.RefModel{id: child_id, module: module} + child_fabric = %Fabric.Model{parent: self, self: child_ref, children: nil} params = Map.put(params, :fabric, child_fabric) - %LiveComponent.Model{ref: child_ref, params: params} + %Fabric.LiveComponent.Model{ref: child_ref, params: params} + end + + def install_children(%Phoenix.LiveView.Socket{assigns: %{fabric: fabric}} = socket, children) + when is_list(children) do + Phoenix.Component.assign(socket, fabric: install_children(fabric, children)) + end + + def install_children(%{fabric: fabric} = assigns, children) when is_list(children) do + Phoenix.Component.assign(assigns, fabric: install_children(fabric, children)) + end + + def install_children(%Fabric.Model{} = fabric, children) when is_list(children) do + %Fabric.Model{fabric | children: children} end def get_child(%Phoenix.LiveView.Socket{assigns: %{fabric: fabric}}, child_id) do get_child(fabric, child_id) end + def get_child(%{fabric: fabric}, child_id) do + get_child(fabric, child_id) + end + def get_child(%Fabric.Model{children: children}, child_id) do - Enum.find(children, &(&1.ref.id == child_id)) + Enum.find(List.wrap(children), &(&1.ref.id == child_id)) + end + + def exists?(context, child_id) do + get_child(context, child_id) != nil end def new_fabric(%Phoenix.LiveView.Socket{} = socket) do @@ -32,56 +132,93 @@ defmodule Fabric do end def new_fabric() do - %Fabric.Model{parent: nil, children: []} + %Fabric.Model{parent: nil, children: nil} + end + + def show_child( + %Phoenix.LiveView.Socket{assigns: assigns} = socket, + %Fabric.LiveComponent.Model{} = child + ) do + %Phoenix.LiveView.Socket{socket | assigns: show_child(assigns, child)} end - def show_child(%Phoenix.LiveView.Socket{} = socket, %LiveComponent.Model{} = child) do - socket |> add_child(child) + def show_child(%{fabric: fabric} = assigns, %Fabric.LiveComponent.Model{} = child) do + Phoenix.Component.assign(assigns, fabric: add_child(fabric, child)) end def replace_child( - %Phoenix.LiveView.Socket{} = socket, - %LiveComponent.Model{ref: %{id: id}} = child + %Phoenix.LiveView.Socket{assigns: assigns} = socket, + %Fabric.LiveComponent.Model{} = child ) do - socket - |> remove_child(id) - |> add_child(child) + %Phoenix.LiveView.Socket{socket | assigns: replace_child(assigns, child)} end - def hide_child(%Phoenix.LiveView.Socket{} = socket, child_id) do - socket |> remove_child(child_id) + def replace_child( + %{fabric: fabric} = assigns, + %Fabric.LiveComponent.Model{ref: %{id: id}} = child + ) do + Phoenix.Component.assign(assigns, + fabric: + fabric + |> remove_child(id) + |> add_child(child) + ) end - def show_popup(%Phoenix.LiveView.Socket{} = socket, %LiveComponent.Model{} = child) do - socket - |> add_child(child) - |> send_event(:root, "show_popup", child) + def hide_child(%Phoenix.LiveView.Socket{assigns: assigns} = socket, child_id) do + %Phoenix.LiveView.Socket{socket | assigns: hide_child(assigns, child_id)} end - def hide_popup(%Phoenix.LiveView.Socket{} = socket, child_id) do - socket - |> remove_child(child_id) - |> send_event(:root, "hide_popup") + def hide_child(%{fabric: fabric} = assigns, child_id) do + Phoenix.Component.assign(assigns, fabric: remove_child(fabric, child_id)) end - def add_child(%Phoenix.LiveView.Socket{assigns: %{fabric: fabric}} = socket, child) do - fabric = add_child(fabric, child) - Phoenix.Component.assign(socket, :fabric, fabric) + def show_popup( + %Phoenix.LiveView.Socket{assigns: assigns} = socket, + %Fabric.LiveComponent.Model{} = child + ) do + %Phoenix.LiveView.Socket{socket | assigns: show_popup(assigns, child)} end - def add_child(%Fabric.Model{children: children} = fabric, %LiveComponent.Model{} = child) do - %Fabric.Model{fabric | children: children ++ [child]} + def show_popup(%{fabric: fabric} = assigns, %Fabric.LiveComponent.Model{} = child) do + send_event(fabric, :root, "show_popup", child) + Phoenix.Component.assign(assigns, fabric: add_child(fabric, child)) end - def remove_child(%Phoenix.LiveView.Socket{assigns: %{fabric: fabric}} = socket, child_id) do - fabric = remove_child(fabric, child_id) - Phoenix.Component.assign(socket, :fabric, fabric) + def hide_popup(%Phoenix.LiveView.Socket{assigns: assigns} = socket, child_id) do + %Phoenix.LiveView.Socket{socket | assigns: hide_popup(assigns, child_id)} + end + + def hide_popup(%{fabric: fabric} = assigns, child_id) do + send_event(fabric, :root, "hide_popup") + Phoenix.Component.assign(assigns, fabric: remove_child(fabric, child_id)) + end + + def add_child(%Fabric.Model{children: children} = fabric, %Fabric.LiveComponent.Model{} = child) do + %Fabric.Model{fabric | children: List.wrap(children) ++ List.wrap(child)} end + def remove_child(%Fabric.Model{} = fabric, nil), do: fabric + def remove_child(%Fabric.Model{children: children} = fabric, child_id) do - %Fabric.Model{fabric | children: Enum.filter(children, &(&1.ref.id != child_id))} + %Fabric.Model{fabric | children: Enum.filter(List.wrap(children), &(&1.ref.id != child_id))} + end + + # Flow + def show_next(%Phoenix.LiveView.Socket{assigns: %{fabric: fabric}} = socket) do + Phoenix.Component.assign(socket, fabric: show_next(fabric)) + end + + def show_next(%Fabric.Model{children: children} = fabric) do + %Fabric.Model{fabric | children: List.wrap(children) |> List.delete_at(0)} end + def get_current_child(%Fabric.Model{children: children}) do + List.wrap(children) |> List.first() + end + + # Events + def send_event(_, _, _, payload \\ %{}) def send_event( @@ -111,17 +248,25 @@ defmodule Fabric do send_event(self, %{name: name, payload: payload}) end + def send_event(%Fabric.Model{children: [%{ref: ref} | _]}, :flow, name, payload) do + send_event(ref, %{name: name, payload: payload}) + end + + def send_event(%Fabric.Model{}, :flow, name, _payload) do + raise "Sending event '#{name}' to empty flow" + end + def send_event(%Fabric.Model{} = fabric, child_id, name, payload) do if child = get_child(fabric, child_id) do send_event(child.ref, %{name: name, payload: payload}) end end - def send_event(%LiveComponent.RefModel{id: id, module: module}, event) do + def send_event(%Fabric.LiveComponent.RefModel{id: id, module: module}, event) do Phoenix.LiveView.send_update(module, %{id: id, fabric_event: event}) end - def send_event(%LiveView.RefModel{pid: pid}, event) do + def send_event(%Fabric.LiveView.RefModel{pid: pid}, event) do send_event(pid, event) end diff --git a/core/frameworks/fabric/html.ex b/core/frameworks/fabric/html.ex index b2f04f6a5..f1da296e4 100644 --- a/core/frameworks/fabric/html.ex +++ b/core/frameworks/fabric/html.ex @@ -10,9 +10,41 @@ defmodule Fabric.Html do ~H""" <%= if child = Fabric.get_child(@fabric, @id) do %> <%= render_slot(@header) %> - <.live_component {Map.from_struct(child.ref)} {child.params}/> + <.live_child {Map.from_struct(child)} /> <%= render_slot(@footer) %> <% end %> """ end + + attr(:fabric, :map, required: true) + + def flow(assigns) do + ~H""" + <%= if child = Fabric.get_current_child(@fabric) do %> + <.live_child {Map.from_struct(child)} /> + <% end %> + """ + end + + attr(:fabric, :map, required: true) + attr(:gap, :string, default: "gap-4") + + def stack(assigns) do + ~H""" +
+ <%= for child <- @fabric.children do %> + <.live_child {Map.from_struct(child)} /> + <% end %> +
+ """ + end + + attr(:ref, :map, required: true) + attr(:params, :map, required: true) + + def live_child(assigns) do + ~H""" + <.live_component {Map.from_struct(@ref)} {@params}/> + """ + end end diff --git a/core/frameworks/fabric/live_component.ex b/core/frameworks/fabric/live_component.ex index b0f22da5c..f64370867 100644 --- a/core/frameworks/fabric/live_component.ex +++ b/core/frameworks/fabric/live_component.ex @@ -13,19 +13,29 @@ defmodule Fabric.LiveComponent do defmacro __using__(_opts) do quote do - import Fabric - import Fabric.Html + use Fabric + use Phoenix.LiveComponent + @impl true def update(%{fabric_event: %{name: name, payload: payload}}, socket) do - {:noreply, socket} = __MODULE__.handle_event(name, payload, socket) + {:noreply, socket} = handle_event(name, payload, socket) {:ok, socket} end + @impl true def update(%{id: _id, fabric: fabric} = params, socket) do params = Map.drop(params, [:fabric]) socket = assign(socket, fabric: fabric) update(params, socket) end + + @impl true + def handle_event(_name, _payload, socket) do + Logger.error("handle_event/3 not implemented") + {:noreply, socket} + end + + defoverridable handle_event: 3 end end end diff --git a/core/frameworks/fabric/live_view.ex b/core/frameworks/fabric/live_view.ex index 79e376979..e2a77a233 100644 --- a/core/frameworks/fabric/live_view.ex +++ b/core/frameworks/fabric/live_view.ex @@ -4,16 +4,37 @@ defmodule Fabric.LiveView do defstruct [:pid] end - defmacro __using__(_opts) do + defmacro __using__(layout) do quote do - import Fabric - import Fabric.Html + use Phoenix.LiveView, layout: {unquote(layout), :live} + unquote(helpers()) + end + end + + defmacro __using__() do + quote do + use Phoenix.LiveView + unquote(helpers()) + end + end + + def helpers() do + quote do + use Fabric @before_compile Fabric.LiveView + @impl true def handle_info(%{fabric_event: %{name: name, payload: payload}}, socket) do - __MODULE__.handle_event(name, payload, socket) + handle_event(name, payload, socket) end + + @impl true + def handle_event(_name, _payload, _socket) do + raise "handle_event/3 not implemented" + end + + defoverridable handle_event: 3 end end @@ -26,7 +47,7 @@ defmodule Fabric.LiveView do """ def mount(params, session, socket) do self = %Fabric.LiveView.RefModel{pid: self()} - fabric = %Fabric.Model{parent: nil, self: self, children: []} + fabric = %Fabric.Model{parent: nil, self: self, children: nil} socket = Phoenix.Component.assign(socket, :fabric, fabric) super(params, session, socket) end diff --git a/core/frameworks/fabric/model.ex b/core/frameworks/fabric/model.ex index 44202f992..6e7db1b0a 100644 --- a/core/frameworks/fabric/model.ex +++ b/core/frameworks/fabric/model.ex @@ -5,11 +5,15 @@ defmodule Fabric.Model do @type ref :: LiveView.RefModel.t() | LiveComponent.RefModel.t() @type ref_optional :: ref | nil @type model :: LiveComponent.Model.t() + @type model_id :: atom() + @type model_id_optional :: model_id() | nil + @type model_list :: [model()] + @type model_list_optional :: model_list() | nil @type t :: %__MODULE__{ parent: ref_optional(), - self: ref, - children: [model()] + self: ref(), + children: model_list_optional() } defstruct [:parent, :self, :children] diff --git a/core/lib/core_web.ex b/core/lib/core_web.ex index 0fa0f969c..fc3868475 100644 --- a/core/lib/core_web.ex +++ b/core/lib/core_web.ex @@ -101,14 +101,60 @@ defmodule CoreWeb do end end + def live_view_fabric do + quote do + unquote(component_helpers()) + + import Phoenix.Controller, + only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1] + + use CoreWeb.LiveLocale + use CoreWeb.LiveUri + import Core.Authorization, only: [can_access?: 2] + use Frameworks.GreenLight.Live, Core.Authorization + alias CoreWeb.Router.Helpers, as: Routes + + use CoreWeb.LiveAssignHelper + import Core.FeatureFlags + + use Frameworks.Pixel.Flash + + import CoreWeb.UrlResolver, only: [url_resolver: 1] + + import CoreWeb.UI.Popup + import CoreWeb.UI.Empty + alias CoreWeb.UI.Margin + + alias CoreWeb.UI.Area + + # Routes generation with the ~p sigil + unquote(verified_routes()) + end + end + def live_component do quote do unquote(component_helpers()) use Phoenix.LiveComponent + unquote(live_component_helpers()) + unquote(verified_routes()) + end + end + + def live_component_fabric do + quote do + unquote(component_helpers()) + unquote(live_component_helpers()) + unquote(verified_routes()) + end + end + + def live_component_helpers do + quote do def update_target(%{id: id, type: type}, message) when is_map(message) do - send_update(type, message |> Map.put(:id, id)) + Phoenix.LiveView.send_update(type, message |> Map.put(:id, id)) end def update_target(pid, message) when is_pid(pid) do @@ -124,9 +170,6 @@ defmodule CoreWeb do Map.put(socket, :assigns, assigns) end - - # Routes generation with the ~p sigil - unquote(verified_routes()) end end diff --git a/core/lib/core_web/live_form.ex b/core/lib/core_web/live_form.ex index 5250fff1c..2793913c4 100644 --- a/core/lib/core_web/live_form.ex +++ b/core/lib/core_web/live_form.ex @@ -1,9 +1,22 @@ defmodule CoreWeb.LiveForm do + defmacro __using__(:fabric) do + quote do + use CoreWeb, :live_component_fabric + unquote(flash_helpers()) + unquote(form_helpers()) + end + end + defmacro __using__(_opts) do quote do use CoreWeb, :live_component - import Frameworks.Pixel.Form + unquote(flash_helpers()) + unquote(form_helpers()) + end + end + def flash_helpers() do + quote do def hide_flash(socket) do Frameworks.Pixel.Flash.push_hide() socket @@ -29,6 +42,12 @@ defmodule CoreWeb.LiveForm do Frameworks.Pixel.Flash.push_info(message) socket end + end + end + + def form_helpers() do + quote do + import Frameworks.Pixel.Form def save(socket, changeset) do socket diff --git a/core/priv/gettext/en/LC_MESSAGES/eyra-consent.po b/core/priv/gettext/en/LC_MESSAGES/eyra-consent.po index 95f6a6712..514c23985 100644 --- a/core/priv/gettext/en/LC_MESSAGES/eyra-consent.po +++ b/core/priv/gettext/en/LC_MESSAGES/eyra-consent.po @@ -18,3 +18,7 @@ msgstr "Someone made changes to the consent text. Please refresh the page to con #, elixir-autogen, elixir-format msgid "default.consent.text" msgstr "
Write here your custom consent terms and conditions..
" + +#, elixir-autogen, elixir-format +msgid "onboarding.consent.checkbox" +msgstr "I have read and agree with the above terms" diff --git a/core/priv/gettext/eyra-consent.pot b/core/priv/gettext/eyra-consent.pot index 191c27999..3c1fbcac2 100644 --- a/core/priv/gettext/eyra-consent.pot +++ b/core/priv/gettext/eyra-consent.pot @@ -18,3 +18,7 @@ msgstr "" #, elixir-autogen, elixir-format msgid "default.consent.text" msgstr "" + +#, elixir-autogen, elixir-format +msgid "onboarding.consent.checkbox" +msgstr "" diff --git a/core/priv/gettext/nl/LC_MESSAGES/eyra-consent.po b/core/priv/gettext/nl/LC_MESSAGES/eyra-consent.po index 1c43d4e89..37ca4e1e4 100644 --- a/core/priv/gettext/nl/LC_MESSAGES/eyra-consent.po +++ b/core/priv/gettext/nl/LC_MESSAGES/eyra-consent.po @@ -18,3 +18,7 @@ msgstr "Iemand heeft de consent tekst is aangepast. Ververs de pagina om verder #, elixir-autogen, elixir-format msgid "default.consent.text" msgstr "
Beschrijf hier de consent voorwaarden
" + +#, elixir-autogen, elixir-format +msgid "onboarding.consent.checkbox" +msgstr "Ik heb de bovenstaande voorwaarden gelezen en ga ermee akkoord" diff --git a/core/systems/alliance/content_page.ex b/core/systems/alliance/content_page.ex index 9337e10b3..1e887c857 100644 --- a/core/systems/alliance/content_page.ex +++ b/core/systems/alliance/content_page.ex @@ -1,4 +1,5 @@ defmodule Systems.Alliance.ContentPage do + use CoreWeb, :live_view use Systems.Content.Page alias Systems.{ diff --git a/core/systems/assignment/connection_view.ex b/core/systems/assignment/connection_view.ex index 9a317a821..db9528f24 100644 --- a/core/systems/assignment/connection_view.ex +++ b/core/systems/assignment/connection_view.ex @@ -1,5 +1,5 @@ defmodule Systems.Assignment.ConnectionView do - use CoreWeb, :live_component + use CoreWeb, :live_component_fabric use Fabric.LiveComponent alias Frameworks.Pixel.Panel diff --git a/core/systems/assignment/connector_popup_panel.ex b/core/systems/assignment/connector_popup_panel.ex index dea885588..e769b825f 100644 --- a/core/systems/assignment/connector_popup_panel.ex +++ b/core/systems/assignment/connector_popup_panel.ex @@ -1,5 +1,5 @@ defmodule Systems.Assignment.ConnectorPopupPanel do - use CoreWeb, :live_component + use CoreWeb, :live_component_fabric use Fabric.LiveComponent import CoreWeb.UI.Dialog diff --git a/core/systems/assignment/connector_popup_storage.ex b/core/systems/assignment/connector_popup_storage.ex index 925641c67..62909c467 100644 --- a/core/systems/assignment/connector_popup_storage.ex +++ b/core/systems/assignment/connector_popup_storage.ex @@ -1,5 +1,5 @@ defmodule Systems.Assignment.ConnectorPopupStorage do - use CoreWeb, :live_component + use CoreWeb, :live_component_fabric use Fabric.LiveComponent import CoreWeb.UI.Dialog diff --git a/core/systems/assignment/connector_view.ex b/core/systems/assignment/connector_view.ex index 5ff02d90f..2a984b7dc 100644 --- a/core/systems/assignment/connector_view.ex +++ b/core/systems/assignment/connector_view.ex @@ -1,5 +1,5 @@ defmodule Systems.Assignment.ConnectorView do - use CoreWeb, :live_component + use CoreWeb, :live_component_fabric use Fabric.LiveComponent alias Systems.{ diff --git a/core/systems/assignment/content_page.ex b/core/systems/assignment/content_page.ex index 1e4807770..eb5c77dd7 100644 --- a/core/systems/assignment/content_page.ex +++ b/core/systems/assignment/content_page.ex @@ -1,6 +1,7 @@ defmodule Systems.Assignment.ContentPage do + use CoreWeb, :live_view_fabric + use Fabric.LiveView, CoreWeb.Layouts use Systems.Content.Page - use Fabric.LiveView alias Systems.{ Assignment, diff --git a/core/systems/assignment/crew_page.ex b/core/systems/assignment/crew_page.ex index af3b88345..3d30ef6c2 100644 --- a/core/systems/assignment/crew_page.ex +++ b/core/systems/assignment/crew_page.ex @@ -1,24 +1,20 @@ defmodule Systems.Assignment.CrewPage do - use CoreWeb, :live_view + use CoreWeb, :live_view_fabric + use Fabric.LiveView, CoreWeb.Layouts + use Systems.Observatory.Public use CoreWeb.LiveRemoteIp use CoreWeb.Layouts.Stripped.Component, :projects require Logger - alias Frameworks.Concept + + alias CoreWeb.UI.Timestamp alias Systems.{ Assignment, - Project, - Workflow, - Crew, Storage } - import Assignment.StartView - import Project.ToolRefView - import Workflow.ItemViews, only: [work_list: 1] - @impl true def get_authorization_context(%{"id" => id}, _session, _socket) do %{crew: crew} = Assignment.Public.get!(String.to_integer(id), [:crew]) @@ -26,7 +22,7 @@ defmodule Systems.Assignment.CrewPage do end @impl true - def mount(%{"id" => id} = _params, _session, socket) do + def mount(%{"id" => id}, session, socket) do model = Assignment.Public.get!(id, Assignment.Model.preload_graph(:down)) { @@ -34,16 +30,11 @@ defmodule Systems.Assignment.CrewPage do socket |> assign( id: id, - model: model, - tool_ref_view: nil + model: model ) + |> update_panel_info(session) |> observe_view_model() - |> update_onboarding() - |> update_selected_item_id() - |> update_selected_item() - |> update_start_view() - |> update_work_list() - |> update_menus() + |> update_flow() } end @@ -51,255 +42,64 @@ defmodule Systems.Assignment.CrewPage do def handle_view_model_updated(socket) do socket - |> update_onboarding() - |> update_selected_item_id() - |> update_selected_item() - |> update_start_view() - |> update_work_list() + |> update_flow() |> update_menus() end - defp update_onboarding(%{assigns: %{vm: %{onboarding: onboarding}}} = socket) do - socket |> assign(onboarding: onboarding) - end - - defp update_selected_item_id(%{assigns: %{selected_item_id: selected_item_id}} = socket) - when not is_nil(selected_item_id) do - socket - end - - defp update_selected_item_id(%{assigns: %{vm: %{items: []}}} = socket) do - socket |> assign(selected_item_id: nil) - end - - defp update_selected_item_id(%{assigns: %{vm: %{items: [{%{id: id}, _}]}}} = socket) do - socket |> assign(selected_item_id: id) - end - - defp update_selected_item_id(%{assigns: %{vm: %{items: items}}} = socket) do - {%{id: selected_item_id}, _} = - Enum.find(items, List.first(items), fn {_, %{status: status}} -> status == :pending end) - - socket |> assign(selected_item_id: selected_item_id) - end - - defp update_selected_item( - %{assigns: %{selected_item_id: selected_item_id, vm: %{items: items}}} = socket - ) do - selected_item = Enum.find(items, fn {%{id: id}, _} -> id == selected_item_id end) - - socket |> assign(selected_item: selected_item) - end - - defp update_start_view( - %{ - assigns: %{ - selected_item: - {%{title: title, description: description, group: group}, _task} = selected_item - } - } = socket - ) do - button = %{ - action: start_action(selected_item), - face: %{type: :primary, label: "Start"} - } - - start_view = %{ - title: title, - description: description, - icon: group, - button: button - } - - socket |> assign(start_view: start_view) - end - - defp update_start_view(socket) do - socket |> assign(start_view: nil) - end - - defp start_action({%{tool_ref: tool_ref}, _task} = item) do - Project.ToolRefModel.tool(tool_ref) - |> Concept.ToolModel.launcher() - |> start_action(item) - end - - defp start_action(%{function: _, props: _}, {%{id: id}, _}) do - %{type: :send, event: "start", item: id} - end - - defp start_action(%{url: url}, _) do - %{type: :http_get, to: url, target: "_blank"} + defp update_panel_info(socket, %{"panel_info" => panel_info}) do + assign(socket, panel_info: panel_info) end - defp start_action(_, {%{id: id}, _}) do - %{type: :send, event: "start", item: id} + defp update_panel_info(socket, _) do + assign(socket, panel_info: nil) end - defp update_work_list( - %{assigns: %{vm: %{items: items}, selected_item: {%{id: selected_item_id}, _}}} = socket - ) do - work_list = %{ - items: Enum.map(items, &map_item/1), - selected_item_id: selected_item_id - } - - socket |> assign(work_list: work_list, show_left_column: Enum.count(items) > 1) - end - - defp update_work_list(socket) do - socket |> assign(work_list: nil, show_left_column: false) - end - - defp map_item({%{id: id, title: title, group: group}, task}) do - %{id: id, title: title, icon: group, status: task_status(task)} + defp update_flow(%{assigns: %{vm: %{flow: flow}}} = socket) do + socket |> install_children(flow) end - defp task_status(%{status: status}), do: status - defp task_status(_), do: :pending - @impl true - def handle_info( - {:store_participant_data, _data}, - %{assigns: %{model: %{storage_endpoint: nil}}} = socket - ) do - raise "Unable to store participant data: no storage endpoint configurated" - {:noreply, socket} + def handle_event("continue", _payload, socket) do + {:noreply, socket |> show_next()} end @impl true - def handle_info( - {:store_participant_data, data}, - %{assigns: %{model: %{storage_endpoint: endpoint}, remote_ip: remote_ip}} = socket - ) do - Storage.Public.store(endpoint, data, remote_ip) - {:noreply, socket} - end - - @impl true - def handle_info( - {:complete_task, _}, - %{assigns: %{vm: %{items: items}, selected_item: {%{id: selected_item_id}, _}}} = socket - ) do - {_, task} = Enum.find(items, fn {%{id: id}, _} -> id == selected_item_id end) - - Crew.Public.activate_task(task) - - {:noreply, socket} - end - - @impl true - def handle_info({:onboarding_continue, _}, %{assigns: %{onboarding: onboarding}} = socket) do - {_, onboarding} = List.pop_at(onboarding, 0) - + def handle_event("feldspar_event", event, socket) do { :noreply, - socket |> assign(onboarding: onboarding) + socket |> send_event(:flow, "feldspar_event", event) } end @impl true - def handle_event( - "work_item_selected", - %{"item" => item_id}, - socket - ) do - item_id = String.to_integer(item_id) - - { - :noreply, - socket - |> assign( - selected_item_id: item_id, - tool_ref_view: nil - ) - |> update_selected_item() - |> update_start_view() - |> update_work_list() - } + def handle_event("store", %{key: key, data: data}, socket) do + {:noreply, socket |> store(key, data)} end - @impl true - def handle_event("start", %{"item" => item_id}, %{assigns: %{vm: %{items: items}}} = socket) do - item_id = String.to_integer(item_id) - {%{tool_ref: tool_ref}, task} = Enum.find(items, fn {%{id: id}, _} -> id == item_id end) - - tool_ref_view = %{ - tool_ref: tool_ref, - task: task - } - - Crew.Public.lock_task(task) - - { - :noreply, - socket |> assign(tool_ref_view: tool_ref_view, start_view: nil) - } - end - - @impl true - def handle_event("app_event", event, socket) do - { - :noreply, - socket |> handle_app_event(event) + def store( + %{assigns: %{panel_info: panel_info, model: assignment, remote_ip: remote_ip}} = socket, + key, + data + ) do + meta_data = %{ + remote_ip: remote_ip, + timestamp: Timestamp.now() |> DateTime.to_unix(), + key: key } - end - defp handle_app_event(%{assigns: %{selected_item: {_, task}}} = socket, %{ - "__type__" => "CommandSystemExit", - "code" => code, - "info" => _info - }) do - if code == 0 do - Crew.Public.activate_task(task) - socket - else - Frameworks.Pixel.Flash.put_error(socket, "Application stopped") - end - end + assignment + |> Storage.Private.storage_info() + |> Storage.Public.store(panel_info, data, meta_data) - defp handle_app_event(socket, %{ - "__type__" => "CommandSystemDonate", - "json_string" => _json_string - }) do - socket |> Frameworks.Pixel.Flash.put_info("Donation received") - end - - defp handle_app_event(socket, %{"__type__" => type}) do - socket |> Frameworks.Pixel.Flash.put_error("Unsupported event " <> type) - end - - defp handle_app_event(socket, _) do - socket |> Frameworks.Pixel.Flash.put_error("Unsupported event") + socket end @impl true def render(assigns) do ~H""" - <.stripped menus={@menus} footer?={false}> - <%= if view = List.first(@onboarding) do %> - <.live_component {view} /> - <% else %> -
- <%= if @work_list && @show_left_column do %> -
- <.work_list {@work_list} /> -
-
-
- <% end %> -
- <%= if @tool_ref_view do %> - <.tool_ref_view {@tool_ref_view}/> - <% else %> - <%= if @start_view do %> - <.start_view {@start_view} /> - <% end %> - <% end %> -
-
- <% end %> - + <.stripped menus={@menus} footer?={false}> + <.flow fabric={@fabric} /> + """ end end diff --git a/core/systems/assignment/crew_page_builder.ex b/core/systems/assignment/crew_page_builder.ex index b7aff0b4f..1fe0e65ed 100644 --- a/core/systems/assignment/crew_page_builder.ex +++ b/core/systems/assignment/crew_page_builder.ex @@ -8,61 +8,68 @@ defmodule Systems.Assignment.CrewPageBuilder do def view_model(assignment, assigns) do %{ - onboarding: onboarding(assignment, assigns), - items: items(assignment, assigns) + flow: flow(assignment, assigns) } end - defp onboarding(%{status: status} = assignment, assigns) do + defp flow(%{status: status} = assignment, assigns) do if is_tester?(assignment, assigns) or status == :online do - onboarding(assignment, assigns, current_onboarding(assigns)) + flow(assignment, assigns, current_flow(assigns)) else [] end end - defp onboarding(assignment, assigns, nil), do: full_onboarding(assignment, assigns) + defp flow(assignment, assigns, nil), do: full_flow(assignment, assigns) - defp onboarding(assignment, assigns, current_onboarding) do - full_onboarding(assignment, assigns) - |> Enum.filter(fn %{id: id} -> - Enum.find(current_onboarding, &(&1.id == id)) != nil + defp flow(assignment, assigns, current_flow) do + full_flow(assignment, assigns) + |> Enum.filter(fn %{ref: %{id: id}} -> + Enum.find(current_flow, &(&1.ref.id == id)) != nil end) end - defp full_onboarding(assignment, assigns) do - [consent_view(assignment, assigns)] + defp full_flow(assignment, assigns) do + [ + consent_view(assignment, assigns), + work_view(assignment, assigns) + ] end - defp current_onboarding(%{onboarding: onboarding}), do: onboarding - defp current_onboarding(_), do: nil + defp current_flow(%{fabric: %{children: children}}), do: children - defp consent_view(%{consent_agreement: consent_agreement}, %{current_user: user}) do + defp consent_view(%{consent_agreement: consent_agreement}, %{current_user: user, fabric: fabric}) do revision = Consent.Public.latest_revision(consent_agreement) - %{ - id: :onboarding_consent_view, - module: Assignment.OnboardingConsentView, + Fabric.prepare_child(fabric, :onboarding_view, Assignment.OnboardingConsentView, %{ revision: revision, user: user - } + }) + end + + defp work_view(assignment, %{fabric: fabric} = assigns) do + work_items = work_items(assignment, assigns) + + Fabric.prepare_child(fabric, :work_view, Assignment.CrewWorkView, %{ + work_items: work_items + }) end - defp items(%{status: status, crew: crew} = assignment, %{current_user: user} = assigns) do + defp work_items(%{status: status, crew: crew} = assignment, %{current_user: user} = assigns) do if is_tester?(assignment, assigns) or status == :online do member = Crew.Public.get_member(crew, user) - items(assignment, member) + work_items(assignment, member) else [] end end - defp items(%{workflow: workflow} = assignment, %{} = member) do + defp work_items(%{workflow: workflow} = assignment, %{} = member) do ordered_items = Workflow.Model.ordered_items(workflow) Enum.map(ordered_items, &{&1, get_or_create_task(&1, assignment, member)}) end - defp items(_assignment, nil), do: [] + defp work_items(_assignment, nil), do: [] defp is_tester?(%{crew: crew}, %{current_user: user}) do Core.Authorization.user_has_role?(user, crew, :tester) diff --git a/core/systems/assignment/crew_work_view.ex b/core/systems/assignment/crew_work_view.ex new file mode 100644 index 000000000..b4f624464 --- /dev/null +++ b/core/systems/assignment/crew_work_view.ex @@ -0,0 +1,218 @@ +defmodule Systems.Assignment.CrewWorkView do + use CoreWeb, :live_component_fabric + use Fabric.LiveComponent + + alias Systems.{ + Assignment, + Crew, + Workflow, + Project + } + + def update(%{work_items: work_items}, %{assigns: %{id: _id}} = socket) do + { + :ok, + socket + |> assign(work_items: work_items) + |> compose_child(:start_view) + |> compose_child(:work_list_view) + |> compose_child(:tool_ref_view, only: :update) + } + end + + def update(%{id: id, work_items: work_items}, socket) do + { + :ok, + socket + |> assign( + id: id, + work_items: work_items + ) + |> update_selected_item_id() + |> update_selected_item() + |> compose_child(:start_view) + |> compose_child(:work_list_view) + } + end + + defp update_selected_item_id(%{assigns: %{selected_item_id: selected_item_id}} = socket) + when not is_nil(selected_item_id) do + socket + end + + defp update_selected_item_id(%{assigns: %{work_items: []}} = socket) do + socket |> assign(selected_item_id: nil) + end + + defp update_selected_item_id(%{assigns: %{work_items: [{%{id: id}, _}]}} = socket) do + socket |> assign(selected_item_id: id) + end + + defp update_selected_item_id(%{assigns: %{work_items: work_items}} = socket) do + {%{id: selected_item_id}, _} = + Enum.find(work_items, List.first(work_items), fn {_, %{status: status}} -> + status == :pending + end) + + socket |> assign(selected_item_id: selected_item_id) + end + + defp update_selected_item( + %{assigns: %{selected_item_id: selected_item_id, work_items: work_items}} = socket + ) do + selected_item = Enum.find(work_items, fn {%{id: id}, _} -> id == selected_item_id end) + + socket |> assign(selected_item: selected_item) + end + + @impl true + def compose(:start_view, %{selected_item: selected_item}) when not is_nil(selected_item) do + %{module: Assignment.StartView, params: %{work_item: selected_item}} + end + + @impl true + def compose(:start_view, _assigns), do: nil + + @impl true + def compose(:work_list_view, %{ + work_items: [_one, _two | _] = work_items, + selected_item_id: selected_item_id + }) + when not is_nil(selected_item_id) do + work_list = %{ + items: Enum.map(work_items, &map_item/1), + selected_item_id: selected_item_id + } + + %{module: Workflow.WorkListView, params: %{work_list: work_list}} + end + + @impl true + def compose(:work_list_view, _assigns), do: nil + + @impl true + def compose(:tool_ref_view, %{selected_item: {%{tool_ref: tool_ref}, task}}) do + %{module: Project.ToolRefView, params: %{tool_ref: tool_ref, task: task}} + end + + @impl true + def compose(:tool_ref_view, _assigns), do: nil + + defp map_item({%{id: id, title: title, group: group}, task}) do + %{id: id, title: title, icon: group, status: task_status(task)} + end + + defp task_status(%{status: status}), do: status + defp task_status(_), do: :pending + + defp lock_task(socket, task) do + Crew.Public.lock_task(task) + socket + end + + @impl true + def handle_event( + "complete_task", + _, + %{assigns: %{work_items: work_items, selected_item: {%{id: selected_item_id}, _}}} = + socket + ) do + {_, task} = Enum.find(work_items, fn {%{id: id}, _} -> id == selected_item_id end) + + Crew.Public.activate_task(task) + + {:noreply, socket} + end + + @impl true + def handle_event( + "work_item_selected", + %{"item" => item_id}, + socket + ) do + item_id = String.to_integer(item_id) + + { + :noreply, + socket + |> assign( + selected_item_id: item_id, + tool_ref_view: nil + ) + |> update_selected_item() + |> compose_child(:start_view) + |> compose_child(:work_list_view) + } + end + + @impl true + def handle_event("start", _, %{assigns: %{selected_item: {_, task}}} = socket) do + { + :noreply, + socket + |> compose_child(:tool_ref_view) + |> lock_task(task) + } + end + + @impl true + def handle_event("feldspar_event", event, socket) do + { + :noreply, + socket |> handle_feldspar_event(event) + } + end + + defp handle_feldspar_event(%{assigns: %{selected_item: {_, task}}} = socket, %{ + "__type__" => "CommandSystemExit", + "code" => code, + "info" => _info + }) do + if code == 0 do + Crew.Public.activate_task(task) + socket + else + Frameworks.Pixel.Flash.put_info(socket, "Application stopped") + end + end + + defp handle_feldspar_event(socket, %{ + "__type__" => "CommandSystemDonate", + "key" => key, + "json_string" => json_string + }) do + socket + |> send_event(:parent, "store", %{key: key, data: json_string}) + |> Frameworks.Pixel.Flash.put_info("Donated") + end + + defp handle_feldspar_event(socket, %{"__type__" => type}) do + socket |> Frameworks.Pixel.Flash.put_error("Unsupported event " <> type) + end + + defp handle_feldspar_event(socket, _) do + socket |> Frameworks.Pixel.Flash.put_error("Unsupported event") + end + + @impl true + def render(assigns) do + ~H""" +
+ <%= if exists?(@fabric, :tool_ref_view) do %> + <.child id={:tool_ref_view} fabric={@fabric} /> + <% else %> + <%= if exists?(@fabric, :work_list_view) do %> +
+ <.child id={:work_list_view} fabric={@fabric} /> +
+
+
+ <% end %> +
+ <.child id={:start_view} fabric={@fabric} /> +
+ <% end %> +
+ """ + end +end diff --git a/core/systems/assignment/external_panel_controller.ex b/core/systems/assignment/external_panel_controller.ex index 62ed3feee..cc0c57e36 100644 --- a/core/systems/assignment/external_panel_controller.ex +++ b/core/systems/assignment/external_panel_controller.ex @@ -1,57 +1,47 @@ defmodule Systems.Assignment.ExternalPanelController do use CoreWeb, :controller - @supported_locales ~w(nl en) - @centerdata_callback_url "https://quest.centerdata.nl/eyra/dd.php" - - def create(conn, %{"id" => id, "panel" => panel} = params) do - [] - |> add_locale(params) - |> add_panel_info(String.to_existing_atom(panel), params) - |> start_assignment(conn, id) + def create(conn, %{"id" => _id, "panel" => _panel} = params) do + conn + |> add_panel_info(params) + |> redirect(to: path(params)) end - defp add_panel_info(opts, panel, params) do + defp add_panel_info(conn, params) do panel_info = %{ - callback_url: get_callback_url(panel), - participant: get_participant(panel, params), - language: get_language(panel, params), + participant: get_participant(params), query_string: params } - Keyword.put(opts, :panel_info, panel_info) + conn |> put_session(:panel_info, panel_info) end - defp add_locale(opts, %{"locale" => locale}), do: add_locale(opts, locale) - defp add_locale(opts, %{"lang" => locale}), do: add_locale(opts, locale) + defp path(%{"id" => id} = params) do + query_string = query_string(params) + "/assignment/#{id}#{query_string}" + end - defp add_locale(opts, locale) when is_binary(locale) do - if is_supported?(locale) do - Keyword.put(opts, :locale, locale) + defp query_string(params) do + if locale = get_locale(params) do + "?locale=#{locale}" else - opts + "" end end - defp add_locale(opts, _), do: opts - - defp is_supported?(locale) when is_binary(locale) do - locale in @supported_locales - end - - defp start_assignment(_opts, conn, id) do - path = ~p"/assignment/#{id}" - redirect(conn, to: path) - end + # @supported_locales ~w(nl en) + # defp is_supported?(locale) when is_binary(locale) do + # locale in @supported_locales + # end # Param Mappings - defp get_participant(:liss, %{"respondent" => respondent}), do: respondent - defp get_participant(_, %{"participant" => participant}), do: participant - - defp get_language(:liss, %{"lang" => lang}), do: lang - defp get_language(_, %{"language" => language}), do: language + defp get_participant(%{"respondent" => respondent}), do: respondent + defp get_participant(%{"participant" => participant}), do: participant + defp get_participant(_), do: nil - defp get_callback_url(:liss), do: @centerdata_callback_url - defp get_callback_url(_), do: nil + defp get_locale(%{"lang" => lang}), do: lang + defp get_locale(%{"language" => language}), do: language + defp get_locale(%{"locale" => locale}), do: locale + defp get_locale(_), do: nil end diff --git a/core/systems/assignment/onboarding_consent_view.ex b/core/systems/assignment/onboarding_consent_view.ex index f4dbaeb00..963c4bece 100644 --- a/core/systems/assignment/onboarding_consent_view.ex +++ b/core/systems/assignment/onboarding_consent_view.ex @@ -1,20 +1,11 @@ defmodule Systems.Assignment.OnboardingConsentView do - use CoreWeb.LiveForm + use CoreWeb.LiveForm, :fabric + use Fabric.LiveComponent alias Systems.{ Consent } - @impl true - def update(%{consent_clickwrap_view: :continue}, %{assigns: %{id: id}} = socket) do - send(self(), {:onboarding_continue, id}) - - { - :ok, - socket - } - end - @impl true def update(%{id: id, revision: revision, user: user}, socket) do { @@ -29,18 +20,19 @@ defmodule Systems.Assignment.OnboardingConsentView do } end - defp update_clickwrap_view( - %{assigns: %{revision: revision, user: user, myself: myself}} = socket - ) do - clickwrap_view = %{ - id: :consent_clickwrap_view, - module: Consent.ClickWrapView, - revision: revision, - user: user, - target: myself - } + defp update_clickwrap_view(%{assigns: %{revision: revision, user: user}} = socket) do + child = + prepare_child(socket, :clickwrap_view, Consent.ClickWrapView, %{ + revision: revision, + user: user + }) - assign(socket, clickwrap_view: clickwrap_view) + show_child(socket, child) + end + + @impl true + def handle_event("continue", _payload, socket) do + {:noreply, socket |> send_event(:parent, "continue")} end @impl true @@ -50,7 +42,7 @@ defmodule Systems.Assignment.OnboardingConsentView do <%= dgettext("eyra-assignment", "onboarding.consent.title") %> - <.live_component {@clickwrap_view} /> + <.child id={:clickwrap_view} fabric={@fabric} /> """ diff --git a/core/systems/assignment/settings_view.ex b/core/systems/assignment/settings_view.ex index 3881fd5ae..0f9db4009 100644 --- a/core/systems/assignment/settings_view.ex +++ b/core/systems/assignment/settings_view.ex @@ -1,5 +1,5 @@ defmodule Systems.Assignment.SettingsView do - use CoreWeb, :live_component + use CoreWeb, :live_component_fabric use Fabric.LiveComponent alias Systems.{ diff --git a/core/systems/assignment/start_view.ex b/core/systems/assignment/start_view.ex index 564369e85..449c58bcd 100644 --- a/core/systems/assignment/start_view.ex +++ b/core/systems/assignment/start_view.ex @@ -1,17 +1,71 @@ defmodule Systems.Assignment.StartView do - use CoreWeb, :html + use CoreWeb, :live_component_fabric + use Fabric.LiveComponent alias Frameworks.Pixel.Align alias Frameworks.Pixel.Button alias Frameworks.Pixel.Text + alias Frameworks.Concept - attr(:id, :any, required: true) - attr(:title, :string, required: true) - attr(:icon, :string, required: true) - attr(:description, :string, required: true) - attr(:button, :map, required: true) + alias Systems.Project - def start_view(assigns) do + def update(%{id: id, work_item: work_item}, socket) do + { + :ok, + socket + |> assign( + id: id, + work_item: work_item + ) + |> compose_element(:title) + |> compose_element(:description) + |> compose_element(:icon) + |> compose_element(:button) + } + end + + @impl true + def compose(:button, %{work_item: work_item}) do + %{ + action: start_action(work_item), + face: %{type: :primary, label: "Start"} + } + end + + @impl true + def compose(:title, %{work_item: {%{title: title}, _}}), do: title + + @impl true + def compose(:description, %{work_item: {%{description: description}, _}}), do: description + + @impl true + def compose(:icon, %{work_item: {%{group: group}, _}}), do: group + + defp start_action({%{tool_ref: tool_ref}, _task} = item) do + Project.ToolRefModel.tool(tool_ref) + |> Concept.ToolModel.launcher() + |> start_action(item) + end + + defp start_action(%{function: _, props: _}, _) do + %{type: :send, event: "start"} + end + + defp start_action(%{url: url}, _) do + %{type: :http_get, to: url, target: "_blank"} + end + + defp start_action(_, _) do + %{type: :send, event: "start"} + end + + @impl true + def handle_event("start", _, socket) do + {:noreply, socket |> send_event(:parent, "start")} + end + + @impl true + def render(assigns) do ~H"""
diff --git a/core/systems/benchmark/content_page.ex b/core/systems/benchmark/content_page.ex index 27dbd907c..82109eb07 100644 --- a/core/systems/benchmark/content_page.ex +++ b/core/systems/benchmark/content_page.ex @@ -1,4 +1,5 @@ defmodule Systems.Benchmark.ContentPage do + use CoreWeb, :live_view use Systems.Content.Page alias Systems.{ diff --git a/core/systems/consent/clickwrap_view.ex b/core/systems/consent/clickwrap_view.ex index 73000ffef..7b1ae070f 100644 --- a/core/systems/consent/clickwrap_view.ex +++ b/core/systems/consent/clickwrap_view.ex @@ -2,14 +2,15 @@ defmodule Systems.Consent.ClickWrapView do # Clickwrap agreements are a type of electronic signature that involves a user # clicking a simple button to accept the agreement. - use CoreWeb.LiveForm + use CoreWeb.LiveForm, :fabric + use Fabric.LiveComponent alias Systems.{ Consent } @impl true - def update(%{id: id, revision: revision, user: user, target: target}, socket) do + def update(%{id: id, revision: revision, user: user}, socket) do signature = Consent.Public.get_signature(revision, user) selected? = signature != nil @@ -20,12 +21,12 @@ defmodule Systems.Consent.ClickWrapView do id: id, revision: revision, user: user, - target: target, signature: signature, selected?: selected? ) |> update_form() |> update_continue_button() + |> update_checkbox() } end @@ -47,6 +48,15 @@ defmodule Systems.Consent.ClickWrapView do assign(socket, continue_button: continue_button) end + defp update_checkbox(socket) do + checkbox = %{ + field: :signature_check, + label_text: dgettext("eyra-consent", "onboarding.consent.checkbox") + } + + assign(socket, checkbox: checkbox) + end + @impl true def handle_event( "toggle", @@ -78,9 +88,8 @@ defmodule Systems.Consent.ClickWrapView do |> handle_continue() end - def handle_continue(%{assigns: %{id: id, signature: %{id: _}, target: target}} = socket) do - send_update(target, %{id => :continue}) - socket + def handle_continue(%{assigns: %{signature: %{id: _}}} = socket) do + socket |> send_event(:parent, "continue") end @impl true @@ -92,11 +101,7 @@ defmodule Systems.Consent.ClickWrapView do
<.spacing value="M" /> <.form id={@id} :let={form} for={@form} phx-target={@myself} > - <.checkbox - form={form} - field={:signature_check} - label_text="I have read and agree with the above terms." - /> + <.checkbox {@checkbox} form={form} /> <.wrap> diff --git a/core/systems/content/page.ex b/core/systems/content/page.ex index 929f26183..3db6591f6 100644 --- a/core/systems/content/page.ex +++ b/core/systems/content/page.ex @@ -81,9 +81,8 @@ defmodule Systems.Content.Page do def tabbar_size({:unknown, _}), do: :unknown def tabbar_size(bp), do: Breakpoint.value(bp, :narrow, sm: %{30 => :wide}) - defmacro __using__(_opts) do + defmacro __using__(_) do quote do - use CoreWeb, :live_view use CoreWeb.Layouts.Workspace.Component, :projects use CoreWeb.UI.Responsive.Viewport use Systems.Observatory.Public @@ -91,7 +90,7 @@ defmodule Systems.Content.Page do alias Frameworks.Pixel.Flash import CoreWeb.Gettext - import Systems.Content.Page + import Systems.Content.Page, except: [helpers: 0] defp initialize(socket, id, model, tabbar_id, initial_tab, locale) do socket diff --git a/core/systems/document/content_page.ex b/core/systems/document/content_page.ex index 6e93263f3..be3ee36dd 100644 --- a/core/systems/document/content_page.ex +++ b/core/systems/document/content_page.ex @@ -1,4 +1,5 @@ defmodule Systems.Document.ContentPage do + use CoreWeb, :live_view use Systems.Content.Page alias Systems.{ diff --git a/core/systems/email/form.ex b/core/systems/email/form.ex index a06cee217..b81b64b89 100644 --- a/core/systems/email/form.ex +++ b/core/systems/email/form.ex @@ -1,6 +1,5 @@ defmodule Systems.Email.Form do use CoreWeb, :live_component - import Phoenix.LiveView alias Core.Accounts alias CoreWeb.UI.Timestamp @@ -105,15 +104,6 @@ defmodule Systems.Email.Form do |> Enum.map(&username(&1)) end - # data(subject, :string) - # data(message, :string) - # data(model, :map) - # data(changeset, :any) - # data(validate?, :boolean) - - attr(:users, :list) - attr(:from_user, :map) - @impl true def render(assigns) do ~H""" diff --git a/core/systems/feldspar/app_page.ex b/core/systems/feldspar/app_page.ex index 8fd9479cf..41e8afce0 100644 --- a/core/systems/feldspar/app_page.ex +++ b/core/systems/feldspar/app_page.ex @@ -27,7 +27,7 @@ defmodule Systems.Feldspar.AppPage do end @impl true - def handle_event("app_event", %{"__type__" => type, "json_string" => event}, socket) do + def handle_event("feldspar_event", %{"__type__" => type, "json_string" => event}, socket) do { :noreply, socket |> handle(type, event) @@ -35,7 +35,7 @@ defmodule Systems.Feldspar.AppPage do end @impl true - def handle_event("app_event", event, socket) do + def handle_event("feldspar_event", event, socket) do { :noreply, socket |> handle(nil, inspect(event)) @@ -43,7 +43,12 @@ defmodule Systems.Feldspar.AppPage do end defp handle(socket, "CommandSystemDonate", event) do - send(self(), {:store_participant_data, event}) + Frameworks.Pixel.Flash.put_error(socket, "Unsupported CommandSystemDonate " <> event) + socket + end + + defp handle(socket, "CommandSystemExit", event) do + Frameworks.Pixel.Flash.put_error(socket, "Unsupported CommandSystemExit " <> event) socket end diff --git a/core/systems/feldspar/content_page.ex b/core/systems/feldspar/content_page.ex index 4a08b71b2..9f0056a3d 100644 --- a/core/systems/feldspar/content_page.ex +++ b/core/systems/feldspar/content_page.ex @@ -1,4 +1,5 @@ defmodule Systems.Feldspar.ContentPage do + use CoreWeb, :live_view use Systems.Content.Page alias Systems.{ diff --git a/core/systems/lab/content_page.ex b/core/systems/lab/content_page.ex index 5df5829e2..2023d9cef 100644 --- a/core/systems/lab/content_page.ex +++ b/core/systems/lab/content_page.ex @@ -1,4 +1,5 @@ defmodule Systems.Lab.ContentPage do + use CoreWeb, :live_view use Systems.Content.Page alias Systems.{ diff --git a/core/systems/project/tool_ref_view.ex b/core/systems/project/tool_ref_view.ex index 724461531..bd8aa66c6 100644 --- a/core/systems/project/tool_ref_view.ex +++ b/core/systems/project/tool_ref_view.ex @@ -1,5 +1,6 @@ defmodule Systems.Project.ToolRefView do - use CoreWeb, :html + use CoreWeb, :live_component_fabric + use Fabric.LiveComponent alias Frameworks.Concept @@ -7,22 +8,30 @@ defmodule Systems.Project.ToolRefView do Project } - defp get_tool(tool_ref), do: Project.ToolRefModel.tool(tool_ref) - defp get_work(tool), do: Concept.ToolModel.launcher(tool) - - attr(:tool_ref, :map, required: true) - attr(:task, :map, required: true) + def update(%{id: id, tool_ref: tool_ref, task: task}, socket) do + { + :ok, + socket + |> assign( + id: id, + tool_ref: tool_ref, + task: task + ) + |> compose_element(:launcher) + } + end - def tool_ref_view(%{tool_ref: tool_ref} = assigns) do - assigns = - tool_ref - |> get_tool() - |> get_work() - |> then(&assign(assigns, :work, &1)) + @impl true + def compose(:launcher, %{tool_ref: tool_ref}) do + Project.ToolRefModel.tool(tool_ref) + |> Concept.ToolModel.launcher() + end + @impl true + def render(assigns) do ~H"""
- <.function_component function={@work.function} props={@work.props} /> + <.function_component {@launcher} />
""" end diff --git a/core/systems/storage/_private.ex b/core/systems/storage/_private.ex index 87917fd55..977c1dd60 100644 --- a/core/systems/storage/_private.ex +++ b/core/systems/storage/_private.ex @@ -3,6 +3,8 @@ defmodule Systems.Storage.Private do Storage } + @centerdata_callback_url "https://quest.centerdata.nl/eyra/dd.php" + def allowed_service_ids() do Keyword.get(config(), :services, []) end @@ -13,6 +15,30 @@ defmodule Systems.Storage.Private do def build_special(:aws), do: %Storage.AWS.EndpointModel{} def build_special(:azure), do: %Storage.Azure.EndpointModel{} - def build_special(:centerdata), do: %Storage.Centerdata.EndpointModel{} def build_special(:yoda), do: %Storage.Yoda.EndpointModel{} + + def backend_info(%Storage.AWS.EndpointModel{}), do: {:aws, Storage.AWS.Backend} + def backend_info(%Storage.Azure.EndpointModel{}), do: {:azure, Storage.Azure.Backend} + def backend_info(%Storage.Yoda.EndpointModel{}), do: {:yoda, Storage.Yoda.Backend} + + def storage_info(%{storage_endpoint: %{} = storage_endpoint, external_panel: external_panel}) do + if endpoint = Storage.EndpointModel.special(storage_endpoint) do + {key, backend} = backend_info(endpoint) + %{key: key, backend: backend, endpoint: endpoint} + else + storage_info(external_panel) + end + end + + def storage_info(%{external_panel: external_panel}) do + storage_info(external_panel) + end + + def storage_info(:liss) do + endpoint = %Storage.Centerdata.EndpointModel{url: @centerdata_callback_url} + backend = Storage.Centerdata.Backend + %{key: :centerdata, endpoint: endpoint, backend: backend} + end + + def storage_info(_), do: nil end diff --git a/core/systems/storage/_public.ex b/core/systems/storage/_public.ex index dcfde7334..8c076a66e 100644 --- a/core/systems/storage/_public.ex +++ b/core/systems/storage/_public.ex @@ -4,18 +4,21 @@ defmodule Systems.Storage.Public do Storage } - def store(%Storage.EndpointModel{} = endpoint, data, remote_ip) do - storage = %{ - key: Storage.EndpointModel.special_field_id(endpoint), - endpoint: Storage.EndpointModel.special(endpoint) - } - + def store( + %{key: key, backend: backend, endpoint: endpoint}, + panel_info, + data, + %{remote_ip: remote_ip} = meta_data + ) do packet_size = String.length(data) - with :granted <- Rate.Public.request_permission(storage.key, remote_ip, packet_size) do + with :granted <- Rate.Public.request_permission(key, remote_ip, packet_size) do %{ - storage: storage, - data: data + backend: backend, + endpoint: endpoint, + panel_info: panel_info, + data: data, + meta_data: meta_data } |> Storage.Delivery.new() |> Oban.insert() diff --git a/core/systems/storage/aws/backend.ex b/core/systems/storage/aws/backend.ex index 51ebeffb5..60bb76c00 100644 --- a/core/systems/storage/aws/backend.ex +++ b/core/systems/storage/aws/backend.ex @@ -4,26 +4,21 @@ defmodule Systems.Storage.AWS.Backend do alias ExAws.S3 def store( - state, - %{storage_info: %{key: key}} = _vm, - data + %{"s3_bucket_name" => bucket} = _endpoint, + panel_info, + data, + meta_data ) do [data] - |> S3.upload(bucket(), path(key, state)) + |> S3.upload(bucket, path(panel_info, meta_data)) |> ExAws.request() end - defp bucket do - :core - |> Application.fetch_env!(:aws) - |> Keyword.fetch!(:bucket) + defp path(%{"participant" => participant}, %{"key" => key, "timestamp" => timestamp}) do + "#{participant}/#{key}/#{timestamp}.json" end - defp path(key, %{"participant" => participant, "timestamp" => timestamp} = _state) do - "#{key}/#{participant}/#{timestamp}.json" - end - - defp path(key, %{"participant" => participant} = _state) do + defp path(%{"participant" => participant}, %{"key" => key}) do "#{key}/#{participant}.json" end end diff --git a/core/systems/storage/aws/endpoint_model.ex b/core/systems/storage/aws/endpoint_model.ex index c6be57792..c8b202df5 100644 --- a/core/systems/storage/aws/endpoint_model.ex +++ b/core/systems/storage/aws/endpoint_model.ex @@ -4,6 +4,10 @@ defmodule Systems.Storage.AWS.EndpointModel do import Ecto.Changeset + @fields ~w(access_key_id secret_access_key s3_bucket_name region_code)a + @required_fields @fields + + @derive {Jason.Encoder, only: @fields} schema "storage_endpoints_aws" do field(:access_key_id, :string) field(:secret_access_key, :string) @@ -13,9 +17,6 @@ defmodule Systems.Storage.AWS.EndpointModel do timestamps() end - @fields ~w(access_key_id secret_access_key s3_bucket_name region_code)a - @required_fields @fields - def changeset(model, params) do model |> cast(params, @fields) diff --git a/core/systems/storage/azure/backend.ex b/core/systems/storage/azure/backend.ex index c1a065757..10c70fe05 100644 --- a/core/systems/storage/azure/backend.ex +++ b/core/systems/storage/azure/backend.ex @@ -4,20 +4,19 @@ defmodule Systems.Storage.Azure.Backend do require Logger def store( - %{"participant" => participant, "key" => donation_key}, - %{"storage_info" => %{"key" => root_key}}, - data + endpoint, + panel_info, + data, + meta_data ) do - path = path(root_key, participant, donation_key) + path = path(panel_info, meta_data) headers = [ {"Content-Type", "text/plain"}, {"x-ms-blob-type", "BlockBlob"} ] - config = config() - - case url(config, path) do + case url(endpoint, path) do {:ok, url} -> HTTPoison.put(url, data, headers) |> case do @@ -37,27 +36,27 @@ defmodule Systems.Storage.Azure.Backend do end end - def path(root_key, participant, donation_key) do - "#{root_key}/#{participant}/#{donation_key}.json" + defp path(%{"participant" => participant}, %{"key" => key, "timestamp" => timestamp}) do + "#{participant}/#{key}/#{timestamp}.json" + end + + defp path(%{"participant" => participant}, %{"key" => key}) do + "#{key}/#{participant}.json" end defp url( - config, + %{ + "storage_account_name" => storage_account_name, + "container" => container, + "sas_token" => sas_token + }, path ) do - storage_account_name = Keyword.get(config, :storage_account_name) - container = Keyword.get(config, :container) - sas_token = Keyword.get(config, :sas_token) - - if storage_account_name && container && sas_token do - {:ok, - "https://#{storage_account_name}.blob.core.windows.net/#{container}/#{path}#{sas_token}"} - else - {:error, "Unable to deliver donation: invalid Azure config"} - end + {:ok, + "https://#{storage_account_name}.blob.core.windows.net/#{container}/#{path}#{sas_token}"} end - defp config() do - Application.get_env(:core, :azure_storage_backend) + defp url(_, _) do + {:error, "Unable to deliver donation: invalid Azure config"} end end diff --git a/core/systems/storage/azure/endpoint_model.ex b/core/systems/storage/azure/endpoint_model.ex index 384e1dfa8..5c32a7d5e 100644 --- a/core/systems/storage/azure/endpoint_model.ex +++ b/core/systems/storage/azure/endpoint_model.ex @@ -4,6 +4,10 @@ defmodule Systems.Storage.Azure.EndpointModel do import Ecto.Changeset + @fields ~w(account_name container sas_token)a + @required_fields @fields + + @derive {Jason.Encoder, only: @fields} schema "storage_endpoints_azure" do field(:account_name, :string) field(:container, :string) @@ -12,9 +16,6 @@ defmodule Systems.Storage.Azure.EndpointModel do timestamps() end - @fields ~w(account_name container sas_token)a - @required_fields @fields - def changeset(model, params) do model |> cast(params, @fields) diff --git a/core/systems/storage/backend.ex b/core/systems/storage/backend.ex index 6ca300cc7..24c3a8beb 100644 --- a/core/systems/storage/backend.ex +++ b/core/systems/storage/backend.ex @@ -1,7 +1,8 @@ defmodule Systems.Storage.Backend do @callback store( - session :: map(), - vm :: map(), - data :: binary() + endpoint :: map(), + panel_info :: map(), + data :: binary(), + meta_data :: map() ) :: any() end diff --git a/core/systems/storage/centerdata/backend.ex b/core/systems/storage/centerdata/backend.ex index d3315da72..867c57442 100644 --- a/core/systems/storage/centerdata/backend.ex +++ b/core/systems/storage/centerdata/backend.ex @@ -4,18 +4,29 @@ defmodule Systems.Storage.Centerdata.Backend do require Logger def store( + %{"url" => url} = _endpoint, %{ - "url" => url, - "varname1" => varname1, - "page" => page, - "respondent" => respondent, - "token" => token - } = _session, - %{storage_info: %{quest: quest}} = _vm, - data + "query_string" => %{ + "quest" => quest, + "varname1" => varname1, + "respondent" => respondent, + "token" => token, + "page" => page + } + } = _panel_info, + data, + _meta_data ) do body = - "{\"#{varname1}\": \"#{data}\", \"button_next\": \"Next\", \"page\": \"#{page}\", \"_respondent\": \"#{respondent}\", \"token\": \"#{token}\", \"quest\": \"#{quest}}\"" + %{ + "#{varname1}" => Jason.decode!(data), + button_next: "Next", + page: page, + _respondent: respondent, + token: token, + quest: quest + } + |> Jason.encode!() post(url, body) end diff --git a/core/systems/storage/centerdata/endpoint_model.ex b/core/systems/storage/centerdata/endpoint_model.ex index 47f4ad874..5fb7faada 100644 --- a/core/systems/storage/centerdata/endpoint_model.ex +++ b/core/systems/storage/centerdata/endpoint_model.ex @@ -4,15 +4,16 @@ defmodule Systems.Storage.Centerdata.EndpointModel do import Ecto.Changeset + @fields ~w(url)a + @required_fields @fields + + @derive {Jason.Encoder, only: @fields} schema "storage_endpoints_centerdata" do field(:url, :string) timestamps() end - @fields ~w(url)a - @required_fields @fields - def changeset(model, params) do model |> cast(params, @fields) diff --git a/core/systems/storage/delivery.ex b/core/systems/storage/delivery.ex index 49970cf48..f63658fd6 100644 --- a/core/systems/storage/delivery.ex +++ b/core/systems/storage/delivery.ex @@ -26,28 +26,12 @@ defmodule Systems.Storage.Delivery do end defp deliver(%{ - "storage_key" => storage_key, - "state" => state, - "vm" => vm, - "data" => data + "backend" => backend, + "endpoint" => endpoint, + "panel_info" => panel_info, + "data" => data, + "meta_data" => meta_data }) do - storage = storage(storage_key) - storage.store(state, vm, data) - end - - defp config() do - Application.fetch_env!(:core, :data_donation_storage_backend) - end - - defp storage(storage_key) do - config = config() - - case Keyword.get(config, String.to_existing_atom(storage_key)) do - nil -> - raise DeliveryError, "Could not deliver donated data, invalid config for #{storage_key}" - - value -> - value - end + String.to_existing_atom(backend).store(endpoint, panel_info, data, meta_data) end end diff --git a/core/systems/storage/endpoint_form.ex b/core/systems/storage/endpoint_form.ex index 8cd8b35d0..7ae59a8bd 100644 --- a/core/systems/storage/endpoint_form.ex +++ b/core/systems/storage/endpoint_form.ex @@ -1,5 +1,5 @@ defmodule Systems.Storage.EndpointForm do - use CoreWeb.LiveForm + use CoreWeb.LiveForm, :fabric use Fabric.LiveComponent alias Frameworks.Concept diff --git a/core/systems/storage/endpoint_form_helper.ex b/core/systems/storage/endpoint_form_helper.ex index 9606cb8d7..fc88da0a1 100644 --- a/core/systems/storage/endpoint_form_helper.ex +++ b/core/systems/storage/endpoint_form_helper.ex @@ -1,7 +1,7 @@ defmodule Systems.Storage.EndpointForm.Helper do defmacro __using__(model) do quote do - use CoreWeb.LiveForm + use CoreWeb.LiveForm, :fabric use Fabric.LiveComponent alias unquote(model), as: Model diff --git a/core/systems/storage/fake_backend.ex b/core/systems/storage/fake_backend.ex index bff1109fc..e6774f0ae 100644 --- a/core/systems/storage/fake_backend.ex +++ b/core/systems/storage/fake_backend.ex @@ -1,7 +1,7 @@ defmodule Systems.Storage.FakeBackend do @behaviour Systems.Storage.Backend - def store(_state, _vm, data) do + def store(_endpoint, _panel_info, data, _meta_data) do IO.puts("fake store: #{data}") :ok end diff --git a/core/systems/storage/yoda/backend.ex b/core/systems/storage/yoda/backend.ex index abdbe669d..4df7a7a45 100644 --- a/core/systems/storage/yoda/backend.ex +++ b/core/systems/storage/yoda/backend.ex @@ -1,2 +1,14 @@ defmodule Systems.Storage.Yoda.Backend do + @behaviour Systems.Storage.Backend + + require Logger + + def store( + _endpoint, + _panel_info, + _data, + _meta_data + ) do + Logger.warn("Yoda backend not implemented yet") + end end diff --git a/core/systems/storage/yoda/endpoint_model.ex b/core/systems/storage/yoda/endpoint_model.ex index 2f7e300fc..84c9f3303 100644 --- a/core/systems/storage/yoda/endpoint_model.ex +++ b/core/systems/storage/yoda/endpoint_model.ex @@ -4,6 +4,10 @@ defmodule Systems.Storage.Yoda.EndpointModel do import Ecto.Changeset + @fields ~w(url user password)a + @required_fields @fields + + @derive {Jason.Encoder, only: @fields} schema "storage_endpoints_yoda" do field(:url, :string) field(:user, :string) @@ -12,9 +16,6 @@ defmodule Systems.Storage.Yoda.EndpointModel do timestamps() end - @fields ~w(url user password)a - @required_fields @fields - def changeset(endpoint, params) do endpoint |> cast(params, @fields) diff --git a/core/systems/workflow/item_views.ex b/core/systems/workflow/item_views.ex index e30eab368..b7b2e0d83 100644 --- a/core/systems/workflow/item_views.ex +++ b/core/systems/workflow/item_views.ex @@ -7,12 +7,9 @@ defmodule Systems.Workflow.ItemViews do import CoreWeb.UI.StepIndicator alias Systems.{ - Workflow, - Project + Workflow } - import Project.ToolRefView - attr(:title, :string, required: true) attr(:description, :string, required: true) attr(:items, :list, required: true) @@ -55,19 +52,6 @@ defmodule Systems.Workflow.ItemViews do """ end - attr(:items, :list, required: true) - attr(:selected_item_id, :integer, required: true) - - def work_list(assigns) do - ~H""" -
- <%= for {item, index} <- Enum.with_index(@items) do %> - <.work_item {item} index={index} selected?={item.id == @selected_item_id} /> - <% end %> -
- """ - end - attr(:id, :any, required: true) attr(:title, :map, required: true) attr(:icon, :string, required: true) @@ -107,17 +91,6 @@ defmodule Systems.Workflow.ItemViews do """ end - attr(:item, :map, required: true) - attr(:task, :map, required: true) - - def launcher(%{item: %{tool_ref: tool_ref}} = assigns) when not is_nil(tool_ref) do - ~H""" -
- <.tool_ref_view tool_ref={@item.tool_ref} task={@task} /> -
- """ - end - defp relative_position(0, _count), do: :top defp relative_position(position, count) when position == count - 1, do: :bottom defp relative_position(_position, _count), do: :middle diff --git a/core/systems/workflow/work_list_view.ex b/core/systems/workflow/work_list_view.ex new file mode 100644 index 000000000..a4b2cad05 --- /dev/null +++ b/core/systems/workflow/work_list_view.ex @@ -0,0 +1,30 @@ +defmodule Systems.Workflow.WorkListView do + use CoreWeb, :live_component_fabric + use Fabric.LiveComponent + + import Systems.Workflow.ItemViews, only: [work_item: 1] + + @impl true + def update(%{id: id, items: items, selected_item_id: selected_item_id}, socket) do + { + :ok, + socket + |> assign( + id: id, + items: items, + selected_item_id: selected_item_id + ) + } + end + + @impl true + def render(assigns) do + ~H""" +
+ <%= for {item, index} <- Enum.with_index(@items) do %> + <.work_item {item} index={index} selected?={item.id == @selected_item_id} /> + <% end %> +
+ """ + end +end diff --git a/core/test/frameworks/fabric/factories.ex b/core/test/frameworks/fabric/factories.ex index 8cb0690f7..26804ea66 100644 --- a/core/test/frameworks/fabric/factories.ex +++ b/core/test/frameworks/fabric/factories.ex @@ -4,11 +4,11 @@ defmodule Fabric.Factories do end def create_fabric(%Fabric.LiveView.RefModel{} = self) do - %Fabric.Model{parent: nil, self: self, children: []} + %Fabric.Model{parent: nil, self: self, children: nil} end def create_fabric(%Fabric.LiveComponent.RefModel{} = self) do - %Fabric.Model{parent: nil, self: self, children: []} + %Fabric.Model{parent: nil, self: self, children: nil} end def create_child(id, module \\ Fabric.LiveComponentMock, params \\ %{}) do diff --git a/core/test/frameworks/fabric/live_component_mock.ex b/core/test/frameworks/fabric/live_component_mock.ex index 928ce5900..b4b429cf6 100644 --- a/core/test/frameworks/fabric/live_component_mock.ex +++ b/core/test/frameworks/fabric/live_component_mock.ex @@ -1,5 +1,4 @@ defmodule Fabric.LiveComponentMock do - use Phoenix.LiveComponent use Fabric.LiveComponent @impl true diff --git a/core/test/frameworks/fabric/live_view_mock.ex b/core/test/frameworks/fabric/live_view_mock.ex index d01b49e69..e085af4dd 100644 --- a/core/test/frameworks/fabric/live_view_mock.ex +++ b/core/test/frameworks/fabric/live_view_mock.ex @@ -1,6 +1,5 @@ defmodule Fabric.LiveViewMock do - use Phoenix.LiveView - use Fabric.LiveView + use Fabric.LiveView, Fabric.TestLayouts @impl true def mount(:not_mounted_at_router, _session, socket) do diff --git a/core/test/frameworks/fabric/test.ex b/core/test/frameworks/fabric/test.exs similarity index 86% rename from core/test/frameworks/fabric/test.ex rename to core/test/frameworks/fabric/test.exs index 49adc62e2..4b9a7e2f7 100644 --- a/core/test/frameworks/fabric/test.ex +++ b/core/test/frameworks/fabric/test.exs @@ -12,7 +12,7 @@ defmodule Fabric.Test do assert %Phoenix.LiveView.Socket{ assigns: %{ __changed__: %{fabric: true}, - fabric: %Fabric.Model{parent: nil, self: nil, children: []} + fabric: %Fabric.Model{parent: nil, self: nil, children: nil} } } = socket end @@ -21,13 +21,13 @@ defmodule Fabric.Test do describe "new_fabric/0" do test "default" do fabric = Fabric.new_fabric() - assert %Fabric.Model{parent: nil, self: nil, children: []} = fabric + assert %Fabric.Model{parent: nil, self: nil, children: nil} = fabric end end describe "prepare_child/4" do test "socket" do - fabric = %Fabric.Model{parent: nil, children: []} + fabric = %Fabric.Model{parent: nil, children: nil} child = %Phoenix.LiveView.Socket{} @@ -43,14 +43,14 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } = child end test "fabric" do - fabric = %Fabric.Model{parent: nil, children: []} + fabric = %Fabric.Model{parent: nil, children: nil} child = Fabric.prepare_child(fabric, :child, Fabric.LiveComponentMock, %{}) @@ -63,7 +63,7 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } = child @@ -89,7 +89,7 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } = child @@ -109,7 +109,7 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } = child @@ -119,7 +119,7 @@ defmodule Fabric.Test do describe "show_child/2" do test "socket" do child = create_child(:child) - fabric = %Fabric.Model{parent: nil, children: []} + fabric = %Fabric.Model{parent: nil, children: nil} socket = %Phoenix.LiveView.Socket{} @@ -144,7 +144,7 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } @@ -158,37 +158,31 @@ defmodule Fabric.Test do describe "add_child/2" do test "socket" do child = create_child(:child) - fabric = %Fabric.Model{parent: nil, children: []} - socket = - %Phoenix.LiveView.Socket{} - |> Phoenix.Component.assign(:fabric, fabric) + fabric = + %Fabric.Model{parent: nil, children: nil} |> Fabric.add_child(child) - assert %Phoenix.LiveView.Socket{ - assigns: %{ - fabric: %Fabric.Model{ - children: [ - %Fabric.LiveComponent.Model{ - ref: %Fabric.LiveComponent.RefModel{ + assert %Fabric.Model{ + children: [ + %Fabric.LiveComponent.Model{ + ref: %Fabric.LiveComponent.RefModel{ + id: :child, + module: Fabric.LiveComponentMock + }, + params: %{ + fabric: %Fabric.Model{ + parent: nil, + self: %Fabric.LiveComponent.RefModel{ id: :child, module: Fabric.LiveComponentMock }, - params: %{ - fabric: %Fabric.Model{ - parent: nil, - self: %Fabric.LiveComponent.RefModel{ - id: :child, - module: Fabric.LiveComponentMock - }, - children: [] - } - } + children: nil } - ] + } } - } - } = socket + ] + } = fabric end end @@ -223,7 +217,7 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } @@ -261,22 +255,15 @@ defmodule Fabric.Test do test "socket" do child = create_child(:child, Fabric.LiveComponentMock) - fabric = %Fabric.Model{parent: nil, children: [child]} - - socket = - %Phoenix.LiveView.Socket{} - |> Phoenix.Component.assign(:fabric, fabric) + fabric = + %Fabric.Model{parent: nil, children: [child]} |> Fabric.remove_child(:child) - assert %Phoenix.LiveView.Socket{ - assigns: %{ - fabric: %Fabric.Model{ - parent: nil, - self: nil, - children: [] - } - } - } = socket + assert %Fabric.Model{ + parent: nil, + self: nil, + children: [] + } = fabric end end @@ -295,7 +282,7 @@ defmodule Fabric.Test do fabric: %Fabric.Model{ parent: nil, self: %Fabric.LiveComponent.RefModel{id: :child, module: Fabric.LiveComponentMock}, - children: [] + children: nil } }, ref: %Fabric.LiveComponent.RefModel{id: :child, module: Fabric.LiveComponentMock} @@ -323,7 +310,7 @@ defmodule Fabric.Test do id: :child, module: Fabric.LiveComponentMock }, - children: [] + children: nil } } } diff --git a/core/test/frameworks/fabric/test_layouts.ex b/core/test/frameworks/fabric/test_layouts.ex new file mode 100644 index 000000000..ef4165da3 --- /dev/null +++ b/core/test/frameworks/fabric/test_layouts.ex @@ -0,0 +1,5 @@ +defmodule Fabric.TestLayouts do + use Phoenix.Component + + embed_templates("test_layouts/*") +end diff --git a/core/test/frameworks/fabric/test_layouts/live.html.leex b/core/test/frameworks/fabric/test_layouts/live.html.leex new file mode 100644 index 000000000..965815270 --- /dev/null +++ b/core/test/frameworks/fabric/test_layouts/live.html.leex @@ -0,0 +1,3 @@ +
+ <%= @inner_content %> +
diff --git a/core/test/frameworks/fabric/test_layouts/root.html.leex b/core/test/frameworks/fabric/test_layouts/root.html.leex new file mode 100644 index 000000000..9b29fc027 --- /dev/null +++ b/core/test/frameworks/fabric/test_layouts/root.html.leex @@ -0,0 +1,9 @@ + + + + Fabric + + + <%= @inner_content %> + + diff --git a/core/test/systems/feldspar/app_page_test.exs b/core/test/systems/feldspar/app_page_test.exs index 550e13f4c..57aea79f4 100644 --- a/core/test/systems/feldspar/app_page_test.exs +++ b/core/test/systems/feldspar/app_page_test.exs @@ -14,7 +14,7 @@ defmodule Systems.Feldspar.AppPageTest do test "can receive random app_event data", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/feldspar/apps/test") - assert render_hook(view, :app_event, %{unexpected_key: "some data"}) =~ + assert render_hook(view, :feldspar_event, %{unexpected_key: "some data"}) =~ "Unsupported " end end diff --git a/core/test/test_helper.exs b/core/test/test_helper.exs index 33c77e5a6..a2462e732 100644 --- a/core/test/test_helper.exs +++ b/core/test/test_helper.exs @@ -27,11 +27,3 @@ Mox.defmock(BankingClient.MockClient, for: BankingClient.API) Application.put_env(:core, BankingClient, client: BankingClient.MockClient) Mox.defmock(Systems.Storage.MockBackend, for: Systems.Storage.Backend) - -Application.put_env( - :core, - :data_donation_storage_backend, - s3: Systems.Storage.MockBackend, - centerdata: Systems.Storage.MockBackend, - yoda: Systems.Storage.MockBackend -)