Coverage for src/lexigram/admin/ui/organisms/data_table/coordinator.py: 100%

31 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-21 15:04 +0800

1"""Data table coordinator component.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from lexigram.admin.config import TableConfiguration 

8from lexigram.admin.ui.organisms.data_table.actions import ActionManager 

9from lexigram.admin.ui.organisms.data_table.rendering import DataTableRenderer 

10from lexigram.ui import Component, TableState 

11 

12 

13class DataTable(Component): 

14 """ 

15 Main DataTable Coordinator. 

16 Handles configuration, state management, and dispatches rendering to specific views. 

17 """ 

18 

19 def __init__( 

20 self, 

21 columns: list | None = None, 

22 data: list[dict] | None = None, 

23 state: TableState | None = None, 

24 config: TableConfiguration | None = None, 

25 # Legacy params 

26 page: int = 1, 

27 per_page: int = 20, 

28 total: int | None = None, 

29 filters: dict | None = None, 

30 resource_prefix: str | None = None, 

31 sort_by: str | None = None, 

32 sort_order: str = "asc", 

33 actions: list | None = None, 

34 header_actions: list | None = None, 

35 bulk_actions: list | None = None, 

36 filter_options: dict | None = None, 

37 layout_type: str | None = None, 

38 data_view: str | None = None, 

39 next_cursor: str | None = None, 

40 summary: dict[str, Any] | None = None, 

41 **props, 

42 ) -> None: 

43 super().__init__(**props) 

44 

45 # Resolve Configuration 

46 if config: 

47 self.config = config 

48 else: 

49 self.config = TableConfiguration( 

50 columns=columns or [], 

51 actions=actions or [], 

52 header_actions=header_actions or [], 

53 bulk_actions=bulk_actions or [], 

54 filter_options=filter_options, 

55 resource_prefix=resource_prefix, 

56 resource_name=props.get("resource_name"), 

57 default_view=data_view or "tabular", 

58 default_layout=layout_type or "stack", 

59 default_sort_by=sort_by, 

60 default_sort_order=sort_order, 

61 ) 

62 

63 # Resolve State 

64 if state: 

65 self.state = state 

66 else: 

67 self.state = TableState( 

68 search=filters.get("search", "") if filters else "", 

69 sort_by=sort_by, 

70 sort_order=sort_order, 

71 page=page, 

72 per_page=per_page, 

73 filters=filters or {}, 

74 view=data_view or self.config.default_view, 

75 layout=layout_type or self.config.default_layout, 

76 ) 

77 

78 self.data = data or [] 

79 self.total = total 

80 self.user = props.get("user") 

81 self.loading = props.get("loading", False) 

82 self.error = props.get("error") 

83 self.next_cursor = next_cursor 

84 self.summary = summary 

85 self.props = props 

86 

87 # Permission state: no permission service is bound at construction 

88 # time in the framework; async callers may hoist checks via 

89 # PermissionManager and inject the resulting dict here. 

90 self.permissions = props.get("permissions") or { 

91 "can_view": True, 

92 "can_create": True, 

93 "can_update": True, 

94 "can_delete": True, 

95 } 

96 

97 # Configure actions 

98 self.action_manager = ActionManager(self.config, self.permissions) 

99 self.action_manager.configure_actions() 

100 

101 # Show delete based on permissions 

102 self.show_delete = ( 

103 props.get("show_delete", True) and self.permissions["can_delete"] 

104 ) 

105 

106 def render(self) -> Any: 

107 """Render the data table using the modular renderer.""" 

108 renderer = DataTableRenderer( 

109 data=self.data, 

110 config=self.config, 

111 state=self.state, 

112 total=self.total, 

113 user=self.user, 

114 loading=self.loading, 

115 error=self.error, 

116 next_cursor=self.next_cursor, 

117 summary=self.summary, 

118 props=self.props, 

119 ) 

120 return renderer.render() 

121 

122 

123__all__ = ["DataTable"]