chore: update docs
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Moritz 2026-01-13 17:20:15 +01:00 committed by moritz
parent f7f25ad69a
commit 55401eda3a
19 changed files with 732 additions and 499 deletions

View file

@ -83,7 +83,18 @@ lib/
│ ├── member.ex # Member resource
│ ├── custom_field_value.ex # Custom field value resource
│ ├── custom_field.ex # CustomFieldValue type resource
│ ├── setting.ex # Global settings (singleton resource)
│ └── email.ex # Email custom type
├── membership_fees/ # MembershipFees domain
│ ├── membership_fees.ex # Domain definition
│ ├── membership_fee_type.ex # Membership fee type resource
│ ├── membership_fee_cycle.ex # Membership fee cycle resource
│ └── changes/ # Ash changes for membership fees
├── mv/authorization/ # Authorization domain
│ ├── authorization.ex # Domain definition
│ ├── role.ex # Role resource
│ ├── permission_sets.ex # Hardcoded permission sets
│ └── checks/ # Authorization checks
├── mv/ # Core application modules
│ ├── accounts/ # Domain-specific logic
│ │ └── user/
@ -107,7 +118,7 @@ lib/
│ │ ├── table_components.ex
│ │ ├── layouts.ex
│ │ └── layouts/ # Layout templates
│ │ ├── navbar.ex
│ │ ├── sidebar.ex
│ │ └── root.html.heex
│ ├── controllers/ # HTTP controllers
│ │ ├── auth_controller.ex
@ -123,7 +134,12 @@ lib/
│ │ ├── member_live/ # Member CRUD LiveViews
│ │ ├── custom_field_value_live/ # CustomFieldValue CRUD LiveViews
│ │ ├── custom_field_live/
│ │ └── user_live/ # User management LiveViews
│ │ ├── user_live/ # User management LiveViews
│ │ ├── role_live/ # Role management LiveViews
│ │ ├── membership_fee_type_live/ # Membership fee type LiveViews
│ │ ├── membership_fee_settings_live.ex # Membership fee settings
│ │ ├── global_settings_live.ex # Global settings
│ │ └── contribution_type_live/ # Contribution types (mock-up)
│ ├── auth_overrides.ex # AshAuthentication overrides
│ ├── endpoint.ex # Phoenix endpoint
│ ├── gettext.ex # I18n configuration
@ -818,14 +834,17 @@ end
```heex
<!-- Leverage DaisyUI component classes -->
<div class="navbar bg-base-100">
<div class="navbar-start">
<a class="btn btn-ghost text-xl">Mila</a>
<!-- Note: Navbar has been replaced with Sidebar (see lib/mv_web/components/layouts/sidebar.ex) -->
<div class="drawer lg:drawer-open">
<input id="drawer-toggle" type="checkbox" class="drawer-toggle" />
<div class="drawer-content">
<!-- Page content -->
</div>
<div class="navbar-end">
<.link navigate={~p"/members"} class="btn btn-primary">
Members
</.link>
<div class="drawer-side">
<label for="drawer-toggle" class="drawer-overlay"></label>
<aside class="w-64 min-h-full bg-base-200">
<!-- Sidebar content -->
</aside>
</div>
</div>
```
@ -1535,15 +1554,57 @@ policies do
authorize_if always()
end
# Specific permissions
policy action_type([:read, :update]) do
authorize_if relates_to_actor_via(:user)
# Use HasPermission check for role-based authorization
policy action_type([:read, :update, :create, :destroy]) do
authorize_if Mv.Authorization.Checks.HasPermission
end
end
```
policy action_type(:destroy) do
authorize_if actor_attribute_equals(:role, :admin)
**Actor Handling in LiveViews:**
Always use the `current_actor/1` helper for consistent actor access:
```elixir
# In LiveView modules
import MvWeb.LiveHelpers, only: [current_actor: 1, ash_actor_opts: 1, submit_form: 3]
def mount(_params, _session, socket) do
actor = current_actor(socket)
case Ash.read(Mv.Membership.Member, ash_actor_opts(actor)) do
{:ok, members} ->
{:ok, assign(socket, :members, members)}
{:error, error} ->
{:ok, put_flash(socket, :error, "Failed to load members")}
end
end
def handle_event("save", %{"member" => params}, socket) do
actor = current_actor(socket)
form = AshPhoenix.Form.for_create(Mv.Membership.Member, :create)
case submit_form(form, params, actor) do
{:ok, member} ->
{:noreply, push_navigate(socket, to: ~p"/members/#{member.id}")}
{:error, form} ->
{:noreply, assign(socket, :form, form)}
end
end
```
**Never use bang calls (`Ash.read!`, `Ash.get!`) without error handling:**
```elixir
# Bad - will crash on authorization errors
members = Ash.read!(Mv.Membership.Member, actor: actor)
# Good - proper error handling
case Ash.read(Mv.Membership.Member, actor: actor) do
{:ok, members} -> # success
{:error, %Ash.Error.Forbidden{}} -> # handle authorization error
{:error, error} -> # handle other errors
end
```
### 5.2 Password Security