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 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion lib/atomic_web/components/avatar.ex
Original file line number Diff line number Diff line change
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, Socials}
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(%{"organization_id" => organization_id, "id" => id} = params, _, 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
185 changes: 108 additions & 77 deletions lib/atomic_web/live/partner_live/show.html.heex
Original file line number Diff line number Diff line change
@@ -1,70 +1,88 @@
<.page title="Partners">
<:actions>
<%= if @has_permissions? do %>
<div class="lg:border-orange-500 lg:block">
<.button navigate={~p"/organizations/#{@organization}/partners/#{@partner}/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} />
<!--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-->
<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="relative 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 %>
<!-- Location -->
<div class="flex flex-row items-center gap-x-1 text-md leading-6">
<.icon name="hero-map-pin" class="h-5 w-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 %>
<!-- Socials and Website -->
<div class="flex flex-wrap gap-x-4 items-center mt-2">
<!-- Website -->
<%= 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-blue-500" href={@partner.socials.website}>Website</.link>
</div>
<% end %>
<!-- Socials -->
<%= if @partner.socials do %>
<.socials entity={@partner.socials} />
<% 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 %>
<%= if @has_permissions? do %>
<div class="absolute top-0 right-0 lg:border-orange-500">
<.button navigate={~p"/organizations/#{@partner.organization_id}/partners/#{@partner.id}/edit"} icon="hero-pencil">
<%= gettext("Edit Partner") %>
</.button>
</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 @@ -76,33 +94,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