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

Allow users to apply for yearbook class #25

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions lib/yearbook/university.ex
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,33 @@ defmodule Yearbook.University do
def change_class(%Class{} = class, attrs \\ %{}) do
Class.changeset(class, attrs)
end

alias Yearbook.University.ClassStudent

def create_class_student(attrs \\ %{}) do
student = student_classes_count(attrs["student_id"], attrs["class_id"])

if student > 0 do
Copy link
Member

Choose a reason for hiding this comment

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

These types of verifications should be at the database level. Otherwise, you can not guaranty it.

Just make sure you have the unique index of [:student_id, :class_id] 💪

{:error, %Ecto.Changeset{}}
else
%ClassStudent{}
|> ClassStudent.changeset(attrs)
|> Repo.insert()
end
end

def student_classes_count(student_id, class_id) do
ClassStudent
|> where(student_id: ^student_id)
|> where(class_id: ^class_id)
|> Repo.all()
|> Enum.count()
end

def list_student_classes(student_id) do
ClassStudent
|> where(student_id: ^student_id)
|> where(accepted: true)
|> Repo.all()
end
end
4 changes: 2 additions & 2 deletions lib/yearbook/university/class_student.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ defmodule Yearbook.University.ClassStudent do
schema "classes_students" do
belongs_to :class, University.Class
belongs_to :student, Accounts.User

field :accepted, :boolean, default: false
timestamps()
end

@doc false
def changeset(class_student, attrs) do
class_student
|> cast(attrs, [:class_id, :student_id])
|> cast(attrs, [:class_id, :student_id, :accepted])
|> validate_required([:class_id, :student_id])
|> unique_constraint([:class_id, :student_id])
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule YearbookWeb.Admin.ClassLive.FormComponent do
defmodule YearbookWeb.ClassLive.FormComponent do
@moduledoc false
use YearbookWeb, :live_component

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule YearbookWeb.Admin.ClassLive.Index do
defmodule YearbookWeb.ClassLive.Index do
@moduledoc false
use YearbookWeb, :live_view

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
<div class="sm:flex-auto">
<h1 class="text-xl font-semibold text-gray-900">Lista de Turmas</h1>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<%= live_patch to: Routes.admin_class_index_path(@socket, :new) do %>
<button type="button" class="inline-flex items-center justify-center rounded-md border border-transparent bg-orange-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 sm:w-auto">
Nova Turma
</button>
<% end %>
</div>
<%= if assigns[:current_user] && (@current_user.permissions == [:admin, :sysadmin] || @current_user.permissions == [:admin] || @current_user.permissions == [:sysadmin]) do %>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<%= live_patch to: Routes.admin_class_index_path(@socket, :new) do %>
<button type="button" class="inline-flex items-center justify-center rounded-md border border-transparent bg-orange-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 sm:w-auto">
Nova Turma
</button>
<% end %>
</div>
<% end %>
</div>

<%= if @live_action in [:new, :edit] do %>
<.modal return_to={Routes.admin_class_index_path(@socket, :index)}>
<.live_component module={YearbookWeb.Admin.ClassLive.FormComponent} id={@class.id || :new} title={@page_title} action={@live_action} class={@class} return_to={Routes.admin_class_index_path(@socket, :index)} />
<.modal return_to={Routes.class_index_path(@socket, :index)}>
<.live_component module={YearbookWeb.ClassLive.FormComponent} id={@class.id || :new} title={@page_title} action={@live_action} class={@class} return_to={Routes.class_index_path(@socket, :index)} />
</.modal>
<% end %>

Expand Down Expand Up @@ -46,15 +48,16 @@
</td>
<td class="py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
<%= live_redirect to: Routes.yearbook_show_path(@socket, :show, class), class: "text-orange-600 hover:text-orange-900" do %>
Show<span class="sr-only"></span>
<% end %>

<%= live_patch to: Routes.admin_class_index_path(@socket, :edit, class), class: "text-orange-600 hover:text-orange-900" do %>
Edit<span class="sr-only"></span>
Ver<span class="sr-only"></span>
<% end %>
<%= if assigns[:current_user] && (@current_user.permissions == [:admin, :sysadmin] || @current_user.permissions == [:admin] || @current_user.permissions == [:sysadmin]) do %>
<%= live_patch to: Routes.admin_class_index_path(@socket, :edit, class), class: "text-orange-600 hover:text-orange-900" do %>
Edit<span class="sr-only"></span>
<% end %>

<%= link to: "#", phx_click: "delete", phx_value_id: class.id, data: [confirm: "Are you sure?"], class: "text-orange-600 hover:text-orange-900" do %>
Delete<span class="sr-only">Delete</span>
<%= link to: "#", phx_click: "delete", phx_value_id: class.id, data: [confirm: "Are you sure?"], class: "text-orange-600 hover:text-orange-900" do %>
Delete<span class="sr-only">Delete</span>
<% end %>
<% end %>
</td>
</tr>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule YearbookWeb.Admin.ClassLive.Show do
defmodule YearbookWeb.ClassLive.Show do
@moduledoc false
use YearbookWeb, :live_view

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<%= if @live_action in [:edit] do %>
<.modal return_to={Routes.admin_class_show_path(@socket, :show, @class)}>
<.live_component module={YearbookWeb.Admin.ClassLive.FormComponent} id={@class.id} title={@page_title} action={@live_action} class={@class} return_to={Routes.admin_class_show_path(@socket, :show, @class)} />
<.live_component module={YearbookWeb.ClassLive.FormComponent} id={@class.id} title={@page_title} action={@live_action} class={@class} return_to={Routes.admin_class_show_path(@socket, :show, @class)} />
</.modal>
<% end %>

Expand All @@ -14,5 +14,5 @@
</li>
</ul>

<span><%= live_patch("Edit", to: Routes.admin_class_show_path(@socket, :edit, @class), class: "button") %></span> | <span><%= live_redirect("Back", to: Routes.admin_class_index_path(@socket, :index)) %></span>
<span><%= live_patch("Edit", to: Routes.admin_class_show_path(@socket, :edit, @class), class: "button") %></span> | <span><%= live_redirect("Back", to: Routes.class_index_path(@socket, :index)) %></span>
</div>
29 changes: 29 additions & 0 deletions lib/yearbook_web/live/yearbook_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,29 @@ defmodule YearbookWeb.YearbookLive.Show do
{:ok, socket}
end

@impl true
def handle_event("apply", _payload, socket) do
attrs = %{
"class_id" => socket.assigns.class.id,
"student_id" => socket.assigns.current_user.id,
"accepted" => false
}

case University.create_class_student(attrs) do
{:ok, _class_student} ->
{:noreply,
socket
|> put_flash(:info, "Applied to class successfully")
|> push_redirect(to: Routes.class_index_path(socket, :index))}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply,
assign(socket, :changeset, changeset)
|> put_flash(:error, "You have already applied to this class")
|> push_redirect(to: Routes.class_index_path(socket, :index))}
end
end

@impl true
def handle_params(%{"class_id" => id}, _, socket) do
class = University.get_class!(id, [:students, :degree, :academic_year])
Expand All @@ -19,4 +42,10 @@ defmodule YearbookWeb.YearbookLive.Show do
|> assign(:page_title, class.degree.name)
|> assign(:class, class)}
end

defp is_accepted?(student, class) do
classes = University.list_student_classes(student)

Enum.any?(classes, fn cs -> cs.accepted == true && cs.class_id == class end)
end
end
85 changes: 47 additions & 38 deletions lib/yearbook_web/live/yearbook_live/show.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,57 @@
</div>
<div class="mx-auto py-12 px-4 text-center sm:px-6 lg:px-8">
<div class="space-y-8 sm:space-y-12">
<%= if @current_user && Yearbook.University.list_student_classes(@current_user.id) == [] && (@current_user.permissions != [:admin, :sysadmin] || @current_user.permissions != [:admin] || @current_user.permissions != [:sysadmin]) do %>
<div class="flex mx-auto max-w-7xl">
<button class="inline-flex items-center justify-center rounded-md border border-transparent bg-orange-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 sm:w-auto" phx-click="apply">
Pertencer
</button>
</div>
<% end %>
<ul role="list" class="mx-auto space-y-16 sm:grid sm:grid-cols-2 sm:gap-16 sm:space-y-0 lg:grid-cols-4 lg:max-w-7xl">
<%= for student <- @class.students do %>
<li>
<div class="space-y-6">
<%= if is_nil(student.avatar) do %>
<div>
<span class="inline-flex justify-center items-center w-40 h-40 xl:w-56 xl:h-56 bg-gray-500 rounded-full">
<span class="text-5xl font-medium leading-none text-white"><%= extract_initials(student.name) %></span>
</span>
<%= if is_accepted?(student.id, @class.id) do %>
<li>
<div class="space-y-6">
<%= if is_nil(student.avatar) do %>
<div>
<span class="inline-flex justify-center items-center w-40 h-40 xl:w-56 xl:h-56 bg-gray-500 rounded-full">
<span class="text-5xl font-medium leading-none text-white"><%= extract_initials(student.name) %></span>
</span>
</div>
<% else %>
<img class="mx-auto h-40 w-40 rounded-full xl:w-56 xl:h-56" src={Uploaders.Avatar.url({student.avatar, student}, :thumb)} alt="" />
<% end %>
<div class="space-y-2">
<div class="text-lg leading-6 font-medium space-y-1">
<h3><%= student.name %></h3>
</div>
<ul role="list" class="flex justify-center space-x-5">
<li>
<a href="#" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Twitter</span>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84" />
</svg>
</a>
</li>
<li>
<a href="#" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">LinkedIn</span>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path
fill-rule="evenodd"
d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z"
clip-rule="evenodd"
/>
</svg>
</a>
</li>
</ul>
</div>
<% else %>
<img class="mx-auto h-40 w-40 rounded-full xl:w-56 xl:h-56" src={Uploaders.Avatar.url({student.avatar, student}, :thumb)} alt="" />
<% end %>
<div class="space-y-2">
<div class="text-lg leading-6 font-medium space-y-1">
<h3><%= student.name %></h3>
</div>
<ul role="list" class="flex justify-center space-x-5">
<li>
<a href="#" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Twitter</span>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84" />
</svg>
</a>
</li>
<li>
<a href="#" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">LinkedIn</span>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path
fill-rule="evenodd"
d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z"
clip-rule="evenodd"
/>
</svg>
</a>
</li>
</ul>
</div>
</div>
</li>
</li>
<% end %>
<% end %>
</ul>
</div>
Expand Down
15 changes: 9 additions & 6 deletions lib/yearbook_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ defmodule YearbookWeb.Router do

