Coverage for src/lexigram/admin/pages/resource_pages.py: 97%
61 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:56 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:56 +0800
1"""Resource page implementations for lexigram-admin.
3Provides concrete Page subclasses for standard CRUD operations:
4ListPage, CreatePage, EditPage, and ViewPage.
5"""
7from __future__ import annotations
9from typing import TYPE_CHECKING, Any
11if TYPE_CHECKING:
12 from lexigram.admin.relations.manager_ext import RelationManager
14from lexigram.admin.pages.base import Page
15from lexigram.admin.pages.types import PageResponse
18class ListPage(Page):
19 """Default resource list page — table with search, pagination, actions."""
21 title: str = "List"
23 def __init__(self, resource_name: str = "", path: str = "") -> None:
24 self.resource_name = resource_name
25 if path:
26 self.path = path
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 )
35 def _render_list(self, request: Any) -> str:
36 """Render the list HTML."""
37 return f"<div>List of {self.resource_name}</div>"
40class CreatePage(Page):
41 """Default resource create page — form for a new record."""
43 title: str = "Create"
45 def __init__(self, resource_name: str = "", path: str = "") -> None:
46 self.resource_name = resource_name
47 if path:
48 self.path = path
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 )
57 def _render_form(self, request: Any) -> str:
58 """Render the form HTML."""
59 return f"<form>Create {self.resource_name}</form>"
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 )
69class EditPage(Page):
70 """Default resource edit page — form pre-filled with existing record."""
72 title: str = "Edit"
74 def __init__(self, resource_name: str = "", path: str = "") -> None:
75 self.resource_name = resource_name
76 if path:
77 self.path = path
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 )
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", "")
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>"
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 )
104class ViewPage(Page):
105 """Default resource view page — read-only detail."""
107 title: str = "View"
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
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 )
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>"
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 )
150 return main_content + relations_html
153__all__ = [
154 "CreatePage",
155 "EditPage",
156 "ListPage",
157 "ViewPage",
158]