From fddcdf4ed4111cd5187c45f300b69a9daef06128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Wed, 10 Jul 2024 11:48:46 -0300 Subject: [PATCH 01/14] add workspace navbar icons --- lib/oli_web/icons.ex | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/oli_web/icons.ex b/lib/oli_web/icons.ex index cc99e532eca..842de156551 100644 --- a/lib/oli_web/icons.ex +++ b/lib/oli_web/icons.ex @@ -1163,12 +1163,15 @@ defmodule OliWeb.Icons do """ end - def graduation_cap(assigns) do + attr :is_active, :boolean, default: false + attr :class, :string, default: "stroke-white" + + def graduation_cap(%{is_active: false} = assigns) do ~H""" + + + """ + end + + attr :is_active, :boolean, default: false + + def writing_pencil(%{is_active: false} = assigns) do + ~H""" + + + + """ + end + + def writing_pencil(%{is_active: true} = assigns) do + ~H""" + + + + """ + end + + attr :is_active, :boolean, default: false + + def growing_bars(%{is_active: false} = assigns) do + ~H""" + + + + """ + end + + def growing_bars(%{is_active: true} = assigns) do + ~H""" + + + + """ + end + ########## Studend Delivery Icons (end) ########## end From 5a7c6cf52016628d81bbf2150bd438c0b71e4a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Wed, 10 Jul 2024 11:53:53 -0300 Subject: [PATCH 02/14] remove unused section attribute from UserAccount.menu function component --- lib/oli_web/components/delivery/instructor_dashboard.ex | 1 - lib/oli_web/components/delivery/layouts.ex | 8 +------- lib/oli_web/components/delivery/user_account.ex | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/oli_web/components/delivery/instructor_dashboard.ex b/lib/oli_web/components/delivery/instructor_dashboard.ex index 7cff41631b1..e9d5dc0ad1d 100644 --- a/lib/oli_web/components/delivery/instructor_dashboard.ex +++ b/lib/oli_web/components/delivery/instructor_dashboard.ex @@ -215,7 +215,6 @@ defmodule OliWeb.Components.Delivery.InstructorDashboard do - +
@@ -190,7 +185,6 @@ defmodule OliWeb.Components.Delivery.Layouts do id="mobile-user-account-menu-sidebar" ctx={@ctx} is_system_admin={@is_system_admin} - section={@section} dropdown_class="absolute -translate-y-[calc(100%+58px)] right-0 border" />
diff --git a/lib/oli_web/components/delivery/user_account.ex b/lib/oli_web/components/delivery/user_account.ex index 2bdbdee62cf..470601a0713 100644 --- a/lib/oli_web/components/delivery/user_account.ex +++ b/lib/oli_web/components/delivery/user_account.ex @@ -15,7 +15,6 @@ defmodule OliWeb.Components.Delivery.UserAccount do attr(:id, :string, required: true) attr(:ctx, SessionContext) attr(:is_system_admin, :boolean, required: true) - attr(:section, Section, default: nil) attr(:class, :string, default: "") attr(:dropdown_class, :string, default: "") From 3a0ff6ba4cb9a330caf49530de52881f427b1f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Wed, 10 Jul 2024 12:33:13 -0300 Subject: [PATCH 03/14] implement workspace navbar within a new workspace layout --- lib/oli_web/components/delivery/layouts.ex | 276 ++++++++++++++++-- .../components/layouts/workspace.html.heex | 35 +++ .../live/delivery/open_and_free_index.ex | 233 ++++++++------- 3 files changed, 401 insertions(+), 143 deletions(-) create mode 100644 lib/oli_web/components/layouts/workspace.html.heex diff --git a/lib/oli_web/components/delivery/layouts.ex b/lib/oli_web/components/delivery/layouts.ex index 38969a12853..7c3433b3e8d 100644 --- a/lib/oli_web/components/delivery/layouts.ex +++ b/lib/oli_web/components/delivery/layouts.ex @@ -104,6 +104,8 @@ defmodule OliWeb.Components.Delivery.Layouts do ~H"""
<.sidebar_toggler - active_tab={@active_tab} + active={@active_tab} section={@section} preview_mode={@preview_mode} sidebar_expanded={@sidebar_expanded} @@ -194,7 +196,7 @@ defmodule OliWeb.Components.Delivery.Layouts do end attr(:section, Section, default: nil) - attr(:active_tab, :atom) + attr(:active, :atom, required: true, doc: "The current selected menu link") attr(:preview_mode, :boolean) attr(:notification_badges, :map, default: %{}) attr(:sidebar_expanded, :boolean, default: true) @@ -203,7 +205,7 @@ defmodule OliWeb.Components.Delivery.Layouts do ~H""" + """ + end + + attr(:ctx, SessionContext) + attr(:is_system_admin, :boolean, required: true) + attr(:active_workspace, :atom) + attr(:url_params, :map, required: true) + attr(:preview_mode, :boolean) + + def workspace_sidebar_nav(assigns) do + ~H""" +
+ + +
+ """ + end + + attr(:preview_mode, :boolean) + attr(:url_params, :map, required: true) + + def workspace_sidebar_links(assigns) do + ~H""" +
+ <.nav_link + id="course_author_workspace_nav_link" + href={path_for_workspace(:course_author_workspace, @url_params)} + is_active={@url_params.active_workspace == :course_author_workspace} + sidebar_expanded={@url_params.sidebar_expanded} + on_active_bg="bg-[#F4CFFF] hover:!bg-[#F4CFFF] dark:bg-[#7E2899] dark:hover:!bg-[#7E2899]" + navigation_type="href" + > + <:icon> + + + <:text>Course Author + + + <.nav_link + id="instructor_workspace_nav_link" + href={path_for_workspace(:instructor_workspace, @url_params)} + is_active={@url_params.active_workspace == :instructor_workspace} + sidebar_expanded={@url_params.sidebar_expanded} + on_active_bg="bg-[#A8EED8] hover:!bg-[#A8EED8] dark:bg-[#086F4E] dark:hover:!bg-[#086F4E]" + > + <:icon> + + + <:text>Instructor + + + <.nav_link + id="student_workspace_nav_link" + href={path_for_workspace(:student_workspace, @url_params)} + is_active={@url_params.active_workspace == :student_workspace} + sidebar_expanded={@url_params.sidebar_expanded} + on_active_bg="bg-[#AAC3F0]/75 hover:!bg-[#AAC3F0]/75 dark:bg-[#2957A9] dark:hover:!bg-[#2957A9]" + > + <:icon> + + + <:text>Student + +
+ """ + end + attr(:section, Section, default: nil) attr(:active_tab, :atom) attr(:preview_mode, :boolean) @@ -292,6 +459,35 @@ defmodule OliWeb.Components.Delivery.Layouts do """ end + defp toggled_workspace_path(url_params) do + case url_params.active_workspace do + :course_author_workspace -> + ~p"/authoring/projects?#{%{url_params | sidebar_expanded: !url_params.sidebar_expanded}}" + + _ -> + ~p"/sections?#{%{url_params | sidebar_expanded: !url_params.sidebar_expanded}}" + end + end + + defp path_for_workspace(target_workspace, url_params) + when target_workspace in [:student_workspace, :instructor_workspace] do + updated_url_params = %{ + active_workspace: target_workspace, + sidebar_expanded: url_params.sidebar_expanded + } + + ~p"/sections?#{updated_url_params}" + end + + defp path_for_workspace(:course_author_workspace, url_params) do + updated_url_params = %{ + active_workspace: :course_author_workspace, + sidebar_expanded: url_params.sidebar_expanded + } + + ~p"/authoring/projects?#{updated_url_params}" + end + defp path_for(:index, %Section{slug: section_slug}, preview_mode, sidebar_expanded) do if preview_mode do ~p"/sections/#{section_slug}/preview" @@ -373,46 +569,66 @@ defmodule OliWeb.Components.Delivery.Layouts do attr :sidebar_expanded, :boolean, default: true attr :id, :string attr :badge, :integer, default: nil + attr :on_active_bg, :string, default: "bg-zinc-400 bg-opacity-20" + attr :navigation_type, :string, default: "navigate" - def nav_link(assigns) do + def nav_link(%{navigation_type: "navigate"} = assigns) do ~H""" <.link id={@id} navigate={@href} class={["w-full h-11 flex-col justify-center items-center flex hover:no-underline"]} > -
-
- <%= render_slot(@icon) %> + <.nav_link_content {assigns} /> + + """ + end + + def nav_link(%{navigation_type: "href"} = assigns) do + ~H""" + <.link + id={@id} + href={@href} + class={["w-full h-11 flex-col justify-center items-center flex hover:no-underline"]} + > + <.nav_link_content {assigns} /> + + """ + end + + def nav_link_content(assigns) do + ~H""" +
+
+ <%= render_slot(@icon) %> +
+
+
+ <%= render_slot(@text) %>
-
+ + <%= if @badge do %>
- <%= render_slot(@text) %> + <.badge variant={:primary} class="ml-2"><%= @badge %>
- - <%= if @badge do %> -
- <.badge variant={:primary} class="ml-2"><%= @badge %> -
- <% end %> -
+ <% end %>
- +
""" end - attr(:section, Section) + attr(:section, Section, default: nil) def logo_img(assigns) do assigns = @@ -461,7 +677,7 @@ defmodule OliWeb.Components.Delivery.Layouts do >
-
+
Exit Course
diff --git a/lib/oli_web/components/layouts/workspace.html.heex b/lib/oli_web/components/layouts/workspace.html.heex new file mode 100644 index 00000000000..4588aadee05 --- /dev/null +++ b/lib/oli_web/components/layouts/workspace.html.heex @@ -0,0 +1,35 @@ +
+
+
+
+ +
+
+ +
+ <%= @inner_content %> +
+
+ +
diff --git a/lib/oli_web/live/delivery/open_and_free_index.ex b/lib/oli_web/live/delivery/open_and_free_index.ex index e16f0c5e020..df877cc434e 100644 --- a/lib/oli_web/live/delivery/open_and_free_index.ex +++ b/lib/oli_web/live/delivery/open_and_free_index.ex @@ -3,17 +3,21 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do alias Oli.Delivery.Sections alias OliWeb.Components.Delivery.Utils - alias OliWeb.Common.SearchInput + alias OliWeb.Common.{Params, SearchInput} alias Oli.Delivery.Metrics import Ecto.Query, warn: false import OliWeb.Common.SourceImage import OliWeb.Components.Delivery.Layouts - @default_params %{text_search: ""} + @default_params %{ + text_search: "", + sidebar_expanded: true, + active_workspace: :instructor_workspace + } @impl Phoenix.LiveView - def mount(_params, _session, socket) do + def mount(params, _session, socket) do sections = Sections.list_user_open_and_free_sections(socket.assigns.current_user) |> add_user_role(socket.assigns.current_user) @@ -23,143 +27,131 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do {:ok, assign(socket, sections: sections, - params: @default_params, + params: decode_params(params), filtered_sections: sections, show_role_badges: show_role_badges(sections) - )} + ), layout: {OliWeb.Layouts, :workspace}} end @impl Phoenix.LiveView - def handle_params(%{"text_search" => text_search}, _uri, socket) do - filtered_sections = - socket.assigns.sections - |> maybe_filter_by_text(text_search) + def handle_params(params, _uri, socket) do + %{sections: sections} = socket.assigns + params = decode_params(params) {:noreply, assign(socket, - filtered_sections: filtered_sections, - params: Map.put(socket.assigns.params, :text_search, text_search) + filtered_sections: maybe_filter_by_text(sections, params.text_search), + params: params )} end @impl Phoenix.LiveView - def handle_params(_params, _uri, socket), do: {:noreply, socket} - - @impl Phoenix.LiveView - # TODO add bg image to welcome header when we can export it from Figma def render(assigns) do ~H""" -
- -
-
-
-

- Hi, <%= user_given_name(@ctx) %> -

+
+
+

+ Hi, <%= user_given_name(@ctx) %> +

+
+
+
+

+ Courses available +

+
+ <.link + :if={is_independent_instructor?(@current_user)} + href={~p"/sections/independent/create"} + class="torus-button primary !py-[10px] !px-5 !rounded-[3px] !text-sm flex items-center justify-center" + > + New Section + + <.form for={%{}} phx-change="search_section" class="w-[330px]"> + +
-
-
-

- Courses available -

-
- <.link - :if={is_independent_instructor?(@current_user)} - href={~p"/sections/independent/create"} - class="torus-button primary !py-[10px] !px-5 !rounded-[3px] !text-sm flex items-center justify-center" - > - New Section - - <.form for={%{}} phx-change="search_section" class="w-[330px]"> - - -
-
+
-
- <%= if length(@sections) == 0 do %> -

You are not enrolled in any courses.

- <% else %> -
- <.link - :for={{section, index} <- Enum.with_index(@filtered_sections)} - href={get_course_url(section)} - phx-click={JS.add_class("opacity-0", to: "#content")} - phx-mounted={ - JS.transition( - {"ease-out duration-300", "opacity-0 -translate-x-1/2", - "opacity-100 translate-x-0"}, - time: 300 + index * 60 - ) - |> JS.remove_class("opacity-100 translate-x-0") - } - class="opacity-0 relative flex items-center self-stretch h-[201px] w-full bg-cover py-12 px-24 text-white hover:text-white rounded-xl shadow-lg hover:no-underline transition-all hover:translate-x-3" - style={"background-image: url('#{cover_image(section)}');"} - > -
-
-
- - Complete - - + <%= if length(@sections) == 0 do %> +

You are not enrolled in any courses.

+ <% else %> +
+ <.link + :for={{section, index} <- Enum.with_index(@filtered_sections)} + href={get_course_url(section)} + phx-click={JS.add_class("opacity-0", to: "#content")} + phx-mounted={ + JS.transition( + {"ease-out duration-300", "opacity-0 -translate-x-1/2", + "opacity-100 translate-x-0"}, + time: 300 + index * 60 + ) + |> JS.remove_class("opacity-100 translate-x-0") + } + class="opacity-0 relative flex items-center self-stretch h-[201px] w-full bg-cover py-12 px-24 text-white hover:text-white rounded-xl shadow-lg hover:no-underline transition-all hover:translate-x-3" + style={"background-image: url('#{cover_image(section)}');"} + > +
+
+
+ + Complete + + + <%= section.user_role %> + +
+
+
+ <%= section.title %> +
+
- <%= section.user_role %> - -
-
-
- <%= section.title %> -
-
-

- Course Progress -

- <.progress_bar percent={section.progress} show_percent={true} width="100px" /> -
-
- +

+ Course Progress +

+ <.progress_bar percent={section.progress} show_percent={true} width="100px" />
- -

- No course found matching "<%= @params.text_search %>" -

+
+
- <% end %> + +

+ No course found matching "<%= @params.text_search %>" +

-
+ <% end %>
-
- +
""" end @impl Phoenix.LiveView def handle_event("search_section", %{"text_search" => text_search}, socket) do - {:noreply, push_patch(socket, to: ~p"/sections?#{%{text_search: text_search}}")} + {:noreply, + push_patch(socket, to: ~p"/sections?#{%{socket.assigns.params | text_search: text_search}}")} end defp add_user_role([], _user), do: [] @@ -236,4 +228,19 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do defp get_course_url(%{user_role: "student", slug: slug}), do: ~p"/sections/#{slug}" defp get_course_url(%{slug: slug}), do: ~p"/sections/#{slug}/instructor_dashboard/manage" + + defp decode_params(params) do + %{ + text_search: Params.get_param(params, "text_search", @default_params.text_search), + sidebar_expanded: + Params.get_boolean_param(params, "sidebar_expanded", @default_params.sidebar_expanded), + active_workspace: + Params.get_atom_param( + params, + "active_workspace", + [:course_author_workspace, :instructor_workspace, :student_workspace], + @default_params.active_workspace + ) + } + end end From 8a3f6e52a5105c288dc80b21fbe0a96f76e2f8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Wed, 10 Jul 2024 14:22:23 -0300 Subject: [PATCH 04/14] add workspace label and adjust sidebar width --- lib/oli_web/components/delivery/layouts.ex | 18 +++++++++++++----- .../layouts/student_delivery.html.heex | 7 +++++-- .../components/layouts/workspace.html.heex | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/oli_web/components/delivery/layouts.ex b/lib/oli_web/components/delivery/layouts.ex index 7c3433b3e8d..05561fd1186 100644 --- a/lib/oli_web/components/delivery/layouts.ex +++ b/lib/oli_web/components/delivery/layouts.ex @@ -114,7 +114,7 @@ defmodule OliWeb.Components.Delivery.Layouts do md:flex flex-col justify-between - md:w-[190px] + md:w-[200px] shadow-sm bg-delivery-navbar dark:bg-delivery-navbar-dark @@ -258,7 +258,7 @@ defmodule OliWeb.Components.Delivery.Layouts do md:flex flex-col justify-between - md:w-[190px] + md:w-[200px] shadow-sm bg-delivery-navbar dark:bg-delivery-navbar-dark @@ -286,6 +286,14 @@ defmodule OliWeb.Components.Delivery.Layouts do preview_mode={@preview_mode} url_params={@url_params} /> +
+

+ WORKSPACE +

+
<.workspace_sidebar_links preview_mode={@preview_mode} url_params={@url_params} />
@@ -334,7 +342,7 @@ defmodule OliWeb.Components.Delivery.Layouts do def workspace_sidebar_links(assigns) do ~H""" -
+
<.nav_link id="course_author_workspace_nav_link" href={path_for_workspace(:course_author_workspace, @url_params)} @@ -577,7 +585,7 @@ defmodule OliWeb.Components.Delivery.Layouts do <.link id={@id} navigate={@href} - class={["w-full h-11 flex-col justify-center items-center flex hover:no-underline"]} + class={["w-full h-[35px] flex-col justify-center items-center flex hover:no-underline"]} > <.nav_link_content {assigns} /> @@ -589,7 +597,7 @@ defmodule OliWeb.Components.Delivery.Layouts do <.link id={@id} href={@href} - class={["w-full h-11 flex-col justify-center items-center flex hover:no-underline"]} + class={["w-full h-[35px] flex-col justify-center items-center flex hover:no-underline"]} > <.nav_link_content {assigns} /> diff --git a/lib/oli_web/components/layouts/student_delivery.html.heex b/lib/oli_web/components/layouts/student_delivery.html.heex index faf08455e34..888af5b7c08 100644 --- a/lib/oli_web/components/layouts/student_delivery.html.heex +++ b/lib/oli_web/components/layouts/student_delivery.html.heex @@ -21,8 +21,11 @@ discussions_enabled={@discussions_enabled} />
<.flash_group flash={@flash} /> diff --git a/lib/oli_web/components/layouts/workspace.html.heex b/lib/oli_web/components/layouts/workspace.html.heex index 4588aadee05..55f427bb947 100644 --- a/lib/oli_web/components/layouts/workspace.html.heex +++ b/lib/oli_web/components/layouts/workspace.html.heex @@ -21,7 +21,7 @@ class={[ "transition-all duration-100 mt-14", if(@params.sidebar_expanded, - do: "md:w-[calc(100%-190px)] md:ml-[190px]", + do: "md:w-[calc(100%-200px)] md:ml-[200px]", else: "md:w-[calc(100%-60px)] md:ml-[60px]" ) ]} From 7449c4f1d43a55c0d21645ea4d9c45fedf70819c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 09:58:20 -0300 Subject: [PATCH 05/14] implement new instructor dashboard --- lib/oli_web/backgrounds.ex | 65 ++++++++ .../components/layouts/workspace.html.heex | 77 +++++---- lib/oli_web/icons.ex | 13 +- lib/oli_web/live/common/search_input.ex | 2 +- .../live/delivery/open_and_free_index.ex | 152 +++++++++++++++++- 5 files changed, 268 insertions(+), 41 deletions(-) diff --git a/lib/oli_web/backgrounds.ex b/lib/oli_web/backgrounds.ex index 5d8a2eb65ec..89a9d264616 100644 --- a/lib/oli_web/backgrounds.ex +++ b/lib/oli_web/backgrounds.ex @@ -74,6 +74,71 @@ defmodule OliWeb.Backgrounds do """ end + def instructor_dashboard_header(assigns) do + ~H""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + end + defp student_signin_background_color do Application.fetch_env!(:oli, :student_sign_in)[:background_color] end diff --git a/lib/oli_web/components/layouts/workspace.html.heex b/lib/oli_web/components/layouts/workspace.html.heex index 55f427bb947..702496a9938 100644 --- a/lib/oli_web/components/layouts/workspace.html.heex +++ b/lib/oli_web/components/layouts/workspace.html.heex @@ -1,35 +1,44 @@ -
-
-
-
- -
+
+
+
+
- -
- <%= @inner_content %> -
-
- -
+
+ +
+ <%= @inner_content %> +
+ + diff --git a/lib/oli_web/icons.ex b/lib/oli_web/icons.ex index 842de156551..bc0384c4777 100644 --- a/lib/oli_web/icons.ex +++ b/lib/oli_web/icons.ex @@ -1225,13 +1225,22 @@ defmodule OliWeb.Icons do end attr :is_active, :boolean, default: false + attr :stroke_class, :string, default: "stroke-[#757682] dark:stroke-[#BAB8BF]" + attr :width, :integer, default: 24 + attr :height, :integer, default: 24 def growing_bars(%{is_active: false} = assigns) do ~H""" - + add_user_role(socket.assigns.current_user) + |> filter_by_role(params.active_workspace) |> add_instructors() |> add_sections_progress(socket.assigns.current_user.id) {:ok, assign(socket, sections: sections, - params: decode_params(params), + params: params, filtered_sections: sections, show_role_badges: show_role_badges(sections) ), layout: {OliWeb.Layouts, :workspace}} @@ -47,7 +52,140 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do @impl Phoenix.LiveView - def render(assigns) do + def render(%{params: %{active_workspace: :instructor_workspace}} = assigns) do + ~H""" +
+
+
+ +
+
+
+ +
+ Instructor Dashboard +
+
+
+ Gain insights into student engagement, progress, and learning patterns. +
+
+
+ +
+
+

+ My courses +

+
+ To create course sections, + +
+
+
+ <.link + href={if(is_independent_instructor?(@current_user), do: ~p"/sections/independent/create")} + class={[ + "h-12 px-5 py-3 hover:no-underline rounded-md justify-center items-center gap-2 inline-flex", + if(is_independent_instructor?(@current_user), + do: "bg-[#0080FF] hover:bg-[#0075EB] dark:bg-[#0062F2] dark:hover:bg-[#0D70FF]", + else: "bg-zinc-600 cursor-not-allowed" + ) + ]} + > +
+
+
+
+ Create New Section +
+ + <.form for={%{}} phx-change="search_section" class="w-[330px]"> + + +
+ +
+ <%= if length(@sections) == 0 do %> +

You are not enrolled in any courses.

+ <% else %> +
+ <.course_card + :for={{section, index} <- Enum.with_index(@filtered_sections)} + index={index} + section={section} + /> +

+ No course found matching "<%= @params.text_search %>" +

+
+ <% end %> +
+
+
+ """ + end + + attr :section, :map + attr :index, :integer + + def course_card(assigns) do + ~H""" +
+
+
+
+
+ <%= @section.title %> +
+
+

+ Students will explore the structure, function, and diversity of living organisms, and diversity of living organisms, and diversity of living organisms, as well as the processes that govern life on Earth. +

+
+
+ <.link + href={get_course_url(@section)} + class="px-5 py-3 bg-[#0080FF] hover:bg-[#0075EB] dark:bg-[#0062F2] dark:hover:bg-[#0D70FF] hover:no-underline rounded-md justify-center items-center gap-2 flex text-white text-base font-normal leading-normal" + > +
+ View Course +
+ +
+
+
+ """ + end + + def render(%{params: %{active_workspace: :student_workspace}} = assigns) do ~H"""
s.user_role == "instructor" end) + + defp filter_by_role(sections, :student_workspace), + do: Enum.filter(sections, fn s -> s.user_role == "student" end) + defp maybe_filter_by_text(sections, nil), do: sections defp maybe_filter_by_text(sections, ""), do: sections From 8ce333b343d3c169d45839fe1f70adf84ae53997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 09:59:12 -0300 Subject: [PATCH 06/14] remove warning --- .../live/delivery/open_and_free_index.ex | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/lib/oli_web/live/delivery/open_and_free_index.ex b/lib/oli_web/live/delivery/open_and_free_index.ex index 8b090cdcc8f..02b54f76e18 100644 --- a/lib/oli_web/live/delivery/open_and_free_index.ex +++ b/lib/oli_web/live/delivery/open_and_free_index.ex @@ -139,52 +139,6 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do """ end - attr :section, :map - attr :index, :integer - - def course_card(assigns) do - ~H""" -
-
-
-
-
- <%= @section.title %> -
-
-

- Students will explore the structure, function, and diversity of living organisms, and diversity of living organisms, and diversity of living organisms, as well as the processes that govern life on Earth. -

-
-
- <.link - href={get_course_url(@section)} - class="px-5 py-3 bg-[#0080FF] hover:bg-[#0075EB] dark:bg-[#0062F2] dark:hover:bg-[#0D70FF] hover:no-underline rounded-md justify-center items-center gap-2 flex text-white text-base font-normal leading-normal" - > -
- View Course -
- -
-
-
- """ - end - def render(%{params: %{active_workspace: :student_workspace}} = assigns) do ~H"""
@@ -286,6 +240,52 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do """ end + attr :section, :map + attr :index, :integer + + def course_card(assigns) do + ~H""" +
+
+
+
+
+ <%= @section.title %> +
+
+

+ Students will explore the structure, function, and diversity of living organisms, and diversity of living organisms, and diversity of living organisms, as well as the processes that govern life on Earth. +

+
+
+ <.link + href={get_course_url(@section)} + class="px-5 py-3 bg-[#0080FF] hover:bg-[#0075EB] dark:bg-[#0062F2] dark:hover:bg-[#0D70FF] hover:no-underline rounded-md justify-center items-center gap-2 flex text-white text-base font-normal leading-normal" + > +
+ View Course +
+ +
+
+
+ """ + end + @impl Phoenix.LiveView def handle_event("search_section", %{"text_search" => text_search}, socket) do {:noreply, From 4db7cdb6ff9ae7fa50a694ae96afa6768ed014f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 10:00:19 -0300 Subject: [PATCH 07/14] remove 'New Section' link from student workspace - now on instructor workspace --- lib/oli_web/live/delivery/open_and_free_index.ex | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/oli_web/live/delivery/open_and_free_index.ex b/lib/oli_web/live/delivery/open_and_free_index.ex index 02b54f76e18..a9d572edbe1 100644 --- a/lib/oli_web/live/delivery/open_and_free_index.ex +++ b/lib/oli_web/live/delivery/open_and_free_index.ex @@ -156,13 +156,6 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do Courses available
- <.link - :if={is_independent_instructor?(@current_user)} - href={~p"/sections/independent/create"} - class="torus-button primary !py-[10px] !px-5 !rounded-[3px] !text-sm flex items-center justify-center" - > - New Section - <.form for={%{}} phx-change="search_section" class="w-[330px]"> Date: Thu, 11 Jul 2024 10:32:02 -0300 Subject: [PATCH 08/14] add course description to card --- lib/oli_web/live/delivery/open_and_free_index.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oli_web/live/delivery/open_and_free_index.ex b/lib/oli_web/live/delivery/open_and_free_index.ex index a9d572edbe1..2fb9f563f86 100644 --- a/lib/oli_web/live/delivery/open_and_free_index.ex +++ b/lib/oli_web/live/delivery/open_and_free_index.ex @@ -261,7 +261,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do

- Students will explore the structure, function, and diversity of living organisms, and diversity of living organisms, and diversity of living organisms, as well as the processes that govern life on Earth. + <%= @section.description %>

From a63d9bb0ba6698186864a1c63f94d24628c4d5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 10:33:46 -0300 Subject: [PATCH 09/14] remove unnecessary base_project preload on open and free sections query --- lib/oli/delivery/sections.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/oli/delivery/sections.ex b/lib/oli/delivery/sections.ex index 6c13fb86429..d39e78c6781 100644 --- a/lib/oli/delivery/sections.ex +++ b/lib/oli/delivery/sections.ex @@ -577,7 +577,6 @@ defmodule Oli.Delivery.Sections do where: e.user_id == ^user_id and s.open_and_free == true and s.status == :active and e.status == :enrolled, - preload: [:base_project], select: s ) From c9c0c5e301021838d423de1e7429c4e1337c33b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 10:51:19 -0300 Subject: [PATCH 10/14] remove tests for role badge and progress since they were removed from the new design --- .../delivery/open_and_free_index_test.exs | 92 ------------------- 1 file changed, 92 deletions(-) diff --git a/test/oli_web/live/delivery/open_and_free_index_test.exs b/test/oli_web/live/delivery/open_and_free_index_test.exs index 41f2082a805..ec0218b324f 100644 --- a/test/oli_web/live/delivery/open_and_free_index_test.exs +++ b/test/oli_web/live/delivery/open_and_free_index_test.exs @@ -111,43 +111,6 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do assert has_element?(view, ~s{a[href="/sections/#{section.slug}"]}) end - test "section badge gets rendered correctly considering the user role", - %{ - conn: conn, - user: user - } do - section_1 = - insert(:section, %{ - open_and_free: true, - description: "This is a description", - title: "The best course ever!" - }) - - section_2 = - insert(:section, %{ - open_and_free: true, - description: "This is another description", - title: "Advanced Elixir" - }) - - Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_learner)]) - Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) - - {:ok, view, _html} = live(conn, ~p"/sections") - - assert has_element?( - view, - ~s{a[href="/sections/#{section_1.slug}"] span.badge}, - "student" - ) - - assert has_element?( - view, - ~s{a[href="/sections/#{section_2.slug}/instructor_dashboard/manage"] span.badge}, - "instructor" - ) - end - test "if no cover image is set, renders default image in enrollment page", %{ conn: conn, user: user @@ -244,60 +207,5 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do refute has_element?(view, "h5", "Maths") refute has_element?(view, "h5", "Elixir") end - - test "sees the correct course progress if enrolled as student", %{conn: conn, user: user} do - section_1 = section_with_progress_for_user(user.id, 1.0) - section_2 = section_with_progress_for_user(user.id, 0.5) - section_3 = section_with_progress_for_user(user.id, 0.0) - - {:ok, view, _html} = live(conn, ~p"/sections") - - assert view - |> element("div[role=\"progress_for_section_#{section_1.id}\"]") - |> render() =~ "100%" - - assert view - |> element("div[role=\"progress_for_section_#{section_2.id}\"]") - |> render() =~ "50%" - - assert view - |> element("div[role=\"progress_for_section_#{section_3.id}\"]") - |> render() =~ "0%" - end - - test "does not see the course progress if enrolled as instuctor", %{conn: conn, user: user} do - section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) - section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) - - Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_instructor)]) - Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_learner)]) - - {:ok, view, _html} = live(conn, ~p"/sections") - - refute has_element?(view, "div[role=\"progress_for_section_#{section_1.id}\"]") - assert has_element?(view, "div[role=\"progress_for_section_#{section_2.id}\"]") - end - - test "does see the complete badge on section card if progress = 100", %{ - conn: conn, - user: user - } do - section_1 = section_with_progress_for_user(user.id, 1.0) - section_2 = section_with_progress_for_user(user.id, 0.5) - - {:ok, view, _html} = live(conn, ~p"/sections") - - assert has_element?( - view, - ~s{span[role="complete_badge_for_section_#{section_1.id}"]}, - "Complete" - ) - - refute has_element?( - view, - ~s{span[role="complete_badge_for_section_#{section_2.id}"]}, - "Complete" - ) - end end end From 6756f229f50e4b4be9f87b16a6b7006258d75881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 10:59:20 -0300 Subject: [PATCH 11/14] update tests that match student workspace --- .../delivery/open_and_free_index_test.exs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/test/oli_web/live/delivery/open_and_free_index_test.exs b/test/oli_web/live/delivery/open_and_free_index_test.exs index ec0218b324f..d59a9dec1b4 100644 --- a/test/oli_web/live/delivery/open_and_free_index_test.exs +++ b/test/oli_web/live/delivery/open_and_free_index_test.exs @@ -49,8 +49,8 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do describe "user" do setup [:user_conn] - test "can access when logged in as student", %{conn: conn} do - {:ok, view, _html} = live(conn, ~p"/sections") + test "can access student workspace when logged in as student", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert has_element?(view, "h3", "Courses available") assert has_element?(view, "p", "You are not enrolled in any courses.") @@ -73,7 +73,10 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do }}} = live(conn, ~p"/sections") end - test "can access when user is unlocked after being locked", %{conn: conn, user: user} do + test "can access student workspace when user is unlocked after being locked", %{ + conn: conn, + user: user + } do # Lock the user {:ok, date, _timezone} = DateTime.from_iso8601("2019-05-22 20:30:00Z") {:ok, user} = Accounts.update_user(user, %{locked_at: date}) @@ -81,13 +84,13 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do # Unlock the user UserContext.unlock(user) - {:ok, view, _html} = live(conn, ~p"/sections") + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert has_element?(view, "h3", "Courses available") assert has_element?(view, "p", "You are not enrolled in any courses.") end - test "renders product title, image and description in sections index with a link to access to it", + test "in student workspace sees product title, image and description in sections index with a link to access to it", %{ conn: conn, user: user @@ -102,7 +105,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do Sections.enroll(user.id, section.id, [ContextRoles.get_role(:context_learner)]) - {:ok, view, _html} = live(conn, ~p"/sections") + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert render(view) =~ ~s|style=\"background-image: url('https://example.com/some-image-url.png');\"| @@ -119,20 +122,20 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do Sections.enroll(user.id, section.id, [ContextRoles.get_role(:context_learner)]) - {:ok, view, _html} = live(conn, ~p"/sections") + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert render(view) =~ ~s|style=\"background-image: url('/images/course_default.png');\"| end - test "can search by course name", %{conn: conn, user: user} do + test "can search by course name in student workspace", %{conn: conn, user: user} do section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_learner)]) - Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_learner)]) - {:ok, view, _html} = live(conn, ~p"/sections") + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert has_element?(view, "h5", "The best course ever!") assert has_element?(view, "h5", "Maths") @@ -159,7 +162,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do refute has_element?(view, "h5", "Maths") end - test "can search by instructor name", %{conn: conn, user: user} do + test "can search by instructor name in student workspace", %{conn: conn, user: user} do section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) section_3 = insert(:section, %{open_and_free: true, title: "Elixir"}) @@ -177,7 +180,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do Sections.enroll(instructor_2.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) Sections.enroll(instructor_2.id, section_3.id, [ContextRoles.get_role(:context_instructor)]) - {:ok, view, _html} = live(conn, ~p"/sections") + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert has_element?(view, "h5", "The best course ever!") assert has_element?(view, "h5", "Maths") From 0d2192ac95e9e478e44c696db61e276846b3c4b8 Mon Sep 17 00:00:00 2001 From: Raphael Gachuhi Date: Thu, 11 Jul 2024 10:29:12 -0400 Subject: [PATCH 12/14] fixes archived product dissapearing bug --- lib/oli_web/live/products/products_view.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/oli_web/live/products/products_view.ex b/lib/oli_web/live/products/products_view.ex index cf23d5a62de..ac7f53387df 100644 --- a/lib/oli_web/live/products/products_view.ex +++ b/lib/oli_web/live/products/products_view.ex @@ -182,7 +182,8 @@ defmodule OliWeb.Products.ProductsView do offset: offset, table_model: table_model, total_count: total_count, - text_search: text_search + text_search: text_search, + include_archived: include_archived )} end @@ -268,7 +269,8 @@ defmodule OliWeb.Products.ProductsView do sort_by: socket.assigns.table_model.sort_by_spec.name, sort_order: socket.assigns.table_model.sort_order, offset: socket.assigns.offset, - text_search: socket.assigns.text_search + text_search: socket.assigns.text_search, + include_archived: socket.assigns.include_archived }, changes ) From 562e6a661e764643e403f6486f8c1e1ca8ff4464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 16:53:26 -0300 Subject: [PATCH 13/14] add tests --- .../live/delivery/open_and_free_index.ex | 22 +- .../delivery/open_and_free_index_test.exs | 380 ++++++++++++++++-- .../live/delivery/student/learn_live_test.exs | 9 +- 3 files changed, 363 insertions(+), 48 deletions(-) diff --git a/lib/oli_web/live/delivery/open_and_free_index.ex b/lib/oli_web/live/delivery/open_and_free_index.ex index 2fb9f563f86..572ca99ff85 100644 --- a/lib/oli_web/live/delivery/open_and_free_index.ex +++ b/lib/oli_web/live/delivery/open_and_free_index.ex @@ -55,20 +55,20 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do def render(%{params: %{active_workspace: :instructor_workspace}} = assigns) do ~H"""
-
-
+
+
-
+

Instructor Dashboard -

+
-
+

Gain insights into student engagement, progress, and learning patterns. -

+
@@ -79,6 +79,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do
To create course sections, @@ -120,7 +121,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do
<%= if length(@sections) == 0 do %> -

You are not enrolled in any courses.

+

You are not enrolled in any courses as an instructor.

<% else %>
<.course_card @@ -239,6 +240,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do def course_card(assigns) do ~H"""
<%= @section.title %>
-

+

<%= @section.description %>

diff --git a/test/oli_web/live/delivery/open_and_free_index_test.exs b/test/oli_web/live/delivery/open_and_free_index_test.exs index d59a9dec1b4..e6188b21114 100644 --- a/test/oli_web/live/delivery/open_and_free_index_test.exs +++ b/test/oli_web/live/delivery/open_and_free_index_test.exs @@ -7,35 +7,9 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do alias Lti_1p3.Tool.ContextRoles alias Oli.Delivery.Sections - alias Oli.{Accounts, Seeder} - alias Oli.Delivery.Attempts.Core + alias Oli.Accounts alias OliWeb.Pow.UserContext - defp set_progress(section_id, resource_id, user_id, progress, revision) do - {:ok, resource_access} = - Core.track_access(resource_id, section_id, user_id) - |> Core.update_resource_access(%{progress: progress}) - - insert(:resource_attempt, %{ - resource_access: resource_access, - revision: revision, - lifecycle_state: :evaluated - }) - end - - defp section_with_progress_for_user(user_id, progress) do - map = Seeder.base_project_with_larger_hierarchy() - {:ok, _} = Sections.rebuild_contained_pages(map.section) - Sections.enroll(user_id, map.section.id, [ContextRoles.get_role(:context_learner)]) - - Sections.fetch_all_pages(map.section.slug) - |> Enum.each(fn page_revision -> - set_progress(map.section.id, page_revision.resource_id, user_id, progress, page_revision) - end) - - map.section - end - describe "user cannot access when is not logged in" do test "redirects to new session", %{ conn: conn @@ -49,20 +23,21 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do describe "user" do setup [:user_conn] - test "can access student workspace when logged in as student", %{conn: conn} do + #### Student Workspace #### + test "can access student workspace when logged in", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert has_element?(view, "h3", "Courses available") assert has_element?(view, "p", "You are not enrolled in any courses.") end - test "can access when user is not enrolled to any section", %{conn: conn} do - {:ok, view, _html} = live(conn, ~p"/sections") + test "can access student workspace when not enrolled to any section", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") assert has_element?(view, "p", "You are not enrolled in any courses.") end - test "cannot access when user is locked", %{conn: conn, user: user} do + test "cannot access student workspace when locked", %{conn: conn, user: user} do UserContext.lock(user) {:error, @@ -73,7 +48,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do }}} = live(conn, ~p"/sections") end - test "can access student workspace when user is unlocked after being locked", %{ + test "can access student workspace when is unlocked after being locked", %{ conn: conn, user: user } do @@ -90,7 +65,7 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do assert has_element?(view, "p", "You are not enrolled in any courses.") end - test "in student workspace sees product title, image and description in sections index with a link to access to it", + test "can see product title, image and description in sections index with a link to access to it in the student workspace", %{ conn: conn, user: user @@ -114,10 +89,11 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do assert has_element?(view, ~s{a[href="/sections/#{section.slug}"]}) end - test "if no cover image is set, renders default image in enrollment page", %{ - conn: conn, - user: user - } do + test "if no cover image is set, renders default image in enrollment page in the student workspace", + %{ + conn: conn, + user: user + } do section = insert(:section, %{open_and_free: true}) Sections.enroll(user.id, section.id, [ContextRoles.get_role(:context_learner)]) @@ -210,5 +186,335 @@ defmodule OliWeb.Delivery.OpenAndFreeIndexTest do refute has_element?(view, "h5", "Maths") refute has_element?(view, "h5", "Elixir") end + + test "only sees sections enrolled as student on student workspace", %{conn: conn, user: user} do + section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) + section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) + + Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_learner)]) + Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") + + assert has_element?(view, "h5", "The best course ever!") + refute has_element?(view, "h5", "Maths") + end + + #### Instructor Workspace #### + + test "can access instructor workspace when logged in", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + assert has_element?(view, "h1", "Instructor Dashboard") + assert has_element?(view, "p", "You are not enrolled in any courses as an instructor.") + end + + test "can see product title, image and description in sections index with a link to manage it on instructor workspace", + %{ + conn: conn, + user: user + } do + section = + insert(:section, %{ + open_and_free: true, + cover_image: "https://example.com/some-image-url.png", + description: "This is a description", + title: "The best course ever!" + }) + + Sections.enroll(user.id, section.id, [ContextRoles.get_role(:context_instructor)]) + + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + assert render(view) =~ + ~s|style=\"background-image: url('https://example.com/some-image-url.png');\"| + + assert has_element?(view, "h5", "The best course ever!") + + assert has_element?( + view, + ~s{a[href="/sections/#{section.slug}/instructor_dashboard/manage"]} + ) + end + + test "if no cover image is set, renders default image in enrollment page on instructor workspace", + %{ + conn: conn, + user: user + } do + section = insert(:section, %{open_and_free: true}) + + Sections.enroll(user.id, section.id, [ContextRoles.get_role(:context_learner)]) + + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") + + assert render(view) =~ + ~s|style=\"background-image: url('/images/course_default.png');\"| + end + + test "can search by course name in instructor workspace", %{conn: conn, user: user} do + section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) + section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) + + Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_instructor)]) + Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + assert has_element?(view, "h5", "The best course ever!") + assert has_element?(view, "h5", "Maths") + + view + |> form("form[phx-change=search_section]") + |> render_change(%{text_search: "best"}) + + assert has_element?(view, "h5", "The best course ever!") + refute has_element?(view, "h5", "Maths") + + view + |> form("form[phx-change=search_section]") + |> render_change(%{text_search: ""}) + + assert has_element?(view, "h5", "The best course ever!") + assert has_element?(view, "h5", "Maths") + + view + |> form("form[phx-change=search_section]") + |> render_change(%{text_search: "a not existing course"}) + + refute has_element?(view, "h5", "The best course ever!") + refute has_element?(view, "h5", "Maths") + end + + test "can search by instructor name in instructor workspace", %{conn: conn, user: user} do + section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) + section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) + section_3 = insert(:section, %{open_and_free: true, title: "Elixir"}) + + instructor_1 = insert(:user, %{name: "Lionel Messi"}) + instructor_2 = insert(:user, %{name: "Angel Di Maria"}) + + Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_instructor)]) + Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + Sections.enroll(user.id, section_3.id, [ContextRoles.get_role(:context_instructor)]) + + Sections.enroll(instructor_1.id, section_1.id, [ContextRoles.get_role(:context_instructor)]) + Sections.enroll(instructor_1.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + + Sections.enroll(instructor_2.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + Sections.enroll(instructor_2.id, section_3.id, [ContextRoles.get_role(:context_instructor)]) + + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + assert has_element?(view, "h5", "The best course ever!") + assert has_element?(view, "h5", "Maths") + assert has_element?(view, "h5", "Elixir") + + view + |> form("form[phx-change=search_section]") + |> render_change(%{text_search: "messi"}) + + assert has_element?(view, "h5", "The best course ever!") + assert has_element?(view, "h5", "Maths") + refute has_element?(view, "h5", "Elixir") + + view + |> form("form[phx-change=search_section]") + |> render_change(%{text_search: "maria"}) + + refute has_element?(view, "h5", "The best course ever!") + assert has_element?(view, "h5", "Maths") + assert has_element?(view, "h5", "Elixir") + + view + |> form("form[phx-change=search_section]") + |> render_change(%{text_search: "a not existing instructor"}) + + refute has_element?(view, "h5", "The best course ever!") + refute has_element?(view, "h5", "Maths") + refute has_element?(view, "h5", "Elixir") + end + + test "only sees sections enrolled as instructor on instructor workspace", %{ + conn: conn, + user: user + } do + section_1 = insert(:section, %{open_and_free: true, title: "The best course ever!"}) + section_2 = insert(:section, %{open_and_free: true, title: "Maths"}) + + Sections.enroll(user.id, section_1.id, [ContextRoles.get_role(:context_learner)]) + Sections.enroll(user.id, section_2.id, [ContextRoles.get_role(:context_instructor)]) + + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + refute has_element?(view, "h5", "The best course ever!") + assert has_element?(view, "h5", "Maths") + end + + test "can not create sections on instructor worskpace when logged in as student", %{ + conn: conn + } do + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + assert has_element?( + view, + "div[role='create section instructions']", + "To create course sections," + ) + + assert has_element?( + view, + "div[role='create section instructions'] button[onclick='window.showHelpModal();']", + "contact support." + ) + + assert has_element?(view, "a[href='#']", "Create New Section") + end + + test "can navigate between the different workspaces through the left navbar", %{ + conn: conn + } do + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=student_workspace") + + assert has_element?( + view, + "a[href='/authoring/projects?active_workspace=course_author_workspace&sidebar_expanded=true']", + "Course Author" + ) + + assert has_element?( + view, + "a[href='/sections?active_workspace=instructor_workspace&sidebar_expanded=true']", + "Instructor" + ) + + assert has_element?( + view, + "a[href='/sections?active_workspace=student_workspace&sidebar_expanded=true']", + "Student" + ) + + # we are currently on the student workspace + refute has_element?(view, "h1", "Instructor Dashboard") + assert has_element?(view, "h3", "Courses available") + + # we go to the instructor workspace + view + |> element(~s{nav[id=desktop-workspace-nav-menu] a}, "Instructor") + |> render_click() + + assert_redirected( + view, + ~p"/sections?active_workspace=instructor_workspace&sidebar_expanded=true" + ) + + {:ok, view, _html} = + live(conn, ~p"/sections?active_workspace=instructor_workspace&sidebar_expanded=true") + + assert has_element?(view, "h1", "Instructor Dashboard") + refute has_element?(view, "h3", "Courses available") + + # we go back to the student workspace + view + |> element(~s{nav[id=desktop-workspace-nav-menu] a}, "Student") + |> render_click() + + assert_redirected( + view, + ~p"/sections?active_workspace=student_workspace&sidebar_expanded=true" + ) + + {:ok, view, _html} = + live(conn, ~p"/sections?active_workspace=student_workspace&sidebar_expanded=true") + + refute has_element?(view, "h1", "Instructor Dashboard") + assert has_element?(view, "h3", "Courses available") + + # we go to the course author workspace + view + |> element(~s{nav[id=desktop-workspace-nav-menu] a}, "Course Author") + |> render_click() + + assert_redirected( + view, + ~p"/authoring/projects?active_workspace=course_author_workspace&sidebar_expanded=true" + ) + end + + test "can see expanded/collapsed sidebar nav", %{ + conn: conn + } do + {:ok, view, _html} = live(conn, ~p"/sections") + + assert has_element?(view, ~s{nav[id=desktop-workspace-nav-menu][aria-expanded=true]}) + + labels = ["Course Author", "Instructor", "Student"] + + Enum.each(labels, fn label -> + assert view + |> element(~s{nav[id=desktop-workspace-nav-menu]}) + |> render() =~ label + end) + + {:ok, view, _html} = live(conn, ~p"/sections?sidebar_expanded=false") + + assert has_element?(view, ~s{nav[id=desktop-workspace-nav-menu][aria-expanded=false]}) + + Enum.each(labels, fn label -> + refute view + |> element(~s{nav[id=desktop-workspace-nav-menu]}) + |> render() =~ label + end) + end + + test "navbar expanded or collapsed state is kept after navigating to other menu link", %{ + conn: conn + } do + {:ok, view, _html} = live(conn, ~p"/sections?sidebar_expanded=true") + + assert has_element?(view, ~s{nav[id=desktop-workspace-nav-menu][aria-expanded=true]}) + + view + |> element(~s{nav[id=desktop-workspace-nav-menu] a}, "Student") + |> render_click() + + assert_redirect(view, "/sections?active_workspace=student_workspace&sidebar_expanded=true") + + {:ok, view, _html} = live(conn, ~p"/sections?sidebar_expanded=false") + + assert has_element?(view, ~s{nav[id=desktop-workspace-nav-menu][aria-expanded=false]}) + + view + |> element(~s{nav[id=desktop-workspace-nav-menu] a[id="student_workspace_nav_link"]}) + |> render_click() + + assert_redirect(view, "/sections?active_workspace=student_workspace&sidebar_expanded=false") + end + end + + describe "user as instructor" do + setup [:instructor_conn] + + #### Instructor Workspace #### + + test "can create sections on instructor worskpace when logged in", %{ + conn: conn + } do + {:ok, view, _html} = live(conn, ~p"/sections?active_workspace=instructor_workspace") + + refute has_element?( + view, + "div[role='create section instructions']", + "To create course sections," + ) + + refute has_element?( + view, + "div[role='create section instructions'] button[onclick='window.showHelpModal();']", + "contact support." + ) + + assert has_element?(view, "a[href='/sections/independent/create']", "Create New Section") + end end end diff --git a/test/oli_web/live/delivery/student/learn_live_test.exs b/test/oli_web/live/delivery/student/learn_live_test.exs index 88a83d1c587..1c186ce9f78 100644 --- a/test/oli_web/live/delivery/student/learn_live_test.exs +++ b/test/oli_web/live/delivery/student/learn_live_test.exs @@ -2532,7 +2532,10 @@ defmodule OliWeb.Delivery.Student.ContentLiveTest do test "can switch from Outline to Gallery view", %{conn: conn, section: section} do {:ok, view, _html} = - live(conn, Utils.learn_live_path(section.slug, selected_view: :outline)) + live( + conn, + Utils.learn_live_path(section.slug, selected_view: :outline, sidebar_expanded: true) + ) # selector text matches current view assert has_element?(view, ~s{div[id=view_selector] div}, "Outline") @@ -2550,7 +2553,7 @@ defmodule OliWeb.Delivery.Student.ContentLiveTest do assert_patch( view, - Utils.learn_live_path(section.slug, selected_view: :gallery, sidebar_expanded: true) + Utils.learn_live_path(section.slug, sidebar_expanded: true, selected_view: :gallery) ) # selector text matches target view @@ -2577,7 +2580,7 @@ defmodule OliWeb.Delivery.Student.ContentLiveTest do assert_patch( view, - Utils.learn_live_path(section.slug, selected_view: :outline, sidebar_expanded: true) + Utils.learn_live_path(section.slug, sidebar_expanded: true, selected_view: :outline) ) # selector text matches target view From 6ad45112fc4f9de9618201aee0ecc896b14a71e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cirio?= Date: Thu, 11 Jul 2024 17:01:50 -0300 Subject: [PATCH 14/14] improve header and background for light mode --- lib/oli_web/backgrounds.ex | 28 ++++++++++++++----- .../live/delivery/open_and_free_index.ex | 10 +++++-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/oli_web/backgrounds.ex b/lib/oli_web/backgrounds.ex index 89a9d264616..7f595914c69 100644 --- a/lib/oli_web/backgrounds.ex +++ b/lib/oli_web/backgrounds.ex @@ -77,24 +77,38 @@ defmodule OliWeb.Backgrounds do def instructor_dashboard_header(assigns) do ~H""" - + - + - + - + - + - + diff --git a/lib/oli_web/live/delivery/open_and_free_index.ex b/lib/oli_web/live/delivery/open_and_free_index.ex index 572ca99ff85..4ba83c6516a 100644 --- a/lib/oli_web/live/delivery/open_and_free_index.ex +++ b/lib/oli_web/live/delivery/open_and_free_index.ex @@ -61,12 +61,16 @@ defmodule OliWeb.Delivery.OpenAndFreeIndex do
- -

+ +

Instructor Dashboard

-

+

Gain insights into student engagement, progress, and learning patterns.