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: department pages #470

Merged
merged 47 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
5f97303
refactor: department index & show
joaodiaslobo Feb 24, 2024
80d3c7e
feat: pagination
joaodiaslobo Feb 24, 2024
a8e8250
feat: collaborators view
joaodiaslobo Feb 25, 2024
7597828
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Feb 25, 2024
7cb85d4
feat: department collaborator applications configuration
joaodiaslobo Feb 26, 2024
03e92aa
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Feb 27, 2024
3eb77a9
feat: collaborator edit modal
joaodiaslobo Feb 27, 2024
f552711
chore: format
joaodiaslobo Feb 27, 2024
deee179
fix: unecessary parameter
joaodiaslobo Feb 27, 2024
0c1daa5
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Mar 13, 2024
3ba4ccb
fix: woop
joaodiaslobo Mar 13, 2024
368fc95
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Mar 13, 2024
c384855
feat: confirm modals
joaodiaslobo Mar 16, 2024
8339f19
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Mar 16, 2024
367caae
feat: remove collaborator
joaodiaslobo Mar 16, 2024
6f78aef
feat: table improvements
joaodiaslobo Mar 16, 2024
df087e8
refactor: action buttons
joaodiaslobo Mar 17, 2024
0dbcb01
feat: edit & new page
joaodiaslobo Mar 19, 2024
6fef032
refactor: single file component
joaodiaslobo Mar 19, 2024
e378b7b
refactor: code readability
joaodiaslobo Mar 19, 2024
e6547e8
feat: initial banner logic
joaodiaslobo Mar 19, 2024
41c639d
refactor: ui changes
joaodiaslobo Mar 19, 2024
233d348
feat: add icon to new department button
joaodiaslobo Mar 19, 2024
a9a25fd
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Mar 19, 2024
347dcdd
feat: accept or deny collaborator requests
joaodiaslobo Mar 21, 2024
6cab79c
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Mar 21, 2024
5773091
refactor: use pattern matching for department form
joaodiaslobo Mar 21, 2024
4f8efd7
Merge branch 'develop' into jl/improve-departments-pages
joaodiaslobo Mar 22, 2024
aa71f14
refactor: use new forms components
joaodiaslobo Mar 24, 2024
2c8a19a
feat: empty states
joaodiaslobo Mar 27, 2024
234cf40
fix: stupid bug
joaodiaslobo Mar 27, 2024
62c0d71
feat: archive and delete actions
joaodiaslobo Apr 10, 2024
f3fc197
fix: department banner upload
joaodiaslobo Apr 11, 2024
be1945e
fix: department show on mobile
joaodiaslobo Apr 11, 2024
aceb458
chore: remove kanban board button (will be added when implemented)
joaodiaslobo Apr 14, 2024
f86da76
ux: delete department tooltip
joaodiaslobo Apr 14, 2024
01976a3
chore: suggestions and ensure strings uses `gettext`
joaodiaslobo Apr 15, 2024
fcab0eb
chore: specify `button` type
joaodiaslobo Apr 15, 2024
dac435b
refactor: modal parent notify logic
joaodiaslobo Apr 15, 2024
d9fd1b5
BREAKING CHANGE: 👹
joaodiaslobo Apr 15, 2024
cb4115c
feat: banner resolution recommendation
joaodiaslobo Apr 15, 2024
e2d291b
feat: gradient component
joaodiaslobo Apr 15, 2024
5e27395
ci: lint
joaodiaslobo Apr 15, 2024
226f079
fix: stupid
joaodiaslobo Apr 15, 2024
66dd16c
feat: add collaborator accepted email
joaodiaslobo Apr 17, 2024
4184f1b
feat: notify department admins on new collaborator request
joaodiaslobo Apr 18, 2024
41631d0
fix: mario
joaodiaslobo Apr 18, 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
2 changes: 1 addition & 1 deletion assets/css/components/button.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Buttons */

.atomic-button {
@apply inline-flex items-center justify-center font-medium transition duration-150 ease-in-out border rounded-md focus:outline-none;
@apply inline-flex items-center justify-center font-medium transition duration-150 ease-in-out border rounded-md select-none focus:outline-none;
}

/* Buttons - sizes */
Expand Down
7 changes: 5 additions & 2 deletions lib/atomic/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Atomic.Accounts.User do

alias Atomic.Accounts.Course
alias Atomic.Activities.ActivityEnrollment
alias Atomic.Organizations.{Membership, Organization}
alias Atomic.Organizations.{Collaborator, Membership, Organization}
alias Atomic.Uploaders.ProfilePicture

@required_fields ~w(email password)a
Expand All @@ -23,14 +23,17 @@ defmodule Atomic.Accounts.User do
field :password, :string, virtual: true, redact: true
field :hashed_password, :string, redact: true
field :confirmed_at, :naive_datetime

