Skip to content

feat: organization show and tabs pages #578

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

Draft
wants to merge 18 commits into
base: rl/organizations-frontend
Choose a base branch
from
Draft
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
14 changes: 14 additions & 0 deletions lib/atomic/feed.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ defmodule Atomic.Feed do
|> Repo.paginate(cursor_fields: [:inserted_at, :id], limit: @posts_limit)
end

def list_organization_posts_paginated(organization_id, opts \\ []) do
Post
|> join(:left, [p], a in assoc(p, :activity))
|> join(:left, [p, a], an in assoc(p, :announcement))
|> where(
[p, a, an],
(not is_nil(a.id) and a.organization_id == ^organization_id) or
(not is_nil(an.id) and an.organization_id == ^organization_id)
)
|> apply_filters(opts)
|> preload(activity: :organization, announcement: :organization)
|> Repo.paginate(cursor_fields: [:inserted_at, :id], limit: @posts_limit)
end

def list_next_posts_paginated(cursor_after, opts \\ []) do
Post
|> apply_filters(opts)
Expand Down
8 changes: 4 additions & 4 deletions lib/atomic_web/components/page.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule AtomicWeb.Components.Page do

def page(assigns) do
~H"""
<%= render_slot(@header) %>
{render_slot(@header)}

<div class="flex min-h-full flex-col items-stretch justify-between lg:flex-row">
<div class="min-h-[100vh] flex w-full flex-col bg-white lg:flex-row lg:border-r">
Expand All @@ -27,14 +27,14 @@ defmodule AtomicWeb.Components.Page do
<div class="my-6 flex min-w-0 flex-row items-center justify-between">
<div class="flex flex-col">
<h1 class="flex-1 select-none truncate text-2xl font-bold text-zinc-900">
<%= @title %>
{@title}
</h1>
<h3 :if={@description} class="flex-1 select-none truncate text-sm font-medium text-zinc-500">
<%= @description %>
{@description}
</h3>
</div>
<div class="flex space-x-4">
<%= render_slot(@actions) %>
{render_slot(@actions)}
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion lib/atomic_web/components/tabs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule AtomicWeb.Components.Tabs do
]}
aria-label="Tabs"
>
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</nav>
"""
end
Expand Down
26 changes: 13 additions & 13 deletions lib/atomic_web/live/department_live/show.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@
color={:white}
icon="hero-user-plus-solid"
>
<%= gettext("Collaborate") %>
{gettext("Collaborate")}
</.button>
<% end %>
<% else %>
<%= if ! @current_collaborator.accepted do %>
<.button color={:white} icon="hero-user-plus-solid" aria-label={gettext("You have requested to collaborate with this department. Please wait for the department owner to accept your request.")} disabled>
<%= gettext("Collaborate") %>
{gettext("Collaborate")}
</.button>
<% end %>
<% end %>
Expand All @@ -103,17 +103,17 @@
<%= if @current_view == "show" do %>
<div class="mx-4 mt-8 grid grid-cols-3 grid-rows-1 sm:mx-6 lg:mx-8">
<div class="col-span-3 sm:col-span-2">
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900"><%= gettext("Recent Activity") %></h2>
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900">{gettext("Recent Activity")}</h2>
<!-- Recent activity empty state -->
<.link class="mt-4 flex flex-col items-center justify-center" navigate={~p"/organizations/#{@organization.id}"}>
<.avatar class="mb-4 p-1" type={:organization} color={:white} size={:lg} name={@organization.name} src={Uploaders.Logo.url({@organization.logo, @organization}, :original)} />
<p class="select-none text-center text-zinc-900"><%= gettext("This department doesn't have any recent activity.") %></p>
<p class="select-none text-center text-zinc-900"><%= gettext("In the meantime, check out %{organization_name}.", organization_name: @organization.name) %></p>
<p class="select-none text-center text-zinc-900">{gettext("This department doesn't have any recent activity.")}</p>
<p class="select-none text-center text-zinc-900">{gettext("In the meantime, check out %{organization_name}.", organization_name: @organization.name)}</p>
</.link>
</div>
<!-- People -->
<div class="hidden w-full sm:block">
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900"><%= gettext("People") %></h2>
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900">{gettext("People")}</h2>
<div class="mb-2 flex max-w-full flex-wrap gap-1">
<%= for collaborator <- @all_collaborators |> Enum.take(18) do %>
<div class="h-12">
Expand All @@ -123,11 +123,11 @@
<!-- People empty state -->
<%= if length(@all_collaborators) == 0 do %>
<.icon class="mx-32 text-zinc-900" name="hero-users" />
<p class="mb-4 select-none text-center text-zinc-900"><%= gettext("This department doesn't have any collaborators yet!") %></p>
<p class="mb-4 select-none text-center text-zinc-900">{gettext("This department doesn't have any collaborators yet!")}</p>
<% end %>
</div>
<.link :if={length(@all_collaborators) != 0} patch={~p"/organizations/#{@organization}/departments/#{@department}?tab=collaborators"} class="text-orange-500 hover:cursor-pointer hover:underline">
<%= gettext("View all collaborators") %>
{gettext("View all collaborators")}
</.link>
</div>
</div>
Expand All @@ -141,9 +141,9 @@
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900">Collaborators</h2>
<div class="border-t border-l">
<.table items={@collaborators} meta={@meta} filter={[]}>
<:col :let={collaborator} label="Name" field={:string}><%= collaborator.user.name %></:col>
<:col :let={collaborator} label="Email" field={:string}><%= collaborator.user.email %></:col>
<:col :let={collaborator} label="Phone number" field={:string}><%= collaborator.user.phone_number %></:col>
<:col :let={collaborator} label="Name" field={:string}>{collaborator.user.name}</:col>
<:col :let={collaborator} label="Email" field={:string}>{collaborator.user.email}</:col>
<:col :let={collaborator} label="Phone number" field={:string}>{collaborator.user.phone_number}</:col>
<:col :let={collaborator} label="Accepted" field={:string}>
<input type="checkbox" disabled={true} checked={collaborator.accepted} class="mx-auto block rounded-md text-zinc-600" />
</:col>
Expand All @@ -167,8 +167,8 @@
<div class="flex">
<.avatar name={collaborator.user.name} />
<div class="ml-3 flex h-full flex-col self-center">
<p><%= collaborator.user.name %></p>
<p>@<%= collaborator.user.slug %></p>
<p>{collaborator.user.name}</p>
<p>@{collaborator.user.slug}</p>
</div>
</div>
</div>
Expand Down
30 changes: 15 additions & 15 deletions lib/atomic_web/live/organization_live/components/about.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,50 @@ defmodule AtomicWeb.OrganizationLive.Components.About do
def about(assigns) do
~H"""
<div id="organization-about">
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900"><%= gettext("Description") %></h2>
<p><%= @organization.description %></p>
<h2 class="mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900">{gettext("Description")}</h2>
<p>{@organization.description}</p>

<div :if={@organization.location}>
<h2 class="mt-8 mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900"><%= gettext("Location") %></h2>
<p class="text-zinc-900"><%= @organization.location %></p>
<h2 class="mt-8 mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900">{gettext("Location")}</h2>
<p class="text-zinc-900">{@organization.location}</p>
</div>

<div :if={@organization.socials}>
<h2 class="mt-8 mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900"><%= gettext("Socials") %></h2>
<h2 class="mt-8 mb-2 flex-1 select-none truncate text-lg font-semibold text-zinc-900">{gettext("Socials")}</h2>

<ul role="list" class="flex flex-col space-y-2 md:flex-row md:items-center md:space-x-6 md:space-y-0">
<li :if={@organization.socials.website}>
<.link href={@organization.socials.website} target="_blank" class="group flex items-center space-x-1">
<.icon name="hero-link" class="size-4" />
<p class="group-hover:underline"><%= @organization.socials.website %></p>
<.icon name="hero-link" class="size-4" />
<p class="group-hover:underline">{@organization.socials.website}</p>
</.link>
</li>

<li :if={@organization.socials.facebook}>
<.link href={Socials.link(:facebook, @organization.socials.facebook)} target="_blank" class="group flex items-center space-x-1">
<%= Socials.icon(:facebook) |> raw() %>
<p class="group-hover:underline"><%= @organization.socials.facebook %></p>
{Socials.icon(:facebook) |> raw()}
<p class="group-hover:underline">{@organization.socials.facebook}</p>
</.link>
</li>

<li :if={@organization.socials.instagram}>
<.link href={Socials.link(:instagram, @organization.socials.instagram)} target="_blank" class="group flex items-center space-x-1">
<%= Socials.icon(:instagram) |> raw() %>
<p class="group-hover:underline"><%= @organization.socials.instagram %></p>
{Socials.icon(:instagram) |> raw()}
<p class="group-hover:underline">{@organization.socials.instagram}</p>
</.link>
</li>

<li :if={@organization.socials.x}>
<.link href={Socials.link(:x, @organization.socials.x)} target="_blank" class="group flex items-center space-x-1">
<%= Socials.icon(:x) |> raw() %>
<p class="group-hover:underline"><%= @organization.socials.x %></p>
{Socials.icon(:x) |> raw()}
<p class="group-hover:underline">{@organization.socials.x}</p>
</.link>
</li>

<li :if={@organization.socials.linkedin}>
<.link href={Socials.link(:linkedin, @organization.socials.linkedin)} target="_blank" class="group flex items-center space-x-1">
<%= Socials.icon(:linkedin) |> raw() %>
<p class="group-hover:underline"><%= @organization.socials.linkedin %></p>
{Socials.icon(:linkedin) |> raw()}
<p class="group-hover:underline">{@organization.socials.linkedin}</p>
</.link>
</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ defmodule AtomicWeb.OrganizationLive.Components.MembershipBanner do
~H"""
<div class="mx-auto rounded-3xl ring-1 ring-gray-200 lg:mx-0 lg:flex lg:max-w-none">
<div class="p-8 sm:p-10">
<h3 class="text-2xl font-bold tracking-tight text-gray-900"><%= gettext("Lifetime membership") %></h3>
<h3 class="text-2xl font-bold tracking-tight text-gray-900">{gettext("Lifetime membership")}</h3>
<div class="mt-10 flex items-center gap-x-4">
<h4 class="flex-none text-sm font-semibold leading-6 text-orange-600"><%= gettext("What’s included") %></h4>
<h4 class="flex-none text-sm font-semibold leading-6 text-orange-600">{gettext("What’s included")}</h4>
<div class="h-px flex-auto bg-gray-100"></div>
</div>