scope "/", YearbookWeb do
pipe_through :browser

get "/", PageController, :index
get "/contacts", PageController, :contacts
get "/terms", PageController, :terms
Expand Down Expand Up @@ -59,6 +58,15 @@ defmodule YearbookWeb.Router do

live_session :logged_in, on_mount: [{YearbookWeb.Hooks, :current_user}] do
live "/yearbook/:class_id", YearbookLive.Show, :show
live "/classes", ClassLive.Index, :index

scope "/admin", as: :admin do
pipe_through :require_admin_user

live "/classes/new", ClassLive.Index, :new
live "/classes/:id/edit", ClassLive.Index, :edit
live "/classes/:id/show/edit", ClassLive.Show, :edit
end

scope "/admin", Admin, as: :admin do
pipe_through :require_admin_user
Expand All @@ -74,11 +82,6 @@ defmodule YearbookWeb.Router do
live "/degrees/:id/edit", DegreeLive.Index, :edit
live "/degrees/:id", DegreeLive.Show, :show
live "/degrees/:id/show/edit", DegreeLive.Show, :edit

live "/classes", ClassLive.Index, :index
live "/classes/new", ClassLive.Index, :new
live "/classes/:id/edit", ClassLive.Index, :edit
live "/classes/:id/show/edit", ClassLive.Show, :edit
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/yearbook_web/templates/layout/_auth_menu.html.heex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= if @current_user do %>
<%= if assigns[:current_user] do %>
<div href="#" class="block flex-shrink-0 group">
<div class="flex items-center">
<%= if is_nil(@current_user.avatar) do %>
Expand Down
2 changes: 1 addition & 1 deletion lib/yearbook_web/templates/layout/_header.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="flex items-center">
<a href="/">
<span class="sr-only">CeSIUM</span>
<%= if @current_user do %>
<%= if assigns[:current_user] do %>
<img class="w-auto h-10" src={Routes.static_path(YearbookWeb.Endpoint, "/images/logo-WHITE.svg")} alt="CeSIUM's Logo" />
<% else %>
<img class="w-auto h-10" src={Routes.static_path(YearbookWeb.Endpoint, "/images/cesium-WHITE.svg")} alt="CeSIUM's Logo" />
Expand Down
2 changes: 1 addition & 1 deletion lib/yearbook_web/views/layout_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule YearbookWeb.LayoutView do
},
%{
title: "Turmas",
path: Routes.admin_class_index_path(@conn, :index)
path: Routes.class_index_path(@conn, :index)
}
]
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Yearbook.Repo.Migrations.CreateClassesStudents do
add :id, :binary_id, primary_key: true
add :class_id, references(:classes, on_delete: :nothing, type: :binary_id)
add :student_id, references(:users, on_delete: :nothing, type: :binary_id)

add :accepted, :boolean, default: false
timestamps()
end

Expand Down
2 changes: 1 addition & 1 deletion priv/repo/seeds.exs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ for degree <- [lei, mei, miei, dei],
|> Repo.update!()

%ClassStudent{}
|> ClassStudent.changeset(%{student_id: student.id, class_id: class.id})
|> ClassStudent.changeset(%{student_id: student.id, class_id: class.id, accepted: true})
|> Repo.insert!()
end)
end
Loading