Coverage for src / lexigram / admin / pages / resource_pages.py: 0%

61 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-11 02:25 +0800

1"""Resource page implementations for lexigram-admin. 

2 

3Provides concrete Page subclasses for standard CRUD operations: 

4ListPage, CreatePage, EditPage, and ViewPage. 

5""" 

6 

7from __future__ import annotations 

8 

9from typing import TYPE_CHECKING, Any 

10 

11if TYPE_CHECKING: 

12 from lexigram.admin.relations.manager_ext import RelationManager 

13 

14from lexigram.admin.pages.base import Page 

15from lexigram.admin.pages.types import PageResponse 

16 

17 

18class ListPage(Page): 

19 """Default resource list page — table with search, pagination, actions.""" 

20 

21 title: str = "List" 

22 

23 def __init__(self, resource_name: str = "", path: str = "") -> None: 

24 self.resource_name = resource_name 

25 if path: 

26 self.path = path 

27 

28 async def view(self, request: Any) -> PageResponse: 

29 """Render list page with resource records.""" 

30 return PageResponse( 

31 content=self._render_list(request), 

32 title=self.title, 

33 ) 

34 

35 def _render_list(self, request: Any) -> str: 

36 """Render the list HTML.""" 

37 return f"<div>List of {self.resource_name}</div>" 

38 

39 

40class CreatePage(Page): 

41 """Default resource create page — form for a new record.""" 

42 

43 title: str = "Create" 

44 

45 def __init__(self, resource_name: str = "", path: str = "") -> None: 

46 self.resource_name = resource_name 

47 if path: 

48 self.path = path 

49 

50 async def view(self, request: Any) -> PageResponse: 

51 """Render creation form.""" 

52 return PageResponse( 

53 content=self._render_form(request), 

54 title=self.title, 

55 ) 

56 

57 def _render_form(self, request: Any) -> str: 

58 """Render the form HTML.""" 

59 return f"<form>Create {self.resource_name}</form>" 

60 

61 async def post(self, request: Any) -> PageResponse: 

62 """Handle form submission for creating a record.""" 

63 return PageResponse( 

64 content=f"<div>Created {self.resource_name}</div>", 

65 title=self.title, 

66 ) 

67 

68 

69class EditPage(Page): 

70 """Default resource edit page — form pre-filled with existing record.""" 

71 

72 title: str = "Edit" 

73 

74 def __init__(self, resource_name: str = "", path: str = "") -> None: 

75 self.resource_name = resource_name 

76 if path: 

77 self.path = path 

78 

79 async def view(self, request: Any) -> PageResponse: 

80 """Render edit form pre-filled with record data.""" 

81 record_id = self._get_record_id(request) 

82 return PageResponse( 

83 content=self._render_form(request, record_id), 

84 title=self.title, 

85 ) 

86 

87 def _get_record_id(self, request: Any) -> str: 

88 """Extract record ID from request path/query parameters.""" 

89 return getattr(request, "path_params", {}).get("id", "") 

90 

91 def _render_form(self, request: Any, record_id: str) -> str: 

92 """Render the edit form HTML.""" 

93 return f"<form>Edit {self.resource_name} #{record_id}</form>" 

94 

95 async def post(self, request: Any) -> PageResponse: 

96 """Handle form submission for updating a record.""" 

97 record_id = self._get_record_id(request) 

98 return PageResponse( 

99 content=f"<div>Updated {self.resource_name} #{record_id}</div>", 

100 title=self.title, 

101 ) 

102 

103 

104class ViewPage(Page): 

105 """Default resource view page — read-only detail.""" 

106 

107 title: str = "View" 

108 

109 def __init__( 

110 self, 

111 resource_name: str = "", 

112 path: str = "", 

113 relations: list[type[RelationManager]] | None = None, 

114 ) -> None: 

115 self.resource_name = resource_name 

116 self._relations = relations or [] 

117 if path: 

118 self.path = path 

119 

120 async def view(self, request: Any) -> PageResponse: 

121 """Render read-only detail view.""" 

122 record_id = getattr(request, "path_params", {}).get("id", "") 

123 return PageResponse( 

124 content=self._render_detail(request, record_id), 

125 title=self.title, 

126 breadcrumbs=[ 

127 ("Home", "/admin"), 

128 (self.resource_name, f"/admin/{self.resource_name}"), 

129 ("View", ""), 

130 ], 

131 ) 

132 

133 def _render_detail(self, request: Any, record_id: str) -> str: 

134 """Render the detail HTML with optional relation panels.""" 

135 main_content = f"<div>Detail of {self.resource_name} #{record_id}</div>" 

136 

137 relations_html = "" 

138 for relation_cls in self._relations: 

139 rel_name = getattr(relation_cls, "relationship_name", "") or "" 

140 rel_url = f"/admin/{self.resource_name}/{record_id}/relations/{rel_name}" 

141 relations_html += ( 

142 f'<div hx-get="{rel_url}" ' 

143 f'hx-trigger="load" ' 

144 f'hx-swap="outerHTML" ' 

145 f'id="relation-{rel_name}">' 

146 f"Loading {rel_name}..." 

147 f"</div>" 

148 ) 

149 

150 return main_content + relations_html 

151 

152 

153__all__ = [ 

154 "CreatePage", 

155 "EditPage", 

156 "ListPage", 

157 "ViewPage", 

158]