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: companies #405

Merged
merged 6 commits into from
Oct 14, 2024
Merged
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
5 changes: 3 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import "phoenix_html"
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import { QrScanner, Wheel, Confetti } from "./hooks";
import { QrScanner, Wheel, Confetti, Sorting } from "./hooks";

let Hooks = {
QrScanner: QrScanner,
Wheel: Wheel,
Confetti: Confetti
Confetti: Confetti,
Sorting: Sorting
};

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
Expand Down
3 changes: 2 additions & 1 deletion assets/js/hooks/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { QrScanner } from "./qr_reading.js";
export { Wheel } from "./wheel.js";
export { Confetti } from "./confetti.js";
export { Confetti } from "./confetti.js";
export { Sorting } from "./sorting.js";
21 changes: 21 additions & 0 deletions assets/js/hooks/sorting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Sortable from "../../vendor/sortable";

export const Sorting = {
mounted() {
const placeholder = document.createElement('div');
new Sortable(this.el, {
animation: 150,
ghostClass: "opacity-20",
dragClass: "shadow-2xl",
handle: ".handle",
onEnd: () => {
const elements = Array.from(this.el.children)
const ids = elements.map(elm => elm.id)
this.pushEventTo(this.el, "update-sorting", {ids: ids})
},
setData: (dataTransfer, _) => {
dataTransfer.setDragImage(placeholder, 0, 0);
}
})
}
}
2 changes: 2 additions & 0 deletions assets/vendor/sortable.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/safira/accounts/roles/permissions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Safira.Accounts.Roles.Permissions do
%{
"attendees" => ["show", "edit"],
"staffs" => ["show", "edit", "roles_edit"],
"companies" => ["edit"],
"products" => ["show", "edit", "delete"],
"purchases" => ["show", "redeem", "refund"],
"badges" => ["show", "edit", "delete", "give", "revoke", "give_without_restrictions"],
Expand Down
253 changes: 253 additions & 0 deletions lib/safira/companies.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
defmodule Safira.Companies do
@moduledoc """
The Companies context.
"""

use Safira.Context

alias Safira.Companies.Company

@doc """
Returns the list of companies.

## Examples

iex> list_companies()
[%Company{}, ...]

"""
def list_companies do
Company
|> Repo.all()
end

def list_companies(opts) when is_list(opts) do
Company
|> apply_filters(opts)
|> Repo.all()
end

def list_companies(params) do
Company
|> join(:left, [c], t in assoc(c, :tier), as: :tier)
|> preload(:tier)
|> Flop.validate_and_run(params, for: Company)
end

def list_companies(%{} = params, opts) when is_list(opts) do
Company
|> apply_filters(opts)
|> join(:left, [c], t in assoc(c, :tier), as: :tier)
|> preload(:tier)
|> Flop.validate_and_run(params, for: Company)
end

@doc """
Gets a single company.

Raises `Ecto.NoResultsError` if the Company does not exist.

## Examples

iex> get_company!(123)
%Company{}

iex> get_company!(456)
** (Ecto.NoResultsError)

"""
def get_company!(id), do: Repo.get!(Company, id)

@doc """
Creates a company.

## Examples

iex> create_company(%{field: value})
{:ok, %Company{}}

iex> create_company(%{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def create_company(attrs \\ %{}) do
%Company{}
|> Company.changeset(attrs)
|> Repo.insert()
end

@doc """
Updates a company.

## Examples

iex> update_company(company, %{field: new_value})
{:ok, %Company{}}

iex> update_company(company, %{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def update_company(%Company{} = company, attrs) do
company
|> Company.changeset(attrs)
|> Repo.update()
end

@doc """
Updates a company logo.

## Examples

iex> update_company_logo(company, %{logo: image})
{:ok, %Company{}}

iex> update_company_logo(company, %{logo: bad_image})
{:error, %Ecto.Changeset{}}

"""
def update_company_logo(%Company{} = company, attrs) do
company
|> Company.image_changeset(attrs)
|> Repo.update()
end

@doc """
Deletes a company.

## Examples

iex> delete_company(company)
{:ok, %Company{}}

iex> delete_company(company)
{:error, %Ecto.Changeset{}}

"""
def delete_company(%Company{} = company) do
Repo.delete(company)
end

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

## Examples

iex> change_company(company)
%Ecto.Changeset{data: %Company{}}

"""
def change_company(%Company{} = company, attrs \\ %{}) do
Company.changeset(company, attrs)
end

alias Safira.Companies.Tier

@doc """
Returns the list of tiers.

## Examples

iex> list_tiers()
[%Tier{}, ...]

"""
def list_tiers do
Tier
|> order_by(:priority)
|> Repo.all()
end

@doc """
Gets a single tier.

Raises `Ecto.NoResultsError` if the Tier does not exist.

## Examples

iex> get_tier!(123)
%Tier{}

iex> get_tier!(456)
** (Ecto.NoResultsError)

"""
def get_tier!(id), do: Repo.get!(Tier, id)

@doc """
Creates a tier.

## Examples

iex> create_tier(%{field: value})
{:ok, %Tier{}}

iex> create_tier(%{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def create_tier(attrs \\ %{}) do
%Tier{}
|> Tier.changeset(attrs)
|> Repo.insert()
end

@doc """
Updates a tier.

## Examples

iex> update_tier(tier, %{field: new_value})
{:ok, %Tier{}}

iex> update_tier(tier, %{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def update_tier(%Tier{} = tier, attrs) do
tier
|> Tier.changeset(attrs)
|> Repo.update()
end

@doc """
Deletes a tier.

## Examples

iex> delete_tier(tier)
{:ok, %Tier{}}

iex> delete_tier(tier)
{:error, %Ecto.Changeset{}}

"""
def delete_tier(%Tier{} = tier) do
Repo.delete(tier)
end

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

## Examples

iex> change_tier(tier)
%Ecto.Changeset{data: %Tier{}}

"""
def change_tier(%Tier{} = tier, attrs \\ %{}) do
Tier.changeset(tier, attrs)
end

@doc """
Returns the next priority a tier should have.

## Examples

iex> get_next_tier_priority()
5
"""
def get_next_tier_priority do
(Repo.aggregate(from(t in Tier), :max, :priority) || -1) + 1
end
end
52 changes: 52 additions & 0 deletions lib/safira/companies/company.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
defmodule Safira.Companies.Company do
@moduledoc """
Companies present at the event.
"""
use Safira.Schema

@derive {
Flop.Schema,
filterable: [:name],
sortable: [:name, :tier],
default_limit: 11,
join_fields: [
tier: [
binding: :tier,
field: :priority,
path: [:tier, :priority],
ecto_type: :integer
]
]
}

@required_fields ~w(name tier_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 :badge, Safira.Contest.Badge
belongs_to :tier, Safira.Companies.Tier

timestamps(type: :utc_datetime)
end

@doc false
def changeset(company, attrs) do
company
|> cast(attrs, @required_fields ++ @optional_fields)
|> unique_constraint(:badge_id)
|> cast_assoc(:badge)
|> cast_assoc(:tier)
|> validate_required(@required_fields)
|> validate_url(:url)
end

@doc false
def image_changeset(badge, attrs) do
badge
|> cast_attachments(attrs, [:logo])
end
end
26 changes: 26 additions & 0 deletions lib/safira/companies/tier.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule Safira.Companies.Tier do
@moduledoc """
Sponsor tiers for companies.
"""
use Safira.Schema

@required_fields ~w(name priority)a

@derive {Flop.Schema, sortable: [:priority], filterable: []}

schema "tiers" do
field :name, :string
field :priority, :integer

has_many :companies, Safira.Companies.Company, foreign_key: :tier_id

timestamps(type: :utc_datetime)
end

@doc false
def changeset(tier, attrs) do
tier
|> cast(attrs, @required_fields)
|> validate_required(@required_fields)
end
end
Loading
Loading