Concept for Groups #354

Closed
simon wants to merge 118 commits from feature/concept-groups into main
Showing only changes of commit 1c7c56130d - Show all commits

View file

@ -117,10 +117,10 @@ CREATE TABLE groups (
description TEXT,
inserted_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
CONSTRAINT groups_name_unique UNIQUE (name)
CONSTRAINT groups_name_unique UNIQUE (LOWER(name))
);
CREATE INDEX groups_name_index ON groups(name);
CREATE INDEX groups_name_index ON groups(LOWER(name));
```
**Attributes:**
@ -130,7 +130,7 @@ CREATE INDEX groups_name_index ON groups(name);
- `inserted_at` / `updated_at` - Timestamps
**Constraints:**
- `name` must be unique
- `name` must be unique (case-insensitive, using LOWER(name))
- `name` cannot be null
- `name` max length: 100 characters
- `description` max length: 500 characters
@ -194,7 +194,7 @@ end
- `destroy` - Delete group (with confirmation)
**Validations:**
- `name` required, unique, max 100 chars
- `name` required, unique (case-insensitive), max 100 chars
- `description` optional, max 500 chars
#### `Mv.Membership.MemberGroup`
@ -319,7 +319,7 @@ end
- Display group badges for each member
- Badge shows group name
- Multiple badges if member in multiple groups
- Click badge to filter by that group
- *(Optional)* Click badge to filter by that group (enhanced UX, can be added later)
**Filtering:**
- Dropdown/select to filter by group
@ -374,7 +374,11 @@ end
</span>
```
**Clickable Group Badge (for filtering):**
**Clickable Group Badge (for filtering) - Optional:**
**Note:** This is an optional enhancement. The dropdown filter provides the same functionality. The clickable badge improves UX by showing the active filter visually and allowing quick removal.
**Estimated effort:** 1.5-2.5 hours
```heex
<button
@ -712,13 +716,13 @@ Das MVP umfasst die **grundlegenden Funktionen**, die notwendig sind, um Gruppen
7. ✅ Nach Gruppen filtern
8. ✅ Nach Gruppen sortieren
9. ✅ Gruppen in Mitgliederdetail anzeigen
10. ✅ Gruppen in Mitgliedersuche (automatisch via search_vector)
**Nicht im MVP:**
simon marked this conversation as resolved

I think thats also part of MVP right? :)

I think thats also part of MVP right? :)

the search yes, the aspects above I'd leave out for the mvp

think that also matches what we decided in the planning meeting?
grafik

the search yes, the aspects above I'd leave out for the mvp think that also matches what we decided in the planning meeting? ![grafik](/attachments/95724d2a-44e4-49d4-8f8c-c68fcb14a679)

Yeah, I just meant the last line :)

Yeah, I just meant the last line :)
- ❌ Hierarchische Gruppen
- ❌ Rollen/Positionen in Gruppen
- ❌ Erweiterte Gruppenattribute (Datum, Status, etc.)
- ❌ Gruppen-spezifische Berechtigungen
- ❌ Gruppen in Mitgliedersuche (kann später kommen)
### Fachliche Einheiten (Vertikale Slices)
@ -846,11 +850,9 @@ Das MVP umfasst die **grundlegenden Funktionen**, die notwendig sind, um Gruppen
- ✅ Einheit 3: Gruppen-Verwaltungs-UI
- ✅ Einheit 4: Gruppen in Mitgliederübersicht
- ✅ Einheit 5: Gruppen in Mitgliederdetail
- ✅ Einheit 6: Gruppen in Mitgliedersuche (automatisch via search_vector)
- ✅ Einheit 7: Berechtigungen
**Optional für MVP (kann später kommen):**
- ⏸️ Einheit 6: Gruppen in Mitgliedersuche (kann in Phase 2 kommen)
**Total MVP Estimation:** 13-15h
### Implementierungsreihenfolge
@ -947,11 +949,13 @@ Jede fachliche Einheit kann als **separates Issue** umgesetzt werden:
3. Group filter dropdown
4. Group sorting
5. URL query param persistence
6. *(Optional)* Clickable group badges for filtering (enhanced UX)
**Deliverables:**
- Groups visible in member overview
- Filter by group
- Filter by group (via dropdown)
- Sort by group
- *(Optional)* Clickable badges for quick filtering
**Estimation:** 2-3h
@ -1046,12 +1050,14 @@ Jede fachliche Einheit kann als **separates Issue** umgesetzt werden:
- Group filter dropdown
- Group sorting
- URL persistence
- *(Optional)* Clickable group badges for filtering
**Acceptance Criteria:**
- Groups visible in member overview
- Can filter by group
- Can filter by group (via dropdown)
- Can sort by group
- Filter persists in URL
- *(Optional)* Can filter by clicking group badge
### Issue 4: Member Detail - Groups Display
**Type:** Frontend
@ -1069,13 +1075,16 @@ Jede fachliche Einheit kann als **separates Issue** umgesetzt werden:
**Type:** Backend
**Estimation:** 2h
**Tasks:**
- Update search vector trigger
- Extend fuzzy search
- Test search
- Update search vector trigger to include group names
- Extend fuzzy search to search group names
- Test search functionality
**Acceptance Criteria:**
- Group names searchable in member search
- Group names searchable in member search (automatic via search_vector)
- Search finds members by group name
- Search vector updates automatically when member-group associations change
**Note:** This is part of MVP as group names are automatically included in full-text search once the search_vector trigger is updated.
### Issue 6: Authorization
**Type:** Backend/Frontend
@ -1132,11 +1141,12 @@ defmodule Mv.Membership.GroupTest do
assert %{name: ["has already been taken"]} = errors_on(changeset)
end
test "name uniqueness is case-sensitive" do
test "name uniqueness is case-insensitive" do
Group.create!(%{name: "Vorstand"})
attrs = %{name: "VORSTAND"}
# For MVP: case-sensitive uniqueness
assert {:ok, _} = Group.create(attrs)
# Name uniqueness should be case-insensitive
assert {:error, changeset} = Group.create(attrs)
assert %{name: ["has already been taken"]} = errors_on(changeset)
end
test "allows description to be nil" do
@ -1458,8 +1468,8 @@ create table(:groups, primary_key: false) do
add :updated_at, :utc_datetime_usec, null: false
end
create unique_index(:groups, [:name])
create index(:groups, [:name])
create unique_index(:groups, [fragment("LOWER(name)")], name: :groups_name_unique)
create index(:groups, [fragment("LOWER(name)")], name: :groups_name_index)
```
**Migration 2: Create member_groups join table**