Compare commits
3 commits
4a09ab1f7b
...
7470d7e9ab
| Author | SHA1 | Date | |
|---|---|---|---|
| 7470d7e9ab | |||
| 6feee45f80 | |||
| 9166db8a52 |
11 changed files with 56 additions and 503 deletions
|
|
@ -42,10 +42,6 @@ defmodule Mv.Membership.Member do
|
||||||
@member_search_limit 10
|
@member_search_limit 10
|
||||||
@default_similarity_threshold 0.2
|
@default_similarity_threshold 0.2
|
||||||
|
|
||||||
# Use constants from Mv.Constants for member fields
|
|
||||||
# This ensures consistency across the codebase
|
|
||||||
@member_fields Mv.Constants.member_fields()
|
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
table "members"
|
table "members"
|
||||||
repo Mv.Repo
|
repo Mv.Repo
|
||||||
|
|
@ -62,7 +58,21 @@ defmodule Mv.Membership.Member do
|
||||||
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
||||||
argument :user, :map, allow_nil?: true
|
argument :user, :map, allow_nil?: true
|
||||||
|
|
||||||
accept @member_fields
|
accept [
|
||||||
|
:first_name,
|
||||||
|
:last_name,
|
||||||
|
:email,
|
||||||
|
:birth_date,
|
||||||
|
:paid,
|
||||||
|
:phone_number,
|
||||||
|
:join_date,
|
||||||
|
:exit_date,
|
||||||
|
:notes,
|
||||||
|
:city,
|
||||||
|
:street,
|
||||||
|
:house_number,
|
||||||
|
:postal_code
|
||||||
|
]
|
||||||
|
|
||||||
change manage_relationship(:custom_field_values, type: :create)
|
change manage_relationship(:custom_field_values, type: :create)
|
||||||
|
|
||||||
|
|
@ -95,7 +105,21 @@ defmodule Mv.Membership.Member do
|
||||||
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
||||||
argument :user, :map, allow_nil?: true
|
argument :user, :map, allow_nil?: true
|
||||||
|
|
||||||
accept @member_fields
|
accept [
|
||||||
|
:first_name,
|
||||||
|
:last_name,
|
||||||
|
:email,
|
||||||
|
:birth_date,
|
||||||
|
:paid,
|
||||||
|
:phone_number,
|
||||||
|
:join_date,
|
||||||
|
:exit_date,
|
||||||
|
:notes,
|
||||||
|
:city,
|
||||||
|
:street,
|
||||||
|
:house_number,
|
||||||
|
:postal_code
|
||||||
|
]
|
||||||
|
|
||||||
change manage_relationship(:custom_field_values, on_match: :update, on_no_match: :create)
|
change manage_relationship(:custom_field_values, on_match: :update, on_no_match: :create)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ defmodule Mv.Membership do
|
||||||
# It's only used internally as fallback in get_settings/0
|
# It's only used internally as fallback in get_settings/0
|
||||||
# Settings should be created via seed script
|
# Settings should be created via seed script
|
||||||
define :update_settings, action: :update
|
define :update_settings, action: :update
|
||||||
define :update_member_field_visibility, action: :update_member_field_visibility
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -124,37 +123,4 @@ defmodule Mv.Membership do
|
||||||
|> Ash.Changeset.for_update(:update, attrs)
|
|> Ash.Changeset.for_update(:update, attrs)
|
||||||
|> Ash.update(domain: __MODULE__)
|
|> Ash.update(domain: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Updates the member field visibility configuration.
|
|
||||||
|
|
||||||
This is a specialized action for updating only the member field visibility settings.
|
|
||||||
It validates that all keys are valid member fields and all values are booleans.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
- `settings` - The settings record to update
|
|
||||||
- `visibility_config` - A map of member field names (strings) to boolean visibility values
|
|
||||||
(e.g., `%{"street" => false, "house_number" => false}`)
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
- `{:ok, updated_settings}` - Successfully updated settings
|
|
||||||
- `{:error, error}` - Validation or update error
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> {:ok, settings} = Mv.Membership.get_settings()
|
|
||||||
iex> {:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{"street" => false, "house_number" => false})
|
|
||||||
iex> updated.member_field_visibility
|
|
||||||
%{"street" => false, "house_number" => false}
|
|
||||||
|
|
||||||
"""
|
|
||||||
def update_member_field_visibility(settings, visibility_config) do
|
|
||||||
settings
|
|
||||||
|> Ash.Changeset.for_update(:update_member_field_visibility, %{
|
|
||||||
member_field_visibility: visibility_config
|
|
||||||
})
|
|
||||||
|> Ash.update(domain: __MODULE__)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ defmodule Mv.Membership.Setting do
|
||||||
|
|
||||||
## Attributes
|
## Attributes
|
||||||
- `club_name` - The name of the association/club (required, cannot be empty)
|
- `club_name` - The name of the association/club (required, cannot be empty)
|
||||||
- `member_field_visibility` - JSONB map storing visibility configuration for member fields
|
|
||||||
(e.g., `%{"street" => false, "house_number" => false}`). Fields not in the map default to `true`.
|
|
||||||
|
|
||||||
## Singleton Pattern
|
## Singleton Pattern
|
||||||
This resource uses a singleton pattern - there should only be one settings record.
|
This resource uses a singleton pattern - there should only be one settings record.
|
||||||
|
|
@ -30,9 +28,6 @@ defmodule Mv.Membership.Setting do
|
||||||
|
|
||||||
# Update club name
|
# Update club name
|
||||||
{:ok, updated} = Mv.Membership.update_settings(settings, %{club_name: "New Name"})
|
{:ok, updated} = Mv.Membership.update_settings(settings, %{club_name: "New Name"})
|
||||||
|
|
||||||
# Update member field visibility
|
|
||||||
{:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{"street" => false, "house_number" => false})
|
|
||||||
"""
|
"""
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
domain: Mv.Membership,
|
domain: Mv.Membership,
|
||||||
|
|
@ -54,65 +49,18 @@ defmodule Mv.Membership.Setting do
|
||||||
# Used only as fallback in get_settings/0 if settings don't exist
|
# Used only as fallback in get_settings/0 if settings don't exist
|
||||||
# Settings should normally be created via seed script
|
# Settings should normally be created via seed script
|
||||||
create :create do
|
create :create do
|
||||||
accept [:club_name, :member_field_visibility]
|
accept [:club_name]
|
||||||
end
|
end
|
||||||
|
|
||||||
update :update do
|
update :update do
|
||||||
primary? true
|
primary? true
|
||||||
require_atomic? false
|
accept [:club_name]
|
||||||
accept [:club_name, :member_field_visibility]
|
|
||||||
end
|
|
||||||
|
|
||||||
update :update_member_field_visibility do
|
|
||||||
description "Updates the visibility configuration for member fields in the overview"
|
|
||||||
require_atomic? false
|
|
||||||
accept [:member_field_visibility]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
validations do
|
validations do
|
||||||
validate present(:club_name), on: [:create, :update]
|
validate present(:club_name), on: [:create, :update]
|
||||||
validate string_length(:club_name, min: 1), on: [:create, :update]
|
validate string_length(:club_name, min: 1), on: [:create, :update]
|
||||||
|
|
||||||
# Validate member_field_visibility map structure and content
|
|
||||||
validate fn changeset, _context ->
|
|
||||||
visibility = Ash.Changeset.get_attribute(changeset, :member_field_visibility)
|
|
||||||
|
|
||||||
if visibility && is_map(visibility) do
|
|
||||||
# Validate all values are booleans
|
|
||||||
invalid_values =
|
|
||||||
Enum.filter(visibility, fn {_key, value} ->
|
|
||||||
not is_boolean(value)
|
|
||||||
end)
|
|
||||||
|
|
||||||
# Validate all keys are valid member fields
|
|
||||||
valid_field_strings = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)
|
|
||||||
|
|
||||||
invalid_keys =
|
|
||||||
Enum.filter(visibility, fn {key, _value} ->
|
|
||||||
key not in valid_field_strings
|
|
||||||
end)
|
|
||||||
|> Enum.map(fn {key, _value} -> key end)
|
|
||||||
|
|
||||||
cond do
|
|
||||||
not Enum.empty?(invalid_values) ->
|
|
||||||
{:error,
|
|
||||||
field: :member_field_visibility,
|
|
||||||
message: "All values in member_field_visibility must be booleans"}
|
|
||||||
|
|
||||||
not Enum.empty?(invalid_keys) ->
|
|
||||||
{:error,
|
|
||||||
field: :member_field_visibility,
|
|
||||||
message: "Invalid member field keys: #{inspect(invalid_keys)}"}
|
|
||||||
|
|
||||||
true ->
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
else
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on: [:create, :update]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
|
|
@ -127,12 +75,6 @@ defmodule Mv.Membership.Setting do
|
||||||
min_length: 1
|
min_length: 1
|
||||||
]
|
]
|
||||||
|
|
||||||
attribute :member_field_visibility, :map,
|
|
||||||
allow_nil?: true,
|
|
||||||
public?: true,
|
|
||||||
description:
|
|
||||||
"Configuration for member field visibility in overview (JSONB map). Keys are member field names (atoms), values are booleans."
|
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
defmodule Mv.Constants do
|
|
||||||
@moduledoc """
|
|
||||||
Module for defining constants and atoms.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@member_fields [
|
|
||||||
:first_name,
|
|
||||||
:last_name,
|
|
||||||
:email,
|
|
||||||
:birth_date,
|
|
||||||
:paid,
|
|
||||||
:phone_number,
|
|
||||||
:join_date,
|
|
||||||
:exit_date,
|
|
||||||
:notes,
|
|
||||||
:city,
|
|
||||||
:street,
|
|
||||||
:house_number,
|
|
||||||
:postal_code
|
|
||||||
]
|
|
||||||
|
|
||||||
def member_fields, do: @member_fields
|
|
||||||
end
|
|
||||||
|
|
@ -30,18 +30,11 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
import Ash.Expr
|
import Ash.Expr
|
||||||
|
|
||||||
alias Mv.Membership
|
|
||||||
alias MvWeb.MemberLive.Index.Formatter
|
alias MvWeb.MemberLive.Index.Formatter
|
||||||
|
|
||||||
# Prefix used in sort field names for custom fields (e.g., "custom_field_<id>")
|
# Prefix used in sort field names for custom fields (e.g., "custom_field_<id>")
|
||||||
@custom_field_prefix "custom_field_"
|
@custom_field_prefix "custom_field_"
|
||||||
|
|
||||||
# Member fields that are loaded for the overview
|
|
||||||
# Uses constants from Mv.Constants to ensure consistency
|
|
||||||
# Note: :id is always included for member identification
|
|
||||||
# All member fields are loaded, but visibility is controlled via settings
|
|
||||||
@overview_fields [:id | Mv.Constants.member_fields()]
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Initializes the LiveView state.
|
Initializes the LiveView state.
|
||||||
|
|
||||||
|
|
@ -60,14 +53,6 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
|> Ash.Query.sort(name: :asc)
|
|> Ash.Query.sort(name: :asc)
|
||||||
|> Ash.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
# Load settings once to avoid N+1 queries
|
|
||||||
settings =
|
|
||||||
case Membership.get_settings() do
|
|
||||||
{:ok, s} -> s
|
|
||||||
# Fallback if settings can't be loaded
|
|
||||||
{:error, _} -> %{member_field_visibility: %{}}
|
|
||||||
end
|
|
||||||
|
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, gettext("Members"))
|
|> assign(:page_title, gettext("Members"))
|
||||||
|
|
@ -77,7 +62,6 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
|> assign(:paid_filter, nil)
|
|> assign(:paid_filter, nil)
|
||||||
|> assign(:selected_members, MapSet.new())
|
|> assign(:selected_members, MapSet.new())
|
||||||
|> assign(:custom_fields_visible, custom_fields_visible)
|
|> assign(:custom_fields_visible, custom_fields_visible)
|
||||||
|> assign(:member_fields_visible, get_visible_member_fields(settings))
|
|
||||||
|
|
||||||
# We call handle params to use the query from the URL
|
# We call handle params to use the query from the URL
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
|
|
@ -432,7 +416,19 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
query =
|
query =
|
||||||
Mv.Membership.Member
|
Mv.Membership.Member
|
||||||
|> Ash.Query.new()
|
|> Ash.Query.new()
|
||||||
|> Ash.Query.select(@overview_fields)
|
|> Ash.Query.select([
|
||||||
|
:id,
|
||||||
|
:first_name,
|
||||||
|
:last_name,
|
||||||
|
:email,
|
||||||
|
:street,
|
||||||
|
:house_number,
|
||||||
|
:postal_code,
|
||||||
|
:city,
|
||||||
|
:phone_number,
|
||||||
|
:join_date,
|
||||||
|
:paid
|
||||||
|
])
|
||||||
|
|
||||||
# Load custom field values for visible custom fields
|
# Load custom field values for visible custom fields
|
||||||
custom_field_ids_list = Enum.map(socket.assigns.custom_fields_visible, & &1.id)
|
custom_field_ids_list = Enum.map(socket.assigns.custom_fields_visible, & &1.id)
|
||||||
|
|
@ -562,13 +558,18 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
defp maybe_sort(query, _, _, _), do: {query, false}
|
defp maybe_sort(query, _, _, _), do: {query, false}
|
||||||
|
|
||||||
# Validate that a field is sortable
|
# Validate that a field is sortable
|
||||||
# Uses member fields from constants, but excludes fields that don't make sense to sort
|
|
||||||
# (e.g., :notes is too long, :paid is boolean and not very useful for sorting)
|
|
||||||
defp valid_sort_field?(field) when is_atom(field) do
|
defp valid_sort_field?(field) when is_atom(field) do
|
||||||
# All member fields are sortable, but we exclude some that don't make sense
|
valid_fields = [
|
||||||
# :id is not in member_fields, but we don't want to sort by it anyway
|
:first_name,
|
||||||
non_sortable_fields = [:notes, :paid]
|
:last_name,
|
||||||
valid_fields = Mv.Constants.member_fields() -- non_sortable_fields
|
:email,
|
||||||
|
:street,
|
||||||
|
:house_number,
|
||||||
|
:postal_code,
|
||||||
|
:city,
|
||||||
|
:phone_number,
|
||||||
|
:join_date
|
||||||
|
]
|
||||||
|
|
||||||
field in valid_fields or custom_field_sort?(field)
|
field in valid_fields or custom_field_sort?(field)
|
||||||
end
|
end
|
||||||
|
|
@ -898,32 +899,4 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
"#{name} <#{member.email}>"
|
"#{name} <#{member.email}>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gets the list of member fields that should be visible in the overview.
|
|
||||||
#
|
|
||||||
# Reads the visibility configuration from Settings and returns only the fields
|
|
||||||
# where show_in_overview is true. Fields not configured in settings default to true.
|
|
||||||
#
|
|
||||||
# Performance: This function uses the already-loaded settings to avoid N+1 queries.
|
|
||||||
# Settings should be loaded once in mount/3 and passed to this function.
|
|
||||||
#
|
|
||||||
# Parameters:
|
|
||||||
# - `settings` - The settings struct loaded from the database
|
|
||||||
#
|
|
||||||
# Returns a list of atoms representing visible member field names.
|
|
||||||
#
|
|
||||||
# Fields are read from the global Constants module.
|
|
||||||
@spec get_visible_member_fields(map()) :: [atom()]
|
|
||||||
defp get_visible_member_fields(settings) do
|
|
||||||
# Get all eligible fields from the global constants
|
|
||||||
all_fields = Mv.Constants.member_fields()
|
|
||||||
|
|
||||||
# JSONB stores keys as strings
|
|
||||||
visibility_config = settings.member_field_visibility || %{}
|
|
||||||
|
|
||||||
# Filter to only return visible fields
|
|
||||||
Enum.filter(all_fields, fn field ->
|
|
||||||
Map.get(visibility_config, Atom.to_string(field), true)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:email in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
@ -115,7 +114,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:street in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
@ -133,7 +131,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:house_number in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
@ -151,7 +148,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:postal_code in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
@ -169,7 +165,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:city in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
@ -187,7 +182,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:phone_number in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
@ -205,7 +199,6 @@
|
||||||
</:col>
|
</:col>
|
||||||
<:col
|
<:col
|
||||||
:let={member}
|
:let={member}
|
||||||
:if={:join_date in @member_fields_visible}
|
|
||||||
label={
|
label={
|
||||||
~H"""
|
~H"""
|
||||||
<.live_component
|
<.live_component
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
defmodule Mv.Repo.Migrations.AddMemberFieldVisibilityToSettings do
|
|
||||||
@moduledoc """
|
|
||||||
Updates resources based on their most recent snapshots.
|
|
||||||
|
|
||||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def up do
|
|
||||||
alter table(:settings) do
|
|
||||||
add :member_field_visibility, :map
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def down do
|
|
||||||
alter table(:settings) do
|
|
||||||
remove :member_field_visibility
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"gen_random_uuid()\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": true,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "id",
|
|
||||||
"type": "uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "name",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "slug",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "value_type",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": true,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "description",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "false",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "immutable",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "false",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "required",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "true",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "show_in_overview",
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"base_filter": null,
|
|
||||||
"check_constraints": [],
|
|
||||||
"custom_indexes": [],
|
|
||||||
"custom_statements": [],
|
|
||||||
"has_create_action": true,
|
|
||||||
"hash": "D31160C95D3D32BA715D493DE2D2B8D6572E0EC68AE14B928D99975BC8A81542",
|
|
||||||
"identities": [
|
|
||||||
{
|
|
||||||
"all_tenants?": false,
|
|
||||||
"base_filter": null,
|
|
||||||
"index_name": "custom_fields_unique_name_index",
|
|
||||||
"keys": [
|
|
||||||
{
|
|
||||||
"type": "atom",
|
|
||||||
"value": "name"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "unique_name",
|
|
||||||
"nils_distinct?": true,
|
|
||||||
"where": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"all_tenants?": false,
|
|
||||||
"base_filter": null,
|
|
||||||
"index_name": "custom_fields_unique_slug_index",
|
|
||||||
"keys": [
|
|
||||||
{
|
|
||||||
"type": "atom",
|
|
||||||
"value": "slug"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "unique_slug",
|
|
||||||
"nils_distinct?": true,
|
|
||||||
"where": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"multitenancy": {
|
|
||||||
"attribute": null,
|
|
||||||
"global": null,
|
|
||||||
"strategy": null
|
|
||||||
},
|
|
||||||
"repo": "Elixir.Mv.Repo",
|
|
||||||
"schema": null,
|
|
||||||
"table": "custom_fields"
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"gen_random_uuid()\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": true,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "id",
|
|
||||||
"type": "uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "club_name",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": true,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "member_field_visibility",
|
|
||||||
"type": "map"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "inserted_at",
|
|
||||||
"type": "utc_datetime_usec"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "updated_at",
|
|
||||||
"type": "utc_datetime_usec"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"base_filter": null,
|
|
||||||
"check_constraints": [],
|
|
||||||
"custom_indexes": [],
|
|
||||||
"custom_statements": [],
|
|
||||||
"has_create_action": true,
|
|
||||||
"hash": "F2823210AA9E6476074A218375F64CD80E7F9E04EECC4E94D4C7FD31A773C016",
|
|
||||||
"identities": [],
|
|
||||||
"multitenancy": {
|
|
||||||
"attribute": null,
|
|
||||||
"global": null,
|
|
||||||
"strategy": null
|
|
||||||
},
|
|
||||||
"repo": "Elixir.Mv.Repo",
|
|
||||||
"schema": null,
|
|
||||||
"table": "settings"
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
defmodule Mv.Membership.MemberFieldVisibilityTest do
|
|
||||||
@moduledoc """
|
|
||||||
Tests for member field visibility configuration.
|
|
||||||
|
|
||||||
Tests cover:
|
|
||||||
- Member fields are visible by default (show_in_overview: true)
|
|
||||||
- Member fields can be hidden (show_in_overview: false)
|
|
||||||
- Checking if a specific field is visible
|
|
||||||
- Configuration is stored in Settings resource
|
|
||||||
"""
|
|
||||||
use Mv.DataCase, async: true
|
|
||||||
|
|
||||||
alias Mv.Membership.Member
|
|
||||||
end
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
defmodule MvWeb.MemberLive.IndexMemberFieldsDisplayTest do
|
|
||||||
use MvWeb.ConnCase, async: true
|
|
||||||
import Phoenix.LiveViewTest
|
|
||||||
require Ash.Query
|
|
||||||
|
|
||||||
alias Mv.Membership.Member
|
|
||||||
|
|
||||||
setup do
|
|
||||||
{:ok, member1} =
|
|
||||||
Member
|
|
||||||
|> Ash.Changeset.for_create(:create_member, %{
|
|
||||||
first_name: "Alice",
|
|
||||||
last_name: "Anderson",
|
|
||||||
email: "alice@example.com",
|
|
||||||
street: "Main Street",
|
|
||||||
house_number: "123",
|
|
||||||
postal_code: "12345",
|
|
||||||
city: "Berlin",
|
|
||||||
phone_number: "+49123456789",
|
|
||||||
join_date: ~D[2020-01-15]
|
|
||||||
})
|
|
||||||
|> Ash.create()
|
|
||||||
|
|
||||||
{:ok, member2} =
|
|
||||||
Member
|
|
||||||
|> Ash.Changeset.for_create(:create_member, %{
|
|
||||||
first_name: "Bob",
|
|
||||||
last_name: "Brown",
|
|
||||||
email: "bob@example.com"
|
|
||||||
})
|
|
||||||
|> Ash.create()
|
|
||||||
|
|
||||||
%{
|
|
||||||
member1: member1,
|
|
||||||
member2: member2
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "shows multiple members correctly", %{conn: conn, member1: m1, member2: m2} do
|
|
||||||
conn = conn_with_oidc_user(conn)
|
|
||||||
{:ok, _view, html} = live(conn, "/members")
|
|
||||||
|
|
||||||
for m <- [m1, m2], field <- [m.first_name, m.last_name, m.email] do
|
|
||||||
assert html =~ field
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "respects show_in_overview config", %{conn: conn, member1: m} do
|
|
||||||
{:ok, settings} = Mv.Membership.get_settings()
|
|
||||||
fields_to_hide = [:street, :house_number]
|
|
||||||
|
|
||||||
{:ok, _} =
|
|
||||||
Mv.Membership.update_settings(settings, %{
|
|
||||||
member_field_visibility: Map.new(fields_to_hide, &{Atom.to_string(&1), false})
|
|
||||||
})
|
|
||||||
|
|
||||||
conn = conn_with_oidc_user(conn)
|
|
||||||
{:ok, _view, html} = live(conn, "/members")
|
|
||||||
|
|
||||||
assert html =~ "Email"
|
|
||||||
assert html =~ m.email
|
|
||||||
refute html =~ m.street
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue