Spaces Admin CRUD — Design Spec
For agentic workers: REQUIRED SUB-SKILL: Use compose:plan to create implementation plan from this spec.
Goal: Add complete CRUD management for Spaces, Space Groups, and Tags to the admin panel.
Architecture: Follow the existing 3-layer pattern (apiClient → hooks → components). Backend needs search/filter/ordering added to ViewSets. Frontend builds from scratch using the same patterns as Users CRUD.
Tech Stack: React 19, TanStack Router, TanStack Query, shadcn/ui (@repo/ui), Tailwind CSS
[S1] Problem
The admin panel has no way to manage Spaces, Space Groups, or Tags. These are core platform entities that admins need to create, edit, and delete. The backend has full CRUD endpoints but no search/filter/ordering configured for admin use.
[S2] Solution overview
Add three new admin CRUD screens following the established 3-layer pattern:
- Spaces — list with search/filter/sort, create/edit form dialog, delete confirmation, detail view
- Space Groups — list with search/sort, create/edit form dialog, delete confirmation
- Tags — list with search/sort, create/edit form dialog, delete confirmation
Backend changes: Add filter_backends, search_fields, ordering_fields, and filterset_fields to SpaceViewSet, SpaceGroupViewSet, and TagViewSet.
[S3] Backend changes
SpaceViewSet additions
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['space_type', 'access_level', 'space_group', 'is_welcome_space']
search_fields = ['name', 'description']
ordering_fields = ['name', 'order', 'created_at']SpaceGroupViewSet additions
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['name']
ordering_fields = ['name', 'order']TagViewSet additions
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['name']
ordering_fields = ['name'][S4] Frontend types
Shared types file types/space.ts:
Space— matches DRF serializer fields (id, space_group, name, slug, description, space_type, access_level, access_groups, is_welcome_space, order)SpaceGroup— id, name, slug, orderTag— id, name, slug
[S5] Space CRUD
List view
- Paginated data table with columns: Name, Slug, Type, Access, Group, Order, Actions
- Server-side search by name/description
- Filter by space_type, access_level
- Sort by name, order, created_at
- Row actions: Edit, Delete
- Header: "Novo Espaço" button opens create dialog
Create/Edit dialog (SpaceFormDialog)
- Fields: name (required), slug (auto-generated, editable), description, space_type (select), access_level (select), space_group (select, loads from API), is_welcome_space (checkbox, only when space_type=basic), order (number)
- On create: POST to
/api/v1/spaces/ - On edit: PATCH to
/api/v1/spaces/{id}/ - Toast feedback on success/error
Delete dialog
- Confirmation with space name
- DELETE to
/api/v1/spaces/{id}/ - Toast feedback
Detail view (/spaces/$spaceId)
- Shows all space fields
- Edit and Delete action buttons
- Back link to list
[S6] Space Group CRUD
List view
- Paginated data table: Name, Slug, Order, Actions
- Search by name
- Sort by name, order
- Row actions: Edit, Delete
Create/Edit dialog (SpaceGroupFormDialog)
- Fields: name (required), slug (auto-generated, editable), order (number)
- POST/PATCH to
/api/v1/space-groups/
Delete dialog
- Confirmation with group name
- DELETE to
/api/v1/space-groups/{id}/
[S7] Tag CRUD
List view
- Paginated data table: Name, Slug, Actions
- Search by name
- Sort by name
- Row actions: Edit, Delete
Create/Edit dialog (TagFormDialog)
- Fields: name (required), slug (auto-generated, editable)
- POST/PATCH to
/api/v1/tags/
Delete dialog
- Confirmation with tag name
- DELETE to
/api/v1/tags/{id}/
[S8] Sidebar navigation
Update sidebar-data.ts:
- Replace placeholder "Posts" item with "Espaços" →
/spaces - Add "Groups" →
/space-groupsunder Conteúdo - Add "Tags" →
/tagsunder Conteúdo - Remove
disabled: truefrom these items - Keep Events, Courses, Reports, Settings as disabled placeholders
[S9] Route registration
Add routes to TanStack Router file-based routing:
routes/spaces/index.tsx→/spacesroutes/spaces/$spaceId.tsx→/spaces/$spaceIdroutes/space-groups.tsx→/space-groupsroutes/tags.tsx→/tags
[S10] Banner/Icon
Banner and icon uploads are out of scope for this iteration. The DRF serializer does not expose these fields. Image management will be handled separately if needed.
[S11] Testing
- TypeScript compilation passes (
bunx tsc --noEmit) - All CRUD operations work against the real backend API
- Toast feedback on all mutations
- Skeleton loading states on all list/detail views