field :phone_number, :string
field :profile_picture, ProfilePicture.Type
field :role, Ecto.Enum, values: @roles, default: :student

belongs_to :course, Course
belongs_to :current_organization, Organization

has_many :activity_enrollments, ActivityEnrollment
has_many :memberships, Membership
has_many :collaborators, Collaborator

many_to_many :organizations, Organization, join_through: Membership

timestamps()
Expand Down
194 changes: 191 additions & 3 deletions lib/atomic/departments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ defmodule Atomic.Departments do
"""
use Atomic.Context

alias Atomic.Accounts.User
alias Atomic.Organizations.{Collaborator, Department}
alias AtomicWeb.DepartmentEmails
alias AtomicWeb.Router.Helpers

@doc """
Returns the list of departments.
Expand Down Expand Up @@ -87,10 +90,11 @@ defmodule Atomic.Departments do
{:error, %Ecto.Changeset{}}

"""
def create_department(attrs \\ %{}) do
def create_department(attrs \\ %{}, after_save \\ &{:ok, &1}) do
%Department{}
|> Department.changeset(attrs)
|> Repo.insert()
|> after_save(after_save)
end

@doc """
Expand All @@ -105,10 +109,11 @@ defmodule Atomic.Departments do
{:error, %Ecto.Changeset{}}

"""
def update_department(%Department{} = department, attrs) do
def update_department(%Department{} = department, attrs, after_save \\ &{:ok, &1}) do
department
|> Department.changeset(attrs)
|> Repo.update()
|> after_save(after_save)
end

@doc """
Expand All @@ -127,6 +132,42 @@ defmodule Atomic.Departments do
Repo.delete(department)
end

@doc """
Archives a department.

## Examples

iex> archive_department(department)
{:ok, %Department{}}

iex> archive_department(department)
{:error, %Ecto.Changeset{}}

"""
def archive_department(%Department{} = department) do
department
|> Department.changeset(%{archived: true})
|> Repo.update()
end

@doc """
Unarchives a department.

## Examples

iex> unarchive_department(department)
{:ok, %Department{}}

iex> unarchive_department(department)
{:error, %Ecto.Changeset{}}

"""
def unarchive_department(%Department{} = department) do
department
|> Department.changeset(%{archived: false})
|> Repo.update()
end

@doc """
Returns an `%Ecto.Changeset{}` for tracking department changes.

Expand Down Expand Up @@ -180,7 +221,11 @@ defmodule Atomic.Departments do
** (Ecto.NoResultsError)

"""
def get_collaborator!(id), do: Repo.get!(Collaborator, id)
def get_collaborator!(id, opts \\ []) do
Collaborator
|> apply_filters(opts)
|> Repo.get!(id)
end

@doc """
Gets a single collaborator.
Expand Down Expand Up @@ -251,6 +296,24 @@ defmodule Atomic.Departments do
|> Repo.update()
end

@doc """
Accepts a collaborator.

## Examples

iex> accept_collaborator(collaborator)
{:ok, %Collaborator{}}

iex> accept_collaborator(collaborator)
{:error, %Ecto.Changeset{}}

"""
def accept_collaborator(%Collaborator{} = collaborator) do
collaborator
|> Collaborator.changeset(%{accepted: true, accepted_at: NaiveDateTime.utc_now()})
|> Repo.update()
end

@doc """
Deletes a collaborator.

Expand Down Expand Up @@ -280,6 +343,21 @@ defmodule Atomic.Departments do
Collaborator.changeset(collaborator, attrs)
end

@doc """
Returns a paginated list of collaborators.

## Examples

iex> list_display_collaborators()
[%Collaborator{}, ...]

"""
def list_display_collaborators(%{} = flop, opts \\ []) do
Collaborator
|> apply_filters(opts)
|> Flop.validate_and_run(flop, for: Collaborator)
end

@doc """
Returns the list of collaborators belonging to a department.

Expand All @@ -295,4 +373,114 @@ defmodule Atomic.Departments do
|> where([c], c.department_id == ^id)
|> Repo.all()
end

@doc """
Updates a department banner.

## Examples

iex> update_department_banner(department, %{field: new_value})
{:ok, %Department{}}

iex> update_department_banner(department, %{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def update_department_banner(%Department{} = department, attrs) do
department
|> Department.banner_changeset(attrs)
|> Repo.update()
end

@doc """
Get all admins of an organization that are collaborators of a department.

## Examples

iex> get_admin_collaborators(department)
[%User{}, ...]

"""
def get_admin_collaborators(%Department{} = department) do
User
|> join(:inner, [u], c in assoc(u, :collaborators))
|> where([u, c], c.department_id == ^department.id and c.accepted == true)
|> join(:inner, [u, c], m in assoc(u, :memberships))
|> where(
[u, c, m],
m.organization_id == ^department.organization_id and m.role in [:admin, :owner]
)
|> Repo.all()
end

@doc """
Request collaborator access and send email.

## Examples

iex> request_collaborator_access(user, department)
{:ok, %Collaborator{}}

iex> request_collaborator_access(user, department)
{:error, %Ecto.Changeset{}}

"""
def request_collaborator_access(%User{} = user, %Department{} = department) do
case create_collaborator(%{department_id: department.id, user_id: user.id}) do
{:ok, %Collaborator{} = collaborator} ->
DepartmentEmails.send_collaborator_request_email(
collaborator |> Repo.preload(:user),
department,
Helpers.department_show_path(
AtomicWeb.Endpoint,
:edit_collaborator,
department.organization_id,
department,
collaborator,
tab: "collaborators"
),
to: get_admin_collaborators(department) |> Enum.map(& &1.email)
)

{:ok, collaborator}

error ->
error
end
end

@doc """
Accept collaborator request and send email.

## Examples

iex> accept_collaborator_request(collaborator)
{:ok, %Collaborator{}}

iex> accept_collaborator_request(collaborator)
{:error, %Ecto.Changeset{}}

"""
def accept_collaborator_request(%Collaborator{} = collaborator) do
collaborator
|> Repo.preload(department: [:organization])
|> accept_collaborator()
|> case do
{:ok, collaborator} ->
DepartmentEmails.send_collaborator_accepted_email(
collaborator,
collaborator.department,
Helpers.department_show_path(
AtomicWeb.Endpoint,
:show,
collaborator.department.organization,
collaborator.department
),
to: collaborator.user.email
)

error ->
error
end
end
end
19 changes: 17 additions & 2 deletions lib/atomic/organizations/collaborator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,29 @@ defmodule Atomic.Organizations.Collaborator do
alias Atomic.Accounts.User
alias Atomic.Organizations.Department

@required_fields ~w(user_id department_id)a
@optional_fields ~w(accepted)a
@required_fields ~w(user_id department_id accepted)a
@optional_fields ~w(accepted_at)a

@derive {
Flop.Schema,
default_limit: 7,
filterable: [:accepted],
sortable: [:collaborator_name, :inserted_at, :updated_at],
default_order: %{
order_by: [:inserted_at],
order_directions: [:desc]
},
join_fields: [
collaborator_name: [binding: :user, field: :name, path: [:user, :name]]
]
}

schema "collaborators" do
belongs_to :user, User
belongs_to :department, Department

field :accepted, :boolean, default: false
field :accepted_at, :naive_datetime

timestamps()
end
Expand Down
10 changes: 9 additions & 1 deletion lib/atomic/organizations/department.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ defmodule Atomic.Organizations.Department do
alias Atomic.Organizations.Organization

@required_fields ~w(name organization_id)a
@optional_fields ~w(description)a
@optional_fields ~w(description collaborator_applications archived)a

schema "departments" do
field :name, :string
field :description, :string
field :banner, Atomic.Uploaders.Banner.Type
field :collaborator_applications, :boolean, default: false
field :archived, :boolean, default: false

belongs_to :organization, Organization, on_replace: :delete_if_exists

Expand All @@ -22,4 +25,9 @@ defmodule Atomic.Organizations.Department do
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
end

def banner_changeset(department, attrs) do
department
|> cast_attachments(attrs, [:banner])
end
end
12 changes: 12 additions & 0 deletions lib/atomic/uploaders/banner.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Atomic.Uploaders.Banner do
@moduledoc """
Uploader for department banners.
"""
use Atomic.Uploader

alias Atomic.Organizations.Department

def storage_dir(_version, {_file, %Department{} = scope}) do
"uploads/atomic/departments/#{scope.id}/banner"
end
end
1 change: 0 additions & 1 deletion lib/atomic_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ defmodule AtomicWeb do
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
import Phoenix.LiveView.Helpers
import Phoenix.Component
import AtomicWeb.LiveHelpers

# Import commonly used components
unquote(components())
Expand Down
10 changes: 9 additions & 1 deletion lib/atomic_web/components/avatar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ defmodule AtomicWeb.Components.Avatar do

attr :name, :string, required: true, doc: "The name of the entity associated with the avatar."

attr :auto_generate_initials, :boolean,
default: true,
doc: "Whether to automatically generate the initials from the name."

attr :type, :atom,
values: [:user, :organization, :company],
default: :user,
Expand Down Expand Up @@ -42,7 +46,11 @@ defmodule AtomicWeb.Components.Avatar do
<%= if @src do %>
<img src={@src} class={"atomic-avatar--#{assigns.type} h-full w-full"} />
<% else %>
<%= extract_initials(@name) %>
<%= if @auto_generate_initials do %>
<%= extract_initials(@name) %>
<% else %>
<%= @name %>
<% end %>
<% end %>
</span>
"""
Expand Down
Loading
Loading