Skip to content

Commit

Permalink
feat: event dates config & fix: no speakers bug
Browse files Browse the repository at this point in the history
  • Loading branch information
joaodiaslobo committed Nov 4, 2024
1 parent 216f07d commit 85337c8
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 167 deletions.
4 changes: 3 additions & 1 deletion lib/safira/activities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,11 @@ defmodule Safira.Activities do
"""
def upsert_activity_speakers(%Activity{} = activity, speaker_ids) do
ids = speaker_ids || []

speakers =
Speaker
|> where([s], s.id in ^speaker_ids)
|> where([s], s.id in ^ids)
|> Repo.all()

activity
Expand Down
31 changes: 31 additions & 0 deletions lib/safira/activities/activity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ defmodule Safira.Activities.Activity do
"""
use Safira.Schema

alias Safira.Event

@required_fields ~w(title date time_start time_end)a
@optional_fields ~w(description category_id location has_enrolments max_enrolments)a

Expand Down Expand Up @@ -47,6 +49,7 @@ defmodule Safira.Activities.Activity do
activity
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> validate_activity_date()
end

@doc false
Expand All @@ -55,4 +58,32 @@ defmodule Safira.Activities.Activity do
|> cast(%{}, @required_fields ++ @optional_fields)
|> put_assoc(:speakers, speakers)
end

def validate_activity_date(activity) do
event_start = Event.get_event_start_date()
event_end = Event.get_event_end_date()
date = get_field(activity, :date)

if date != nil do
if Date.compare(date, event_start) in [:lt] do
activity
|> Ecto.Changeset.add_error(
:date,
"must be after or in the event's start date (#{Date.to_string(event_start)})"
)
else
if Date.compare(date, event_end) in [:gt] do
activity
|> Ecto.Changeset.add_error(
:date,
"must be before or in the event's end date (#{Date.to_string(event_end)})"
)
else
activity
end
end
else
activity
end
end
end
74 changes: 74 additions & 0 deletions lib/safira/event.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
defmodule Safira.Event do
@moduledoc """
The event context.
"""
alias Safira.Constants

@doc """
Returns the event's start date.
If the date is not set, it will be set to today's date by default.
## Examples
iex> get_event_start_date()
~D[2025-02-11]
"""
def get_event_start_date do
case Constants.get("event_start_date") do
{:ok, date} ->
date |> Date.from_iso8601!()

{:error, _} ->
# If the date is not set, set it to today's date by default
today = Timex.today()
change_event_start_date(today)
today
end
end

@doc """
Returns the event's end date.
If the date is not set, it will be set to today's date by default.
## Examples
iex> get_event_end_date()
~D[2025-02-14]
"""
def get_event_end_date do
case Constants.get("event_end_date") do
{:ok, date} ->
date |> Date.from_iso8601!()

{:error, _} ->
# If the date is not set, set it to today's date by default
today = Timex.today()
change_event_end_date(today)
today
end
end

@doc """
Changes the event's start date.
## Examples
iex> change_event_start_date(~D[2025-02-11])
:ok
"""
def change_event_start_date(date) do
Constants.set("event_start_date", date)
end

@doc """
Changes the event's end date.
## Examples
iex> change_event_end_date(~D[2025-02-14])
:ok
"""
def change_event_end_date(date) do
Constants.set("event_end_date", date)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
defmodule SafiraWeb.Backoffice.ScheduleLive.ActivityLive.FormComponent do
use SafiraWeb, :live_component

alias Safira.Activities
alias Safira.Activities.Speaker
import SafiraWeb.Components.Forms

