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

refactor: improve announcements pages #530

Open
wants to merge 32 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e9f3a43
refactor: announcement forms
JoaoCoelho2003 Sep 9, 2024
750bedf
fix: upload images annoucments
FilipeR13 Sep 10, 2024
8f56c58
feat: completed announcement feed page
JoaoCoelho2003 Sep 10, 2024
55b65af
feat: show announcement page
JoaoCoelho2003 Sep 10, 2024
f61df59
fix: textarea size in create an announcement page
JoaoCoelho2003 Sep 11, 2024
a010415
fix: create announcement elems positioning
JoaoCoelho2003 Sep 11, 2024
88d0b12
fix: padding
JoaoCoelho2003 Sep 11, 2024
1c8defc
feat: completed create announcement page
JoaoCoelho2003 Sep 11, 2024
57d689d
fix: show announcement page padding
JoaoCoelho2003 Sep 11, 2024
3802137
fix: format
JoaoCoelho2003 Sep 12, 2024
0d17920
fix: text overflow
JoaoCoelho2003 Sep 17, 2024
23558c6
Merge branch 'develop' into jc/improve-announcements
JoaoCoelho2003 Sep 19, 2024
f1800bc
fix: conflits
JoaoCoelho2003 Sep 23, 2024
6b0f7ec
fix: new icons
JoaoCoelho2003 Sep 23, 2024
678c82d
fix: fix test
JoaoCoelho2003 Sep 23, 2024
fac57ec
fix: test
JoaoCoelho2003 Sep 23, 2024
424175a
fix: main page nil announcement bug
JoaoCoelho2003 Sep 24, 2024
040ee69
fix: delete announcement test
JoaoCoelho2003 Sep 24, 2024
5812fae
fix: format
JoaoCoelho2003 Sep 24, 2024
a7a468b
feat: created an announcement card component
JoaoCoelho2003 Sep 24, 2024
8735240
fix: format
JoaoCoelho2003 Sep 24, 2024
7f1935f
fix: icon and removed hover effect
JoaoCoelho2003 Sep 24, 2024
8f248e5
refacotr: announcement card
JoaoCoelho2003 Sep 25, 2024
3d16dca
fix: padding
JoaoCoelho2003 Oct 3, 2024
4384942
Merge branch 'develop' into jc/improve-announcements
JoaoCoelho2003 Nov 7, 2024
4a36fa3
fix: errors after merge
JoaoCoelho2003 Nov 7, 2024
a80c0fa
refactor: announcements is not part of the admin pages
JoaoCoelho2003 Nov 7, 2024
1af4b79
refactor: finished updating the routes
JoaoCoelho2003 Nov 7, 2024
abe715e
feat: announcements page only shows posts by the organization logged in
JoaoCoelho2003 Nov 7, 2024
01dfeb0
fix: format
JoaoCoelho2003 Nov 7, 2024
66bda3f
fix: function documentation
JoaoCoelho2003 Nov 7, 2024
e8c16b7
fix: format
JoaoCoelho2003 Nov 7, 2024
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
49 changes: 40 additions & 9 deletions lib/atomic/organizations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -495,19 +495,24 @@ defmodule Atomic.Organizations do
end

@doc """
Returns the list of announcements belonging to an organization.
Returns the list of announcements belonging to an organization, filtered and validated by the given parameters.

## Examples

iex> list_announcements_by_organization_id("99d7c9e5-4212-4f59-a097-28aaa33c2621")
[%Announcement{}, ...]
iex> list_announcements_by_organization_id("99d7c9e5-4212-4f59-a097-28aaa33c2621", %{})
{:ok, [%Announcement{}, ...]}

iex> list_announcements_by_organization_id("99d7c9e5-4212-4f59-a097-28aaa33c2621", %{},
...> some_option: true
...> )
{:ok, [%Announcement{}, ...]}

"""
def list_announcements_by_organization_id(id, opts \\ []) do
def list_announcements_by_organization_id(id, %{} = flop, opts \\ []) when is_list(opts) do
Announcement
|> where(organization_id: ^id)
|> apply_filters(opts)
|> Repo.all()
|> Flop.validate_and_run(flop, for: Announcement)
end

