Packages

phoenix_kit

1.7.26
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules tickets ticket_attachment.ex
Raw

lib/modules/tickets/ticket_attachment.ex

defmodule PhoenixKit.Modules.Tickets.TicketAttachment do
@moduledoc """
Junction schema for ticket and comment attachments.
Links tickets or comments to uploaded files (images, documents, etc.)
with ordering and optional captions. An attachment belongs to either
a ticket directly OR a comment, but not both.
## Fields
- `ticket_id` - Reference to ticket (if attached to ticket directly)
- `comment_id` - Reference to comment (if attached to comment)
- `file_id` - Reference to the uploaded file (PhoenixKit.Storage.File)
- `position` - Display order (1, 2, 3, etc.)
- `caption` - Optional caption/alt text
Note: Either `ticket_id` OR `comment_id` must be set, but not both.
## Examples
# Attachment on ticket itself
%TicketAttachment{
ticket_id: "018e3c4a-9f6b-7890-abcd-ef1234567890",
comment_id: nil,
file_id: "018e3c4a-1234-5678-abcd-ef1234567890",
position: 1,
caption: "Screenshot of the error"
}
# Attachment on a comment
%TicketAttachment{
ticket_id: nil,
comment_id: "018e3c4a-5678-1234-abcd-ef1234567890",
file_id: "018e3c4a-abcd-efgh-ijkl-mnopqrstuvwx",
position: 1,
caption: nil
}
"""
use Ecto.Schema
import Ecto.Changeset
@primary_key {:id, UUIDv7, autogenerate: true}
@foreign_key_type UUIDv7
@type t :: %__MODULE__{
id: UUIDv7.t() | nil,
ticket_id: UUIDv7.t() | nil,
comment_id: UUIDv7.t() | nil,
file_id: UUIDv7.t(),
position: integer(),
caption: String.t() | nil,
ticket: PhoenixKit.Modules.Tickets.Ticket.t() | Ecto.Association.NotLoaded.t() | nil,
comment:
PhoenixKit.Modules.Tickets.TicketComment.t() | Ecto.Association.NotLoaded.t() | nil,
file: PhoenixKit.Modules.Storage.File.t() | Ecto.Association.NotLoaded.t(),
inserted_at: NaiveDateTime.t() | nil,
updated_at: NaiveDateTime.t() | nil
}
schema "phoenix_kit_ticket_attachments" do
field :position, :integer
field :caption, :string
belongs_to :ticket, PhoenixKit.Modules.Tickets.Ticket
belongs_to :comment, PhoenixKit.Modules.Tickets.TicketComment
belongs_to :file, PhoenixKit.Modules.Storage.File
timestamps(type: :naive_datetime)
end
@doc """
Changeset for creating or updating an attachment.
## Required Fields
- `file_id` - Reference to file
- `position` - Display order (must be positive)
- Either `ticket_id` OR `comment_id` (but not both)
## Validation Rules
- Position must be greater than 0
- Must have exactly one of ticket_id or comment_id
"""
def changeset(attachment, attrs) do
attachment
|> cast(attrs, [:ticket_id, :comment_id, :file_id, :position, :caption])
|> validate_required([:file_id, :position])
|> validate_number(:position, greater_than: 0)
|> validate_parent_reference()
|> foreign_key_constraint(:ticket_id)
|> foreign_key_constraint(:comment_id)
|> foreign_key_constraint(:file_id)
end
@doc """
Check if attachment is attached to a ticket directly.
"""
def ticket_attachment?(%__MODULE__{ticket_id: ticket_id}) when not is_nil(ticket_id), do: true
def ticket_attachment?(_), do: false
@doc """
Check if attachment is attached to a comment.
"""
def comment_attachment?(%__MODULE__{comment_id: comment_id}) when not is_nil(comment_id),
do: true
def comment_attachment?(_), do: false
# Private Functions
defp validate_parent_reference(changeset) do
ticket_id = get_field(changeset, :ticket_id)
comment_id = get_field(changeset, :comment_id)
case {ticket_id, comment_id} do
{nil, nil} ->
add_error(changeset, :ticket_id, "either ticket_id or comment_id must be set")
{id, cid} when not is_nil(id) and not is_nil(cid) ->
add_error(changeset, :comment_id, "cannot set both ticket_id and comment_id")
_ ->
changeset
end
end
end