Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve index and show partners pages design #542

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
2 changes: 2 additions & 0 deletions lib/atomic/organizations/partner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ defmodule Atomic.Organizations.Partner do
field :description, :string
field :notes, :string

field :banner, Atomic.Uploaders.Banner.Type

field :benefits, :string
field :archived, :boolean, default: false
field :image, Uploaders.PartnerImage.Type
Expand Down
4 changes: 2 additions & 2 deletions lib/atomic_web/components/avatar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule AtomicWeb.Components.Avatar do
doc: "The type of entity associated with the avatar."

attr :size, :atom,
values: [:xs, :sm, :md, :lg, :xl],
values: [:xs, :sm, :md, :lg, :xl, :xl],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
values: [:xs, :sm, :md, :lg, :xl, :xl],
values: [:xs, :sm, :md, :lg, :xl],

default: :md,
doc: "The size of the avatar."

Expand Down Expand Up @@ -44,7 +44,7 @@ defmodule AtomicWeb.Components.Avatar do
~H"""
<span class={generate_avatar_classes(assigns)}>
<%= if @src do %>
<img src={@src} class={"atomic-avatar--#{assigns.type} h-full w-full"} />
<img src={@src} class={"atomic-avatar--#{assigns.type} h-full w-full object-contain"} />
<% else %>
<%= if @auto_generate_initials do %>
<%= extract_initials(@name) %>
Expand Down
52 changes: 52 additions & 0 deletions lib/atomic_web/live/partner_live/components/partner_card.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
defmodule AtomicWeb.PartnerLive.Components.PartnerCard do
@moduledoc false
use AtomicWeb, :component

import AtomicWeb.Components.{Avatar, Gradient}

attr :partner, :map, required: true

def partner_card(assigns) do
~H"""
<li class="grid h-full w-full rounded-lg border border-zinc-200 hover:bg-zinc-50">
<div class="h-12 w-full">
<div class="h-full w-full">
<.gradient seed={@partner.id} class="rounded-t-lg" />
</div>
</div>

<div class="grid w-full">
<div class="flex flex-grow px-6">
<div class="relative bottom-6 flex">
<.avatar color={:light_zinc} class="" name={@partner.name} src={Uploaders.PartnerImage.url({@partner.image, @partner}, :original)} type={:company} size={:xl} />
</div>
<div class="mt-5 px-4">
<div class="group relative">
<p class="text-md line-clamp-1 text-lg font-semibold leading-6 text-zinc-900"><%= @partner.name %></p>
<div class="absolute top-full left-14 mt-2 hidden w-max rounded bg-gray-500 p-1 text-sm text-white opacity-100 transition-opacity duration-300 group-hover:block"><%= @partner.name %></div>
<div class="absolute top-full left-16 mt-1 hidden border-r-4 border-b-4 border-l-4 border-r-transparent border-b-gray-500 border-l-transparent group-hover:block"></div>
</div>
<%= if @partner.location do %>
<div class="z-1 flex items-center gap-x-1 leading-6">
<.icon name="hero-map-pin" class="my-1 h-4 w-4 text-zinc-400" />
<p class="text-center text-sm text-blue-400"><%= @partner.location.name %></p>
</div>
<% end %>
</div>
</div>
<div>
<p class="overflow-hidden truncate overflow-ellipsis whitespace-normal px-10 pb-10 text-xs leading-5 text-zinc-500">
<%= Enum.map(String.split(@partner.benefits, "\n"), fn phrase -> %>
<%= if String.length(phrase) < 50 do %>
<%= phrase %><br />
<% else %>
<%= String.slice(phrase, 0..50) <> "..." %> <br />
<% end %>
<% end) %>
</p>
</div>
</div>
</li>
"""
end
end
2 changes: 1 addition & 1 deletion lib/atomic_web/live/partner_live/edit.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
</div>
<% end %>
</:actions>
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
<.live_component module={AtomicWeb.PartnerLive.FormComponent} organization={@current_organization} id={@partner.id || :new} title={@page_title} action={@live_action} partner={@partner} return_to={~p"/organizations/#{@current_organization}/partners"} />
</div>
</.page>
Expand Down
3 changes: 2 additions & 1 deletion lib/atomic_web/live/partner_live/index.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
defmodule AtomicWeb.PartnerLive.Index do
import AtomicWeb.PartnerLive.Components.PartnerCard
use AtomicWeb, :live_view

import AtomicWeb.Components.{Avatar, Button, Empty, Pagination, Tabs}
import AtomicWeb.Components.{Button, Empty, Pagination, Tabs}
alias Atomic.Accounts
alias Atomic.Organizations
alias Atomic.Partners
Expand Down
30 changes: 3 additions & 27 deletions lib/atomic_web/live/partner_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,10 @@
<.empty_state url={~p"/organizations/#{@organization}/partners/new"} placeholder="partner" />
</div>
<% else %>
<ul role="list" class="">
<ul role="list" class="mx-8 my-4 grid grid-cols-2 gap-x-6 gap-y-4">
<%= for partner <- @partners do %>
<.link navigate={~p"/organizations/#{@organization}/partners/#{partner}"} class="block hover:bg-zinc-50">
<li class="px-4 flex flex-col md:flex-row justify-between border-b border-zinc-200 gap-x-6 py-5">
<div class="flex flex-col md:flex-row gap-x-4 w-full">
<div class="flex-shrink-0">
<.avatar color={:light_zinc} name={partner.name} src={Uploaders.PartnerImage.url({partner.image, partner}, :original)} type={:company} size={:sm} />
</div>
<div class="flex-grow">
<p class="text-md font-semibold leading-6 text-zinc-900"><%= partner.name %></p>
<%= if partner.location do %>
<div class="flex flex-row items-center gap-x-1 text-sm leading-6 z-1">
<.icon name="hero-map-pin" class="size-5 text-zinc-400" />
<p class="text-blue-500"><%= partner.location.name %></p>
</div>
<% end %>
<p class="mt-1 truncate text-xs leading-5 text-zinc-500 overflow-hidden overflow-ellipsis whitespace-normal">
<%= Enum.map(String.split(partner.benefits, "\n"), fn phrase -> %>
<%= if String.length(phrase) < 150 do %>
<%= phrase %><br />
<% else %>
<%= String.slice(phrase, 0..150) <> "..." %> <br />
<% end %>
<% end) %>
</p>
</div>
</div>
</li>
<.link navigate={~p"/organizations/#{partner.organization_id}/partners/#{partner.id}"} class="block hover:bg-zinc-50">
<.partner_card partner={partner} />
</.link>
<% end %>
</ul>
Expand Down
8 changes: 6 additions & 2 deletions lib/atomic_web/live/partner_live/show.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule AtomicWeb.PartnerLive.Show do
use AtomicWeb, :live_view

import AtomicWeb.Components.Avatar
import AtomicWeb.Components.{Avatar, Gradient, Tabs}

alias Atomic.Accounts
alias Atomic.Organizations
Expand All @@ -13,14 +13,15 @@ defmodule AtomicWeb.PartnerLive.Show do
end

@impl true
def handle_params(%{"organization_id" => organization_id, "id" => id}, _, socket) do
def handle_params(params = %{"organization_id" => organization_id, "id" => id}, _, socket) do
organization = Organizations.get_organization!(organization_id)
partner = Partners.get_partner!(id)

{:noreply,
socket
|> assign(:page_title, partner.name)
|> assign(:current_page, :partners)
|> assign(:current_tab, current_tab(socket, params))
|> assign(:organization, organization)
|> assign(:partner, partner)
|> assign(
Expand All @@ -30,6 +31,9 @@ defmodule AtomicWeb.PartnerLive.Show do
|> assign(:has_permissions?, has_permissions?(socket, organization_id))}
end

defp current_tab(_socket, params) when is_map_key(params, "tab"), do: params["tab"]
defp current_tab(_socket, _params), do: "benefits"

defp has_permissions?(socket, _organization_id) when not socket.assigns.is_authenticated?,
do: false

Expand Down
181 changes: 103 additions & 78 deletions lib/atomic_web/live/partner_live/show.html.heex
Original file line number Diff line number Diff line change
@@ -1,78 +1,90 @@
<.page title="Partners">
<!--Header-->
<div class="h-32 w-full select-none">
<%= if @partner.banner do %>
<img class="h-32 w-full object-cover" src={Uploaders.Banner.url({@partner.banner, @partner}, :original)} />
<% else %>
<.gradient seed={@partner.id} class="object-cover" />
<% end %>
</div>
<.page title="">
<!--Body-->
<:actions>
<%= if @has_permissions? do %>
<div class="lg:border-orange-500 lg:block">
<.button navigate={~p"/organizations/#{@organization}/partners/#{@partner}/edit"} icon="hero-pencil">
<div class="lg:block lg:border-orange-500">
<.button navigate={~p"/organizations/#{@partner.organization_id}/partners/#{@partner.id}/edit"} icon="hero-pencil">
<%= gettext("Edit Partner") %>
</.button>
</div>
<% end %>
</:actions>
<div class="lg:px-8 sm:px-0 min-h-fit border-t-100 select-none lg:mx-0 max-w-5xl sm:mx-6 ">
<div class="flex flex-col gap-2 mt-6 sm:justify-center">
<div class="flex flex-row">
<div class="flex flex-col lg:justify-none items-center px-2">
<.avatar color={:light_zinc} name={@partner.name} src={Uploaders.PartnerImage.url({@partner.image, @partner}, :original)} type={:company} size={:xl} />
<div class="border-t-100 min-h-fit max-w-5xl select-none sm:mx-6 sm:px-0 lg:mx-0 lg:px-8">
<div class="mt-2 flex flex-col gap-2 sm:justify-center">
<div class="mb-4 flex flex-row gap-x-4">
<div class="flex flex-col items-center px-2 lg:justify-none">
<.avatar class="size-28" color={:light_zinc} name={@partner.name} src={Uploaders.PartnerImage.url({@partner.image, @partner}, :original)} type={:company} size={:xl} />
</div>
<div>
<div class="grid grid-rows-2 gap-y-0">
<div class="flex items-center justify-between">
<h1 class="flex-1 select-none truncate text-wrap text-xl sm:text-2xl font-bold pr-4 text-zinc-900 ml-2">
<h1 class="text-wrap flex-1 select-none truncate pr-4 text-xl font-bold text-gray-900 sm:text-2xl">
<%= @partner.name %>
</h1>
</div>
<%= if @partner.location do %>
<div class="flex flex-row items-center gap-x-1 text-md leading-6">
<.icon name="hero-map-pin" class="size-5 text-zinc-400" />
<.link class="text-blue-500" href={"https://www.google.com/maps/search/?api=1&query=#{@partner.location.name}"}><%= @partner.location.name %></.link>
</div>
<% end %>
<%= if @partner.socials do %>
<div class="grid grid-cols-2 grid-rows-3 lg:flex lg:flex-row lg:gap-x-4">
<%= if @partner.socials.website do %>
<div class="flex flex-row items-center gap-x-1 ">
<.icon name="hero-globe-alt" class="size-5 text-zinc-400" />
<.link class="text-blue-500" href={@partner.socials.website}>Website</.link>
</div>
<% end %>
<%= if @partner.socials.instagram do %>
<div class="flex flex-row items-center gap-x-1 gap-y-0 sm:gap-y-2">
<img src="/images/instagram.svg" class="size-5" alt="Instagram" />
<.link class="text-blue-500" href={"https://instagram.com/" <> @partner.socials.instagram}>Instagram</.link>
</div>
<% end %>
<%= if @partner.socials.facebook do %>
<div class="flex flex-row items-center gap-x-1 gap-y-0 sm:gap-y-2">
<img src="/images/facebook.svg" class="size-5" alt="Facebook" />
<.link class="text-blue-500" href={"https://facebook.com/" <> @partner.socials.facebook}>Facebook</.link>
</div>
<% end %>
<%= if @partner.socials.x do %>
<div class="flex flex-row items-center gap-x-1 gap-y-0 sm:gap-y-2">
<img src="/images/x.svg" class="size-5" alt="X" />
<.link class="text-blue-500" href={"https://x.com/" <> @partner.socials.x}>X</.link>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
<div>
<div class="mt-1 text-zinc-800 py-4 px-2">
<div class="flex flex-row items-center gap-x-2">
<.icon name="hero-clock" class="size-5 mb-2" />
<h2 class="mb-2 flex-1 truncate text-lg font-semibold text-zinc-900">Overview</h2>
<div class="grid grid-rows-2">
<%= if @partner.location do %>
<div class="text-md flex flex-row items-center gap-x-1 leading-6">
<.icon name="hero-map-pin" class="h-5 w-5 text-zinc-400" />
<.link class="text-sm text-blue-500" href={"https://www.google.com/maps/search/?api=1&query=#{@partner.location.name}"}><%= @partner.location.name %></.link>
</div>
<% end %>
<%= if @partner.socials do %>
<div class="grid grid-cols-2 grid-rows-3 lg:flex lg:flex-row lg:gap-x-4">
<%= if @partner.socials.website do %>
<div class="flex flex-row items-center gap-x-1">
<.icon name="hero-globe-alt" class="h-5 w-5 text-zinc-400" />
<.link class="text-sm text-blue-500" href={@partner.socials.website}>Website</.link>
</div>
<% end %>
<%= if @partner.socials.instagram do %>
<div class="flex flex-row items-center gap-x-1 gap-y-0 sm:gap-y-2">
<img src="/images/instagram.svg" class="h-5 w-5" alt="Instagram" />
<.link class="text-sm text-blue-500" href={"https://instagram.com/" <> @partner.socials.instagram}>Instagram</.link>
</div>
<% end %>
<%= if @partner.socials.facebook do %>
<div class="flex flex-row items-center gap-x-1 gap-y-0 sm:gap-y-2">
<img src="/images/facebook.svg" class="h-5 w-5" alt="Facebook" />
<.link class="text-sm text-blue-500" href={"https://facebook.com/" <> @partner.socials.facebook}>Facebook</.link>
</div>
<% end %>
<%= if @partner.socials.x do %>
<div class="flex flex-row items-center gap-x-1 gap-y-0 sm:gap-y-2">
<img src="/images/x.svg" class="h-5 w-5" alt="X" />
<.link class="text-sm text-blue-500" href={"https://x.com/" <> @partner.socials.x}>X</.link>
</div>
<% end %>
</div>
<% end %>
</div>
<span class="py-4">
<%= Enum.map(String.split(@partner.description, "\n"), fn phrase -> %>
<%= phrase %>
<% end) %>
</span>
</div>
<div class="text-zinc-800 py-4 px-2">
<div class="flex flex-row items-center gap-x-2 text-zinc-800">
<.icon name="hero-signal" class="size-5 mb-2" />
<h2 class="mb-2 flex-1 truncate text-lg font-semibold text-zinc-900">Benefits</h2>
</div>
</div>
<!--Tabs-->
<.tabs class="scrollbar-hide mt-2 flex overflow-scroll px-4 sm:px-6 lg:px-8">
<.link patch="?tab=benefits" replace={false}>
<.tab id="benefits-tab" active={@current_tab == "benefits"}>
<.icon name="hero-signal" class="size-5 mr-2" />
<%= gettext("Benefits") %>
</.tab>
</.link>
<.link patch="?tab=about" replace={false}>
<.tab id="about-tab" active={@current_tab == "about"}>
<.icon name="hero-information-circle" class="size-5 mr-2" />
<%= gettext("About") %>
</.tab>
</.link>
</.tabs>
<!--Assign-Index-->
<div :if={@current_tab == "benefits"}>
<div class="px-2 py-4 text-zinc-800">
<span>
<%= Enum.map(String.split(@partner.benefits, "\n"), fn phrase -> %>
<%= if String.length(phrase) < 300 do %>
Expand All @@ -84,33 +96,46 @@
</span>
</div>
<%= if @partners do %>
<div class="flex flex-col gap-x-2 text-zinc-800">
<div class="mt-4 flex flex-col gap-x-2 text-zinc-800">
<div class="flex flex-row items-center gap-x-2 px-2">
<.icon name="hero-star" class="size-5 mb-2" />
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900 py-2">Related Partners</h2>
<.icon name="hero-star" class="mb-2 h-5 w-5" />
<h2 class="mb-2 flex-1 select-none truncate py-2 text-lg font-semibold text-gray-900">Related Partners</h2>
</div>
<div class="sm:grid sm:grid-cols-2 sm:gap-px">
<div class="gap-4 gap-4 px-4 sm:grid sm:grid-cols-2 sm:px-6">
<%= for partner <- @partners |> Enum.filter(fn partner -> partner.id != @partner.id end) do %>
<div class="rounded-lg group relative border border-zinc-200 bg-white p-4 hover:bg-zinc-50 m-2">
<.link href={~p"/organizations/#{@organization}/partners/#{@partner}"}>
<div class="flex flex-row items-center space-x-2">
<.avatar color={:light_zinc} name={partner.name} src={Uploaders.PartnerImage.url({partner.image, partner}, :original)} type={:company} size={:xs} />
<.link href={~p"/organizations/#{partner.organization_id}/partners/#{partner.id}"}>
<li class="flex h-full flex-row justify-between gap-x-4 rounded-lg border border-zinc-200 px-4 py-5 hover:bg-zinc-50 md:flex-row">
<div class="flex-shrink-0">
<.avatar class="size-10" color={:light_zinc} name={partner.name} src={Uploaders.PartnerImage.url({partner.image, partner}, :original)} type={:company} size={:xs} />
</div>
<div class="flex-grow flex-col gap-y-5">
<h1 class="text-lg font-semibold text-zinc-800"><%= partner.name %></h1>
<%= if partner.location do %>
<div class="flex flex-row items-center gap-x-1">
<.icon name="hero-map-pin" class="my-1 h-4 w-4 text-zinc-400" />
<span class="text-xs text-blue-500"><%= partner.location.name %></span>
</div>
<% end %>
<p class="mt-1 overflow-hidden truncate overflow-ellipsis whitespace-normal text-xs leading-5 text-zinc-500">
<span class="text-md text-sm text-zinc-500"><%= partner.description %></span>
</p>
</div>
<%= if partner.location do %>
<div class="flex flex-row items-center gap-x-1">
<.icon name="hero-map-pin" class="size-5 text-zinc-400" />
<span class="text-blue-500"><%= partner.location.name %></span>
</div>
<% end %>
<span class="text-md text-zinc-500"><%= partner.description %></span>
</.link>
</div>
</li>
</.link>
<% end %>
</div>
</div>
<% end %>
</div>
<div :if={@current_tab == "about"}>
<div class="mt-1 px-2 py-4 text-zinc-800">
<span class="py-4">
<%= Enum.map(String.split(@partner.description, "\n"), fn phrase -> %>
<%= phrase %>
<% end) %>
</span>
</div>
</div>
</div>
</div>
</.page>
2 changes: 2 additions & 0 deletions priv/repo/migrations/20221123000537_create_partners.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defmodule Atomic.Repo.Migrations.CreatePartners do
add :location, :map
add :socials, :map

add :banner, :string

add :organization_id, references(:organizations, on_delete: :delete_all, type: :binary_id)

timestamps()
Expand Down
Loading