fh-matui 0.9.5__tar.gz → 0.9.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {fh_matui-0.9.5/fh_matui.egg-info → fh_matui-0.9.7}/PKG-INFO +1 -1
  2. fh_matui-0.9.7/fh_matui/__init__.py +1 -0
  3. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/_modidx.py +3 -0
  4. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/datatable.py +44 -52
  5. {fh_matui-0.9.5 → fh_matui-0.9.7/fh_matui.egg-info}/PKG-INFO +1 -1
  6. {fh_matui-0.9.5 → fh_matui-0.9.7}/settings.ini +1 -1
  7. fh_matui-0.9.5/fh_matui/__init__.py +0 -1
  8. {fh_matui-0.9.5 → fh_matui-0.9.7}/LICENSE +0 -0
  9. {fh_matui-0.9.5 → fh_matui-0.9.7}/MANIFEST.in +0 -0
  10. {fh_matui-0.9.5 → fh_matui-0.9.7}/README.md +0 -0
  11. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/app_pages.py +0 -0
  12. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/components.py +0 -0
  13. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/core.py +0 -0
  14. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/foundations.py +0 -0
  15. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui/web_pages.py +0 -0
  16. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui.egg-info/SOURCES.txt +0 -0
  17. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui.egg-info/dependency_links.txt +0 -0
  18. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui.egg-info/entry_points.txt +0 -0
  19. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui.egg-info/not-zip-safe +0 -0
  20. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui.egg-info/requires.txt +0 -0
  21. {fh_matui-0.9.5 → fh_matui-0.9.7}/fh_matui.egg-info/top_level.txt +0 -0
  22. {fh_matui-0.9.5 → fh_matui-0.9.7}/pyproject.toml +0 -0
  23. {fh_matui-0.9.5 → fh_matui-0.9.7}/setup.cfg +0 -0
  24. {fh_matui-0.9.5 → fh_matui-0.9.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fh-matui
3
- Version: 0.9.5
3
+ Version: 0.9.7
4
4
  Summary: material-ui for fasthtml
5
5
  Home-page: https://github.com/abhisheksreesaila/fh-matui
6
6
  Author: abhishek sreesaila
@@ -0,0 +1 @@
1
+ __version__ = "0.9.6"
@@ -157,7 +157,10 @@ d = { 'settings': { 'branch': 'master',
157
157
  'fh_matui/datatable.py'),
158
158
  'fh_matui.datatable.DataTableResource._wrap_modal': ( 'datatable.html#datatableresource._wrap_modal',
159
159
  'fh_matui/datatable.py'),
160
+ 'fh_matui.datatable.DataTableResource._wrap_response': ( 'datatable.html#datatableresource._wrap_response',
161
+ 'fh_matui/datatable.py'),
160
162
  'fh_matui.datatable._action_menu': ('datatable.html#_action_menu', 'fh_matui/datatable.py'),
163
+ 'fh_matui.datatable._is_htmx_request': ('datatable.html#_is_htmx_request', 'fh_matui/datatable.py'),
161
164
  'fh_matui.datatable._page_size_select': ('datatable.html#_page_size_select', 'fh_matui/datatable.py'),
162
165
  'fh_matui.datatable._safe_int': ('datatable.html#_safe_int', 'fh_matui/datatable.py'),
163
166
  'fh_matui.datatable._to_dict': ('datatable.html#_to_dict', 'fh_matui/datatable.py'),
@@ -383,7 +383,7 @@ class CrudContext:
383
383
  record: dict = None # Form data dict
384
384
  record_id: Optional[Any] = None # ID for update/delete (None for create)
385
385
 
386
- # %% ../nbs/05_datatable.ipynb 12
386
+ # %% ../nbs/05_datatable.ipynb 13
387
387
  from typing import Callable, Optional, Any, Union
388
388
  from dataclasses import asdict, is_dataclass
389
389
  from datetime import datetime
@@ -403,15 +403,22 @@ def _to_dict(obj: Any) -> dict:
403
403
  return dict(obj)
404
404
 
405
405
 
406
+ def _is_htmx_request(req) -> bool:
407
+ """Check if request is an HTMX partial request."""
408
+ headers = getattr(req, 'headers', {})
409
+ return headers.get('HX-Request') == 'true'
410
+
411
+
406
412
  class DataTableResource:
407
413
  """
408
414
  🔧 High-level resource that auto-registers all routes for a data table.
409
415
 
410
416
  **Features:**
417
+ - All callbacks receive `request` for multi-tenant support
411
418
  - Custom CRUD hooks with `CrudContext` for rich business logic
412
419
  - Async/sync hook support for external API integration
413
420
  - Auto-refresh table via HX-Trigger after mutations
414
- - Request state accessors for multi-tenant apps
421
+ - Layout wrapper for full-page (non-HTMX) responses
415
422
 
416
423
  **Auto-registers 3 routes:**
417
424
  - `GET {base_route}` → DataTable list view
@@ -424,11 +431,13 @@ class DataTableResource:
424
431
  app,
425
432
  base_route: str,
426
433
  columns: list[dict],
427
- get_all: Callable[[], list],
428
- get_by_id: Callable[[Any], Any],
429
- create: Callable[[dict], Any] = None,
430
- update: Callable[[Any, dict], Any] = None,
431
- delete: Callable[[Any], bool] = None,
434
+ # Data callbacks - ALL receive request as first param
435
+ get_all: Callable[[Any], list], # (req) -> list
436
+ get_by_id: Callable[[Any, Any], Any], # (req, id) -> record
437
+ create: Callable[[Any, dict], Any] = None, # (req, data) -> record
438
+ update: Callable[[Any, Any, dict], Any] = None, # (req, id, data) -> record
439
+ delete: Callable[[Any, Any], bool] = None, # (req, id) -> bool
440
+ # Display options
432
441
  title: str = "Records",
433
442
  row_id_field: str = "id",
434
443
  crud_ops: dict = None,
@@ -440,12 +449,8 @@ class DataTableResource:
440
449
  on_create: Callable[[CrudContext], dict] = None,
441
450
  on_update: Callable[[CrudContext], dict] = None,
442
451
  on_delete: Callable[[CrudContext], None] = None,
443
- # Multi-tenant
444
- user_filter: Callable = None,
445
- # Request state accessors (for CrudContext)
446
- get_user: Callable = None,
447
- get_db: Callable = None,
448
- get_table: Callable = None,
452
+ # Layout wrapper for full-page responses
453
+ layout_wrapper: Callable[[Any, Any], Any] = None, # (content, req) -> wrapped
449
454
  # Custom generators
450
455
  id_generator: Callable[[], Any] = None,
451
456
  timestamp_fields: dict = None
@@ -480,13 +485,8 @@ class DataTableResource:
480
485
  self.on_update_hook = on_update
481
486
  self.on_delete_hook = on_delete
482
487
 
483
- # Multi-tenant
484
- self.user_filter = user_filter
485
-
486
- # Request state accessors
487
- self.get_user = get_user
488
- self.get_db = get_db
489
- self.get_table = get_table
488
+ # Layout wrapper
489
+ self.layout_wrapper = layout_wrapper
490
490
 
491
491
  # Generators
492
492
  self.id_generator = id_generator
@@ -510,33 +510,25 @@ class DataTableResource:
510
510
  return hook(ctx)
511
511
 
512
512
  def _build_context(self, req, record: dict = None, record_id: Any = None) -> CrudContext:
513
- """🏗️ Build CrudContext from request state."""
513
+ """🏗️ Build CrudContext from request."""
514
514
  user = None
515
515
  db = None
516
516
  tbl = None
517
517
 
518
+ # Extract user from request.state if available
518
519
  try:
519
- if self.get_user and callable(self.get_user):
520
- user = self.get_user(req)
521
- elif hasattr(req, 'state') and hasattr(req.state, 'user'):
520
+ if hasattr(req, 'state') and hasattr(req.state, 'user'):
522
521
  user = req.state.user
523
522
  except AttributeError:
524
523
  pass
525
524
 
525
+ # Extract db from request.state if available
526
526
  try:
527
- if self.get_db and callable(self.get_db):
528
- db = self.get_db(req)
529
- elif hasattr(req, 'state') and hasattr(req.state, 'tenant_db'):
527
+ if hasattr(req, 'state') and hasattr(req.state, 'tenant_db'):
530
528
  db = req.state.tenant_db
531
529
  except AttributeError:
532
530
  pass
533
531
 
534
- try:
535
- if self.get_table and callable(self.get_table):
536
- tbl = self.get_table(req)
537
- except AttributeError:
538
- pass
539
-
540
532
  return CrudContext(
541
533
  request=req,
542
534
  user=user,
@@ -546,6 +538,12 @@ class DataTableResource:
546
538
  record_id=record_id
547
539
  )
548
540
 
541
+ def _wrap_response(self, content, req):
542
+ """Wrap content with layout_wrapper if not an HTMX request."""
543
+ if self.layout_wrapper and not _is_htmx_request(req):
544
+ return self.layout_wrapper(content, req)
545
+ return content
546
+
549
547
  def _register_routes(self):
550
548
  """Register all data table routes with the app."""
551
549
  rt = self.app.route
@@ -563,18 +561,9 @@ class DataTableResource:
563
561
  return await self._handle_save(req)
564
562
 
565
563
  def _get_filtered_data(self, req) -> list:
566
- """Get all data, optionally filtered by user_filter."""
567
- all_data = self.get_all()
568
- data = [_to_dict(item) for item in all_data]
569
-
570
- if self.user_filter and callable(self.user_filter):
571
- filter_criteria = self.user_filter(req)
572
- if filter_criteria:
573
- data = [
574
- row for row in data
575
- if all(row.get(k) == v for k, v in filter_criteria.items())
576
- ]
577
- return data
564
+ """Get all data from user's callback."""
565
+ all_data = self.get_all(req)
566
+ return [_to_dict(item) for item in all_data]
578
567
 
579
568
  def _filter_by_search(self, data: list, search: str) -> list:
580
569
  """Filter data by search term across searchable columns."""
@@ -643,7 +632,10 @@ class DataTableResource:
643
632
  )
644
633
 
645
634
  feedback = Div(id=self.feedback_id)
646
- return Div(feedback, table_container)
635
+ content = Div(feedback, table_container)
636
+
637
+ # Wrap with layout if full-page request
638
+ return self._wrap_response(content, req)
647
639
 
648
640
  def _handle_action(self, req):
649
641
  """Handle action route (view/edit/create/delete)."""
@@ -690,7 +682,7 @@ class DataTableResource:
690
682
  # Get record for view/edit/delete
691
683
  record = None
692
684
  if record_id:
693
- raw_record = self.get_by_id(record_id)
685
+ raw_record = self.get_by_id(req, record_id)
694
686
  record = _to_dict(raw_record) if raw_record else None
695
687
 
696
688
  if not record:
@@ -744,7 +736,7 @@ class DataTableResource:
744
736
  self.on_delete_hook(ctx)
745
737
  else:
746
738
  # Default: use delete_fn
747
- self.delete_fn(record_id)
739
+ self.delete_fn(req, record_id)
748
740
 
749
741
  return self._success_toast("Record deleted successfully.")
750
742
  except Exception as e:
@@ -805,9 +797,9 @@ class DataTableResource:
805
797
  if self.on_update_hook:
806
798
  data = await self._call_hook(self.on_update_hook, ctx)
807
799
  if data is not None:
808
- self.update_fn(record_id, data)
800
+ self.update_fn(req, record_id, data)
809
801
  else:
810
- self.update_fn(record_id, data)
802
+ self.update_fn(req, record_id, data)
811
803
 
812
804
  return self._success_toast("Record updated successfully.")
813
805
  else:
@@ -821,9 +813,9 @@ class DataTableResource:
821
813
  if self.on_create_hook:
822
814
  data = await self._call_hook(self.on_create_hook, ctx)
823
815
  if data is not None:
824
- self.create_fn(data)
816
+ self.create_fn(req, data)
825
817
  else:
826
- self.create_fn(data)
818
+ self.create_fn(req, data)
827
819
 
828
820
  return self._success_toast("Record created successfully.")
829
821
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fh-matui
3
- Version: 0.9.5
3
+ Version: 0.9.7
4
4
  Summary: material-ui for fasthtml
5
5
  Home-page: https://github.com/abhisheksreesaila/fh-matui
6
6
  Author: abhishek sreesaila
@@ -5,7 +5,7 @@
5
5
  ### Python library ###
6
6
  repo = fh-matui
7
7
  lib_name = %(repo)s
8
- version = 0.9.5
8
+ version = 0.9.7
9
9
  min_python = 3.9
10
10
  license = apache2
11
11
  black_formatting = False
@@ -1 +0,0 @@
1
- __version__ = "0.9.4"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes