Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dadd8d1
feat: partners show pages
faria-s Sep 26, 2024
d22fb23
fix: partner_card
faria-s Sep 26, 2024
707e2ad
refactor: partners index page
faria-s Nov 14, 2024
c182523
Merge branch 'develop' into sf/improve-index-and-show-partners-pages-…
faria-s Nov 14, 2024
942216f
fix: router and icons
faria-s Nov 25, 2024
aa15c86
Merge branch 'develop' into sf/improve-index-and-show-partners-pages-…
faria-s Nov 26, 2024
eb1502f
fix: avatar color
faria-s Nov 26, 2024
8973aa9
fix: merge conflitct
faria-s Nov 26, 2024
d7d57a0
fix: remove unused components
faria-s Nov 26, 2024
7f2057e
fix: code format
faria-s Nov 26, 2024
9feefd7
fix: code format
faria-s Nov 26, 2024
96dff8d
fix: new partner button position
faria-s Dec 2, 2024
52b4be0
Update lib/atomic_web/components/avatar.ex
faria-s Dec 2, 2024
acbd1c6
Merge branch 'develop' into sf/improve-index-and-show-partners-pages-…
faria-s Dec 2, 2024
588ff9e
fix: format show.ex
faria-s Dec 2, 2024
d223e31
fix: lint show.ex
faria-s Dec 2, 2024
899f830
fix: mobile responsive
faria-s Mar 4, 2025
4b8619b
fix: mix format
faria-s Mar 11, 2025
735d6d3
Merge branch 'develop' into sf/improve-index-and-show-partners-pages-…
faria-s Mar 11, 2025
3391baf
merge develop
gxnca Apr 30, 2025
9d86782
fix: pending files
gxnca Apr 30, 2025
6e85e7b
refactor: improve partners pages look and structure
gxnca Apr 30, 2025
296103d
fix: formating problems
gxnca Apr 30, 2025
10857b0
fix: improve benefits list
gxnca Apr 30, 2025
3facebd
fix: wrong module attribute
gxnca Apr 30, 2025
3c869bf
Merge branch 'develop' into sf/improve-index-and-show-partners-pages-…
gxnca Jul 3, 2025
40f88c7
fix: reviewed changes
gxnca Jul 4, 2025
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
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],
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