Skip to content

Commit

Permalink
refactor: improve main page (#531)
Browse files Browse the repository at this point in the history
  • Loading branch information
FilipeR13 authored Nov 5, 2024
1 parent d8d39ae commit 1357eaa
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 78 deletions.
10 changes: 5 additions & 5 deletions lib/atomic_web/components/activity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,29 @@ defmodule AtomicWeb.Components.Activity do
</div>
<h2 class="mt-3 text-base font-semibold text-gray-900"><%= @activity.title %></h2>
<div class="text-justify text-sm text-gray-700">
<p><%= @activity.description %></p>
<p><%= maybe_slice_string(@activity.description, 300) %></p>
</div>
<!-- Image -->
<%= if @activity.image do %>
<div class="mt-4">
<img class="max-w-screen rounded-md sm:max-w-xl" src={Uploaders.Post.url({@activity.image, @activity}, :original)} />
<img class="max-w-screen max-h-[32rem] rounded-md object-cover sm:max-w-xl" src={Uploaders.Post.url({@activity.image, @activity}, :original)} />
</div>
<% end %>
<!-- Footer -->
<div class={"#{footer_margin_top_class(@activity)} flex flex-row justify-between"}>
<div class="flex space-x-4">
<span class="inline-flex items-center text-sm">
<span class="inline-flex space-x-2 text-zinc-400">
<.icon name="hero-clock-solid" class="size-5" />
<span class="font-medium text-gray-900"><%= relative_datetime(@activity.start) %></span>
<.icon name="hero-calendar-solid" class="mr-1.5 h-5 w-5 flex-shrink-0 text-zinc-400" />
<span class="font-medium text-gray-900"><%= pretty_display_date(@activity.start) %></span>
<span class="sr-only">starting in</span>
</span>
</span>
<span class="inline-flex items-center text-sm">
<span class="inline-flex space-x-2 text-zinc-400">
<.icon name="hero-user-group-solid" class="size-5" />
<span class="font-medium text-gray-900"><%= @activity.enrolled %>/<%= @activity.maximum_entries %></span>
<span class="sr-only">enrollments</span>
<span class="sr-only text-zinc-400">enrollments</span>
</span>
</span>
<span class="inline-flex items-center text-sm">
Expand Down
4 changes: 2 additions & 2 deletions lib/atomic_web/components/announcement.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ defmodule AtomicWeb.Components.Announcement do
</div>
<h2 class="mt-3 text-base font-semibold text-gray-900"><%= @announcement.title %></h2>
<div class="space-y-4 text-justify text-sm text-gray-700">
<%= @announcement.description %>
<%= maybe_slice_string(@announcement.description, 300) %>
</div>
<!-- Image -->
<%= if @announcement.image do %>
<div class="mt-4">
<img class="max-w-screen rounded-md sm:max-w-xl" src={Uploaders.Post.url({@announcement.image, @announcement}, :original)} />
<img class="max-w-screen max-h-[32rem] rounded-md object-cover sm:max-w-xl" src={Uploaders.Post.url({@announcement.image, @announcement}, :original)} />
</div>
<% end %>
</div>
Expand Down
24 changes: 24 additions & 0 deletions lib/atomic_web/components/unauthenticated.ex
Original file line number Diff line number Diff line change
@@ -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"""
<div id={@id} class="text-center">
<.icon name="hero-user-circle" class="mx-auto h-12 w-12 text-zinc-400" />
<h3 class="mt-2 text-sm font-semibold text-zinc-900"><%= gettext("You are not authenticated") %></h3>
<p class="mt-1 text-sm text-zinc-500"><%= gettext("Please log in to view this content.") %></p>
<div class="mt-4 flex justify-center">
<.button patch={@url} icon="hero-arrow-right-end-on-rectangle-solid" icon_position={:right}>
<%= gettext("Log In") %>
</.button>
</div>
</div>
"""
end
end
2 changes: 1 addition & 1 deletion lib/atomic_web/live/activity_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ defmodule AtomicWeb.HomeLive.Components.FollowSuggestions do
def follow_suggestions(assigns) do
~H"""
<div class="overflow-hidden">
<p class="text-lg font-semibold text-gray-900">
Organizations to follow
<p class="text-gray-90 font-semibold leading-6">
<%= title(@current_user) %>
</p>
<div class="flow-root">
<ul role="list" class="divide-y divide-gray-200">
Expand All @@ -25,12 +25,16 @@ defmodule AtomicWeb.HomeLive.Components.FollowSuggestions do
<% end %>
</ul>
</div>
<div class="my-4">
<div class="mt-2 mb-4">
<.button patch={~p"/organizations"} color={:white} size={:md} full_width>
<%= gettext("View all") %>
</.button>
</div>
</div>
"""
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,28 @@ defmodule AtomicWeb.HomeLive.Components.FollowSuggestions.Suggestion do
</.link>
<div class="flex-shrink-0">
<%= if @is_following do %>
<button type="button" phx-value-organization_id={@organization.id} phx-click="unfollow" phx-target={@myself} class="z-100 inline-flex items-center gap-x-1.5 text-sm font-semibold leading-6 text-gray-900">
<svg class="size-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
</svg>
<.button phx-value-organization_id={@organization.id} phx-click="unfollow" phx-target={@myself} color={:white} size={:xs}>
<.icon name="hero-minus-solid" class="size-5 text-gray-400" />
<span>Unfollow</span>
</button>
</.button>
<% else %>
<button type="button" phx-value-organization_id={@organization.id} phx-click="follow" phx-target={@myself} class="z-100 inline-flex items-center gap-x-1.5 text-sm font-semibold leading-6 text-gray-900">
<svg class="size-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
</svg>
<.button phx-value-organization_id={@organization.id} phx-click="follow" phx-target={@myself} color={:white} size={:xs}>
<.icon name="hero-plus" class="size-5 text-gray-400" />
<span>Follow</span>
</button>
</.button>
<% end %>
</div>
</li>
"""
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 = %{
Expand Down
113 changes: 89 additions & 24 deletions lib/atomic_web/live/home_live/components/schedule.ex
Original file line number Diff line number Diff line change
@@ -1,67 +1,132 @@
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"""
<div class="overflow-hidden">
<div :if={length(@schedule.daily) != 0} class="px-4 pt-6 sm:px-0">
<p class="text-xl font-semibold text-gray-900">
<%= if length(@schedule.daily) == 0 && length(@schedule.weekly) == 0 do %>
<div class="space-y-4 px-4 pt-4 pb-2 text-center text-zinc-400 sm:px-0">
<%= show_empty(assigns) %>
</div>
<% end %>
<div :if={length(@schedule.daily) != 0} class="border-b border-gray-200 px-4 pt-4 pb-2 sm:px-0">
<p class="font-semibold text-zinc-400">
Today
</p>
<div class="flow-root">
<ul role="list" class="divide-y divide-gray-200">
<ul role="list">
<%= for entry <- @schedule.daily do %>
<.link navigate={~p"/activities/#{entry}"}>
<li class="space-y-3 py-4">
<p class="text-md font-semibold hover:underline">
<%= entry.title %>
</p>
<div class="w-[110px] flex h-6 items-center justify-center space-x-1 rounded-md bg-orange-100 text-orange-500 opacity-70">
<.icon name="hero-clock-solid" class="size-4" />
<p class="text-xs font-semibold">
<%= display_time(entry.start) %> - <%= display_time(entry.finish) %>
<li class="space-y-3 pt-4">
<div class="flex justify-between">
<p class="text-md font-semibold hover:underline">
<%= entry.title %>
</p>
<div class="w-[110px] flex h-6 items-center justify-center space-x-1 rounded-md bg-orange-100 text-orange-500 opacity-70">
<.icon name="hero-clock-solid" class="size-4" />
<p class="text-xs font-semibold">
<%= display_time(entry.start) %> - <%= display_time(entry.finish) %>
</p>
</div>
</div>
<p class="text-justify text-sm text-gray-700">
<%= entry.description %>
<%= maybe_slice_string(entry.description, 100) %>
</p>
</li>
</.link>
<%= if check_enrolled(entry, @current_user) do %>
<div class="flex justify-between pt-2">
<.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 %>
</.link>
</div>
<% else %>
<div class="pt-2 text-right">
<.link navigate={~p"/organizations/#{entry.organization_id}"} class="text-xs text-zinc-400 hover:underline">
<%= entry.organization.name %>
</.link>
</div>
<% end %>
<% end %>
</ul>
</div>
</div>
<div :if={length(@schedule.weekly) != 0} class={"#{if length(@schedule.daily) != 0, do: 'pt-3', else: 'pt-6'} px-4 sm:px-0"}>
<p class="text-xl font-semibold text-gray-900">
<div :if={length(@schedule.weekly) != 0} class={"#{if length(@schedule.daily) != 0, do: 'pt-2', else: 'pt-4'} px-4 pb-2 sm:px-0"}>
<p class="font-semibold text-zinc-400">
This week
</p>
<div class="flow-root">
<ul role="list" class="divide-y divide-gray-200">
<ul role="list">
<%= for entry <- @schedule.weekly do %>
<.link navigate={~p"/activities/#{entry}"}>
<li class="space-y-3 py-4">
<p class="text-md font-semibold hover:underline">
<%= entry.title %>
</p>
<div class="w-[110px] flex h-6 items-center justify-center space-x-1 rounded-md bg-orange-100 text-orange-500 opacity-70">
<.icon name="hero-clock-solid" class="size-4" />
<p class="text-xs font-semibold">
<%= pretty_display_date(entry.start) %>
<li class="space-y-3 pt-4">
<div class="flex justify-between">
<p class="text-md font-semibold hover:underline">
<%= entry.title %>
</p>
<div class="w-[110px] flex h-6 items-center justify-center space-x-1 rounded-md bg-orange-100 text-orange-500 opacity-70">
<.icon name="hero-calendar-solid" class="size-4" />
<p class="text-xs font-semibold">
<%= pretty_display_date(entry.start) %>
</p>
</div>
</div>
<p class="text-justify text-sm text-gray-700">
<%= entry.description %>
<%= maybe_slice_string(entry.description, 150) %>
</p>
</li>
</.link>
<%= if check_enrolled(entry, @current_user) do %>
<div class="flex justify-between pt-2">
<.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 %>
</.link>
</div>
<% else %>
<div class="pt-2 text-right">
<.link navigate={~p"/organizations/#{entry.organization.id}"} class="text-xs text-zinc-400 hover:underline">
<%= entry.organization.name %>
</.link>
</div>
<% end %>
<% end %>
</ul>
</div>
</div>
</div>
"""
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"""
<p>
<%= gettext("Nothing to do in the next week.") %>
</p>
<p>
<%= gettext("Try enrolling in some activities.") %>
</p>
<.button patch={~p"/activities"} color={:white} size={:md} icon="hero-academic-cap">
<%= gettext("Browse activities") %>
</.button>
"""
end

defp show_empty(assigns) when assigns.tab == "all" do
~H"""
<p>
<%= gettext("No activities scheduled to the next week.") %>
</p>
"""
end
end
37 changes: 28 additions & 9 deletions lib/atomic_web/live/home_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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}

Expand Down
Loading

0 comments on commit 1357eaa

Please sign in to comment.