This commit is contained in:
parent
e9bcfe4fa6
commit
913d67b978
19 changed files with 732 additions and 499 deletions
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -8,6 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- **Roles and Permissions System (RBAC)** - Complete implementation (#345, 2026-01-08)
|
||||||
|
- Four hardcoded permission sets: `own_data`, `read_only`, `normal_user`, `admin`
|
||||||
|
- Database-backed roles with permission set references
|
||||||
|
- Member resource policies with scope filtering (`:own`, `:linked`, `:all`)
|
||||||
|
- Authorization checks via `Mv.Authorization.Checks.HasPermission`
|
||||||
|
- System role protection (critical roles cannot be deleted)
|
||||||
|
- Role management UI at `/admin/roles`
|
||||||
|
- **Membership Fees System** - Full implementation
|
||||||
|
- Membership fee types with intervals (monthly, quarterly, half_yearly, yearly)
|
||||||
|
- Individual billing cycles per member with payment status tracking
|
||||||
|
- Cycle generation and regeneration
|
||||||
|
- Global membership fee settings
|
||||||
|
- UI components for fee management
|
||||||
|
- **Global Settings Management** - Singleton settings resource
|
||||||
|
- Club name configuration (with environment variable support)
|
||||||
|
- Member field visibility settings
|
||||||
|
- Membership fee default settings
|
||||||
|
- **Sidebar Navigation** - Replaced navbar with standard-compliant sidebar (#260, 2026-01-12)
|
||||||
|
- **CSV Import Templates** - German and English templates (#329, 2026-01-13)
|
||||||
|
- Template files in `priv/static/templates/`
|
||||||
|
- CSV specification documented
|
||||||
- User-Member linking with fuzzy search autocomplete (#168)
|
- User-Member linking with fuzzy search autocomplete (#168)
|
||||||
- PostgreSQL trigram-based member search with typo tolerance
|
- PostgreSQL trigram-based member search with typo tolerance
|
||||||
- WCAG 2.1 AA compliant autocomplete dropdown with ARIA support
|
- WCAG 2.1 AA compliant autocomplete dropdown with ARIA support
|
||||||
|
|
@ -19,8 +40,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- German/English translations
|
- German/English translations
|
||||||
- Docker secrets support via `_FILE` environment variables for all sensitive configuration (SECRET_KEY_BASE, TOKEN_SIGNING_SECRET, OIDC_CLIENT_SECRET, DATABASE_URL, DATABASE_PASSWORD)
|
- Docker secrets support via `_FILE` environment variables for all sensitive configuration (SECRET_KEY_BASE, TOKEN_SIGNING_SECRET, OIDC_CLIENT_SECRET, DATABASE_URL, DATABASE_PASSWORD)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Actor Handling Refactoring** (2026-01-09)
|
||||||
|
- Standardized actor access with `current_actor/1` helper function
|
||||||
|
- `ash_actor_opts/1` helper for consistent authorization options
|
||||||
|
- `submit_form/3` wrapper for form submissions with actor
|
||||||
|
- All Ash operations now properly pass `actor` parameter
|
||||||
|
- **Error Handling Improvements** (2026-01-13)
|
||||||
|
- Replaced `Ash.read!` with proper error handling in LiveViews
|
||||||
|
- Consistent flash message handling for authorization errors
|
||||||
|
- Early return patterns for unauthenticated users
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Email validation false positive when linking user and member with identical emails (#168 Problem #4)
|
- Email validation false positive when linking user and member with identical emails (#168 Problem #4)
|
||||||
- Relationship data extraction from Ash manage_relationship during validation
|
- Relationship data extraction from Ash manage_relationship during validation
|
||||||
- Copy button count now shows only visible selected members when filtering
|
- Copy button count now shows only visible selected members when filtering
|
||||||
|
- Language headers in German `.po` files (corrected from "en" to "de")
|
||||||
|
- Critical deny-filter bug in authorization system (2026-01-08)
|
||||||
|
- HasPermission auto_filter and strict_check implementation (2026-01-08)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,18 @@ lib/
|
||||||
│ ├── member.ex # Member resource
|
│ ├── member.ex # Member resource
|
||||||
│ ├── custom_field_value.ex # Custom field value resource
|
│ ├── custom_field_value.ex # Custom field value resource
|
||||||
│ ├── custom_field.ex # CustomFieldValue type resource
|
│ ├── custom_field.ex # CustomFieldValue type resource
|
||||||
|
│ ├── setting.ex # Global settings (singleton resource)
|
||||||
│ └── email.ex # Email custom type
|
│ └── 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
|
├── mv/ # Core application modules
|
||||||
│ ├── accounts/ # Domain-specific logic
|
│ ├── accounts/ # Domain-specific logic
|
||||||
│ │ └── user/
|
│ │ └── user/
|
||||||
|
|
@ -107,7 +118,7 @@ lib/
|
||||||
│ │ ├── table_components.ex
|
│ │ ├── table_components.ex
|
||||||
│ │ ├── layouts.ex
|
│ │ ├── layouts.ex
|
||||||
│ │ └── layouts/ # Layout templates
|
│ │ └── layouts/ # Layout templates
|
||||||
│ │ ├── navbar.ex
|
│ │ ├── sidebar.ex
|
||||||
│ │ └── root.html.heex
|
│ │ └── root.html.heex
|
||||||
│ ├── controllers/ # HTTP controllers
|
│ ├── controllers/ # HTTP controllers
|
||||||
│ │ ├── auth_controller.ex
|
│ │ ├── auth_controller.ex
|
||||||
|
|
@ -123,7 +134,12 @@ lib/
|
||||||
│ │ ├── member_live/ # Member CRUD LiveViews
|
│ │ ├── member_live/ # Member CRUD LiveViews
|
||||||
│ │ ├── custom_field_value_live/ # CustomFieldValue CRUD LiveViews
|
│ │ ├── custom_field_value_live/ # CustomFieldValue CRUD LiveViews
|
||||||
│ │ ├── custom_field_live/
|
│ │ ├── 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
|
│ ├── auth_overrides.ex # AshAuthentication overrides
|
||||||
│ ├── endpoint.ex # Phoenix endpoint
|
│ ├── endpoint.ex # Phoenix endpoint
|
||||||
│ ├── gettext.ex # I18n configuration
|
│ ├── gettext.ex # I18n configuration
|
||||||
|
|
@ -818,14 +834,17 @@ end
|
||||||
|
|
||||||
```heex
|
```heex
|
||||||
<!-- Leverage DaisyUI component classes -->
|
<!-- Leverage DaisyUI component classes -->
|
||||||
<div class="navbar bg-base-100">
|
<!-- Note: Navbar has been replaced with Sidebar (see lib/mv_web/components/layouts/sidebar.ex) -->
|
||||||
<div class="navbar-start">
|
<div class="drawer lg:drawer-open">
|
||||||
<a class="btn btn-ghost text-xl">Mila</a>
|
<input id="drawer-toggle" type="checkbox" class="drawer-toggle" />
|
||||||
|
<div class="drawer-content">
|
||||||
|
<!-- Page content -->
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-end">
|
<div class="drawer-side">
|
||||||
<.link navigate={~p"/members"} class="btn btn-primary">
|
<label for="drawer-toggle" class="drawer-overlay"></label>
|
||||||
Members
|
<aside class="w-64 min-h-full bg-base-200">
|
||||||
</.link>
|
<!-- Sidebar content -->
|
||||||
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
@ -1535,15 +1554,57 @@ policies do
|
||||||
authorize_if always()
|
authorize_if always()
|
||||||
end
|
end
|
||||||
|
|
||||||
# Specific permissions
|
# Use HasPermission check for role-based authorization
|
||||||
policy action_type([:read, :update]) do
|
policy action_type([:read, :update, :create, :destroy]) do
|
||||||
authorize_if relates_to_actor_via(:user)
|
authorize_if Mv.Authorization.Checks.HasPermission
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
policy action_type(:destroy) do
|
**Actor Handling in LiveViews:**
|
||||||
authorize_if actor_attribute_equals(:role, :admin)
|
|
||||||
|
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
|
||||||
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
|
### 5.2 Password Security
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -40,14 +40,16 @@ Our philosophy: **software should help people spend less time on administration
|
||||||
## 🔑 Features
|
## 🔑 Features
|
||||||
|
|
||||||
- ✅ Manage member data with ease
|
- ✅ Manage member data with ease
|
||||||
- 🚧 Overview of membership fees & payment status
|
- ✅ Membership fees & payment status tracking
|
||||||
- ✅ Full-text search
|
- ✅ Full-text search with fuzzy matching
|
||||||
- 🚧 Sorting & filtering
|
- ✅ Sorting & filtering
|
||||||
- 🚧 Roles & permissions (e.g. board, treasurer)
|
- ✅ Roles & permissions (RBAC system with 4 permission sets)
|
||||||
- ✅ Custom fields (flexible per club needs)
|
- ✅ Custom fields (flexible per club needs)
|
||||||
- ✅ SSO via OIDC (works with Authentik, Rauthy, Keycloak, etc.)
|
- ✅ SSO via OIDC (works with Authentik, Rauthy, Keycloak, etc.)
|
||||||
|
- ✅ Sidebar navigation (standard-compliant, accessible)
|
||||||
|
- ✅ Global settings management
|
||||||
- 🚧 Self-service & online application
|
- 🚧 Self-service & online application
|
||||||
- 🚧 Accessibility, GDPR, usability improvements
|
- ✅ Accessibility improvements (WCAG 2.1 AA compliant keyboard navigation)
|
||||||
- 🚧 Email sending
|
- 🚧 Email sending
|
||||||
|
|
||||||
## 🚀 Quick Start (Development)
|
## 🚀 Quick Start (Development)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
**Version:** 1.0
|
**Version:** 1.0
|
||||||
**Date:** 2025-01-XX
|
**Date:** 2025-01-XX
|
||||||
**Status:** Ready for Implementation
|
**Last Updated:** 2026-01-13
|
||||||
|
**Status:** Templates Created - Import Logic Pending
|
||||||
**Related Documents:**
|
**Related Documents:**
|
||||||
- [Feature Roadmap](./feature-roadmap.md) - Overall feature planning
|
- [Feature Roadmap](./feature-roadmap.md) - Overall feature planning
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ This document provides a comprehensive overview of the Mila Membership Managemen
|
||||||
|
|
||||||
| Metric | Count |
|
| Metric | Count |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
| **Tables** | 5 |
|
| **Tables** | 9 |
|
||||||
| **Domains** | 2 (Accounts, Membership) |
|
| **Domains** | 4 (Accounts, Membership, MembershipFees, Authorization) |
|
||||||
| **Relationships** | 3 |
|
| **Relationships** | 7 |
|
||||||
| **Indexes** | 15+ |
|
| **Indexes** | 20+ |
|
||||||
| **Triggers** | 1 (Full-text search) |
|
| **Triggers** | 1 (Full-text search) |
|
||||||
|
|
||||||
## Tables Overview
|
## Tables Overview
|
||||||
|
|
@ -68,16 +68,39 @@ This document provides a comprehensive overview of the Mila Membership Managemen
|
||||||
- Immutable and required flags
|
- Immutable and required flags
|
||||||
- Centralized custom field management
|
- Centralized custom field management
|
||||||
|
|
||||||
|
#### `settings`
|
||||||
|
- **Purpose:** Global application settings (singleton resource)
|
||||||
|
- **Rows (Estimated):** 1 (singleton pattern)
|
||||||
|
- **Key Features:**
|
||||||
|
- Club name configuration
|
||||||
|
- Member field visibility settings
|
||||||
|
- Membership fee default settings
|
||||||
|
- Environment variable support for club name
|
||||||
|
|
||||||
|
### Authorization Domain
|
||||||
|
|
||||||
|
#### `roles`
|
||||||
|
- **Purpose:** Role-based access control (RBAC)
|
||||||
|
- **Rows (Estimated):** Low (typically 3-10 roles)
|
||||||
|
- **Key Features:**
|
||||||
|
- Links users to permission sets
|
||||||
|
- System role protection
|
||||||
|
- Four hardcoded permission sets: own_data, read_only, normal_user, admin
|
||||||
|
|
||||||
## Key Relationships
|
## Key Relationships
|
||||||
|
|
||||||
```
|
```
|
||||||
User (0..1) ←→ (0..1) Member
|
User (0..1) ←→ (0..1) Member
|
||||||
↓
|
↓ ↓
|
||||||
Tokens (N)
|
Tokens (N) CustomFieldValues (N)
|
||||||
|
↓ ↓
|
||||||
|
Role (N:1) CustomField (1)
|
||||||
|
|
||||||
Member (1) → (N) Properties
|
Member (1) → (N) MembershipFeeCycles
|
||||||
↓
|
↓
|
||||||
CustomField (1)
|
MembershipFeeType (1)
|
||||||
|
|
||||||
|
Settings (1) → MembershipFeeType (0..1)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Relationship Details
|
### Relationship Details
|
||||||
|
|
@ -89,16 +112,39 @@ Member (1) → (N) Properties
|
||||||
- Email synchronization when linked (User.email is source of truth)
|
- Email synchronization when linked (User.email is source of truth)
|
||||||
- `ON DELETE SET NULL` on user side (User preserved when Member deleted)
|
- `ON DELETE SET NULL` on user side (User preserved when Member deleted)
|
||||||
|
|
||||||
2. **Member → Properties (1:N)**
|
2. **User → Role (N:1)**
|
||||||
|
- Many users can be assigned to one role
|
||||||
|
- `ON DELETE RESTRICT` - cannot delete role if users are assigned
|
||||||
|
- Role links user to permission set for authorization
|
||||||
|
|
||||||
|
3. **Member → CustomFieldValues (1:N)**
|
||||||
- One member, many custom_field_values
|
- One member, many custom_field_values
|
||||||
- `ON DELETE CASCADE` - custom_field_values deleted with member
|
- `ON DELETE CASCADE` - custom_field_values deleted with member
|
||||||
- Composite unique constraint (member_id, custom_field_id)
|
- Composite unique constraint (member_id, custom_field_id)
|
||||||
|
|
||||||
3. **CustomFieldValue → CustomField (N:1)**
|
4. **CustomFieldValue → CustomField (N:1)**
|
||||||
- Properties reference type definition
|
- Custom field values reference type definition
|
||||||
- `ON DELETE RESTRICT` - cannot delete type if in use
|
- `ON DELETE RESTRICT` - cannot delete type if in use
|
||||||
- Type defines data structure
|
- Type defines data structure
|
||||||
|
|
||||||
|
5. **Member → MembershipFeeType (N:1, optional)**
|
||||||
|
- Many members can be assigned to one fee type
|
||||||
|
- `ON DELETE RESTRICT` - cannot delete fee type if members are assigned
|
||||||
|
- Optional relationship (member can have no fee type)
|
||||||
|
|
||||||
|
6. **Member → MembershipFeeCycles (1:N)**
|
||||||
|
- One member, many billing cycles
|
||||||
|
- `ON DELETE CASCADE` - cycles deleted when member deleted
|
||||||
|
- Unique constraint (member_id, cycle_start)
|
||||||
|
|
||||||
|
7. **MembershipFeeCycle → MembershipFeeType (N:1)**
|
||||||
|
- Many cycles reference one fee type
|
||||||
|
- `ON DELETE RESTRICT` - cannot delete fee type if cycles exist
|
||||||
|
|
||||||
|
8. **Settings → MembershipFeeType (N:1, optional)**
|
||||||
|
- Settings can reference a default fee type
|
||||||
|
- `ON DELETE SET NULL` - if fee type is deleted, setting is cleared
|
||||||
|
|
||||||
## Important Business Rules
|
## Important Business Rules
|
||||||
|
|
||||||
### Email Synchronization
|
### Email Synchronization
|
||||||
|
|
@ -464,7 +510,7 @@ mix run priv/repo/seeds.exs
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated:** 2025-11-13
|
**Last Updated:** 2026-01-13
|
||||||
**Schema Version:** 1.1
|
**Schema Version:** 1.4
|
||||||
**Database:** PostgreSQL 17.6 (dev) / 16 (prod)
|
**Database:** PostgreSQL 17.6 (dev) / 16 (prod)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
// - https://dbdocs.io
|
// - https://dbdocs.io
|
||||||
// - VS Code Extensions: "DBML Language" or "dbdiagram.io"
|
// - VS Code Extensions: "DBML Language" or "dbdiagram.io"
|
||||||
//
|
//
|
||||||
// Version: 1.3
|
// Version: 1.4
|
||||||
// Last Updated: 2025-12-11
|
// Last Updated: 2026-01-13
|
||||||
|
|
||||||
Project mila_membership_management {
|
Project mila_membership_management {
|
||||||
database_type: 'PostgreSQL'
|
database_type: 'PostgreSQL'
|
||||||
|
|
@ -28,6 +28,7 @@ Project mila_membership_management {
|
||||||
- **Accounts**: User authentication and session management
|
- **Accounts**: User authentication and session management
|
||||||
- **Membership**: Club member data and custom fields
|
- **Membership**: Club member data and custom fields
|
||||||
- **MembershipFees**: Membership fee types and billing cycles
|
- **MembershipFees**: Membership fee types and billing cycles
|
||||||
|
- **Authorization**: Role-based access control (RBAC)
|
||||||
|
|
||||||
## Required PostgreSQL Extensions:
|
## Required PostgreSQL Extensions:
|
||||||
- uuid-ossp (UUID generation)
|
- uuid-ossp (UUID generation)
|
||||||
|
|
@ -500,3 +501,138 @@ TableGroup membership_fees_domain {
|
||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// AUTHORIZATION DOMAIN
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
Table roles {
|
||||||
|
id uuid [pk, not null, default: `uuid_generate_v7()`, note: 'UUIDv7 primary key']
|
||||||
|
name text [not null, unique, note: 'Unique role name (e.g., "Vorstand", "Admin", "Mitglied")']
|
||||||
|
description text [null, note: 'Human-readable description of the role']
|
||||||
|
permission_set_name text [not null, note: 'Permission set name: "own_data", "read_only", "normal_user", or "admin"']
|
||||||
|
is_system_role boolean [not null, default: false, note: 'If true, role cannot be deleted (protects critical roles)']
|
||||||
|
inserted_at timestamp [not null, default: `now() AT TIME ZONE 'utc'`, note: 'Creation timestamp (UTC)']
|
||||||
|
updated_at timestamp [not null, default: `now() AT TIME ZONE 'utc'`, note: 'Last update timestamp (UTC)']
|
||||||
|
|
||||||
|
indexes {
|
||||||
|
name [unique, name: 'roles_unique_name_index']
|
||||||
|
}
|
||||||
|
|
||||||
|
Note: '''
|
||||||
|
**Role-Based Access Control (RBAC)**
|
||||||
|
|
||||||
|
Roles link users to permission sets. Each role references one of four hardcoded
|
||||||
|
permission sets defined in the application code.
|
||||||
|
|
||||||
|
**Permission Sets:**
|
||||||
|
- `own_data`: Users can only access their own linked member data
|
||||||
|
- `read_only`: Users can read all data but cannot modify
|
||||||
|
- `normal_user`: Users can read and modify most data (standard permissions)
|
||||||
|
- `admin`: Full access to all features and settings
|
||||||
|
|
||||||
|
**System Roles:**
|
||||||
|
- System roles (is_system_role = true) cannot be deleted
|
||||||
|
- Protects critical roles like "Mitglied" (member) from accidental deletion
|
||||||
|
- Only set via seed scripts or internal actions
|
||||||
|
|
||||||
|
**Relationships:**
|
||||||
|
- 1:N with users - users assigned to this role
|
||||||
|
- ON DELETE RESTRICT: Cannot delete role if users are assigned
|
||||||
|
|
||||||
|
**Constraints:**
|
||||||
|
- `name` must be unique
|
||||||
|
- `permission_set_name` must be a valid permission set (validated in application)
|
||||||
|
- System roles cannot be deleted (enforced via validation)
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// MEMBERSHIP DOMAIN (Additional Tables)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
Table settings {
|
||||||
|
id uuid [pk, not null, default: `gen_random_uuid()`, note: 'Primary identifier']
|
||||||
|
club_name text [not null, note: 'The name of the association/club (min length: 1)']
|
||||||
|
member_field_visibility jsonb [null, note: 'Visibility configuration for member fields in overview (JSONB map)']
|
||||||
|
include_joining_cycle boolean [not null, default: true, note: 'Whether to include the joining cycle in membership fee generation']
|
||||||
|
default_membership_fee_type_id uuid [null, note: 'FK to membership_fee_types - default fee type for new members']
|
||||||
|
inserted_at timestamp [not null, default: `now() AT TIME ZONE 'utc'`, note: 'Creation timestamp (UTC)']
|
||||||
|
updated_at timestamp [not null, default: `now() AT TIME ZONE 'utc'`, note: 'Last update timestamp (UTC)']
|
||||||
|
|
||||||
|
indexes {
|
||||||
|
default_membership_fee_type_id [name: 'settings_default_membership_fee_type_id_index', note: 'B-tree index for fee type lookups']
|
||||||
|
}
|
||||||
|
|
||||||
|
Note: '''
|
||||||
|
**Global Application Settings (Singleton Resource)**
|
||||||
|
|
||||||
|
Stores global configuration for the association/club. There should only ever
|
||||||
|
be one settings record in the database (singleton pattern).
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `club_name`: The name of the association/club (required, can be set via ASSOCIATION_NAME env var)
|
||||||
|
- `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`.
|
||||||
|
- `include_joining_cycle`: When true, members pay from their joining cycle. When false,
|
||||||
|
they pay from the next full cycle after joining.
|
||||||
|
- `default_membership_fee_type_id`: The membership fee type automatically assigned to
|
||||||
|
new members. Can be nil if no default is set.
|
||||||
|
|
||||||
|
**Singleton Pattern:**
|
||||||
|
- Only one settings record should exist
|
||||||
|
- Designed to be read and updated, not created/destroyed via normal CRUD
|
||||||
|
- Initial settings should be seeded
|
||||||
|
|
||||||
|
**Environment Variable Support:**
|
||||||
|
- `club_name` can be set via `ASSOCIATION_NAME` environment variable
|
||||||
|
- Database values always take precedence over environment variables
|
||||||
|
|
||||||
|
**Relationships:**
|
||||||
|
- Optional N:1 with membership_fee_types - default fee type for new members
|
||||||
|
- ON DELETE SET NULL: If default fee type is deleted, setting is cleared
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// RELATIONSHIPS (Additional)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// User → Role (N:1)
|
||||||
|
// - Many users can be assigned to one role
|
||||||
|
// - ON DELETE RESTRICT: Cannot delete role if users are assigned
|
||||||
|
Ref: users.role_id > roles.id [delete: restrict]
|
||||||
|
|
||||||
|
// Settings → MembershipFeeType (N:1, optional)
|
||||||
|
// - Settings can reference a default membership fee type
|
||||||
|
// - ON DELETE SET NULL: If fee type is deleted, setting is cleared
|
||||||
|
Ref: settings.default_membership_fee_type_id > membership_fee_types.id [delete: set null]
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// TABLE GROUPS (Updated)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
TableGroup authorization_domain {
|
||||||
|
roles
|
||||||
|
|
||||||
|
Note: '''
|
||||||
|
**Authorization Domain**
|
||||||
|
|
||||||
|
Handles role-based access control (RBAC) with hardcoded permission sets.
|
||||||
|
Roles link users to permission sets for authorization.
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
|
||||||
|
TableGroup membership_domain {
|
||||||
|
members
|
||||||
|
custom_field_values
|
||||||
|
custom_fields
|
||||||
|
settings
|
||||||
|
|
||||||
|
Note: '''
|
||||||
|
**Membership Domain**
|
||||||
|
|
||||||
|
Core business logic for club membership management.
|
||||||
|
Supports flexible, extensible member data model.
|
||||||
|
Includes global application settings (singleton).
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1570,15 +1570,19 @@ This project demonstrates a modern Phoenix application built with:
|
||||||
- ✅ **Flexible data model** (EAV pattern with union types)
|
- ✅ **Flexible data model** (EAV pattern with union types)
|
||||||
|
|
||||||
**Key Achievements:**
|
**Key Achievements:**
|
||||||
- 🎯 8 sprints completed
|
- 🎯 9+ sprints completed
|
||||||
- 🚀 82 pull requests merged
|
- 🚀 100+ pull requests merged
|
||||||
- ✅ Core features implemented (CRUD, search, auth, sync)
|
- ✅ Core features implemented (CRUD, search, auth, sync, membership fees, roles & permissions)
|
||||||
|
- ✅ Membership fees system (types, cycles, settings)
|
||||||
|
- ✅ Role-based access control (RBAC) with 4 permission sets
|
||||||
|
- ✅ Member field visibility settings
|
||||||
|
- ✅ Sidebar navigation (WCAG 2.1 AA compliant)
|
||||||
- 📚 Comprehensive documentation
|
- 📚 Comprehensive documentation
|
||||||
- 🔒 Security-focused (audits, validations, policies)
|
- 🔒 Security-focused (audits, validations, policies)
|
||||||
- 🐳 Docker-ready for self-hosting
|
- 🐳 Docker-ready for self-hosting
|
||||||
|
|
||||||
**Next Steps:**
|
**Next Steps:**
|
||||||
- Implement roles & permissions
|
- ✅ ~~Implement roles & permissions~~ - RBAC system implemented (2026-01-08)
|
||||||
- Add payment tracking
|
- Add payment tracking
|
||||||
- ✅ ~~Improve accessibility (WCAG 2.1 AA)~~ - Keyboard navigation implemented
|
- ✅ ~~Improve accessibility (WCAG 2.1 AA)~~ - Keyboard navigation implemented
|
||||||
- Member self-service portal
|
- Member self-service portal
|
||||||
|
|
@ -1586,8 +1590,150 @@ This project demonstrates a modern Phoenix application built with:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Document Version:** 1.3
|
## Recent Updates (2025-12-02 to 2026-01-13)
|
||||||
**Last Updated:** 2025-12-02
|
|
||||||
|
### Membership Fees System Implementation (2025-12-11 to 2025-12-26)
|
||||||
|
|
||||||
|
**PR #283:** *Membership Fee - Database Schema & Ash Domain Foundation* (closes #275)
|
||||||
|
- Created `Mv.MembershipFees` domain
|
||||||
|
- Added `MembershipFeeType` resource with intervals (monthly, quarterly, half_yearly, yearly)
|
||||||
|
- Added `MembershipFeeCycle` resource for individual billing cycles
|
||||||
|
- Database migrations for membership fee tables
|
||||||
|
|
||||||
|
**PR #284:** *Calendar Cycle Calculation Logic* (closes #276)
|
||||||
|
- Calendar-based cycle calculation module
|
||||||
|
- Support for different intervals
|
||||||
|
- Cycle start/end date calculations
|
||||||
|
- Integration with member joining dates
|
||||||
|
|
||||||
|
**PR #290:** *Cycle Generation System* (closes #277)
|
||||||
|
- Automatic cycle generation for members
|
||||||
|
- Cycle regeneration when fee type changes
|
||||||
|
- Integration with member lifecycle hooks
|
||||||
|
- Actor-based authorization for cycle operations
|
||||||
|
|
||||||
|
**PR #291:** *Membership Fee Type Resource & Settings* (closes #278)
|
||||||
|
- Membership fee type CRUD operations
|
||||||
|
- Global membership fee settings
|
||||||
|
- Default fee type assignment
|
||||||
|
- `include_joining_cycle` setting
|
||||||
|
|
||||||
|
**PR #294:** *Cycle Management & Member Integration* (closes #279)
|
||||||
|
- Member-fee type relationship
|
||||||
|
- Cycle status tracking (unpaid, paid, suspended)
|
||||||
|
- Member detail view integration
|
||||||
|
- Cycle regeneration on fee type change
|
||||||
|
|
||||||
|
**PR #304:** *Membership Fee 6 - UI Components & LiveViews* (closes #280)
|
||||||
|
- Membership fee type management LiveViews
|
||||||
|
- Membership fee settings LiveView
|
||||||
|
- Cycle display in member detail view
|
||||||
|
- Payment status indicators
|
||||||
|
|
||||||
|
### Custom Fields Enhancements (2025-12-11 to 2026-01-02)
|
||||||
|
|
||||||
|
**PR #266:** *Implements search for custom fields* (closes #196)
|
||||||
|
- Custom field search in member overview
|
||||||
|
- Integration with full-text search
|
||||||
|
- Custom field value filtering
|
||||||
|
|
||||||
|
**PR #301:** *Implements validation for required custom fields* (closes #274)
|
||||||
|
- Required custom field validation
|
||||||
|
- Form-level validation
|
||||||
|
- Error messages for missing required fields
|
||||||
|
|
||||||
|
**PR #313:** *Fix hidden empty custom fields* (closes #282)
|
||||||
|
- Fixed display of empty custom fields
|
||||||
|
- Improved custom field visibility logic
|
||||||
|
|
||||||
|
### UI/UX Improvements (2025-12-03 to 2025-12-16)
|
||||||
|
|
||||||
|
**PR #240:** *Implement dropdown to show/hide columns in member overview* (closes #209)
|
||||||
|
- Field visibility dropdown
|
||||||
|
- User-specific field selection
|
||||||
|
- Integration with global settings
|
||||||
|
|
||||||
|
**PR #247:** *Visual hierarchy for fields in member view and edit form* (closes #231)
|
||||||
|
- Improved field grouping
|
||||||
|
- Visual hierarchy improvements
|
||||||
|
- Better form layout
|
||||||
|
|
||||||
|
**PR #250:** *UX - Avoid opening member by clicking the checkbox* (closes #233)
|
||||||
|
- Checkbox click handling
|
||||||
|
- Prevented accidental navigation
|
||||||
|
- Improved selection UX
|
||||||
|
|
||||||
|
**PR #259:** *Fix small UI issues* (closes #220)
|
||||||
|
- Various UI bug fixes
|
||||||
|
- Accessibility improvements
|
||||||
|
|
||||||
|
**PR #293:** *Small UX fixes* (closes #281)
|
||||||
|
- Additional UX improvements
|
||||||
|
- Polish and refinement
|
||||||
|
|
||||||
|
**PR #319:** *Reduce member fields* (closes #273)
|
||||||
|
- Removed unnecessary member fields
|
||||||
|
- Streamlined member data model
|
||||||
|
- Migration for field removal
|
||||||
|
|
||||||
|
### Roles and Permissions System (2026-01-06 to 2026-01-08)
|
||||||
|
- ✅ **RBAC Implementation Complete** - Member Resource Policies (#345)
|
||||||
|
- Four hardcoded permission sets: `own_data`, `read_only`, `normal_user`, `admin`
|
||||||
|
- Role-based access control with database-backed roles
|
||||||
|
- Member resource policies with scope filtering (`:own`, `:linked`, `:all`)
|
||||||
|
- Authorization checks via `Mv.Authorization.Checks.HasPermission`
|
||||||
|
- System role protection (cannot delete critical roles)
|
||||||
|
- Comprehensive test coverage
|
||||||
|
|
||||||
|
### Actor Handling Refactoring (2026-01-09)
|
||||||
|
- ✅ **Consistent Actor Access** - `current_actor/1` helper function
|
||||||
|
- Standardized actor access across all LiveViews
|
||||||
|
- `ash_actor_opts/1` helper for consistent authorization options
|
||||||
|
- `submit_form/3` wrapper for form submissions with actor
|
||||||
|
- All Ash operations now properly pass `actor` parameter
|
||||||
|
- Error handling improvements (replaced bang calls with proper error handling)
|
||||||
|
|
||||||
|
### Internationalization Improvements (2026-01-13)
|
||||||
|
- ✅ **Complete German Translations** - All UI strings translated
|
||||||
|
- CI check for empty German translations in lint task
|
||||||
|
- Standardized English `msgstr` entries (all empty for consistency)
|
||||||
|
- Corrected language headers in `.po` files
|
||||||
|
- Added missing translations for error messages
|
||||||
|
|
||||||
|
### Code Quality Improvements (2026-01-13)
|
||||||
|
- ✅ **Error Handling** - Replaced `Ash.read!` with proper error handling
|
||||||
|
- ✅ **Code Complexity** - Reduced nesting depth in `UserLive.Form`
|
||||||
|
- ✅ **Test Infrastructure** - Role tag support in `ConnCase`
|
||||||
|
|
||||||
|
### CSV Import Feature (2026-01-13)
|
||||||
|
- ✅ **CSV Templates** - Member import templates (#329)
|
||||||
|
- German and English CSV templates
|
||||||
|
- Template files in `priv/static/templates/`
|
||||||
|
|
||||||
|
### Sidebar Implementation (2026-01-12)
|
||||||
|
- ✅ **Sidebar Navigation** - Replaced navbar with sidebar (#260)
|
||||||
|
- Standard-compliant sidebar with comprehensive tests
|
||||||
|
- DaisyUI drawer pattern implementation
|
||||||
|
- Desktop expanded/collapsed states
|
||||||
|
- Mobile overlay drawer
|
||||||
|
- localStorage persistence for sidebar state
|
||||||
|
- WCAG 2.1 Level AA compliant
|
||||||
|
|
||||||
|
### Member Field Settings (2026-01-12, PR #300, closes #223)
|
||||||
|
- ✅ **Member Field Visibility Configuration** - Global settings for field visibility
|
||||||
|
- JSONB-based visibility configuration in Settings resource
|
||||||
|
- Per-field visibility toggle (show/hide in member overview)
|
||||||
|
- Atomic updates for single field visibility changes
|
||||||
|
- Integration with member list overview
|
||||||
|
- User-specific field selection (takes priority over global settings)
|
||||||
|
- Custom field visibility support
|
||||||
|
- Default visibility: all fields visible except `exit_date` (hidden by default)
|
||||||
|
- LiveComponent for managing member field visibility in settings page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Document Version:** 1.4
|
||||||
|
**Last Updated:** 2026-01-13
|
||||||
**Maintainer:** Development Team
|
**Maintainer:** Development Team
|
||||||
**Status:** Living Document (update as project evolves)
|
**Status:** Living Document (update as project evolves)
|
||||||
|
|
||||||
|
|
|
||||||
128
docs/documentation-sync-todos.md
Normal file
128
docs/documentation-sync-todos.md
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Documentation Sync - Code Anpassungen Todo-Liste
|
||||||
|
|
||||||
|
**Erstellt:** 2026-01-13
|
||||||
|
**Zweck:** Liste aller Code-Anpassungen, die basierend auf der Dokumentations-Synchronisation identifiziert wurden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Entfernte Dokumentationsdateien
|
||||||
|
|
||||||
|
### 1. `docs/test-status-membership-fee-ui.md`
|
||||||
|
**Grund:** Veraltete temporäre Analyse-Dokumentation
|
||||||
|
- Enthält nur historische Test-Status-Informationen (Datum: 2025-01-XX)
|
||||||
|
- Status "Tests Written - Implementation Complete" ist nicht mehr relevant
|
||||||
|
- Alle Tests sind bereits implementiert und laufen
|
||||||
|
- Informationen sind bereits in `development-progress-log.md` dokumentiert
|
||||||
|
- **Entfernt:** 2026-01-13
|
||||||
|
|
||||||
|
### 2. `docs/test-failures-analysis.md`
|
||||||
|
**Grund:** Veraltete temporäre Analyse-Dokumentation
|
||||||
|
- Analysiert 5 fehlschlagende Tests, die bereits behoben wurden
|
||||||
|
- Enthält Lösungsvorschläge für bereits gelöste Probleme
|
||||||
|
- Informationen sind nur historisch relevant
|
||||||
|
- Keine aktuelle Relevanz für die Codebasis
|
||||||
|
- **Entfernt:** 2026-01-13
|
||||||
|
|
||||||
|
## Als veraltet markierte Dokumentationsdateien
|
||||||
|
|
||||||
|
### 3. `docs/sidebar-analysis-current-state.md`
|
||||||
|
**Grund:** Veraltete Analyse-Dokumentation
|
||||||
|
- Beschreibt den Zustand VOR der Sidebar-Implementierung
|
||||||
|
- Sidebar wurde bereits implementiert (2026-01-12, PR #260)
|
||||||
|
- Wurde durch `sidebar-requirements-v2.md` ersetzt
|
||||||
|
- **Status:** Als veraltet markiert, aber behalten für historische Referenz
|
||||||
|
|
||||||
|
### 4. `docs/umsetzung-sidebar.md`
|
||||||
|
**Grund:** Veraltete Implementierungs-Anleitung
|
||||||
|
- Schritt-für-Schritt-Anleitung für Sidebar-Implementierung
|
||||||
|
- Sidebar wurde bereits implementiert (2026-01-12, PR #260)
|
||||||
|
- Wurde durch `sidebar-requirements-v2.md` ersetzt
|
||||||
|
- **Status:** Als veraltet markiert, aber behalten für historische Referenz
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code-Anpassungen (Priorität: Low)
|
||||||
|
|
||||||
|
### Keine kritischen Code-Anpassungen erforderlich
|
||||||
|
|
||||||
|
Die Dokumentations-Synchronisation hat ergeben, dass der Code aktuell ist und mit der aktualisierten Dokumentation übereinstimmt. Alle identifizierten Unstimmigkeiten waren in der Dokumentation, nicht im Code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Veraltete Code-Patterns
|
||||||
|
|
||||||
|
### Keine veralteten Patterns identifiziert
|
||||||
|
|
||||||
|
Alle Code-Patterns entsprechen den aktuellen Best Practices und sind in `CODE_GUIDELINES.md` dokumentiert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlende Implementierungen
|
||||||
|
|
||||||
|
### Keine fehlenden Implementierungen identifiziert
|
||||||
|
|
||||||
|
Alle in der Dokumentation beschriebenen Features sind implementiert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inconsistente Namensgebung
|
||||||
|
|
||||||
|
### Keine Inkonsistenzen identifiziert
|
||||||
|
|
||||||
|
Die Terminologie ist konsistent zwischen Code und Dokumentation:
|
||||||
|
- `CustomField` / `CustomFieldValue` (nicht mehr "Property" / "PropertyType")
|
||||||
|
- `MembershipFeeType` / `MembershipFeeCycle` (korrekt verwendet)
|
||||||
|
- Domains: `Accounts`, `Membership`, `MembershipFees`, `Authorization` (alle korrekt)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zusammenfassung
|
||||||
|
|
||||||
|
**Status:** ✅ Dokumentation erfolgreich synchronisiert
|
||||||
|
|
||||||
|
- **Aktualisierte Dokumentation:** 15+ Dateien
|
||||||
|
- database_schema.dbml (Version 1.4, +2 Tabellen: roles, settings)
|
||||||
|
- database-schema-readme.md (9 Tabellen, 4 Domains, aktualisierte Relationships)
|
||||||
|
- development-progress-log.md (Last Updated: 2026-01-13)
|
||||||
|
- Neue Sektion: "Recent Updates (2025-12-02 to 2026-01-13)"
|
||||||
|
- Membership Fees System Implementation (6 PRs dokumentiert)
|
||||||
|
- Custom Fields Enhancements (3 PRs dokumentiert)
|
||||||
|
- UI/UX Improvements (6 PRs dokumentiert)
|
||||||
|
- Roles and Permissions System (vollständig dokumentiert)
|
||||||
|
- Key Achievements aktualisiert (100+ PRs, 9+ sprints)
|
||||||
|
- feature-roadmap.md (Last Updated: 2026-01-13)
|
||||||
|
- Routes aktualisiert (alle aktuellen LiveView-Routes dokumentiert)
|
||||||
|
- Membership Fees Endpoints (Status: ✅ Implemented)
|
||||||
|
- Admin Panel Endpoints (Status aktualisiert)
|
||||||
|
- Custom Fields Endpoints (korrigiert: über /settings verwaltet)
|
||||||
|
- CHANGELOG.md (neue Features dokumentiert)
|
||||||
|
- CODE_GUIDELINES.md (Module-Struktur, Actor-Handling-Patterns, navbar → sidebar)
|
||||||
|
- roles-and-permissions-architecture.md (Status: ✅ Implemented)
|
||||||
|
- roles-and-permissions-overview.md (Status: ✅ Implemented)
|
||||||
|
- roles-and-permissions-implementation-plan.md (Status: ✅ Implemented)
|
||||||
|
- membership-fee-architecture.md (Status: ✅ Implemented)
|
||||||
|
- membership-fee-overview.md (Status: ✅ Implemented)
|
||||||
|
- csv-member-import-v1.md (Status: Templates Created)
|
||||||
|
- sidebar-requirements-v2.md (Status: ✅ Implemented)
|
||||||
|
- README.md (Feature-Status aktualisiert)
|
||||||
|
- **Entfernte Dokumentation:** 2 Dateien
|
||||||
|
- test-status-membership-fee-ui.md
|
||||||
|
- test-failures-analysis.md
|
||||||
|
- **Als veraltet markiert:** 2 Dateien
|
||||||
|
- sidebar-analysis-current-state.md
|
||||||
|
- umsetzung-sidebar.md
|
||||||
|
- **Code-Anpassungen erforderlich:** 0
|
||||||
|
- **Kritische Probleme:** 0
|
||||||
|
|
||||||
|
**Dokumentierte Features seit 2025-12-02:**
|
||||||
|
- Membership Fees System (6 PRs: #275, #276, #277, #278, #279, #280)
|
||||||
|
- Custom Fields Enhancements (3 PRs: #196, #274, #282)
|
||||||
|
- UI/UX Improvements (6 PRs: #209, #220, #231, #233, #273, #281)
|
||||||
|
- Roles and Permissions (5 PRs: #321, #322, #323, #325, #345)
|
||||||
|
- Sidebar Implementation (#260)
|
||||||
|
- Member Field Settings (#223, #300)
|
||||||
|
- CSV Import Templates (#329)
|
||||||
|
- Actor Handling Refactoring
|
||||||
|
- Internationalization Improvements
|
||||||
|
|
||||||
|
Die Dokumentation ist jetzt vollständig mit dem aktuellen Code synchronisiert. Alle "Last Updated" Daten wurden auf 2026-01-13 aktualisiert, wo relevant. Alle Routes, Features und Implementierungen sind dokumentiert.
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# Feature Roadmap & Implementation Plan
|
# Feature Roadmap & Implementation Plan
|
||||||
|
|
||||||
**Project:** Mila - Membership Management System
|
**Project:** Mila - Membership Management System
|
||||||
**Last Updated:** 2025-11-10
|
**Last Updated:** 2026-01-13
|
||||||
**Status:** Planning Phase
|
**Status:** Active Development
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -37,17 +37,24 @@
|
||||||
- [#146](https://git.local-it.org/local-it/mitgliederverwaltung/issues/146) - Translate "or" in the login screen (Low)
|
- [#146](https://git.local-it.org/local-it/mitgliederverwaltung/issues/146) - Translate "or" in the login screen (Low)
|
||||||
- [#144](https://git.local-it.org/local-it/mitgliederverwaltung/issues/144) - Add language switch dropdown to login screen (Low)
|
- [#144](https://git.local-it.org/local-it/mitgliederverwaltung/issues/144) - Add language switch dropdown to login screen (Low)
|
||||||
|
|
||||||
|
**Current State:**
|
||||||
|
- ✅ **Role-based access control (RBAC)** - Implemented (2026-01-08, PR #346, closes #345)
|
||||||
|
- ✅ **Permission system** - Four hardcoded permission sets (`own_data`, `read_only`, `normal_user`, `admin`)
|
||||||
|
- ✅ **Database-backed roles** - Roles table with permission set references
|
||||||
|
- ✅ **Resource policies** - Member resource policies with scope filtering
|
||||||
|
- ✅ **Page-level authorization** - LiveView page access control
|
||||||
|
- ✅ **System role protection** - Critical roles cannot be deleted
|
||||||
|
|
||||||
**Missing Features:**
|
**Missing Features:**
|
||||||
- ❌ Role-based access control (RBAC)
|
|
||||||
- ❌ Permission system
|
|
||||||
- ❌ Password reset flow
|
- ❌ Password reset flow
|
||||||
- ❌ Email verification
|
- ❌ Email verification
|
||||||
- ❌ Two-factor authentication (future)
|
- ❌ Two-factor authentication (future)
|
||||||
|
|
||||||
**Related Issues:**
|
**Related Issues:**
|
||||||
- [#191](https://git.local-it.org/local-it/mitgliederverwaltung/issues/191) - Implement Roles in Ash (M)
|
- ✅ [#345](https://git.local-it.org/local-it/mitgliederverwaltung/issues/345) - Member Resource Policies (closed 2026-01-13)
|
||||||
- [#190](https://git.local-it.org/local-it/mitgliederverwaltung/issues/190) - Implement Permissions in Ash (M)
|
- ✅ [#191](https://git.local-it.org/local-it/mitgliederverwaltung/issues/191) - Implement Roles in Ash (M) - Completed
|
||||||
- [#151](https://git.local-it.org/local-it/mitgliederverwaltung/issues/151) - Define implementation plan for roles and permissions (M) [3/7 tasks done]
|
- ✅ [#190](https://git.local-it.org/local-it/mitgliederverwaltung/issues/190) - Implement Permissions in Ash (M) - Completed
|
||||||
|
- ✅ [#151](https://git.local-it.org/local-it/mitgliederverwaltung/issues/151) - Define implementation plan for roles and permissions (M) - Completed
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -187,23 +194,27 @@
|
||||||
|
|
||||||
**Current State:**
|
**Current State:**
|
||||||
- ✅ Basic "paid" boolean field on members
|
- ✅ Basic "paid" boolean field on members
|
||||||
- ✅ **UI Mock-ups for Membership Fee Types & Settings** (2025-12-02)
|
- ✅ **Membership Fee Types Management** - Full CRUD implementation
|
||||||
- ⚠️ No payment tracking
|
- ✅ **Membership Fee Cycles** - Individual billing cycles per member
|
||||||
|
- ✅ **Membership Fee Settings** - Global settings (include_joining_cycle, default_fee_type)
|
||||||
|
- ✅ **Cycle Generation** - Automatic cycle generation for members
|
||||||
|
- ✅ **Payment Status Tracking** - Status per cycle (unpaid, paid, suspended)
|
||||||
|
- ✅ **Member Fee Assignment** - Members can be assigned to fee types
|
||||||
|
- ✅ **Cycle Regeneration** - Regenerate cycles when fee type changes
|
||||||
|
- ✅ **UI Components** - Membership fee status in member list and detail views
|
||||||
|
|
||||||
**Open Issues:**
|
**Open Issues:**
|
||||||
- [#156](https://git.local-it.org/local-it/mitgliederverwaltung/issues/156) - Set up & document testing environment for vereinfacht.digital (L, Low priority)
|
- [#156](https://git.local-it.org/local-it/mitgliederverwaltung/issues/156) - Set up & document testing environment for vereinfacht.digital (L, Low priority)
|
||||||
- [#226](https://git.local-it.org/local-it/mitgliederverwaltung/issues/226) - Payment/Membership Fee Mockup Pages (Preview)
|
- ✅ [#226](https://git.local-it.org/local-it/mitgliederverwaltung/issues/226) - Payment/Membership Fee Mockup Pages (Preview) - Implemented
|
||||||
|
|
||||||
**Mock-Up Pages (Non-Functional Preview):**
|
**Implemented Pages:**
|
||||||
- `/membership_fee_types` - Membership Fee Types Management
|
- `/membership_fee_types` - Membership Fee Types Management (fully functional)
|
||||||
- `/membership_fee_settings` - Global Membership Fee Settings
|
- `/membership_fee_settings` - Global Membership Fee Settings (fully functional)
|
||||||
|
- `/members/:id` - Member detail view with membership fee cycles
|
||||||
|
|
||||||
**Missing Features:**
|
**Missing Features:**
|
||||||
- ❌ Membership fee configuration
|
- ❌ Payment records/transactions (external payment tracking)
|
||||||
- ❌ Payment records/transactions
|
|
||||||
- ❌ Payment history per member
|
|
||||||
- ❌ Payment reminders
|
- ❌ Payment reminders
|
||||||
- ❌ Payment status tracking (pending, paid, overdue)
|
|
||||||
- ❌ Invoice generation
|
- ❌ Invoice generation
|
||||||
- ❌ vereinfacht.digital API integration
|
- ❌ vereinfacht.digital API integration
|
||||||
- ❌ SEPA direct debit support
|
- ❌ SEPA direct debit support
|
||||||
|
|
@ -218,17 +229,18 @@
|
||||||
|
|
||||||
**Current State:**
|
**Current State:**
|
||||||
- ✅ AshAdmin integration (basic)
|
- ✅ AshAdmin integration (basic)
|
||||||
- ⚠️ No user-facing admin UI
|
- ✅ **Global Settings Management** - `/settings` page (singleton resource)
|
||||||
|
- ✅ **Club/Organization profile** - Club name configuration
|
||||||
|
- ✅ **Member Field Visibility Settings** - Configure which fields show in overview
|
||||||
|
- ✅ **CustomFieldValue type management UI** - Full CRUD for custom fields
|
||||||
|
- ✅ **Role Management UI** - Full CRUD for roles (`/admin/roles`)
|
||||||
|
- ✅ **Membership Fee Settings** - Global fee settings management
|
||||||
|
|
||||||
**Open Issues:**
|
**Open Issues:**
|
||||||
- [#186](https://git.local-it.org/local-it/mitgliederverwaltung/issues/186) - Create Architecture docs in Repo (S, Low priority)
|
- [#186](https://git.local-it.org/local-it/mitgliederverwaltung/issues/186) - Create Architecture docs in Repo (S, Low priority)
|
||||||
|
|
||||||
**Missing Features:**
|
**Missing Features:**
|
||||||
- ❌ Global settings management
|
|
||||||
- ❌ Club/Organization profile
|
|
||||||
- ❌ Email templates configuration
|
- ❌ Email templates configuration
|
||||||
- ❌ CustomFieldValue type management UI (user-facing)
|
|
||||||
- ❌ Role and permission management UI
|
|
||||||
- ❌ System health dashboard
|
- ❌ System health dashboard
|
||||||
- ❌ Audit log viewer
|
- ❌ Audit log viewer
|
||||||
- ❌ Backup/restore functionality
|
- ❌ Backup/restore functionality
|
||||||
|
|
@ -273,10 +285,12 @@
|
||||||
|
|
||||||
**Current State:**
|
**Current State:**
|
||||||
- ✅ Seed data script
|
- ✅ Seed data script
|
||||||
- ⚠️ No user-facing import/export
|
- ✅ **CSV Import Templates** - German and English templates (#329, 2026-01-13)
|
||||||
|
- Template files in `priv/static/templates/member_import_de.csv` and `member_import_en.csv`
|
||||||
|
- CSV specification documented in `docs/csv-member-import-v1.md`
|
||||||
|
|
||||||
**Missing Features:**
|
**Missing Features:**
|
||||||
- ❌ CSV import for members
|
- ❌ CSV import implementation (templates ready, import logic pending)
|
||||||
- ❌ Excel import for members
|
- ❌ Excel import for members
|
||||||
- ❌ Import validation and preview
|
- ❌ Import validation and preview
|
||||||
- ❌ Import error handling
|
- ❌ Import error handling
|
||||||
|
|
@ -452,6 +466,7 @@ Since this is a **Phoenix LiveView** application with **Ash Framework**, we have
|
||||||
| `GET` | `/auth/user/rauthy` | Initiate OIDC flow | 🔓 | - | Redirect to Rauthy |
|
| `GET` | `/auth/user/rauthy` | Initiate OIDC flow | 🔓 | - | Redirect to Rauthy |
|
||||||
| `GET` | `/auth/user/rauthy/callback` | Handle OIDC callback | 🔓 | `{code, state}` | Redirect + session cookie |
|
| `GET` | `/auth/user/rauthy/callback` | Handle OIDC callback | 🔓 | `{code, state}` | Redirect + session cookie |
|
||||||
| `POST` | `/auth/user/sign_out` | Sign out user | 🔐 | - | Redirect to login |
|
| `POST` | `/auth/user/sign_out` | Sign out user | 🔐 | - | Redirect to login |
|
||||||
|
| `GET` | `/auth/link-oidc-account` | OIDC account linking (password verification) | 🔓 | - | LiveView form | ✅ Implemented |
|
||||||
| `GET` | `/auth/user/password/reset` | Show password reset form | 🔓 | - | HTML form |
|
| `GET` | `/auth/user/password/reset` | Show password reset form | 🔓 | - | HTML form |
|
||||||
| `POST` | `/auth/user/password/reset` | Request password reset | 🔓 | `{email}` | Success message + email sent |
|
| `POST` | `/auth/user/password/reset` | Request password reset | 🔓 | `{email}` | Success message + email sent |
|
||||||
| `GET` | `/auth/user/password/reset/:token` | Show reset password form | 🔓 | - | HTML form |
|
| `GET` | `/auth/user/password/reset/:token` | Show reset password form | 🔓 | - | HTML form |
|
||||||
|
|
@ -537,13 +552,18 @@ Since this is a **Phoenix LiveView** application with **Ash Framework**, we have
|
||||||
|
|
||||||
### 3. Custom Fields (CustomFieldValue System) Endpoints
|
### 3. Custom Fields (CustomFieldValue System) Endpoints
|
||||||
|
|
||||||
#### LiveView Endpoints
|
#### LiveView Endpoints (✅ Implemented)
|
||||||
|
|
||||||
| Mount | Purpose | Auth | Events |
|
| Mount | Purpose | Auth | Events | Status |
|
||||||
|-------|---------|------|--------|
|
|-------|---------|------|--------|--------|
|
||||||
| `/custom-fields` | List custom fields | 🛡️ | `new`, `edit`, `delete` |
|
| `/settings` | Global settings (includes custom fields management) | 🔐 | `save`, `validate` | ✅ Implemented |
|
||||||
| `/custom-fields/new` | Create custom field | 🛡️ | `save`, `cancel` |
|
| `/custom_field_values` | List all custom field values | 🔐 | `new`, `edit`, `delete` | ✅ Implemented |
|
||||||
| `/custom-fields/:id/edit` | Edit custom field | 🛡️ | `save`, `cancel`, `delete` |
|
| `/custom_field_values/new` | Create custom field value | 🔐 | `save`, `cancel` | ✅ Implemented |
|
||||||
|
| `/custom_field_values/:id` | Custom field value detail | 🔐 | `edit` | ✅ Implemented |
|
||||||
|
| `/custom_field_values/:id/edit` | Edit custom field value | 🔐 | `save`, `cancel` | ✅ Implemented |
|
||||||
|
| `/custom_field_values/:id/show/edit` | Edit from show page | 🔐 | `save`, `cancel` | ✅ Implemented |
|
||||||
|
|
||||||
|
**Note:** Custom fields (definitions) are managed via LiveComponent in `/settings` page, not as separate routes.
|
||||||
|
|
||||||
#### Ash Resource Actions
|
#### Ash Resource Actions
|
||||||
|
|
||||||
|
|
@ -622,63 +642,81 @@ Since this is a **Phoenix LiveView** application with **Ash Framework**, we have
|
||||||
|
|
||||||
### 6. Internationalization Endpoints
|
### 6. Internationalization Endpoints
|
||||||
|
|
||||||
#### HTTP Controller Endpoints
|
#### HTTP Controller Endpoints (✅ Implemented)
|
||||||
|
|
||||||
| Method | Route | Purpose | Auth | Request | Response |
|
| Method | Route | Purpose | Auth | Request | Response | Status |
|
||||||
|--------|-------|---------|------|---------|----------|
|
|--------|-------|---------|------|---------|----------|--------|
|
||||||
| `POST` | `/locale` | Set user locale | 🔐 | `{locale: "de"}` | Redirect with cookie |
|
| `POST` | `/set_locale` | Set user locale | 🔐 | `{locale: "de"}` | Redirect with cookie | ✅ Implemented |
|
||||||
| `GET` | `/locales` | List available locales | 🔓 | - | `["de", "en"]` |
|
| `GET` | `/locales` | List available locales | 🔓 | - | `["de", "en"]` | ❌ Not implemented |
|
||||||
|
|
||||||
|
**Note:** Locale is set via `/set_locale` POST endpoint and persisted in session/cookie. Supported locales: `de` (default), `en`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 7. Payment & Fees Management Endpoints
|
### 7. Payment & Fees Management Endpoints
|
||||||
|
|
||||||
#### LiveView Endpoints (NEW - Issue #156)
|
#### LiveView Endpoints (✅ Implemented)
|
||||||
|
|
||||||
| Mount | Purpose | Auth | Events |
|
| Mount | Purpose | Auth | Events | Status |
|
||||||
|-------|---------|------|--------|
|
|-------|---------|------|--------|--------|
|
||||||
| `/payments` | Payment list | 🔐 | `new`, `record_payment`, `send_reminder` |
|
| `/membership_fee_types` | Membership fee type list | 🔐 | `new`, `edit`, `delete` | ✅ Implemented |
|
||||||
| `/payments/:id` | Payment detail | 🔐 | `edit`, `delete`, `mark_paid` |
|
| `/membership_fee_types/new` | Create membership fee type | 🔐 | `save`, `cancel` | ✅ Implemented |
|
||||||
| `/fees` | Fee configuration | 🛡️ | `create`, `edit`, `delete` |
|
| `/membership_fee_types/:id/edit` | Edit membership fee type | 🔐 | `save`, `cancel` | ✅ Implemented |
|
||||||
| `/invoices` | Invoice list | 🔐 | `generate`, `download`, `send` |
|
| `/membership_fee_settings` | Global membership fee settings | 🔐 | `save` | ✅ Implemented |
|
||||||
|
| `/contributions/member/:id` | Member contribution periods (mock-up) | 🔐 | - | ⚠️ Mock-up only |
|
||||||
|
| `/contribution_types` | Contribution types (mock-up) | 🔐 | - | ⚠️ Mock-up only |
|
||||||
|
|
||||||
#### Ash Resource Actions (NEW)
|
#### Ash Resource Actions (✅ Partially Implemented)
|
||||||
|
|
||||||
| Resource | Action | Purpose | Auth | Input | Output |
|
| Resource | Action | Purpose | Auth | Input | Output | Status |
|
||||||
|----------|--------|---------|------|-------|--------|
|
|----------|--------|---------|------|-------|--------|--------|
|
||||||
| `Fee` | `:create` | Create fee type | 🛡️ | `{name, amount, frequency}` | `{:ok, fee}` |
|
| `MembershipFeeType` | `:create` | Create fee type | 🔐 | `{name, amount, interval, ...}` | `{:ok, fee_type}` | ✅ Implemented |
|
||||||
| `Fee` | `:read` | List fees | 🔐 | - | `[%Fee{}]` |
|
| `MembershipFeeType` | `:read` | List fee types | 🔐 | - | `[%MembershipFeeType{}]` | ✅ Implemented |
|
||||||
| `Payment` | `:create` | Record payment | 🔐 | `{member_id, fee_id, amount, date}` | `{:ok, payment}` |
|
| `MembershipFeeType` | `:update` | Update fee type (name, amount, description) | 🔐 | `{id, attrs}` | `{:ok, fee_type}` | ✅ Implemented |
|
||||||
| `Payment` | `:list_by_member` | Member payment history | 🔐 | `{member_id}` | `[%Payment{}]` |
|
| `MembershipFeeType` | `:destroy` | Delete fee type (if no cycles) | 🔐 | `{id}` | `{:ok, fee_type}` | ✅ Implemented |
|
||||||
| `Payment` | `:mark_paid` | Mark as paid | 🔐 | `{id}` | `{:ok, payment}` |
|
| `MembershipFeeCycle` | `:read` | List cycles for member | 🔐 | `{member_id}` | `[%MembershipFeeCycle{}]` | ✅ Implemented |
|
||||||
| `Invoice` | `:generate` | Generate invoice | 🔐 | `{member_id, fee_id, period}` | `{:ok, invoice}` |
|
| `MembershipFeeCycle` | `:update` | Update cycle status | 🔐 | `{id, status}` | `{:ok, cycle}` | ✅ Implemented |
|
||||||
| `Invoice` | `:send` | Send invoice via email | 🔐 | `{id}` | `{:ok, sent}` |
|
| `Payment` | `:create` | Record payment | 🔐 | `{member_id, fee_id, amount, date}` | `{:ok, payment}` | ❌ Not implemented |
|
||||||
| `Payment` | `:import_vereinfacht` | Import from vereinfacht.digital | 🛡️ | `{transactions}` | `{:ok, count}` |
|
| `Payment` | `:list_by_member` | Member payment history | 🔐 | `{member_id}` | `[%Payment{}]` | ❌ Not implemented |
|
||||||
|
| `Payment` | `:mark_paid` | Mark as paid | 🔐 | `{id}` | `{:ok, payment}` | ❌ Not implemented |
|
||||||
|
| `Invoice` | `:generate` | Generate invoice | 🔐 | `{member_id, fee_id, period}` | `{:ok, invoice}` | ❌ Not implemented |
|
||||||
|
| `Invoice` | `:send` | Send invoice via email | 🔐 | `{id}` | `{:ok, sent}` | ❌ Not implemented |
|
||||||
|
| `Payment` | `:import_vereinfacht` | Import from vereinfacht.digital | 🛡️ | `{transactions}` | `{:ok, count}` | ❌ Not implemented |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 8. Admin Panel & Configuration Endpoints
|
### 8. Admin Panel & Configuration Endpoints
|
||||||
|
|
||||||
#### LiveView Endpoints (NEW)
|
#### LiveView Endpoints (✅ Partially Implemented)
|
||||||
|
|
||||||
| Mount | Purpose | Auth | Events |
|
| Mount | Purpose | Auth | Events | Status |
|
||||||
|-------|---------|------|--------|
|
|-------|---------|------|--------|--------|
|
||||||
| `/admin` | Admin dashboard | 🛡️ | - |
|
| `/settings` | Global settings (club name, member fields, custom fields) | 🔐 | `save`, `validate` | ✅ Implemented |
|
||||||
| `/admin/settings` | Global settings | 🛡️ | `save` |
|
| `/admin/roles` | Role management | 🛡️ | `new`, `edit`, `delete` | ✅ Implemented |
|
||||||
| `/admin/organization` | Organization profile | 🛡️ | `save` |
|
| `/admin/roles/new` | Create role | 🛡️ | `save`, `cancel` | ✅ Implemented |
|
||||||
| `/admin/email-templates` | Email template editor | 🛡️ | `create`, `edit`, `preview` |
|
| `/admin/roles/:id` | Role detail view | 🛡️ | `edit` | ✅ Implemented |
|
||||||
| `/admin/audit-log` | System audit log | 🛡️ | `filter`, `export` |
|
| `/admin/roles/:id/edit` | Edit role | 🛡️ | `save`, `cancel` | ✅ Implemented |
|
||||||
|
| `/admin` | Admin dashboard | 🛡️ | - | ❌ Not implemented |
|
||||||
|
| `/admin/organization` | Organization profile | 🛡️ | `save` | ❌ Not implemented |
|
||||||
|
| `/admin/email-templates` | Email template editor | 🛡️ | `create`, `edit`, `preview` | ❌ Not implemented |
|
||||||
|
| `/admin/audit-log` | System audit log | 🛡️ | `filter`, `export` | ❌ Not implemented |
|
||||||
|
|
||||||
#### Ash Resource Actions (NEW)
|
#### Ash Resource Actions (✅ Partially Implemented)
|
||||||
|
|
||||||
| Resource | Action | Purpose | Auth | Input | Output |
|
| Resource | Action | Purpose | Auth | Input | Output | Status |
|
||||||
|----------|--------|---------|------|-------|--------|
|
|----------|--------|---------|------|-------|--------|--------|
|
||||||
| `Setting` | `:get` | Get setting value | 🔐 | `{key}` | `value` |
|
| `Setting` | `:read` | Get settings (singleton) | 🔐 | - | `{:ok, settings}` | ✅ Implemented |
|
||||||
| `Setting` | `:set` | Set setting value | 🛡️ | `{key, value}` | `{:ok, setting}` |
|
| `Setting` | `:update` | Update settings | 🔐 | `{club_name, member_field_visibility, ...}` | `{:ok, settings}` | ✅ Implemented |
|
||||||
| `Setting` | `:list` | List all settings | 🛡️ | - | `[%Setting{}]` |
|
| `Setting` | `:update_member_field_visibility` | Update field visibility | 🔐 | `{member_field_visibility}` | `{:ok, settings}` | ✅ Implemented |
|
||||||
| `Organization` | `:read` | Get organization info | 🔐 | - | `%Organization{}` |
|
| `Setting` | `:update_single_member_field_visibility` | Atomic field visibility update | 🔐 | `{field, show_in_overview}` | `{:ok, settings}` | ✅ Implemented |
|
||||||
| `Organization` | `:update` | Update organization | 🛡️ | `{name, logo, ...}` | `{:ok, org}` |
|
| `Setting` | `:update_membership_fee_settings` | Update fee settings | 🔐 | `{include_joining_cycle, default_membership_fee_type_id}` | `{:ok, settings}` | ✅ Implemented |
|
||||||
| `AuditLog` | `:list` | List audit entries | 🛡️ | `{filters, pagination}` | `[%AuditLog{}]` |
|
| `Role` | `:read` | List roles | 🛡️ | - | `[%Role{}]` | ✅ Implemented |
|
||||||
|
| `Role` | `:create` | Create role | 🛡️ | `{name, permission_set_name, ...}` | `{:ok, role}` | ✅ Implemented |
|
||||||
|
| `Role` | `:update` | Update role | 🛡️ | `{id, attrs}` | `{:ok, role}` | ✅ Implemented |
|
||||||
|
| `Role` | `:destroy` | Delete role (if not system role) | 🛡️ | `{id}` | `{:ok, role}` | ✅ Implemented |
|
||||||
|
| `Organization` | `:read` | Get organization info | 🔐 | - | `%Organization{}` | ❌ Not implemented |
|
||||||
|
| `Organization` | `:update` | Update organization | 🛡️ | `{name, logo, ...}` | `{:ok, org}` | ❌ Not implemented |
|
||||||
|
| `AuditLog` | `:list` | List audit entries | 🛡️ | `{filters, pagination}` | `[%AuditLog{}]` | ❌ Not implemented |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
**Project:** Mila - Membership Management System
|
**Project:** Mila - Membership Management System
|
||||||
**Feature:** Membership Fee Management
|
**Feature:** Membership Fee Management
|
||||||
**Version:** 1.0
|
**Version:** 1.0
|
||||||
**Last Updated:** 2025-11-27
|
**Last Updated:** 2026-01-13
|
||||||
**Status:** Architecture Design - Ready for Implementation
|
**Status:** ✅ Implemented
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
**Project:** Mila - Membership Management System
|
**Project:** Mila - Membership Management System
|
||||||
**Feature:** Membership Fee Management
|
**Feature:** Membership Fee Management
|
||||||
**Version:** 1.0
|
**Version:** 1.0
|
||||||
**Last Updated:** 2025-11-27
|
**Last Updated:** 2026-01-13
|
||||||
**Status:** Concept - Ready for Review
|
**Status:** ✅ Implemented
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
**Version:** 2.0 (Clean Rewrite)
|
**Version:** 2.0 (Clean Rewrite)
|
||||||
**Date:** 2025-01-13
|
**Date:** 2025-01-13
|
||||||
**Status:** Ready for Implementation
|
**Last Updated:** 2026-01-13
|
||||||
|
**Status:** ✅ Implemented (2026-01-08, PR #346, closes #345)
|
||||||
**Related Documents:**
|
**Related Documents:**
|
||||||
- [Overview](./roles-and-permissions-overview.md) - High-level concepts for stakeholders
|
- [Overview](./roles-and-permissions-overview.md) - High-level concepts for stakeholders
|
||||||
- [Implementation Plan](./roles-and-permissions-implementation-plan.md) - Step-by-step implementation guide
|
- [Implementation Plan](./roles-and-permissions-implementation-plan.md) - Step-by-step implementation guide
|
||||||
|
|
@ -1555,7 +1556,7 @@ end
|
||||||
**Navbar with conditional links:**
|
**Navbar with conditional links:**
|
||||||
|
|
||||||
```heex
|
```heex
|
||||||
<!-- lib/mv_web/components/layouts/navbar.html.heex -->
|
<!-- Note: Navbar has been replaced with Sidebar (lib/mv_web/components/layouts/sidebar.ex) -->
|
||||||
<nav class="navbar">
|
<nav class="navbar">
|
||||||
<!-- Always visible -->
|
<!-- Always visible -->
|
||||||
<.link navigate="/">Home</.link>
|
<.link navigate="/">Home</.link>
|
||||||
|
|
@ -2484,7 +2485,8 @@ iex> MvWeb.Authorization.can_access_page?(user, "/members/new")
|
||||||
---
|
---
|
||||||
|
|
||||||
**Document Version:** 2.0 (Clean Rewrite)
|
**Document Version:** 2.0 (Clean Rewrite)
|
||||||
**Last Updated:** 2025-01-13
|
**Last Updated:** 2026-01-13
|
||||||
|
**Implementation Status:** ✅ Complete (2026-01-08)
|
||||||
**Status:** Ready for Implementation
|
**Status:** Ready for Implementation
|
||||||
|
|
||||||
**Changes from V1:**
|
**Changes from V1:**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
**Version:** 2.0 (Clean Rewrite)
|
**Version:** 2.0 (Clean Rewrite)
|
||||||
**Date:** 2025-01-13
|
**Date:** 2025-01-13
|
||||||
**Status:** Ready for Implementation
|
**Last Updated:** 2026-01-13
|
||||||
|
**Status:** ✅ Implemented (2026-01-08, PR #346, closes #345)
|
||||||
**Related Documents:**
|
**Related Documents:**
|
||||||
- [Overview](./roles-and-permissions-overview.md) - High-level concepts
|
- [Overview](./roles-and-permissions-overview.md) - High-level concepts
|
||||||
- [Architecture](./roles-and-permissions-architecture.md) - Technical specification
|
- [Architecture](./roles-and-permissions-architecture.md) - Technical specification
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
**Project:** Mila - Membership Management System
|
**Project:** Mila - Membership Management System
|
||||||
**Feature:** Role-Based Access Control (RBAC) with Hardcoded Permission Sets
|
**Feature:** Role-Based Access Control (RBAC) with Hardcoded Permission Sets
|
||||||
**Version:** 2.0
|
**Version:** 2.0
|
||||||
**Last Updated:** 2025-11-13
|
**Last Updated:** 2026-01-13
|
||||||
**Status:** Architecture Design - MVP Approach
|
**Status:** ✅ Implemented (2026-01-08, PR #346, closes #345)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
# Sidebar Analysis - Current State
|
# Sidebar Analysis - Current State
|
||||||
|
|
||||||
**Erstellt:** 2025-12-16
|
**Erstellt:** 2025-12-16
|
||||||
**Status:** Analyse für Neuimplementierung
|
**Last Updated:** 2026-01-13
|
||||||
|
**Status:** ⚠️ Veraltet - Sidebar wurde bereits implementiert (2026-01-12, PR #260)
|
||||||
**Autor:** Cursor AI Assistant
|
**Autor:** Cursor AI Assistant
|
||||||
|
|
||||||
|
> **Hinweis:** Diese Dokumentation beschreibt den Zustand VOR der Sidebar-Implementierung. Die Sidebar wurde erfolgreich implementiert und ist jetzt funktionsfähig. Siehe `sidebar-requirements-v2.md` für die finale Spezifikation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Executive Summary
|
## Executive Summary
|
||||||
|
|
||||||
Die aktuelle Sidebar-Implementierung verwendet **nicht existierende Custom-CSS-Variants** (`is-drawer-close:` und `is-drawer-open:`), was zu einer defekten Implementierung führt. Die Sidebar ist strukturell basierend auf DaisyUI's Drawer-Komponente, aber die responsive und state-basierte Funktionalität ist nicht funktionsfähig.
|
~~Die aktuelle Sidebar-Implementierung verwendet **nicht existierende Custom-CSS-Variants** (`is-drawer-close:` und `is-drawer-open:`), was zu einer defekten Implementierung führt. Die Sidebar ist strukturell basierend auf DaisyUI's Drawer-Komponente, aber die responsive und state-basierte Funktionalität ist nicht funktionsfähig.~~
|
||||||
|
|
||||||
**Kritisches Problem:** Die im Code verwendeten Variants `is-drawer-close:*` und `is-drawer-open:*` sind **nicht in Tailwind konfiguriert**, was bedeutet, dass diese Klassen beim Build ignoriert werden.
|
**Status:** Diese Analyse beschreibt Probleme, die bereits behoben wurden. Die Sidebar ist jetzt vollständig implementiert und funktionsfähig.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
**Erstellt:** 2025-12-16
|
**Erstellt:** 2025-12-16
|
||||||
**Version:** 2.0
|
**Version:** 2.0
|
||||||
**Status:** Spezifikation für Neuimplementierung
|
**Last Updated:** 2026-01-13
|
||||||
|
**Status:** ✅ Implementiert (2026-01-12, PR #260)
|
||||||
**Basierend auf:** `sidebar-analysis-current-state.md`, `daisyui-drawer-pattern.md`
|
**Basierend auf:** `sidebar-analysis-current-state.md`, `daisyui-drawer-pattern.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
# Analyse der fehlschlagenden Tests
|
|
||||||
|
|
||||||
## Übersicht
|
|
||||||
|
|
||||||
**Gesamtanzahl fehlschlagender Tests:** 5
|
|
||||||
- **show_test.exs:** 1 Fehler
|
|
||||||
- **sidebar_test.exs:** 4 Fehler
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Kategorisierung
|
|
||||||
|
|
||||||
### Kategorie 1: Test-Assertions passen nicht zur Implementierung (4 Tests)
|
|
||||||
|
|
||||||
Diese Tests erwarten bestimmte Werte/Attribute, die in der aktuellen Implementierung anders sind oder fehlen.
|
|
||||||
|
|
||||||
### Kategorie 2: Datenbank-Isolation Problem (1 Test)
|
|
||||||
|
|
||||||
Ein Test schlägt fehl, weil die Datenbank nicht korrekt isoliert ist.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Detaillierte Analyse
|
|
||||||
|
|
||||||
### 1. `show_test.exs` - Custom Fields Sichtbarkeit
|
|
||||||
|
|
||||||
**Test:** `does not display Custom Fields section when no custom fields exist` (Zeile 112)
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
- Der Test erwartet, dass die "Custom Fields" Sektion NICHT angezeigt wird, wenn keine Custom Fields existieren
|
|
||||||
- Die Sektion wird aber angezeigt, weil in der Datenbank noch Custom Fields von anderen Tests vorhanden sind
|
|
||||||
|
|
||||||
**Ursache:**
|
|
||||||
- Die LiveView lädt alle Custom Fields aus der Datenbank (Zeile 238-242 in `show.ex`)
|
|
||||||
- Die Test-Datenbank wird nicht zwischen Tests geleert
|
|
||||||
- Da `async: false` verwendet wird, sollten die Tests sequenziell laufen, aber Custom Fields bleiben in der Datenbank
|
|
||||||
|
|
||||||
**Kategorie:** Datenbank-Isolation Problem
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. `sidebar_test.exs` - Settings Link
|
|
||||||
|
|
||||||
**Test:** `T3.1: renders flat menu items with icons and labels` (Zeile 174)
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
- Test erwartet `href="#"` für Settings
|
|
||||||
- Tatsächlicher Wert: `href="/settings"`
|
|
||||||
|
|
||||||
**Ursache:**
|
|
||||||
- Die Implementierung verwendet einen echten Link `~p"/settings"` (Zeile 100 in `sidebar.ex`)
|
|
||||||
- Der Test erwartet einen Placeholder-Link `href="#"`
|
|
||||||
|
|
||||||
**Kategorie:** Test-Assertion passt nicht zur Implementierung
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. `sidebar_test.exs` - Drawer Overlay CSS-Klasse
|
|
||||||
|
|
||||||
**Test:** `drawer overlay is present` (Zeile 747)
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
- Test sucht nach exakt `class="drawer-overlay"`
|
|
||||||
- Tatsächlicher Wert: `class="drawer-overlay lg:hidden focus:outline-none focus:ring-2 focus:ring-primary"`
|
|
||||||
|
|
||||||
**Ursache:**
|
|
||||||
- Der Test verwendet eine exakte String-Suche (`~s(class="drawer-overlay")`)
|
|
||||||
- Die Implementierung hat mehrere CSS-Klassen
|
|
||||||
|
|
||||||
**Kategorie:** Test-Assertion passt nicht zur Implementierung
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. `sidebar_test.exs` - Toggle Button ARIA-Attribut
|
|
||||||
|
|
||||||
**Test:** `T5.2: toggle button has correct ARIA attributes` (Zeile 324)
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
- Test erwartet `aria-controls="main-sidebar"` am Toggle-Button
|
|
||||||
- Das Attribut fehlt in der Implementierung (Zeile 45-65 in `sidebar.ex`)
|
|
||||||
|
|
||||||
**Ursache:**
|
|
||||||
- Das `aria-controls` Attribut wurde nicht in der Implementierung hinzugefügt
|
|
||||||
- Der Test erwartet es für bessere Accessibility
|
|
||||||
|
|
||||||
**Kategorie:** Test-Assertion passt nicht zur Implementierung (Accessibility-Feature fehlt)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. `sidebar_test.exs` - Contribution Settings Link
|
|
||||||
|
|
||||||
**Test:** `sidebar structure is complete with all sections` (Zeile 501)
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
- Test erwartet Link `/contribution_settings`
|
|
||||||
- Tatsächlicher Link: `/membership_fee_settings`
|
|
||||||
|
|
||||||
**Ursache:**
|
|
||||||
- Der Test hat eine veraltete/inkorrekte Erwartung
|
|
||||||
- Die Implementierung verwendet `/membership_fee_settings` (Zeile 96 in `sidebar.ex`)
|
|
||||||
|
|
||||||
**Kategorie:** Test-Assertion passt nicht zur Implementierung (veralteter Test)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Lösungsvorschläge
|
|
||||||
|
|
||||||
### Lösung 1: `show_test.exs` - Custom Fields Sichtbarkeit
|
|
||||||
|
|
||||||
**Option A: Test-Datenbank bereinigen (Empfohlen)**
|
|
||||||
- Im `setup` Block alle Custom Fields löschen, bevor der Test läuft
|
|
||||||
- Oder: Explizit prüfen, dass keine Custom Fields existieren
|
|
||||||
|
|
||||||
**Option B: Test anpassen**
|
|
||||||
- Den Test so anpassen, dass er explizit alle Custom Fields löscht
|
|
||||||
- Oder: Die LiveView-Logik ändern, um nur Custom Fields zu laden, die tatsächlich existieren
|
|
||||||
|
|
||||||
**Empfehlung:** Option A - Im Test-Setup alle Custom Fields löschen
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
setup do
|
|
||||||
# Clean up any existing custom fields
|
|
||||||
Mv.Membership.CustomField
|
|
||||||
|> Ash.read!()
|
|
||||||
|> Enum.each(&Ash.destroy!/1)
|
|
||||||
|
|
||||||
# Create test member
|
|
||||||
{:ok, member} = ...
|
|
||||||
%{member: member}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Lösung 2: `sidebar_test.exs` - Settings Link
|
|
||||||
|
|
||||||
**Option A: Test anpassen (Empfohlen)**
|
|
||||||
- Test ändern, um `href="/settings"` zu erwarten statt `href="#"`
|
|
||||||
|
|
||||||
**Option B: Implementierung ändern**
|
|
||||||
- Settings-Link zu `href="#"` ändern (nicht empfohlen, da es ein echter Link sein sollte)
|
|
||||||
|
|
||||||
**Empfehlung:** Option A - Test anpassen
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# Zeile 190 ändern von:
|
|
||||||
assert html =~ ~s(href="#")
|
|
||||||
# zu:
|
|
||||||
assert html =~ ~s(href="/settings")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Lösung 3: `sidebar_test.exs` - Drawer Overlay CSS-Klasse
|
|
||||||
|
|
||||||
**Option A: Test anpassen (Empfohlen)**
|
|
||||||
- Test ändern, um nach der Klasse in der Klasse-Liste zu suchen (mit `has_class?` Helper)
|
|
||||||
|
|
||||||
**Option B: Regex verwenden**
|
|
||||||
- Regex verwenden, um die Klasse zu finden
|
|
||||||
|
|
||||||
**Empfehlung:** Option A - Test anpassen
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# Zeile 752 ändern von:
|
|
||||||
assert html =~ ~s(class="drawer-overlay")
|
|
||||||
# zu:
|
|
||||||
assert has_class?(html, "drawer-overlay")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Lösung 4: `sidebar_test.exs` - Toggle Button ARIA-Attribut
|
|
||||||
|
|
||||||
**Option A: Implementierung anpassen (Empfohlen)**
|
|
||||||
- `aria-controls="main-sidebar"` zum Toggle-Button hinzufügen
|
|
||||||
|
|
||||||
**Option B: Test anpassen**
|
|
||||||
- Test entfernen oder als optional markieren (nicht empfohlen für Accessibility)
|
|
||||||
|
|
||||||
**Empfehlung:** Option A - Implementierung anpassen
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# In sidebar.ex Zeile 45-52, aria-controls hinzufügen:
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="sidebar-toggle"
|
|
||||||
class="hidden lg:flex ml-auto btn btn-ghost btn-sm btn-square"
|
|
||||||
aria-label={gettext("Toggle sidebar")}
|
|
||||||
aria-controls="main-sidebar"
|
|
||||||
aria-expanded="true"
|
|
||||||
onclick="toggleSidebar()"
|
|
||||||
>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Lösung 5: `sidebar_test.exs` - Contribution Settings Link
|
|
||||||
|
|
||||||
**Option A: Test anpassen (Empfohlen)**
|
|
||||||
- Test ändern, um `/membership_fee_settings` statt `/contribution_settings` zu erwarten
|
|
||||||
|
|
||||||
**Option B: Link hinzufügen**
|
|
||||||
- Einen neuen Link `/contribution_settings` hinzufügen (nicht empfohlen, da redundant)
|
|
||||||
|
|
||||||
**Empfehlung:** Option A - Test anpassen
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
# Zeile 519 ändern von:
|
|
||||||
"/contribution_settings",
|
|
||||||
# zu:
|
|
||||||
# Entfernen oder durch "/membership_fee_settings" ersetzen
|
|
||||||
# (da "/membership_fee_settings" bereits in Zeile 518 vorhanden ist)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Zusammenfassung der empfohlenen Änderungen
|
|
||||||
|
|
||||||
1. **show_test.exs:** Custom Fields im Setup löschen
|
|
||||||
2. **sidebar_test.exs (T3.1):** Settings-Link Assertion anpassen
|
|
||||||
3. **sidebar_test.exs (drawer overlay):** CSS-Klasse-Suche mit Helper-Funktion
|
|
||||||
4. **sidebar_test.exs (T5.2):** `aria-controls` Attribut zur Implementierung hinzufügen
|
|
||||||
5. **sidebar_test.exs (edge cases):** Falschen Link aus erwarteter Liste entfernen
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Priorisierung
|
|
||||||
|
|
||||||
1. **Hoch:** Lösung 1 (show_test.exs) - Datenbank-Isolation ist wichtig
|
|
||||||
2. **Mittel:** Lösung 4 (ARIA-Attribut) - Accessibility-Verbesserung
|
|
||||||
3. **Niedrig:** Lösungen 2, 3, 5 - Einfache Test-Anpassungen
|
|
||||||
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
# Test Status: Membership Fee UI Components
|
|
||||||
|
|
||||||
**Date:** 2025-01-XX
|
|
||||||
**Status:** Tests Written - Implementation Complete
|
|
||||||
|
|
||||||
## Übersicht
|
|
||||||
|
|
||||||
Alle Tests für die Membership Fee UI-Komponenten wurden geschrieben. Die Tests sind TDD-konform geschrieben und sollten erfolgreich laufen, da die Implementation bereits vorhanden ist.
|
|
||||||
|
|
||||||
## Test-Dateien
|
|
||||||
|
|
||||||
### Helper Module Tests
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/helpers/membership_fee_helpers_test.exs`
|
|
||||||
- ✅ format_currency/1 formats correctly
|
|
||||||
- ✅ format_interval/1 formats all interval types
|
|
||||||
- ✅ format_cycle_range/2 formats date ranges correctly
|
|
||||||
- ✅ get_last_completed_cycle/2 returns correct cycle
|
|
||||||
- ✅ get_current_cycle/2 returns correct cycle
|
|
||||||
- ✅ status_color/1 returns correct color classes
|
|
||||||
- ✅ status_icon/1 returns correct icon names
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/member_live/index/membership_fee_status_test.exs`
|
|
||||||
- ✅ load_cycles_for_members/2 efficiently loads cycles
|
|
||||||
- ✅ get_cycle_status_for_member/2 returns correct status
|
|
||||||
- ✅ format_cycle_status_badge/1 returns correct badge
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
### Member List View Tests
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/member_live/index_membership_fee_status_test.exs`
|
|
||||||
- ✅ Status column displays correctly
|
|
||||||
- ✅ Shows last completed cycle status by default
|
|
||||||
- ✅ Toggle switches to current cycle view
|
|
||||||
- ✅ Color coding for paid/unpaid/suspended
|
|
||||||
- ✅ Filter "Unpaid in last cycle" works
|
|
||||||
- ✅ Filter "Unpaid in current cycle" works
|
|
||||||
- ✅ Handles members without cycles gracefully
|
|
||||||
- ✅ Loads cycles efficiently without N+1 queries
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
### Member Detail View Tests
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/member_live/show_membership_fees_test.exs`
|
|
||||||
- ✅ Cycles table displays all cycles
|
|
||||||
- ✅ Table columns show correct data
|
|
||||||
- ✅ Membership fee type dropdown shows only same-interval types
|
|
||||||
- ✅ Warning displayed if different interval selected
|
|
||||||
- ✅ Status change actions work (mark as paid/suspended/unpaid)
|
|
||||||
- ✅ Cycle regeneration works
|
|
||||||
- ✅ Handles members without membership fee type gracefully
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
### Membership Fee Types Admin Tests
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/live/membership_fee_type_live/index_test.exs`
|
|
||||||
- ✅ List displays all types with correct data
|
|
||||||
- ✅ Member count column shows correct count
|
|
||||||
- ✅ Create button navigates to form
|
|
||||||
- ✅ Edit button per row navigates to edit form
|
|
||||||
- ✅ Delete button disabled if type is in use
|
|
||||||
- ✅ Delete button works if type is not in use
|
|
||||||
- ✅ Only admin can access
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/live/membership_fee_type_live/form_test.exs`
|
|
||||||
- ✅ Create form works
|
|
||||||
- ✅ Edit form loads existing type data
|
|
||||||
- ✅ Interval field editable on create
|
|
||||||
- ✅ Interval field grayed out on edit
|
|
||||||
- ✅ Amount change warning displays on edit
|
|
||||||
- ✅ Amount change warning shows correct affected member count
|
|
||||||
- ✅ Amount change can be confirmed
|
|
||||||
- ✅ Amount change can be cancelled
|
|
||||||
- ✅ Validation errors display correctly
|
|
||||||
- ✅ Only admin can access
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
### Member Form Tests
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/member_live/form_membership_fee_type_test.exs`
|
|
||||||
- ✅ Membership fee type dropdown displays in form
|
|
||||||
- ✅ Shows available types
|
|
||||||
- ✅ Filters to same interval types if member has type
|
|
||||||
- ✅ Warning displayed if different interval selected
|
|
||||||
- ✅ Warning cleared if same interval selected
|
|
||||||
- ✅ Form saves with selected membership fee type
|
|
||||||
- ✅ New members get default membership fee type
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
|
|
||||||
**Datei:** `test/mv_web/member_live/membership_fee_integration_test.exs`
|
|
||||||
- ✅ End-to-end: Create type → Assign to member → View cycles → Change status
|
|
||||||
- ✅ End-to-end: Change member type → Cycles regenerate
|
|
||||||
- ✅ End-to-end: Update settings → New members get default type
|
|
||||||
- ✅ End-to-end: Delete cycle → Confirmation → Cycle deleted
|
|
||||||
- ✅ End-to-end: Edit cycle amount → Modal → Amount updated
|
|
||||||
|
|
||||||
**Status:** Alle Tests sollten erfolgreich sein (Implementation vorhanden)
|
|
||||||
|
|
||||||
## Test-Ausführung
|
|
||||||
|
|
||||||
Alle Tests können mit folgenden Befehlen ausgeführt werden:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Alle Tests
|
|
||||||
mix test
|
|
||||||
|
|
||||||
# Nur Membership Fee Tests
|
|
||||||
mix test test/mv_web/helpers/membership_fee_helpers_test.exs
|
|
||||||
mix test test/mv_web/member_live/
|
|
||||||
mix test test/mv_web/live/membership_fee_type_live/
|
|
||||||
|
|
||||||
# Mit Coverage
|
|
||||||
mix test --cover
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bekannte Probleme
|
|
||||||
|
|
||||||
Keine bekannten Probleme. Alle Tests sollten erfolgreich laufen, da die Implementation bereits vorhanden ist.
|
|
||||||
|
|
||||||
## Nächste Schritte
|
|
||||||
|
|
||||||
1. ✅ Tests geschrieben
|
|
||||||
2. ⏳ Tests ausführen und verifizieren
|
|
||||||
3. ⏳ Eventuelle Anpassungen basierend auf Test-Ergebnissen
|
|
||||||
4. ⏳ Code-Review durchführen
|
|
||||||
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
# Sidebar Neuimplementierung - Schritt-für-Schritt Anleitung
|
# Sidebar Neuimplementierung - Schritt-für-Schritt Anleitung
|
||||||
|
|
||||||
**Erstellt:** 2025-12-16
|
**Erstellt:** 2025-12-16
|
||||||
**Status:** Bereit zur Umsetzung
|
**Last Updated:** 2026-01-13
|
||||||
|
**Status:** ⚠️ Veraltet - Sidebar wurde bereits implementiert (2026-01-12, PR #260)
|
||||||
**Strategie:** Sequenzielle Tasks mit frischem Kontext pro Task
|
**Strategie:** Sequenzielle Tasks mit frischem Kontext pro Task
|
||||||
|
|
||||||
|
> **Hinweis:** Diese Implementierungs-Anleitung wurde durch die tatsächliche Implementierung obsolet. Die Sidebar wurde erfolgreich implementiert. Siehe `sidebar-requirements-v2.md` für die finale Spezifikation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Übersicht
|
## Übersicht
|
||||||
|
|
||||||
Diese Anleitung zerlegt die komplexe Sidebar-Implementierung in 13 beherrschbare Subtasks. Jeder Task wird mit einem frischen Cursor-Agent im Auto-Mode (oder Sonnet 4.5 für komplexere Aufgaben) umgesetzt.
|
~~Diese Anleitung zerlegt die komplexe Sidebar-Implementierung in 13 beherrschbare Subtasks. Jeder Task wird mit einem frischen Cursor-Agent im Auto-Mode (oder Sonnet 4.5 für komplexere Aufgaben) umgesetzt.~~
|
||||||
|
|
||||||
**Ziel:** Saubere, wartbare Sidebar-Implementierung nahe am DaisyUI-Standard ohne Custom-Variants oder Speziallösungen.
|
**Status:** Diese Implementierungs-Anleitung wurde durch die tatsächliche Implementierung obsolet. Die Sidebar ist jetzt vollständig implementiert und funktionsfähig.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1572,5 +1575,5 @@ Aktualisiere die Projekt-Dokumentation basierend auf der neuen Sidebar-Implement
|
||||||
---
|
---
|
||||||
|
|
||||||
**Version:** 1.0
|
**Version:** 1.0
|
||||||
**Letzte Aktualisierung:** 2025-12-16
|
**Letzte Aktualisierung:** 2026-01-13 (als veraltet markiert)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue