refactor: address review comments for join request settings
This commit is contained in:
parent
05e2a298fe
commit
21812542ad
6 changed files with 29 additions and 15 deletions
|
|
@ -1140,6 +1140,12 @@ let liveSocket = new LiveSocket("/live", Socket, {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Vendor assets (third-party JS):**
|
||||||
|
|
||||||
|
Some JavaScript libraries are committed as vendored files in `assets/vendor/` (e.g. `topbar`, `sortable.js`) when they are not available as npm packages or we need a specific build. Document their origin and how to update them:
|
||||||
|
|
||||||
|
- **Sortable.js** (`assets/vendor/sortable.js`): From [SortableJS](https://github.com/SortableJS/Sortable), version noted in the file header (e.g. `/*! Sortable 1.15.6 - MIT ... */`). To update: download the desired release from the repo and replace the file; keep the header comment for traceability.
|
||||||
|
|
||||||
### 3.8 Code Quality: Credo
|
### 3.8 Code Quality: Credo
|
||||||
|
|
||||||
**Static Code Analysis:**
|
**Static Code Analysis:**
|
||||||
|
|
|
||||||
|
|
@ -219,16 +219,23 @@ Hooks.SortableList = {
|
||||||
|
|
||||||
if (this.grabbedRowId == null) return
|
if (this.grabbedRowId == null) return
|
||||||
|
|
||||||
|
// Do not move into a locked row (e.g. email always first)
|
||||||
if (e.key === "ArrowUp" && idx > 0) {
|
if (e.key === "ArrowUp" && idx > 0) {
|
||||||
|
const targetRow = rows[idx - 1]
|
||||||
|
if (!this.isLocked(targetRow)) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.pushEvent(this.reorderEvent, { from_index: idx, to_index: idx - 1 })
|
this.pushEvent(this.reorderEvent, { from_index: idx, to_index: idx - 1 })
|
||||||
announce(`Position ${idx} of ${total}.`)
|
announce(`Position ${idx} of ${total}.`)
|
||||||
|
}
|
||||||
} else if (e.key === "ArrowDown" && idx < total - 1) {
|
} else if (e.key === "ArrowDown" && idx < total - 1) {
|
||||||
|
const targetRow = rows[idx + 1]
|
||||||
|
if (!this.isLocked(targetRow)) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.pushEvent(this.reorderEvent, { from_index: idx, to_index: idx + 1 })
|
this.pushEvent(this.reorderEvent, { from_index: idx, to_index: idx + 1 })
|
||||||
announce(`Position ${idx + 2} of ${total}.`)
|
announce(`Position ${idx + 2} of ${total}.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.el.addEventListener("keydown", this.handleKeyDown, true)
|
this.el.addEventListener("keydown", this.handleKeyDown, true)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ defmodule Mv.Membership.Setting do
|
||||||
domain: Mv.Membership,
|
domain: Mv.Membership,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
|
# Used in join_form_field_ids validation (compile-time to avoid recompiling regex and list on every validation)
|
||||||
|
@uuid_pattern ~r/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
||||||
|
@valid_join_form_member_fields Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
table "settings"
|
table "settings"
|
||||||
repo Mv.Repo
|
repo Mv.Repo
|
||||||
|
|
@ -255,16 +259,10 @@ defmodule Mv.Membership.Setting do
|
||||||
field_ids = Ash.Changeset.get_attribute(changeset, :join_form_field_ids)
|
field_ids = Ash.Changeset.get_attribute(changeset, :join_form_field_ids)
|
||||||
|
|
||||||
if is_list(field_ids) and field_ids != [] do
|
if is_list(field_ids) and field_ids != [] do
|
||||||
valid_member_fields =
|
|
||||||
Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)
|
|
||||||
|
|
||||||
uuid_pattern =
|
|
||||||
~r/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
||||||
|
|
||||||
invalid_ids =
|
invalid_ids =
|
||||||
Enum.reject(field_ids, fn id ->
|
Enum.reject(field_ids, fn id ->
|
||||||
is_binary(id) and
|
is_binary(id) and
|
||||||
(id in valid_member_fields or Regex.match?(uuid_pattern, id))
|
(id in @valid_join_form_member_fields or Regex.match?(@uuid_pattern, id))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if Enum.empty?(invalid_ids) do
|
if Enum.empty?(invalid_ids) do
|
||||||
|
|
@ -442,6 +440,7 @@ defmodule Mv.Membership.Setting do
|
||||||
|
|
||||||
attribute :join_form_field_ids, {:array, :string} do
|
attribute :join_form_field_ids, {:array, :string} do
|
||||||
allow_nil? true
|
allow_nil? true
|
||||||
|
default []
|
||||||
public? true
|
public? true
|
||||||
|
|
||||||
description "Ordered list of field IDs shown on the join form. Each entry is a member field name (e.g. 'email') or a custom field UUID. Email is always present after normalization."
|
description "Ordered list of field IDs shown on the join form. Each entry is a member field name (e.g. 'email') or a custom field UUID. Email is always present after normalization."
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ defmodule Mv.Membership.Setting.Changes.NormalizeJoinFormSettings do
|
||||||
base = if is_map(required_config), do: required_config, else: %{}
|
base = if is_map(required_config), do: required_config, else: %{}
|
||||||
|
|
||||||
base
|
base
|
||||||
|> Map.filter(fn {key, _} -> key in field_ids end)
|
|> Map.take(field_ids)
|
||||||
|> Map.put("email", true)
|
|> Map.put("email", true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -990,7 +990,7 @@ defmodule MvWeb.CoreComponents do
|
||||||
/>
|
/>
|
||||||
</th>
|
</th>
|
||||||
<th :if={@action != []} class={table_th_sticky_class(@sticky_header)}>
|
<th :if={@action != []} class={table_th_sticky_class(@sticky_header)}>
|
||||||
{gettext("Actions")}
|
<span class="sr-only">{gettext("Actions")}</span>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ defmodule Mv.Membership.SettingJoinFormTest do
|
||||||
- `Mv.Membership.get_join_form_allowlist/0` is implemented and returns the allowlist
|
- `Mv.Membership.get_join_form_allowlist/0` is implemented and returns the allowlist
|
||||||
for the public join form (subtask 4).
|
for the public join form (subtask 4).
|
||||||
"""
|
"""
|
||||||
|
# Settings is a singleton; tests mutate shared DB state. We use async: false and on_exit to restore
|
||||||
|
# original values because Ecto Sandbox transaction rollback does not apply to this singleton pattern.
|
||||||
use Mv.DataCase, async: false
|
use Mv.DataCase, async: false
|
||||||
|
|
||||||
alias Mv.Constants
|
alias Mv.Constants
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue