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: company main page #437

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ npm-debug.log
/assets/node_modules/

.env.dev
.env
32 changes: 31 additions & 1 deletion lib/safira/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Safira.Accounts do
use Safira.Context

alias Safira.Accounts.{Attendee, Course, Credential, Staff, User, UserNotifier, UserToken}

alias Safira.Companies.Company
## Database getters

@doc """
Expand Down Expand Up @@ -82,6 +82,16 @@ defmodule Safira.Accounts do
|> Repo.one()
end

@doc """
Gets a single company by user id.
"""
def get_user_company(user_id, opts \\ []) do
Company
|> where(user_id: ^user_id)
|> apply_filters(opts)
|> Repo.one()
end

@doc """
Gets a single staff.
"""
Expand All @@ -97,6 +107,7 @@ defmodule Safira.Accounts do
user.type,
case user.type do
:attendee -> get_user_attendee(user.id)
:company -> get_user_company(user.id)
:staff -> get_user_staff(user.id, preloads: [:role])
end
)
Expand Down Expand Up @@ -231,6 +242,15 @@ defmodule Safira.Accounts do
|> Repo.insert()
end

# TODO: Docs
def register_company_user(multi, attrs) do
multi
|> Ecto.Multi.insert(
:user,
User.registration_changeset(%User{}, Map.put(attrs, "type", "company"))
)
end

@doc """
Creates an attendee.
"""
Expand Down Expand Up @@ -458,6 +478,16 @@ defmodule Safira.Accounts do
|> Ecto.Multi.delete_all(:tokens, UserToken.by_user_and_contexts_query(user, ["confirm"]))
end

# TODO: Docs
def generate_random_password(length \\ 12) do
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

1..length

:crypto.strong_rand_bytes(length)
|> Enum.map_join(fn b -> b |> :binary.decode_unsigned() |> rem(String.length(alphabet)) end)
end

## Reset password

@doc ~S"""
Expand Down
10 changes: 9 additions & 1 deletion lib/safira/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ defmodule Safira.Accounts.User do
field :hashed_password, :string, redact: true
field :current_password, :string, virtual: true, redact: true
field :confirmed_at, :utc_datetime
field :type, Ecto.Enum, values: [:attendee, :staff], default: :attendee
field :type, Ecto.Enum, values: [:attendee, :staff, :company], default: :attendee

has_one :attendee, Attendee, on_delete: :delete_all
has_one :staff, Staff, on_delete: :delete_all
has_one :company, Company, on_delete: :delete_all

timestamps(type: :utc_datetime)
end
Expand Down Expand Up @@ -79,6 +80,13 @@ defmodule Safira.Accounts.User do
|> validate_password(opts)
end

def changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_email(opts)
|> validate_handle()
end

defp validate_email(changeset, opts) do
changeset
|> validate_required([:email])
Expand Down
23 changes: 22 additions & 1 deletion lib/safira/companies.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Safira.Companies do

use Safira.Context

alias Safira.Accounts
alias Safira.Companies.{Company, Tier}

@doc """
Expand Down Expand Up @@ -56,7 +57,11 @@ defmodule Safira.Companies do
** (Ecto.NoResultsError)

"""
def get_company!(id), do: Repo.get!(Company, id)
def get_company!(id) do
Company
|> Repo.get_by!(id: id)
|> Repo.preload([:user])
end

@doc """
Creates a company.
Expand All @@ -76,6 +81,22 @@ defmodule Safira.Companies do
|> Repo.insert()
end

# TODO: Docs
def create_company_and_user(attrs \\ %{}) do
case Ecto.Multi.new()
|> Accounts.register_company_user(attrs["user"])
|> Ecto.Multi.insert(:company, fn %{user: user} ->
Company.changeset(%Company{}, Map.put(Map.delete(attrs, "user"), "user_id", user.id))
end)
|> Repo.transaction() do
{:ok, %{user: user, company: company}} ->
{:ok, %{user: Map.put(user, "password", attrs["user"]["password"]), company: company}}

{:error, failed_operation, failed_value, changes_so_far} ->
{:error, failed_operation, failed_value, changes_so_far}
end
end

@doc """
Updates a company.

Expand Down
8 changes: 7 additions & 1 deletion lib/safira/companies/company.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ defmodule Safira.Companies.Company do
"""
use Safira.Schema

alias Safira.Accounts.User

@derive {
Flop.Schema,
filterable: [:name],
Expand All @@ -19,14 +21,15 @@ defmodule Safira.Companies.Company do
]
}

@required_fields ~w(name tier_id)a
@required_fields ~w(name tier_id user_id)a
@optional_fields ~w(badge_id url)a

schema "companies" do
field :name, :string
field :url, :string
field :logo, Uploaders.Company.Type

belongs_to :user, User
belongs_to :badge, Safira.Contest.Badge
belongs_to :tier, Safira.Companies.Tier

Expand All @@ -38,10 +41,13 @@ defmodule Safira.Companies.Company do
company
|> cast(attrs, @required_fields ++ @optional_fields)
|> unique_constraint(:badge_id)
|> unique_constraint(:user_id)
|> cast_assoc(:user, with: &User.changeset/2)
|> cast_assoc(:badge)
|> cast_assoc(:tier)
|> validate_required(@required_fields)
|> validate_url(:url)
|> unsafe_validate_unique(:badge_id, Safira.Repo)
end

@doc false
Expand Down
11 changes: 11 additions & 0 deletions lib/safira_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ defmodule SafiraWeb do
end
end

def sponsor_view do
quote do
use Phoenix.LiveView,
layout: {SafiraWeb.Layouts, :sponsor}

import SafiraWeb.Components.Button

unquote(html_helpers())
end
end

def backoffice_view do
quote do
use Phoenix.LiveView,
Expand Down
34 changes: 34 additions & 0 deletions lib/safira_web/components/layouts/sponsor.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<div class="relative h-screen flex overflow-hidden">
<.sidebar
current_user={@current_user}
pages={SafiraWeb.Config.sponsor_pages()}
current_page={Map.get(assigns, :current_page, nil)}
logo_images={%{light: "/images/sei.svg", dark: "/images/sei.svg"}}
background="bg-light dark:bg-dark"
border="border-lightShade dark:border-darkShade"
logo_padding="px-8 pt-8 pb-4 invert"
user_dropdown_name_color="text-dark dark:text-light"
user_dropdown_handle_color="text-darkMuted dark:text-lightMuted"
user_dropdown_icon_color="text-lightShade dark:text-darkShade"
link_class="px-3 dark:hover:bg-darkShade group flex items-center py-2 text-sm font-medium rounded-md transition-colors"
link_active_class="bg-dark text-light hover:bg-darkShade dark:bg-darkShade"
link_inactive_class="text-dark hover:bg-lightShade/40 dark:text-light"
/>
<div class="flex flex-col flex-1 overflow-hidden">
<div class="bg-light dark:bg-dark flex justify-end lg:hidden px-4 sm:px-6 py-2">
<button
class="sidebar-toggle flex items-center justify-center w-16 dark:text-light text-dark"
aria-expanded="false"
phx-click={show_mobile_sidebar()}
>
<.icon class="w-8 h-8" name="hero-bars-3" />
</button>
</div>
<main class="text-dark dark:text-light bg-light dark:bg-dark flex-1 relative z-0 overflow-y-auto focus:outline-none">
<div class="px-4 sm:px-6 lg:px-8 py-8">
<.flash_group flash={@flash} />
<%= @inner_content %>
</div>
</main>
</div>
</div>
2 changes: 1 addition & 1 deletion lib/safira_web/components/table.ex
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ defmodule SafiraWeb.Components.Table do
attr :meta, Flop.Meta, required: true
attr :params, :map, required: true

defp pagination(assigns) do
def pagination(assigns) do
~H"""
<nav
class="flex items-center flex-column flex-wrap md:flex-row justify-between py-4"
Expand Down
11 changes: 11 additions & 0 deletions lib/safira_web/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ defmodule SafiraWeb.Config do
]
end

def sponsor_pages do
[
%{
key: :visitors,
title: "Visitors",
icon: "hero-user",
url: "/sponsor"
}
]
end

def backoffice_pages(user) do
permissions = user.staff.role.permissions

Expand Down
17 changes: 15 additions & 2 deletions lib/safira_web/live/backoffice/companies_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ defmodule SafiraWeb.Backoffice.CompanyLive.FormComponent do
<div class="grid grid-cols-2">
<.field field={@form[:name]} type="text" label="Name" wrapper_class="pr-2" required />
<.field field={@form[:url]} type="text" label="URL" wrapper_class="" />
<.inputs_for :let={user} field={@form[:user]}>
<.field field={user[:email]} type="email" label="Email" wrapper_class="pr-2" required />
<.field
field={user[:password]}
type="password"
label="Password"
wrapper_class=""
required={@action == :new}
/>
<.field field={user[:handle]} type="text" label="Handle" wrapper_class="pr-2" required />
<.field field={user[:name]} type="text" label="User name" wrapper_class="" required />
</.inputs_for>

<.field
field={@form[:tier_id]}
type="select"
Expand Down Expand Up @@ -113,7 +126,7 @@ defmodule SafiraWeb.Backoffice.CompanyLive.FormComponent do
end

defp save_company(socket, :new, company_params) do
case Companies.create_company(company_params) do
case Companies.create_company_and_user(company_params) do
{:ok, company} ->
case consume_image_data(company, socket) do
{:ok, _company} ->
Expand All @@ -123,7 +136,7 @@ defmodule SafiraWeb.Backoffice.CompanyLive.FormComponent do
|> push_patch(to: socket.assigns.patch)}
end

{:error, %Ecto.Changeset{} = changeset} ->
{:error, _, %Ecto.Changeset{} = changeset, _} ->
{:noreply, assign(socket, form: to_form(changeset))}
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/safira_web/live/backoffice/companies_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule SafiraWeb.Backoffice.CompanyLive.Index do

import SafiraWeb.Components.{Table, TableSearch}

alias Safira.Accounts.User

alias Safira.{Companies, Contest}
alias Safira.Companies.{Company, Tier}

Expand Down Expand Up @@ -41,7 +43,7 @@ defmodule SafiraWeb.Backoffice.CompanyLive.Index do
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "New Company")
|> assign(:company, %Company{})
|> assign(:company, %Company{user: %User{}})
|> assign(:tiers, Companies.list_tiers())
|> assign(:badges, Contest.list_badges())
end
Expand Down
25 changes: 25 additions & 0 deletions lib/safira_web/live/sponsor/home_live/components/attendee.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule SafiraWeb.Sponsor.HomeLive.Components.Attendee do
@moduledoc """
Vault item component.
"""
use SafiraWeb, :component

attr :id, :string, required: true
attr :name, :string, required: true
attr :image, :string, required: true

def attendee(assigns) do
~H"""
<li id={@id} class="flex flex-row items-center justify-center">
<.link href={"/attendees/#{@id}"}>
<div class="py-4 px-4">
<img class="w-16 h-16 m-auto" src={@image} />
<h1 class="font-terminal uppercase text-xl">
<%= @name %>
</h1>
</div>
</.link>
</li>
"""
end
end
33 changes: 33 additions & 0 deletions lib/safira_web/live/sponsor/home_live/index.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule SafiraWeb.Sponsor.HomeLive.Index do
use SafiraWeb, :sponsor_view

alias Safira.Accounts

import SafiraWeb.Sponsor.HomeLive.Components.Attendee
import SafiraWeb.Components.Table

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

@impl true
def handle_params(params, _url, socket) do
case Accounts.list_attendees(params) do
{:ok, {attendees, meta}} ->
{:noreply,
socket
|> assign(:current_page, :visitors)
|> assign(:meta, meta)
|> assign(:params, params)
|> stream(:visitors, attendees, reset: true)}

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

defp get_attendee_image(_attendee) do
"/images/attendee.svg"
end
end
Loading
Loading