diff --git a/lib/atomic_web/components/activity.ex b/lib/atomic_web/components/activity.ex index 6f98b8e6..aed7ba50 100644 --- a/lib/atomic_web/components/activity.ex +++ b/lib/atomic_web/components/activity.ex @@ -33,12 +33,12 @@ defmodule AtomicWeb.Components.Activity do

<%= @activity.title %>

-

<%= @activity.description %>

+

<%= maybe_slice_string(@activity.description, 300) %>

<%= if @activity.image do %>
- +
<% end %> @@ -46,8 +46,8 @@ defmodule AtomicWeb.Components.Activity do
- <.icon name="hero-clock-solid" class="size-5" /> - <%= relative_datetime(@activity.start) %> + <.icon name="hero-calendar-solid" class="mr-1.5 h-5 w-5 flex-shrink-0 text-zinc-400" /> + <%= pretty_display_date(@activity.start) %> starting in @@ -55,7 +55,7 @@ defmodule AtomicWeb.Components.Activity do <.icon name="hero-user-group-solid" class="size-5" /> <%= @activity.enrolled %>/<%= @activity.maximum_entries %> - enrollments + enrollments diff --git a/lib/atomic_web/components/announcement.ex b/lib/atomic_web/components/announcement.ex index f96ba8a2..5913ea8a 100644 --- a/lib/atomic_web/components/announcement.ex +++ b/lib/atomic_web/components/announcement.ex @@ -31,12 +31,12 @@ defmodule AtomicWeb.Components.Announcement do

<%= @announcement.title %>

- <%= @announcement.description %> + <%= maybe_slice_string(@announcement.description, 300) %>
<%= if @announcement.image do %>
- +
<% end %> diff --git a/lib/atomic_web/components/unauthenticated.ex b/lib/atomic_web/components/unauthenticated.ex new file mode 100644 index 00000000..27d6f958 --- /dev/null +++ b/lib/atomic_web/components/unauthenticated.ex @@ -0,0 +1,24 @@ +defmodule AtomicWeb.Components.Unauthenticated do + @moduledoc """ + A component for displaying an unauthenticated state. + """ + use AtomicWeb, :component + + attr :id, :string, default: "unauthenticated-state", required: false + attr :url, :string, default: "users/log_in", required: false + + def unauthenticated_state(assigns) do + ~H""" +
+ <.icon name="hero-user-circle" class="mx-auto h-12 w-12 text-zinc-400" /> +

<%= gettext("You are not authenticated") %>

+

<%= gettext("Please log in to view this content.") %>

+
+ <.button patch={@url} icon="hero-arrow-right-end-on-rectangle-solid" icon_position={:right}> + <%= gettext("Log In") %> + +
+
+ """ + end +end diff --git a/lib/atomic_web/live/activity_live/form_component.ex b/lib/atomic_web/live/activity_live/form_component.ex index 4e039b8f..ec6eb317 100644 --- a/lib/atomic_web/live/activity_live/form_component.ex +++ b/lib/atomic_web/live/activity_live/form_component.ex @@ -75,7 +75,7 @@ defmodule AtomicWeb.ActivityLive.FormComponent do consume_uploaded_entries(socket, :image, fn %{path: path}, entry -> Activities.update_activity_image(activity, %{ "image" => %Plug.Upload{ - content_type: entry.content_type, + content_type: entry.client_type, filename: entry.client_name, path: path } diff --git a/lib/atomic_web/live/home_live/components/follow_suggestions/follow_suggestions.ex b/lib/atomic_web/live/home_live/components/follow_suggestions/follow_suggestions.ex index 7e94c593..910739fa 100644 --- a/lib/atomic_web/live/home_live/components/follow_suggestions/follow_suggestions.ex +++ b/lib/atomic_web/live/home_live/components/follow_suggestions/follow_suggestions.ex @@ -15,8 +15,8 @@ defmodule AtomicWeb.HomeLive.Components.FollowSuggestions do def follow_suggestions(assigns) do ~H"""
-

- Organizations to follow +

+ <%= title(@current_user) %>

-
+
<.button patch={~p"/organizations"} color={:white} size={:md} full_width> <%= gettext("View all") %> @@ -33,4 +33,8 @@ defmodule AtomicWeb.HomeLive.Components.FollowSuggestions do
""" end + + defp title(current_user) when is_nil(current_user), do: gettext("Top organizations") + + defp title(_current_user), do: gettext("Organizations you may like") end diff --git a/lib/atomic_web/live/home_live/components/follow_suggestions/suggestion.ex b/lib/atomic_web/live/home_live/components/follow_suggestions/suggestion.ex index 28b03adb..ae4244c8 100644 --- a/lib/atomic_web/live/home_live/components/follow_suggestions/suggestion.ex +++ b/lib/atomic_web/live/home_live/components/follow_suggestions/suggestion.ex @@ -38,25 +38,28 @@ defmodule AtomicWeb.HomeLive.Components.FollowSuggestions.Suggestion do
<%= if @is_following do %> - + <% else %> - + <% end %>
""" end + def handle_event("follow", _params, socket) when is_nil(socket.assigns.current_user) do + {:noreply, + socket + |> put_flash(:info, "You must be logged in to follow organizations") + |> redirect(to: ~p"/users/log_in")} + end + @impl true def handle_event("follow", %{"organization_id" => organization_id} = _params, socket) do attrs = %{ diff --git a/lib/atomic_web/live/home_live/components/schedule.ex b/lib/atomic_web/live/home_live/components/schedule.ex index 9a776dd1..9696ee5a 100644 --- a/lib/atomic_web/live/home_live/components/schedule.ex +++ b/lib/atomic_web/live/home_live/components/schedule.ex @@ -1,62 +1,102 @@ defmodule AtomicWeb.HomeLive.Components.Schedule do @moduledoc false use AtomicWeb, :component + alias Atomic.Activities attr :schedule, :map, required: true, doc: "The schedule to display." + attr :current_user, :map, required: true, doc: "The current user." + attr :tab, :string, default: "all", values: ["all", "user"], doc: "The tab active." def schedule(assigns) do ~H"""
-
-

+ <%= if length(@schedule.daily) == 0 && length(@schedule.weekly) == 0 do %> +

+ <%= show_empty(assigns) %> +
+ <% end %> +
+

Today

-
    +
      <%= for entry <- @schedule.daily do %> <.link navigate={~p"/activities/#{entry}"}> -
    • -

      - <%= entry.title %> -

      -
      - <.icon name="hero-clock-solid" class="size-4" /> -

      - <%= display_time(entry.start) %> - <%= display_time(entry.finish) %> +

    • +
      +

      + <%= entry.title %>

      +
      + <.icon name="hero-clock-solid" class="size-4" /> +

      + <%= display_time(entry.start) %> - <%= display_time(entry.finish) %> +

      +

      - <%= entry.description %> + <%= maybe_slice_string(entry.description, 100) %>

    • + <%= if check_enrolled(entry, @current_user) do %> +
      + <.icon name="hero-user-group-solid" class="size-4 font-bold text-green-500" /> + <.link navigate={~p"/organizations/#{entry.organization_id}"} class="text-xs text-zinc-400 hover:underline"> + <%= entry.organization.name %> + +
      + <% else %> +
      + <.link navigate={~p"/organizations/#{entry.organization_id}"} class="text-xs text-zinc-400 hover:underline"> + <%= entry.organization.name %> + +
      + <% end %> <% end %>
-
-

+

+

This week

-
    +
      <%= for entry <- @schedule.weekly do %> <.link navigate={~p"/activities/#{entry}"}> -
    • -

      - <%= entry.title %> -

      -
      - <.icon name="hero-clock-solid" class="size-4" /> -

      - <%= pretty_display_date(entry.start) %> +

    • +
      +

      + <%= entry.title %>

      +
      + <.icon name="hero-calendar-solid" class="size-4" /> +

      + <%= pretty_display_date(entry.start) %> +

      +

      - <%= entry.description %> + <%= maybe_slice_string(entry.description, 150) %>

    • + <%= if check_enrolled(entry, @current_user) do %> +
      + <.icon name="hero-user-group-solid" class="size-4 font-bold text-green-500" /> + <.link navigate={~p"/organizations/#{entry.organization_id}"} class="text-xs text-zinc-400 hover:underline"> + <%= entry.organization.name %> + +
      + <% else %> +
      + <.link navigate={~p"/organizations/#{entry.organization.id}"} class="text-xs text-zinc-400 hover:underline"> + <%= entry.organization.name %> + +
      + <% end %> <% end %>
@@ -64,4 +104,29 @@ defmodule AtomicWeb.HomeLive.Components.Schedule do
""" end + + defp check_enrolled(_entry, nil), do: false + defp check_enrolled(entry, user), do: Activities.participating?(entry.id, user.id) + + defp show_empty(assigns) when assigns.tab == "user" do + ~H""" +

+ <%= gettext("Nothing to do in the next week.") %> +

+

+ <%= gettext("Try enrolling in some activities.") %> +

+ <.button patch={~p"/activities"} color={:white} size={:md} icon="hero-academic-cap"> + <%= gettext("Browse activities") %> + + """ + end + + defp show_empty(assigns) when assigns.tab == "all" do + ~H""" +

+ <%= gettext("No activities scheduled to the next week.") %> +

+ """ + end end diff --git a/lib/atomic_web/live/home_live/index.ex b/lib/atomic_web/live/home_live/index.ex index f4ebf82a..c4224b28 100644 --- a/lib/atomic_web/live/home_live/index.ex +++ b/lib/atomic_web/live/home_live/index.ex @@ -2,7 +2,7 @@ defmodule AtomicWeb.HomeLive.Index do @moduledoc false use AtomicWeb, :live_view - import AtomicWeb.Components.{Activity, Announcement, Tabs} + import AtomicWeb.Components.{Activity, Announcement, Tabs, Unauthenticated, Dropdown} import AtomicWeb.HomeLive.Components.{FollowSuggestions, Schedule} alias Atomic.Activities @@ -26,8 +26,10 @@ defmodule AtomicWeb.HomeLive.Index do socket |> assign(:current_page, :home) |> assign(:page_title, gettext("Home")) - |> assign(:schedule, fetch_schedule(socket)) + |> assign(:schedule_default, fetch_default_schedule()) + |> assign(:schedule_user, fetch_user_schedule(socket.assigns.current_user)) |> assign(:current_tab, current_tab(socket, params)) + |> assign(:schedule, :default) |> assign(:organizations, list_organizations_to_follow(socket.assigns))} end @@ -71,6 +73,12 @@ defmodule AtomicWeb.HomeLive.Index do def handle_event("load-following", _, socket) when socket.assigns.current_tab == "following", do: {:noreply, socket} + def handle_event("load-following", _, socket) when socket.assigns.is_authenticated? == false, + do: + {:noreply, + socket + |> assign(:current_tab, "following")} + def handle_event("load-following", _, socket) do current_user = socket.assigns.current_user @@ -93,18 +101,29 @@ defmodule AtomicWeb.HomeLive.Index do |> assign(:current_tab, "schedule")} end - defp fetch_schedule(socket) when socket.assigns.is_authenticated? do + def handle_event("show-schedule-default", _, socket) do + {:noreply, + socket + |> assign(:schedule, :default)} + end + + def handle_event("show-schedule-user", _, socket) do + {:noreply, + socket + |> assign(:schedule, :user)} + end + + defp fetch_user_schedule(nil), do: %{daily: [], weekly: []} + + defp fetch_user_schedule(user) do {daily, weekly} = - Activities.list_user_activities(socket.assigns.current_user.id, - preloads: [:organization], - order_by: [desc: :start] - ) + Activities.list_user_activities(user.id, preloads: [:organization], order_by: [desc: :start]) |> Enum.reduce({[], []}, &process_activity/2) %{daily: Enum.take(daily, 3), weekly: Enum.take(weekly, 3)} end - defp fetch_schedule(_socket) do + defp fetch_default_schedule do {daily, weekly} = Activities.list_activities(preloads: [:organization], order_by: [desc: :start]) |> Enum.reduce({[], []}, &process_activity/2) @@ -113,7 +132,7 @@ defmodule AtomicWeb.HomeLive.Index do end defp process_activity(activity, {daily_acc, weekly_acc}) do - case within_today_or_this_week(activity.start) do + case within_today_or_this_week(activity.start |> NaiveDateTime.to_date()) do :today -> {[activity | daily_acc], weekly_acc} diff --git a/lib/atomic_web/live/home_live/index.html.heex b/lib/atomic_web/live/home_live/index.html.heex index 57d96496..022fe426 100644 --- a/lib/atomic_web/live/home_live/index.html.heex +++ b/lib/atomic_web/live/home_live/index.html.heex @@ -30,7 +30,7 @@ <.tab id="tab-following" active={@current_tab == "following"} phx-click="load-following" phx-hook="ScrollToTop"> <%= gettext("Following") %> - <%= if length(@schedule.weekly) + length(@schedule.daily) > 0 do %> + <%= if length(@schedule_default.weekly) + length(@schedule_default.daily) > 0 do %> <.tab id="tab-schedule" active={@current_tab == "schedule"} phx-click="load-schedule" phx-hook="ScrollToTop" class="lg:hidden"> <%= gettext("Schedule") %> @@ -39,35 +39,74 @@
<%= if @current_tab != "schedule" do %> -
    -
  • - <%= if post.type == :activity do %> - <.link navigate={~p"/activities/#{post.activity}"}> - <.activity activity={post.activity} /> - - <% else %> - <.link navigate={~p"/announcements/#{post.announcement}"}> - <.announcement announcement={post.announcement} /> - - <% end %> -
  • -
+ <%= if @is_authenticated? == false && @current_tab == "following" do %> +
+ <.unauthenticated_state /> +
+ <% else %> +
    +
  • + <%= if post.type == :activity do %> + <.link navigate={~p"/activities/#{post.activity}"}> + <.activity activity={post.activity} /> + + <% else %> + <.link navigate={~p"/announcements/#{post.announcement}"}> + <.announcement announcement={post.announcement} /> + + <% end %> +
  • +
+ <% end %> <% else %> - <.schedule schedule={@schedule} /> + <.schedule schedule={@schedule_default} current_user={@current_user} /> <% end %>
- diff --git a/storybook/components/unauthenticated.story.exs b/storybook/components/unauthenticated.story.exs new file mode 100644 index 00000000..286c578a --- /dev/null +++ b/storybook/components/unauthenticated.story.exs @@ -0,0 +1,16 @@ +defmodule AtomicWeb.Storybook.Components.Unauthenticated do + use PhoenixStorybook.Story, :component + + alias AtomicWeb.Components.Unauthenticated + + def function, do: &Unauthenticated.unauthenticated_state/1 + + def variations do + [ + %Variation{ + id: :default, + attributes: %{} + } + ] + end +end