Coverage for src/lexigram/admin/resources/users.py: 0%
58 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
1"""
2User resource definition for Lexigram Admin.
4This module defines the UserResource class that provides admin interface
5for managing users with date range filtering capabilities.
6"""
8from __future__ import annotations
10from typing import TYPE_CHECKING, Any, cast
12from lexigram.admin.actions import (
13 CreateAction,
14 DeleteAction,
15 EditAction,
16 ImpersonateAction,
17 PermissionsAction,
18)
19from lexigram.admin.auth.entity import AdminUserEntity
20from lexigram.admin.resources.base import Resource
21from lexigram.admin.schema import BooleanField, DateField, SelectField, TextField
22from lexigram.admin.ui.filters import SelectFilter, ToggleFilter
23from lexigram.ui import BadgeColumn, DateColumn, TextColumn
25if TYPE_CHECKING:
26 from lexigram.admin.rbac.inventory import PermissionInventoryService
29class UserResource(Resource):
30 """
31 Admin resource for managing users.
33 Provides comprehensive user management with filtering by registration date
34 and last active date using DateRangeFilter.
35 """
37 model = cast("Any", AdminUserEntity)
38 name = "users"
39 label = "Users"
40 icon = "users"
41 category = "system"
42 service = None # Will be set by DI or registry
43 permission_inventory: PermissionInventoryService | None = None
44 """Grouped RBAC permission inventory; wired at mount time for the
45 per-user permission editor."""
46 # Secret/framework-managed columns must never render in generated forms
47 form_exclude_fields = (
48 "id",
49 "created_at",
50 "updated_at",
51 "hashed_password",
52 )
54 # UI Configuration
55 layout_type = "sidebar"
56 data_view = "tabular"
58 # New SchemaField declarations (canonical definition)
59 fields: list[Any] = [
60 TextField(name="name", sortable=True, searchable=True),
61 TextField(name="email", sortable=True, searchable=True),
62 SelectField(
63 name="role",
64 options=[
65 ("admin", "Admin"),
66 ("moderator", "Moderator"),
67 ("user", "User"),
68 ("guest", "Guest"),
69 ],
70 ),
71 SelectField(
72 name="status",
73 options=[
74 ("active", "Active"),
75 ("inactive", "Inactive"),
76 ("pending", "Pending"),
77 ("suspended", "Suspended"),
78 ],
79 ),
80 DateField(name="created_at", label="Registration Date", sortable=True),
81 DateField(name="last_active", sortable=True),
82 BooleanField(name="is_active", label="Active"),
83 ]
85 # Columns configuration (legacy — kept for backward compat)
86 columns: list[Any] = [
87 TextColumn("name").sortable().searchable().weight("semibold"),
88 TextColumn("email").sortable().searchable().copyable().color("blue"),
89 BadgeColumn(
90 "role",
91 colors={
92 "admin": "purple",
93 "moderator": "blue",
94 "user": "gray",
95 "guest": "slate",
96 },
97 ),
98 BadgeColumn(
99 "status",
100 colors={
101 "active": "green",
102 "inactive": "red",
103 "pending": "yellow",
104 "suspended": "orange",
105 },
106 ),
107 DateColumn("created_at", label="Registration Date").datetime().sortable(),
108 DateColumn("last_active").datetime().sortable(),
109 ]
111 # Filter configuration
112 @property
113 def filter_options(self) -> dict[str, Any]:
114 """Define filter options for the user resource."""
115 field_map: dict[str, Any] = {f.name: f for f in self.fields}
116 return {
117 "role": SelectFilter(
118 "role",
119 options=[v for v, _ in field_map["role"].options],
120 label="Role",
121 ),
122 "status": SelectFilter(
123 "status",
124 options=[v for v, _ in field_map["status"].options],
125 label="Status",
126 ),
127 "is_active": ToggleFilter("is_active", label="Active"),
128 }
130 # Actions configuration
131 actions: list[Any] = [
132 EditAction(),
133 DeleteAction(),
134 PermissionsAction(),
135 ImpersonateAction(),
136 ]
138 # Header actions
139 header_actions: list[Any] = [
140 CreateAction(label="Add User"),
141 ]
143 # Search configuration
144 search_fields = ["name", "email"]
146 # Bulk actions
147 bulk_actions: list[Any] = ["delete_selected", "export_csv"]
149 # Permissions
150 def has_view_permission(self, user: Any) -> bool:
151 """Check if user can view users."""
152 return user and (user.is_admin or user.role in ["admin", "moderator"])
154 def has_add_permission(self, user: Any) -> bool:
155 """Check if user can add users."""
156 return user and user.is_admin
158 def has_change_permission(self, user: Any) -> bool:
159 """Check if user can change users."""
160 return user and (user.is_admin or user.role in ["admin", "moderator"])
162 def has_delete_permission(self, user: Any) -> bool:
163 """Check if user can delete users."""
164 return user and user.is_admin
166 # Custom methods
167 async def get_queryset(self, request: Any) -> Any:
168 """Get the base queryset for users."""
169 if self.service:
170 return await self.service.list()
171 return []
173 async def get_object(self, pk: Any) -> Any:
174 """Get a single user object."""
175 if self.service:
176 return await self.service.get_by_id(pk)
177 return None
179 async def create_object(self, data: dict[str, Any]) -> Any:
180 """Create a new user."""
181 if self.service:
182 return await self.service.create(data)
183 return None
185 async def update_object(self, pk: Any, data: dict[str, Any]) -> Any:
186 """Update an existing user."""
187 if self.service:
188 return await self.service.update(pk, data)
189 return None
191 async def delete_object(self, pk: Any) -> bool:
192 """Delete a user."""
193 if self.service:
194 return await self.service.delete(pk)
195 return False