Expand All @@ -39,13 +39,13 @@ defmodule AtomicWeb.OrganizationLive.Components.MembershipBanner do
<div class="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
<div class="ring-gray-900/5 rounded-2xl bg-gray-50 py-10 text-center ring-1 ring-inset lg:flex lg:flex-col lg:justify-center lg:py-16">
<div class="mx-auto flex max-w-xs flex-col items-center px-8">
<p class="text-base font-semibold text-gray-600"><%= gettext("Pay once, be a member forever") %></p>
<p class="text-base font-semibold text-gray-600">{gettext("Pay once, be a member forever")}</p>
<p class="mt-6 flex items-baseline justify-center gap-x-2">
<span class="text-5xl font-bold tracking-tight text-gray-900">10€</span>
<span class="text-sm font-semibold leading-6 tracking-wide text-gray-600">EUR</span>
</p>
<.button icon="hero-banknotes" class="mt-10 text-sm"><%= gettext("Request your membership") %></.button>
<p class="mt-6 text-xs leading-5 text-gray-600"><%= gettext("Payments should be made within our location.") %> <%= @organization.location %></p>
<.button icon="hero-banknotes" class="mt-10 text-sm">{gettext("Request your membership")}</.button>
<p class="mt-6 text-xs leading-5 text-gray-600">{gettext("Payments should be made within our location.")} {@organization.location}</p>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ defmodule AtomicWeb.OrganizationLive.Components.MembershipsTable do
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col" class="py-3 pr-3 pl-4 text-left text-xs font-medium uppercase tracking-wide text-gray-500 sm:pl-0"><%= gettext("Name") %></th>
<th scope="col" class="px-3 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500"><%= gettext("Role") %></th>
<th scope="col" class="px-3 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500"><%= gettext("Joined At") %></th>
<th scope="col" class="py-3 pr-3 pl-4 text-left text-xs font-medium uppercase tracking-wide text-gray-500 sm:pl-0">{gettext("Name")}</th>
<th scope="col" class="px-3 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">{gettext("Role")}</th>
<th scope="col" class="px-3 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">{gettext("Joined At")}</th>
</tr>
</thead>