@impl true
def render(assigns) do
~H"""
<div>
<.page title={@title} subtitle={gettext("Activities that happen troughout the event.")}>
<.simple_form
for={@form}
id="activity-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
<div class="w-full">
<div class="w-full flex gap-2">
<.field field={@form[:title]} type="text" label="Title" required wrapper_class="w-full" />
<.field field={@form[:location]} type="text" label="Location" wrapper_class="w-full" />
</div>
<.field field={@form[:description]} type="textarea" label="Description" />
<div class="w-full grid grid-cols-4 gap-2">
<.field
field={@form[:date]}
type="date"
label="Date"
required
wrapper_class="col-span-2"
/>
<.field
field={@form[:time_start]}
type="time"
label="Start"
required
wrapper_class="col-span-1"
/>
<.field
field={@form[:time_end]}
type="time"
label="End"
required
wrapper_class="col-span-1"
/>
</div>
<div class="flex gap-2">
<.field
field={@form[:category_id]}
type="select"
label="Category"
options={categories_options(@categories)}
wrapper_class="w-full"
/>
<.field_multiselect
id="speakers"
field={@form[:speakers]}
target={@myself}
value_mapper={&value_mapper/1}
wrapper_class="w-full"
placeholder={gettext("Search for speakers")}
/>
</div>
<div class="w-full flex gap-2">
<div class="w-full grid grid-cols-2">
<div class="w-full flex flex-col">
<.label>
<%= gettext("Enrolments") %>
</.label>
<p class="safira-form-help-text">
<%= gettext(
"Enable enrolments to allow participants to sign up for this activity."
) %>
</p>
<.field
field={@form[:has_enrolments]}
type="switch"
label=""
wrapper_class="w-full pt-3"
/>
</div>
<.field
:if={@enrolments_active}
field={@form[:max_enrolments]}
type="number"
label="Max enrolments"
wrapper_class="w-full mt-12"
/>
</div>
</div>
</div>
<:actions>
<.button phx-disable-with="Saving...">Save Activity</.button>
</:actions>
</.simple_form>
</.page>
</div>
"""
end

@impl true
def mount(socket) do
{:ok, socket}
end

@impl true
def update(%{activity: activity} = assigns, socket) do
{:ok,
socket
|> assign(assigns)
|> assign(:enrolments_active, activity.has_enrolments)
|> assign_new(:form, fn ->
to_form(Activities.change_activity(activity))
end)}
end

@impl true
def handle_event("validate", %{"activity" => activity_params}, socket) do
activity =
if Map.has_key?(activity_params, "speakers") do
socket.assigns.activity
else
Map.put(socket.assigns.activity, :speakers, [])
end

changeset = Activities.change_activity(activity, activity_params)

{:noreply,
assign(socket,
form: to_form(changeset, action: :validate),
enrolments_active: activity_params["has_enrolments"] != "false"
)}
end

def handle_event("save", %{"activity" => activity_params}, socket) do
save_activity(socket, socket.assigns.action, activity_params)
end

@impl true
def handle_event("live_select_change", %{"text" => text, "id" => live_select_id}, socket) do
case Activities.list_speakers(%{
"filters" => %{"1" => %{"field" => "name", "op" => "ilike_or", "value" => text}}
}) do
{:ok, {speakers, _meta}} ->
send_update(LiveSelect.Component,
id: live_select_id,
options: speakers |> Enum.map(&{&1.name, &1.id})
)

{:noreply, socket}

{:error, _} ->
{:noreply, socket}
end
end

defp save_activity(socket, :edit, activity_params) do
case Activities.update_activity(socket.assigns.activity, activity_params) do
{:ok, _activity} ->
case Activities.upsert_activity_speakers(
socket.assigns.activity,
activity_params["speakers"]
) do
{:ok, _activity} ->
{:noreply,
socket
|> put_flash(:info, "Activity updated successfully")
|> push_patch(to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end

defp save_activity(socket, :new, activity_params) do
case Activities.create_activity(activity_params) do
{:ok, activity} ->
case Activities.upsert_activity_speakers(
Map.put(activity, :speakers, []),
activity_params["speakers"]
) do
{:ok, _activity} ->
{:noreply,
socket
|> put_flash(:info, "Activity created successfully")
|> push_patch(to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end

defp categories_options(categories) do
[{"None", nil}] ++
Enum.map(categories, &{&1.name, &1.id})
end

defp value_mapper(%Speaker{} = speaker), do: {speaker.name, speaker.id}

defp value_mapper(id), do: id
end
Loading

0 comments on commit 85337c8

Please sign in to comment.