@doc """
Expand Down Expand Up @@ -564,7 +569,7 @@ defmodule Atomic.Organizations do
{:error, %Ecto.Changeset{}}

"""
def create_announcement_with_post(attrs \\ %{}) do
def create_announcement_with_post(attrs \\ %{}, after_save \\ &{:ok, &1}) do
Multi.new()
|> Multi.insert(:post, fn _ ->
%Post{}
Expand All @@ -580,7 +585,7 @@ defmodule Atomic.Organizations do
|> Repo.transaction()
|> case do
{:ok, %{announcement: announcement, post: _post}} ->
{:ok, announcement}
after_save.(announcement)

{:error, _reason, changeset, _actions} ->
{:error, changeset}
Expand All @@ -605,10 +610,11 @@ defmodule Atomic.Organizations do
{:error, %Ecto.Changeset{}}

"""
def update_announcement(%Announcement{} = announcement, attrs, _after_save \\ &{:ok, &1}) do
def update_announcement(%Announcement{} = announcement, attrs, after_save \\ &{:ok, &1}) do
announcement
|> Announcement.changeset(attrs)
|> Repo.update()
|> after_save(after_save)
end

@doc """
Expand All @@ -624,7 +630,14 @@ defmodule Atomic.Organizations do

"""
def delete_announcement(%Announcement{} = announcement) do
Repo.delete(announcement)
Ecto.Multi.new()
|> Ecto.Multi.delete(:announcement, announcement)
|> Ecto.Multi.delete(:post, Repo.get!(Post, announcement.post_id))
|> Repo.transaction()
|> case do
{:ok, _changes} -> {:ok, announcement}
{:error, _step, reason, _changes} -> {:error, reason}
end
end

@doc """
Expand All @@ -639,4 +652,22 @@ defmodule Atomic.Organizations do
def change_announcement(%Announcement{} = announcement, attrs \\ %{}) do
Announcement.changeset(announcement, attrs)
end

@doc """
Updates an announcement image.

## Examples

iex> update_announcement_image(announcement, %{field: new_value})
{:ok, %Announcement{}}

iex> update_announcement_image(announcement, %{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def update_announcement_image(%Announcement{} = announcement, attrs) do
announcement
|> Announcement.image_changeset(attrs)
|> Repo.update()
end
end
14 changes: 7 additions & 7 deletions lib/atomic_web/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ defmodule AtomicWeb.Config do
url: ~p"/organizations/#{current_organization}/departments",
tabs: []
},
%{
key: :announcements,
title: "Announcements",
icon: "hero-newspaper",
url: ~p"/organizations/#{current_organization}/announcements",
tabs: []
},
%{
key: :partners,
title: "Partners",
Expand Down Expand Up @@ -83,13 +90,6 @@ defmodule AtomicWeb.Config do
url: ~p"/activities",
tabs: []
},
%{
key: :announcements,
title: "Announcements",
icon: "hero-newspaper",
url: ~p"/announcements",
tabs: []
},
%{
key: :organizations,
title: "Organizations",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule AtomicWeb.AnnouncementLive.Components.AnnouncementCard do
@moduledoc false

import AtomicWeb.Components.Avatar

use AtomicWeb, :component

def announcement_card(assigns) do
Copy link
Contributor

Choose a reason for hiding this comment

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

image
Should be a bottom border in the last announcement

~H"""
<div class="flex flex-col justify-center rounded-lg bg-white lg:p-4">
<.link navigate={~p"/organizations/#{@organization}/announcements/#{@announcement}"} class="block">
<div class="flex items-center space-x-2 px-4 py-1">
<div class="flex-shrink-0">
<.avatar name={@announcement.organization.name} color={:light_gray} class="!h-10 !w-10" size={:xs} type={:organization} src={Uploaders.Logo.url({@announcement.organization.logo, @announcement.organization}, :original)} />
</div>
<div>
<p class="text-sm font-medium text-zinc-700"><%= @announcement.organization.name %></p>
<p class="text-xs text-gray-500">
<span class="sr-only">Published on</span>
<time><%= relative_datetime(@announcement.inserted_at) %></time>
</p>
</div>
</div>
<div class="px-4 py-2">
<p class="text-lg font-semibold text-zinc-900" title={@announcement.title}>
Comment on lines +17 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

The activitys component is using text-gray instead of text-zinc. This is a issue in all code base, we should normalize this in another PR

<%= @announcement.title %>
</p>
<p class="mt-2 text-sm leading-relaxed text-zinc-500">
<%= @announcement.description %>
</p>
</div>
<%= if @announcement.image do %>
<div class="h-auto w-full overflow-hidden">
<img class="h-full w-full rounded-xl object-cover" src={Uploaders.Post.url({@announcement.image, @announcement}, :original)} alt="Announcement Image" />
Copy link
Contributor

Choose a reason for hiding this comment

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

image
I think in the mobile version the image doenst look really good with this level of rounded

</div>
<% end %>
</.link>
</div>
"""
end
end
2 changes: 1 addition & 1 deletion lib/atomic_web/live/announcement_live/edit.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
</.button>
</:actions>
<div class="pt-4 px-4">
<.live_component module={AtomicWeb.AnnouncementLive.FormComponent} id={@announcement.id} organization={@current_organization} title={@page_title} action={@live_action} announcement={@announcement} return_to={~p"/announcements/#{@announcement}"} />
<.live_component module={AtomicWeb.AnnouncementLive.FormComponent} id={@announcement.id} organization={@current_organization} title={@page_title} action={@live_action} announcement={@announcement} return_to={~p"/organizations/#{@current_organization}/announcements"} />
</div>
</.page>
36 changes: 33 additions & 3 deletions lib/atomic_web/live/announcement_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule AtomicWeb.AnnouncementLive.FormComponent do
use AtomicWeb, :live_component

alias Atomic.Organizations
alias AtomicWeb.Components.ImageUploader

import AtomicWeb.Components.Forms

Expand All @@ -17,7 +18,8 @@ defmodule AtomicWeb.AnnouncementLive.FormComponent do
{:ok,
socket
|> assign(assigns)
|> assign(:changeset, changeset)}
|> assign(:changeset, changeset)
|> allow_upload(:image, accept: Uploaders.Post.extension_whitelist(), max_entries: 1)}
end

@impl true
Expand All @@ -35,10 +37,16 @@ defmodule AtomicWeb.AnnouncementLive.FormComponent do
save_announcement(socket, socket.assigns.action, announcement_params)
end

@impl true
def handle_event("cancel-image", %{"ref" => ref}, socket) do
{:noreply, cancel_upload(socket, :image, ref)}
end

defp save_announcement(socket, :edit, announcement_params) do
case Organizations.update_announcement(
socket.assigns.announcement,
announcement_params
announcement_params,
&consume_image_data(socket, &1)
) do
{:ok, _announcement} ->
{:noreply,
Expand All @@ -55,7 +63,10 @@ defmodule AtomicWeb.AnnouncementLive.FormComponent do
announcement_params =
Map.put(announcement_params, "organization_id", socket.assigns.organization.id)

case Organizations.create_announcement_with_post(announcement_params) do
case Organizations.create_announcement_with_post(
announcement_params,
&consume_image_data(socket, &1)
) do
{:ok, _announcement} ->
{:noreply,
socket
Expand All @@ -66,4 +77,23 @@ defmodule AtomicWeb.AnnouncementLive.FormComponent do
{:noreply, assign(socket, :changeset, changeset)}
end
end

defp consume_image_data(socket, announcement) do
consume_uploaded_entries(socket, :image, fn %{path: path}, entry ->
Organizations.update_announcement_image(announcement, %{
"image" => %Plug.Upload{
content_type: entry.client_type,
filename: entry.client_name,
path: path
}
})
end)
|> case do
[{:ok, announcement}] ->
{:ok, announcement}

_errors ->
{:ok, announcement}
end
end
end
21 changes: 15 additions & 6 deletions lib/atomic_web/live/announcement_live/form_component.html.heex
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
<div>
<.form :let={f} for={@changeset} id="announcement-form" phx-target={@myself} phx-change="validate" phx-submit="save">
<.field field={f[:title]} help_text={gettext("The title of the announcement")} type="text" placeholder="Title" required />
<div class="relative">
<.form :let={f} for={@changeset} id="announcement-form" phx-target={@myself} phx-change="validate" phx-submit="save" class="space-y-6">
<div class="relative pb-16">
<div class="grid gap-4 xl:grid-cols-2">
<div class="flex flex-col">
<.field field={f[:title]} type="text" placeholder="Choose a title for the announcement" required class="w-full" />

<.field field={f[:description]} help_text={gettext("Announcement description")} type="text" placeholder="Description" required />
<.field field={f[:description]} type="textarea" placeholder="Write a description" required class="w-full overflow-auto resize-none h-44 xl:h-64" />
</div>
<div class="space-y-4">
<.live_component module={ImageUploader} id="uploader" uploads={@uploads} target={@myself} class="object-cover" />

<div class="mt-8 flex w-full justify-end">
<.button size={:md} color={:white} icon="hero-cube" type="submit"><%= gettext("Save Changes") %></.button>
<div class="flex justify-end">
<.button size={:md} color={:white} icon="hero-cube" type="submit"><%= gettext("Save Changes") %></.button>
</div>
</div>
</div>
</div>
</.form>
</div>
38 changes: 8 additions & 30 deletions lib/atomic_web/live/announcement_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule AtomicWeb.AnnouncementLive.Index do
use AtomicWeb, :live_view

import AtomicWeb.Components.{Button, Empty, Pagination, Tabs}
import AtomicWeb.AnnouncementLive.Components.AnnouncementCard

alias Atomic.Accounts
alias Atomic.Organizations
Expand All @@ -12,44 +13,24 @@ defmodule AtomicWeb.AnnouncementLive.Index do
end

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

{:noreply,
socket
|> assign(:page_title, gettext("Announcements"))
|> assign(:current_page, :announcements)
|> assign(:current_tab, current_tab(socket, params))
|> assign(:organization, organization)
|> assign(:params, params)
|> assign(:has_permissions?, has_permissions?(socket))
|> assign(list_announcements(socket, params))
|> assign(list_announcements_by_organization(socket, params, organization_id))
|> then(fn complete_socket ->
assign(complete_socket, :empty?, Enum.empty?(complete_socket.assigns.announcements))
end)}
end

defp list_announcements(socket, params) do
params = Map.put(params, "page_size", 6)

case current_tab(socket, params) do
"all" -> list_all_announcements(socket, params)
"following" -> list_following_announcements(socket, params)
end
end

defp list_all_announcements(_socket, params) do
case Organizations.list_announcements(params, preloads: [:organization]) do
{:ok, {announcements, meta}} ->
%{announcements: announcements, meta: meta}

{:error, flop} ->
%{announcements: [], meta: flop}
end
end

defp list_following_announcements(socket, params) do
organizations =
Organizations.list_organizations_followed_by_user(socket.assigns.current_user.id)

case Organizations.list_organizations_announcements(organizations, params,
defp list_announcements_by_organization(_socket, params, organization_id) do
case Organizations.list_announcements_by_organization_id(organization_id, params,
preloads: [:organization]
) do
{:ok, {announcements, meta}} ->
Expand All @@ -60,9 +41,6 @@ defmodule AtomicWeb.AnnouncementLive.Index do
end
end

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

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

defp has_permissions?(socket) do
Expand Down
31 changes: 4 additions & 27 deletions lib/atomic_web/live/announcement_live/index.html.heex
Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
<.page title="Announcements">
<:actions>
<%= if not @empty? and @has_permissions? do %>
<.button navigate={~p"/announcements/new"}>
<.button navigate={~p"/organizations/#{@current_organization}/announcements/new"} icon="hero-plus">
<%= gettext("New") %>
</.button>
<% end %>
</:actions>
<!-- Tabs -->
<.tabs class="max-w-5-xl mx-auto px-4 sm:px-6 lg:px-8">
<.link patch="?tab=all" replace={false}>
<.tab active={@current_tab == "all"}>
<%= gettext("All") %>
</.tab>
</.link>

<.link patch="?tab=following" replace={false}>
<.tab active={@current_tab == "following"}>
<%= gettext("Following") %>
</.tab>
</.link>
</.tabs>
<.tabs></.tabs>
Copy link
Contributor

Choose a reason for hiding this comment

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

This solution to add the border in the page its not that good 😅, but I dont really know other solution. @joaodiaslobo any idea?

Copy link
Member

Choose a reason for hiding this comment

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

The page component actually has a parameter to do this, called bottom_border. Unfortunately, it appears this functionality stopped working on a previous PR 😅.
@JoaoCoelho2003 Feel free to fix it and use it on this PR by making the following change in the page component:

def page(assigns) do
    ~H"""
    <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">
        <main class="relative z-0 mb-10 flex-1 overflow-y-auto focus:outline-none xl:order-last">
          <div class={["mx-auto max-w-5xl px-4 sm:px-6 lg:px-8", @bottom_border && "border-b"]}>
            <div class="my-6 flex min-w-0 flex-row items-center justify-between"}>
              <h1 class="flex-1 select-none truncate text-2xl font-bold text-gray-900">
                <%= @title %>
              </h1>
              <%= render_slot(@actions) %>
            </div>
          </div>

          <%= render_slot(@inner_block) %>
        </main>
      </div>
    </div>
    """
  end

And use it like this:

<.page title="Announcements" bottom_border>
...

By the way, this PR is looking real good! 🤩
Good job! 💪

<!-- Announcements index -->
<%= if @empty? and @has_permissions? do %>
<div class="mt-32">
<.empty_state url={~p"/announcements/new"} placeholder="announcement" />
<.empty_state url={~p"/organizations/#{@organization}/announcements/new"} placeholder="announcement" />
</div>
<% else %>
<div class="overflow-hidden bg-white">
<ul role="list" class="divide-y divide-zinc-200 overflow-auto">
<%= for announcement <- @announcements do %>
<li id={announcement.id}>
<.link navigate={~p"/announcements/#{announcement}"} class="block hover:bg-zinc-50">
<div class="px-4 py-4 lg:px-6">
<div class="flex items-center justify-between">
<p class="truncate text-sm font-medium text-zinc-900">
<%= announcement.title %>
</p>
</div>
<p class="mt-2 text-sm text-zinc-500">
<%= announcement.description %>
</p>
</div>
</.link>
<.announcement_card announcement={announcement} organization={@organization} />
</li>
<% end %>
</ul>
Expand Down
Loading
Loading