Expand All @@ -29,13 +29,13 @@ defmodule AtomicWeb.OrganizationLive.Components.MembershipsTable do
<div class="flex items-center">
<.avatar name={member.user.name} size={:sm} color={:light_zinc} class="ring-1 ring-white" />
<div class="ml-4">
<div class="font-medium text-gray-900"><%= member.user.name %></div>
<div class="mt-1 text-gray-500"><%= member.user.email %></div>
<div class="font-medium text-gray-900">{member.user.name}</div>
<div class="mt-1 text-gray-500">{member.user.email}</div>
</div>
</div>
</td>
<td class="whitespace-nowrap px-3 py-5 text-sm text-gray-900"><%= capitalize_first_letter(member.role) %></td>
<td class="whitespace-nowrap px-3 py-5 text-sm text-gray-500"><%= relative_datetime(member.inserted_at) %></td>
<td class="whitespace-nowrap px-3 py-5 text-sm text-gray-900">{capitalize_first_letter(member.role)}</td>
<td class="whitespace-nowrap px-3 py-5 text-sm text-gray-500">{relative_datetime(member.inserted_at)}</td>
</tr>
</tbody>
</table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@ defmodule AtomicWeb.OrganizationLive.Components.OrganizationCard do
</div>
</div>
<p class="ml-3 text-lg font-semibold text-zinc-900">
<%= @organization.name %>
{@organization.name}
</p>
</div>

<%!-- TODO: Maybe show button when there's no current user, but with a must login warning? --%>
<%= if @current_user do %>
<%= if Organizations.user_following?(@current_user.id, @organization.id) do %>
<.button icon="hero-star-solid"><%= gettext("Following") %></.button>
<.button icon="hero-star-solid">{gettext("Following")}</.button>
<% else %>
<.button icon="hero-star"><%= gettext("Follow") %></.button>
<.button icon="hero-star">{gettext("Follow")}</.button>
<% end %>
<% end %>
</div>

<p class="mt-2 text-sm text-zinc-400">
<%= @organization.long_name %>
{@organization.long_name}
</p>

<div role="list" class="mt-2 flex flex-col space-y-2 md:flex-row md:items-center md:space-x-6 md:space-y-0">
Expand All @@ -60,7 +60,7 @@ defmodule AtomicWeb.OrganizationLive.Components.OrganizationCard do
<.icon name="hero-users" class="size-4" />
<%= if @organization.follower_count != 1 do %>
<p class="text-sm">
<span class="font-semibold"><%= @organization.follower_count %></span> followers
<span class="font-semibold">{@organization.follower_count}</span> followers
</p>
<% else %>
<p class="text-sm">
Expand All @@ -71,14 +71,14 @@ defmodule AtomicWeb.OrganizationLive.Components.OrganizationCard do

<li :if={@organization.location} class="flex items-center space-x-1">
<.icon name="hero-map-pin" class="size-4" />
<p class="text-sm"><%= @organization.location %></p>
<p class="text-sm">{@organization.location}</p>
</li>
</ul>

<div :if={@organization.socials && @organization.socials.website} class="group">
<.link href={@organization.socials.website} target="_blank" class="flex items-center space-x-1">
<.icon name="hero-link" class="size-4" />
<p class="text-sm group-hover:underline"><%= @organization.socials.website %></p>
<p class="text-sm group-hover:underline">{@organization.socials.website}</p>
</.link>
</div>
</div>
Expand Down
51 changes: 51 additions & 0 deletions lib/atomic_web/live/organization_live/components/partners_grid.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule AtomicWeb.OrganizationLive.Components.PartnersGrid do
@moduledoc """
Internal organization-related component for displaying its partners.
"""
use AtomicWeb, :component

alias Atomic.Organizations.{Organization, Partner}
alias Atomic.Repo
import AtomicWeb.Components.Avatar
import Ecto.Query

attr :organization, Organization,
required: true,
doc: "The organization whose partners to display"

def partners_grid(assigns) do
~H"""
<div id="organization-partners" class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-2">
<%= for partner <- list_partners(@organization) do %>
<.link navigate={~p"/organizations/#{@organization.id}/partners/#{partner.id}"}>
<.partner_card partner={partner} />
</.link>
<% end %>
</div>
"""
end

defp list_partners(%Organization{id: organization_id}) do
from(p in Partner,
where: p.organization_id == ^organization_id and not p.archived
)
|> Repo.all()
end

defp partner_card(assigns) do
~H"""
<div class="flex flex-col rounded-2xl border bg-white p-4 shadow transition hover:shadow-md">
<div class="flex flex-row">
<div class="mr-2">
<.avatar color={:light_zinc} name={@partner.name} src={Uploaders.PartnerImage.url({@partner.image, @partner}, :original)} type={:company} size={:sm} />
</div>
<div>
<h3 class="text-lg font-semibold text-zinc-900">{@partner.name}</h3>
<p class="text-sm text-zinc-600"><.icon name="hero-map-pin" class="size-5 mb-1 text-zinc-400" /> {@partner.location.name}</p>
</div>
</div>
<p class="line-clamp-3 mt-2 text-sm text-zinc-500">{@partner.description}</p>
</div>
"""
end
end
Loading