Skip to content

Conversation

@HenriqueSFernandes
Copy link

Hello!
Origin story:
I created a team, but I had a dyslexia moment, and I entered the wrong name, so I sent an email asking to update.

But since I think it would be cool for team admins to be able to edit the team details, I decided to do it myself.
I hope this is useful.

Changes:

  • If the user is the team's admin, they have a button to edit the name and description
  • The button opens a modal, and then users can edit the information and save them.

Greetings from NIAEFEUP 👋!

Copilot AI review requested due to automatic review settings January 6, 2026 15:55
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds the ability for team leaders to edit their team's name and description directly from the profile page. The motivation comes from a real user experience where the contributor needed to email administrators to fix a typo in their team name.

Key changes:

  • Added an edit button next to the team name that is only visible to team leaders
  • Implemented a modal dialog with a form for editing team name and description
  • Added LiveView event handlers to manage modal state, form validation, and team updates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


@impl true
def handle_event("close-edit-modal", _params, socket) do
{:noreply, assign(socket, show_edit_modal: false)}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

When the modal is closed without saving changes (via Cancel button or on_cancel), the form still contains any user-entered data. This could lead to confusion if the user reopens the modal later. Consider resetting the form to the current team values when closing the modal to ensure a clean state on next open.

Suggested change
{:noreply, assign(socket, show_edit_modal: false)}
user = socket.assigns.user
team = user.team
socket =
if team && team.leader_id == user.id do
assign(socket,
show_edit_modal: false,
form: to_form(Teams.change_team(team))
)
else
assign(socket, show_edit_modal: false)
end
{:noreply, socket}

Copilot uses AI. Check for mistakes.
Copy link
Member

@enricoprazeres enricoprazeres left a comment

Choose a reason for hiding this comment

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

Hello! Thanks for the contribution! 💪

It makes total sense and it’s a really useful feature.
When you get a chance, please take a look at the review I just left. There are a few suggestions there.

Nice work!

Comment on lines +166 to +185
def mount(_params, _session, %{assigns: %{current_scope: %{user: user}}} = socket)
when not is_nil(user) do
team =
if user.team_id do
Teams.get_team!(user.team_id)
else
nil
end

{:ok, assign(socket, user: Map.put(user, :team, team))}
socket = assign(socket, user: Map.put(user, :team, team), show_edit_modal: false)

socket =
if team && team.leader_id == user.id do
assign(socket, form: to_form(Teams.change_team(team)))
else
socket
end

{:ok, socket}
end
Copy link
Member

Choose a reason for hiding this comment

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

You could do something more idiomatic with this:

  @impl true
  def mount(_params, _session, %{assigns: %{current_scope: %{user: user}}} = socket) when not is_nil(user) do
    team = if user.team_id do
      Teams.get_team!(user.team_id)
    else
      nil
    end

    socket = socket
             |> assign(user: Map.put(user, :team, team), show_edit_modal: false)
             |> maybe_assign_team_form(team, user)

    {:ok, socket}
  end

  defp maybe_assign_team_form(socket, %{leader_id: id} = team, %{id: id})
       when leader_id == user_id do
    assign(socket, form: to_form(Teams.change_team_edition(team)))
  end

  defp maybe_assign_team_form(socket, _team, _user), do: socket

def handle_event("validate-team", %{"team" => team_params}, socket) do
changeset =
socket.assigns.user.team
|> Teams.change_team(team_params)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
|> Teams.change_team(team_params)
|> Teams.change_team_edition(team_params)

Consider using the new change_team_edition/2 function, which relies on edition_changeset/2, to ensure that only the intended fields can be edited during team updates.

teams.ex

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

  ## Examples

      iex> change_team_edition(team)
      %Ecto.Changeset{data: %Team{}}

  """
  def change_team_edition(%Team{} = team, attrs \\ %{}) do
    Team.edition_changeset(team, attrs)
  end

team.ex

  def edition_changeset(team, attrs) do
    team
    |> cast(attrs, [
      :name, :description
    ])
  end

Comment on lines +207 to 223
@impl true
def handle_event("update-team", %{"team" => team_params}, socket) do
case Teams.update_team(socket.assigns.user.team, team_params) do
{:ok, _updated_team} ->
# Reload team to ensure we have the latest state and members preloaded
team = Teams.get_team!(socket.assigns.user.team_id)
user = Map.put(socket.assigns.user, :team, team)

{:noreply,
socket
|> assign(user: user, show_edit_modal: false)
|> put_flash(:info, "Team updated successfully")}

{:error, changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end
Copy link
Member

Choose a reason for hiding this comment

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

We should verify here if the current user is the team leader.

Suggested change
@impl true
def handle_event("update-team", %{"team" => team_params}, socket) do
case Teams.update_team(socket.assigns.user.team, team_params) do
{:ok, _updated_team} ->
# Reload team to ensure we have the latest state and members preloaded
team = Teams.get_team!(socket.assigns.user.team_id)
user = Map.put(socket.assigns.user, :team, team)
{:noreply,
socket
|> assign(user: user, show_edit_modal: false)
|> put_flash(:info, "Team updated successfully")}
{:error, changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end
@impl true
def handle_event("update-team", %{"team" => team_params}, socket) do
user = socket.assigns.user
team = user.team
if team && team.leader_id == user.id do
case Teams.update_team(socket.assigns.user.team, team_params) do
{:ok, _updated_team} ->
team = Teams.get_team!(socket.assigns.user.team_id)
user = Map.put(socket.assigns.user, :team, team)
{:noreply, socket
|> assign(user: user, show_edit_modal: false)
|> put_flash(:info, "Team updated successfully")
}
{:error, changeset} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
else
{:noreply, socket |> put_flash(:error, "You are not authorized to update this team.") |> assign(show_edit_modal: false)}
end
end


socket =
if team && team.leader_id == user.id do
assign(socket, form: to_form(Teams.change_team(team)))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
assign(socket, form: to_form(Teams.change_team(team)))
assign(socket, form: to_form(Teams.change_team_edition(team)))

Comment on lines +192 to +195
@impl true
def handle_event("close-edit-modal", _params, socket) do
{:noreply, assign(socket, show_edit_modal: false)}
end
Copy link
Member

Choose a reason for hiding this comment

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

When the modal is closed without saving changes, the form still contains any user-entered data. This could lead to confusion if the user reopens the modal later. Consider resetting the form to the current team values when closing the modal to ensure a clean state on next open.

Suggested change
@impl true
def handle_event("close-edit-modal", _params, socket) do
{:noreply, assign(socket, show_edit_modal: false)}
end
@impl true
def handle_event("close-edit-modal", _params, socket) do
user = socket.assigns.user
team = user.team
{:noreply, socket
|> assign(show_edit_modal: false, form: to_form(Teams.change_team_edition(team)))
|> maybe_assign_team_form(team, user)}
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants