Implemented backend API and frontend components for group creation and management.
## Backend
- **Models**: Group, GroupMember, GroupInvite, JoinRequest
- **API**: Full CRUD for groups, member management (add/remove/role transfer), invite link generation, invite-based joining, join request flow
- **Permissions**: GroupAdmin and GroupMember permission classes
## Frontend
- GroupsListPage - list all users groups
- GroupDetailPage - view group details, manage members, create/revoke invites, transfer admin
- CreateGroupPage - create a new group
- JoinGroupPage - join via invite code
- Navigation links added to Library header
## Ref
https://gitea-dev.codescripters.org/HermesFactory/cloud-reader/issues/28
These MUST be resolved before this PR can be merged.
[File: backend/apps/groups/views.py | Lines 860-872]race condition / missing transaction atomicity
The join action has a race condition. Between checking use_count >= max_uses and incrementing use_count, multiple concurrent requests could join with an exhausted invite. Use F() expressions or select_for_update() to prevent this. Also wrap the member creation in a transaction.atomic() block.
[File: frontend/src/pages/GroupDetailPage.tsx | Line 1451]broken member self-check logic
The condition member.user_id !== Number(user?.email ? undefined : undefined) is nonsensical and evaluates to incorrect behavior. The intent was to hide admin controls when viewing your own entry. Fix to: member.user_id !== user?.id.
[File: frontend/src/api/groups.ts | Lines 948-950]any type on API response
The listGroups return type infers any due to improper type handling. The paginated response structure {count, results} differs from what the type claims. Add proper type guards or normalize the API response.
[Files: GroupDetailPage.tsx, GroupsListPage.tsx, JoinGroupPage.tsx, CreateGroupPage.tsx]inline styles violation
All components use inline styles instead of CSS modules, styled-components, or a consistent styling solution. This violates project standards. Refactor to use external CSS or a styling system.
Admin approval flow for join requests - NOT IMPLEMENTED
## Reid's Review — PR #34
**Verdict:** 🔴 Changes Required
---
### 🔴 Blocking Issues
> These MUST be resolved before this PR can be merged.
- **[File: backend/apps/groups/views.py | Lines 860-872]** `race condition / missing transaction atomicity`
The join action has a race condition. Between checking `use_count >= max_uses` and incrementing `use_count`, multiple concurrent requests could join with an exhausted invite. Use F() expressions or select_for_update() to prevent this. Also wrap the member creation in a transaction.atomic() block.
- **[File: frontend/src/pages/GroupDetailPage.tsx | Line 1451]** `broken member self-check logic`
The condition `member.user_id !== Number(user?.email ? undefined : undefined)` is nonsensical and evaluates to incorrect behavior. The intent was to hide admin controls when viewing your own entry. Fix to: `member.user_id !== user?.id`.
- **[File: frontend/src/api/groups.ts | Lines 948-950]** `any type on API response`
The listGroups return type infers `any` due to improper type handling. The paginated response structure `{count, results}` differs from what the type claims. Add proper type guards or normalize the API response.
- **[Files: GroupDetailPage.tsx, GroupsListPage.tsx, JoinGroupPage.tsx, CreateGroupPage.tsx]** `inline styles violation`
All components use inline styles instead of CSS modules, styled-components, or a consistent styling solution. This violates project standards. Refactor to use external CSS or a styling system.
---
### 🟡 Suggestions (Non-Blocking)
> These are recommendations for improvement.
- **[File: backend/apps/groups/views.py | Lines 813-826]** Consider reusing JoinViaInviteSerializer for validate_invite action.
- **[File: backend/apps/groups/views.py | Lines 660-674]** Wrap role transfer in transaction.atomic().
- **[File: backend/apps/groups/models.py]** Consider db_index on GroupMember.role.
---
### 📋 AC Coverage
> Based on linked issue #28
- [x] Group creation with admin role assignment
- [x] Group list/detail views with member counts
- [x] Invite link generation
- [x] Join via invite confirmation
- [x] Admin remove members
- [x] Admin transfer role
- [x] Member leave group (dissolution if last admin)
- [ ] Admin approval flow for join requests - NOT IMPLEMENTED
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implemented backend API and frontend components for group creation and management.
Backend
Frontend
Ref
#28
Reid's Review — PR #34
Verdict: 🔴 Changes Required
🔴 Blocking Issues
[File: backend/apps/groups/views.py | Lines 860-872]
race condition / missing transaction atomicityThe join action has a race condition. Between checking
use_count >= max_usesand incrementinguse_count, multiple concurrent requests could join with an exhausted invite. Use F() expressions or select_for_update() to prevent this. Also wrap the member creation in a transaction.atomic() block.[File: frontend/src/pages/GroupDetailPage.tsx | Line 1451]
broken member self-check logicThe condition
member.user_id !== Number(user?.email ? undefined : undefined)is nonsensical and evaluates to incorrect behavior. The intent was to hide admin controls when viewing your own entry. Fix to:member.user_id !== user?.id.[File: frontend/src/api/groups.ts | Lines 948-950]
any type on API responseThe listGroups return type infers
anydue to improper type handling. The paginated response structure{count, results}differs from what the type claims. Add proper type guards or normalize the API response.[Files: GroupDetailPage.tsx, GroupsListPage.tsx, JoinGroupPage.tsx, CreateGroupPage.tsx]
inline styles violationAll components use inline styles instead of CSS modules, styled-components, or a consistent styling solution. This violates project standards. Refactor to use external CSS or a styling system.
🟡 Suggestions (Non-Blocking)
[File: backend/apps/groups/views.py | Lines 813-826] Consider reusing JoinViaInviteSerializer for validate_invite action.
[File: backend/apps/groups/views.py | Lines 660-674] Wrap role transfer in transaction.atomic().
[File: backend/apps/groups/models.py] Consider db_index on GroupMember.role.
📋 AC Coverage