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 activities #504

Draft
wants to merge 33 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c1840cd
feat: improve activities index
joaodiaslobo Jun 13, 2024
79fd24d
Merge branch 'develop' into jl/activities
joaodiaslobo Jun 13, 2024
cc40c0c
feat: add enrolled count to enrolled tab in activity index page
joaodiaslobo Jun 13, 2024
5cde7c4
feat: improve activities index tabs
joaodiaslobo Jul 5, 2024
626643c
Merge branch 'develop' into jl/activities
joaodiaslobo Jul 5, 2024
1137a6c
feat: add organization tab and useful discover tab
joaodiaslobo Aug 1, 2024
9349249
feat: improve activity show layout
joaodiaslobo Aug 1, 2024
a2d8b14
Merge branch 'develop' into jl/activities
joaodiaslobo Aug 7, 2024
e0e7f84
fix: merge issues
joaodiaslobo Aug 7, 2024
5c0de8a
feat: activity show page
joaodiaslobo Aug 9, 2024
ed0ee8b
refactor: activity pubsub
joaodiaslobo Aug 9, 2024
05df35b
fix: enrollment limit issues
joaodiaslobo Aug 9, 2024
d418efe
Merge branch 'develop' into jl/activities
joaodiaslobo Sep 3, 2024
67b39dc
feat: add map
joaodiaslobo Sep 8, 2024
98e4c99
Merge branch 'develop' into jl/activities
joaodiaslobo Sep 9, 2024
b454d16
feat: improve location schema
joaodiaslobo Sep 9, 2024
c01793b
Merge branch 'develop' into jl/activities
joaodiaslobo Sep 12, 2024
d793395
feat: participant avatars
joaodiaslobo Sep 12, 2024
2f6821c
feat: add page component to edit page
nunom27 Sep 27, 2024
f179cba
feat: add modal for description field in activities form
RicoPleasure Jan 25, 2025
40f9f89
feat: implement description dynamic button and minor layout improvements
RicoPleasure Jan 26, 2025
9ff9ff8
feat: form layout improvements
RicoPleasure Jan 27, 2025
41ac6e6
fix: typo and styling
RicoPleasure Jan 27, 2025
dd3471d
merge develop and resolve conflicts
RicoPleasure Jan 28, 2025
1c9b5a9
feat: view participants on activity page
RicoPleasure Jan 30, 2025
545d0de
chore: enhance code quality
RicoPleasure Jan 31, 2025
8889150
fix: show ticket crashing
RicoPleasure Feb 5, 2025
31a0355
fix: minor design issues
RicoPleasure Feb 5, 2025
11c27a7
refactor: participants modal
RicoPleasure Feb 7, 2025
9d51352
chore: remove minimum entries
RicoPleasure Feb 7, 2025
31e9014
feat: add modal for maximum entries control
RicoPleasure Feb 7, 2025
28c52f0
fix: image uploader
RicoPleasure Feb 8, 2025
0f48326
fix: image uploader styling
RicoPleasure Feb 8, 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
12 changes: 12 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px white inset !important;
}

/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}

/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
258 changes: 184 additions & 74 deletions lib/atomic/activities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ defmodule Atomic.Activities do
alias Atomic.Activities.Enrollment
alias Atomic.Feed.Post

@pubsub Atomic.PubSub

@doc """
Returns the list of activities.

Expand Down Expand Up @@ -222,7 +224,7 @@ defmodule Atomic.Activities do
end

@doc """
Updates a activity.
Updates an activity and broadcasts the changes to all subscribed clients.

## Examples

Expand All @@ -234,10 +236,14 @@ defmodule Atomic.Activities do

"""
def update_activity(%Activity{} = activity, attrs, after_save \\ &{:ok, &1}) do
activity
|> Activity.changeset(attrs)
|> Repo.update()
|> after_save(after_save)
result =
activity
|> Activity.changeset(attrs)
|> Repo.update()
|> after_save(after_save)

broadcast_activity_update(activity.id)
result
end

@doc """
Expand Down Expand Up @@ -322,27 +328,6 @@ defmodule Atomic.Activities do
|> Repo.one()
end

@doc """
Gets the user enrolled in an given activity.

## Examples

iex> get_user_enrolled(user, activity_id)
%Enrollment{}

iex> get_user_enrolled(user, activity_id)
** (Ecto.NoResultsError)
"""
def get_user_enrolled(user, activity_id) do
Enrollment
|> where(user_id: ^user.id, activity_id: ^activity_id)
|> Repo.one()
|> case do
nil -> create_enrollment(activity_id, user)
enrollment -> enrollment
end
end

@doc """
Gets all user enrollments.

Expand All @@ -360,6 +345,26 @@ defmodule Atomic.Activities do
|> Repo.all()
end

@doc """
Returns the list of participants in an activity.

## Examples

iex> list_activity_participants(activity_id)
[%Enrollment{}, ...]
"""
def list_activity_participants(id) do
from(u in User,
join: e in assoc(u, :enrollments),
where: e.activity_id == ^id
)
end

def list_display_participants(id, %{} = flop, opts \\ []) do
list_activity_participants(id)
|> Flop.validate_and_run(flop, opts)
end

@doc """
Returns the list of activities a user has enrolled in.

Expand Down Expand Up @@ -397,7 +402,136 @@ defmodule Atomic.Activities do
end

@doc """
Creates an enrollment.
Returns the list of upcoming activities a user has enrolled in.

## Examples

iex> list_upcoming_user_activities(user_id)
[%Activity{}, ...]
"""
def list_upcoming_user_activities(user_id, params \\ %{})

def list_upcoming_user_activities(user_id, opts) when is_list(opts) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? > now()", a.start))
|> apply_filters(opts)
|> Repo.all()
end

def list_upcoming_user_activities(user_id, flop) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? > now()", a.start))
|> Flop.validate_and_run(flop, for: Activity)
end

def list_upcoming_user_activities(user_id, %{} = flop, opts) when is_list(opts) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? > now()", a.start))
|> apply_filters(opts)
|> Flop.validate_and_run(flop, for: Activity)
end

@doc """
Returns the list of past activities a user has enrolled in.

## Examples

iex> list_past_user_activities(user_id)
[%Activity{}, ...]
"""
def list_past_user_activities(user_id, params \\ %{})

def list_past_user_activities(user_id, opts) when is_list(opts) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? < now()", a.start))
|> apply_filters(opts)
|> Repo.all()
end

def list_past_user_activities(user_id, flop) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? < now()", a.start))
|> Flop.validate_and_run(flop, for: Activity)
end

def list_past_user_activities(user_id, %{} = flop, opts) when is_list(opts) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? < now()", a.start))
|> apply_filters(opts)
|> Flop.validate_and_run(flop, for: Activity)
end

@doc """
Returns the list of activities a user has enrolled in.

## Examples

iex> list_user_activities(user_id)
[%Activity{}, ...]
"""

def list_organization_activities(organization_id, params \\ %{})

def list_organization_activities(organization_id, opts) when is_list(opts) do
from(a in Activity,
where: a.organization_id == ^organization_id
)
|> apply_filters(opts)
|> Repo.all()
end

def list_organization_activities(organization_id, flop) do
from(a in Activity,
where: a.organization_id == ^organization_id
)
|> Flop.validate_and_run(flop, for: Activity)
end

def list_organization_activities(organization_id, %{} = flop, opts) when is_list(opts) do
from(a in Activity,
where: a.organization_id == ^organization_id
)
|> apply_filters(opts)
|> Flop.validate_and_run(flop, for: Activity)
end

@doc """
Returns the count of upcoming activities a user has enrolled in.

## Examples

iex> user_upcoming_activities_count(user_id)
1
"""
def user_upcoming_activities_count(user_id) do
from(a in Activity,
join: e in assoc(a, :enrollments),
where: e.user_id == ^user_id
)
|> where([a], fragment("? > now()", a.start))
|> Repo.aggregate(:count, :id)
end

@doc """
Creates an enrollment and broadcasts the activity change to all subscribed clients.

## Examples

Expand All @@ -409,23 +543,15 @@ defmodule Atomic.Activities do

"""
def create_enrollment(activity_id, %User{} = user) do
Ecto.Multi.new()
|> Ecto.Multi.insert(:enrollments, %Enrollment{
activity_id: activity_id,
user_id: user.id
})
|> Multi.update(:activity, fn %{enrollments: enrollment} ->
activity = get_activity!(enrollment.activity_id)

Activity.changeset(activity, %{enrolled: activity.enrolled + 1})
end)
|> Repo.transaction()
%Enrollment{}
|> Enrollment.changeset(%{activity_id: activity_id, user_id: user.id})
|> Repo.insert()
|> case do
{:ok, %{enrollments: enrollment, activity: _activity}} ->
broadcast({:ok, enrollment}, :new_enrollment)
{:ok, enrollment} ->
broadcast_activity_update(activity_id)
{:ok, enrollment}

{:error, _reason, changeset, _actions} ->
{:error, changeset} ->
{:error, changeset}
end
end
Expand All @@ -449,7 +575,7 @@ defmodule Atomic.Activities do
end

@doc """
Deletes a enrollment.
Deletes a enrollment and broadcasts the activity change to all subscribed clients.

## Examples

Expand All @@ -461,20 +587,15 @@ defmodule Atomic.Activities do

"""
def delete_enrollment(activity_id, %User{} = user) do
Ecto.Multi.new()
|> Ecto.Multi.delete(:enrollments, get_user_enrolled(user, activity_id))
|> Multi.update(:activity, fn %{enrollments: _enrollment} ->
activity = get_activity!(activity_id)

Activity.changeset(activity, %{enrolled: activity.enrolled - 1})
end)
|> Repo.transaction()
get_enrollment!(activity_id, user.id)
|> Enrollment.delete_changeset()
|> Repo.delete()
|> case do
{:ok, %{enrollments: _enrollment, activity: _activity}} ->
broadcast({1, nil}, :deleted_enrollment)
{1, nil}
{:ok, _} ->
broadcast_activity_update(activity_id)
{:ok, nil}

{:error, _reason, changeset, _actions} ->
{:error, changeset} ->
{:error, changeset}
end
end
Expand All @@ -493,32 +614,21 @@ defmodule Atomic.Activities do
end

@doc """
Broadcasts an event to the pubsub.
Subscribes the caller to the specific activity's updates.

## Examples

iex> broadcast(:new_enrollment, enrollment)
{:ok, %Enrollment{}}

iex> broadcast(:deleted_enrollment, nil)
{:ok, nil}

iex> subscribe_to_activity_update(activity_id)
:ok
"""
def subscribe(topic) when topic in ["new_enrollment", "deleted_enrollment"] do
Phoenix.PubSub.subscribe(Atomic.PubSub, topic)
def subscribe_to_activity_update(activity_id) do
Phoenix.PubSub.subscribe(@pubsub, topic(activity_id))
end

defp broadcast({:error, _reason} = error, _event), do: error

defp broadcast({:ok, %Enrollment{} = enrollment}, event)
when event in [:new_enrollment] do
Phoenix.PubSub.broadcast!(Atomic.PubSub, "new_enrollment", {event, enrollment})
{:ok, enrollment}
end
defp topic(activity_id), do: "activity:#{activity_id}"

defp broadcast({number, nil}, event)
when event in [:deleted_enrollment] do
Phoenix.PubSub.broadcast!(Atomic.PubSub, "deleted_enrollment", {event, nil})
{number, nil}
defp broadcast_activity_update(activity_id) do
activity = get_activity!(activity_id)
Phoenix.PubSub.broadcast(@pubsub, topic(activity_id), activity)
end
end
Loading