splent-feature-team 0.3.2__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 (80) hide show
  1. splent_feature_team-0.3.2/MANIFEST.in +27 -0
  2. splent_feature_team-0.3.2/PKG-INFO +5 -0
  3. splent_feature_team-0.3.2/pyproject.toml +58 -0
  4. splent_feature_team-0.3.2/setup.cfg +4 -0
  5. splent_feature_team-0.3.2/src/splent_feature_team.egg-info/PKG-INFO +5 -0
  6. splent_feature_team-0.3.2/src/splent_feature_team.egg-info/SOURCES.txt +78 -0
  7. splent_feature_team-0.3.2/src/splent_feature_team.egg-info/dependency_links.txt +1 -0
  8. splent_feature_team-0.3.2/src/splent_feature_team.egg-info/top_level.txt +1 -0
  9. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/__init__.py +25 -0
  10. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/assets/css/team.css +138 -0
  11. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/assets/dist/splent_feature_team.bundle.js +20 -0
  12. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/assets/dist/splent_feature_team.bundle.js.map +1 -0
  13. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/assets/js/scripts.js +12 -0
  14. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/assets/js/webpack.config.js +14 -0
  15. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/commands.py +21 -0
  16. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/config.py +19 -0
  17. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/forms.py +6 -0
  18. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/hooks.py +23 -0
  19. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/migrations/alembic.ini +36 -0
  20. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/migrations/env.py +9 -0
  21. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/migrations/script.py.mako +24 -0
  22. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/migrations/versions/1bb39441d98d_splent_feature_team.py +46 -0
  23. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/migrations/versions/3f76830b5164_splent_feature_team.py +29 -0
  24. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/migrations/versions/tm0003_roles_m2m.py +101 -0
  25. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/models.py +73 -0
  26. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/repositories.py +38 -0
  27. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/routes.py +203 -0
  28. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/alberto-valenzuela-munoz.jpg +0 -0
  29. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/ana-eva-chacon-luna.jpg +0 -0
  30. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/antonio-german-marquez-trujillo.jpg +0 -0
  31. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/antonio-j-dominguez.jpg +0 -0
  32. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/antonio-jimenez-ortega.jpg +0 -0
  33. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/antonio-manuel-guiterrez-fernandez.webp +0 -0
  34. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/asmaa-hasan-sbaih.jpg +0 -0
  35. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/cristian-vidal-silva.jpg +0 -0
  36. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/daniel-molina-torres.jpg +0 -0
  37. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/david-benavides.jpg +0 -0
  38. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/david-romero-organvidez.jpg +0 -0
  39. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/esther-rocio-valladolid-ortiz.jpg +0 -0
  40. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/f-j-dominguez-mayo.jpg +0 -0
  41. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/felipe-escalera-gonzalez.jpg +0 -0
  42. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/francisco-sebastian-benitez-ruis-diaz.jpg +0 -0
  43. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/guillermo-ciria-gonzalez.jpg +0 -0
  44. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/isabel-mylonas.jpg +0 -0
  45. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/javier-aponte.jpg +0 -0
  46. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jesennia-cardenas-cobo.jpg +0 -0
  47. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jesus-fernandez-romero.jpg +0 -0
  48. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jesus-moreno-leon.jpg +0 -0
  49. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jorge-garcia.jpg +0 -0
  50. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jorge-rodas-silva.jpg +0 -0
  51. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jose-a-galindo.jpg +0 -0
  52. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jose-a-zamudio.jpg +0 -0
  53. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jose-manuel-sanchez-ruiz.jpg +0 -0
  54. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/jose-miguel-horcas.webp +0 -0
  55. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/julio-navarro-rodriguez.jpg +0 -0
  56. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/mario-rivas-becerra.jpg +0 -0
  57. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/mariuxi-vinueza-morales.jpg +0 -0
  58. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/megha-bhushan.jpg +0 -0
  59. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/miguel-moreno-martin-palomo.png +0 -0
  60. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/miguel-olivero.jpg +0 -0
  61. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/noelia-lopez-duran.jpg +0 -0
  62. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/pablo-neira.png +0 -0
  63. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/pablo-pazo.jpg +0 -0
  64. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/pablo-ramos-berciano.jpg +0 -0
  65. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/rafael-harana-mancilla.png +0 -0
  66. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/salvador-munoz-hermoso.jpg +0 -0
  67. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/victor-ramirez-de-la-corte.jpg +0 -0
  68. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seed_media/victor-rea-sanchez.jpg +0 -0
  69. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/seeders.py +621 -0
  70. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/services.py +17 -0
  71. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/signals.py +19 -0
  72. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/templates/team/admin/form.html +81 -0
  73. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/templates/team/admin/list.html +88 -0
  74. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/templates/team/admin/roles.html +82 -0
  75. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/templates/team/detail.html +44 -0
  76. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/templates/team/index.html +10 -0
  77. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/templates/team/list.html +33 -0
  78. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/translations/es/LC_MESSAGES/messages.mo +0 -0
  79. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/translations/es/LC_MESSAGES/messages.po +44 -0
  80. splent_feature_team-0.3.2/src/splent_io/splent_feature_team/translations/messages.pot +43 -0
@@ -0,0 +1,27 @@
1
+ # Frontend assets: the whole tree, not only the compiled bundles.
2
+ #
3
+ # A feature that registers a plain stylesheet with register_asset("css", …)
4
+ # serves it straight from assets/css, and a vendored library lives under
5
+ # assets/vendor. Shipping assets/dist alone put no stylesheet at all in the
6
+ # built package, so every production image came up without its theme, its
7
+ # skin and its editor, while development looked fine because that path
8
+ # installs the checkout in place and reads the files off disk.
9
+ recursive-include src/splent_io/splent_feature_team/assets *.css *.js *.map *.json
10
+ recursive-include src/splent_io/splent_feature_team/assets *.svg *.png *.jpg *.jpeg *.gif *.webp *.ico
11
+ recursive-include src/splent_io/splent_feature_team/assets *.woff *.woff2 *.ttf *.eot
12
+
13
+ # Build inputs, never shipped.
14
+ prune src/splent_io/splent_feature_team/assets/node_modules
15
+
16
+ # Seed photos, imported through the media feature on db:seed.
17
+ recursive-include src/splent_io/splent_feature_team/seed_media *.jpg *.jpeg *.png *.webp
18
+
19
+ # Jinja templates (feature views + hook fragments)
20
+ recursive-include src/splent_io/splent_feature_team/templates *.html
21
+
22
+ # Alembic migration config and scripts
23
+ recursive-include src/splent_io/splent_feature_team/migrations *.py *.ini *.mako
24
+
25
+ # Translations. The .mo is what gettext reads at runtime; the .po
26
+ # travels with it so the next translator has something to edit.
27
+ recursive-include src/splent_io/splent_feature_team/translations *.mo *.po *.pot
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: splent_feature_team
3
+ Version: 0.3.2
4
+ Requires-Python: >=3.13
5
+ Description-Content-Type: text/markdown
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80.3.1", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "splent_feature_team"
7
+ version = "0.3.2"
8
+ readme = "README.md"
9
+ requires-python = ">=3.13"
10
+
11
+ dependencies = [
12
+
13
+ ]
14
+
15
+ [tool.setuptools]
16
+ package-dir = { "" = "src" }
17
+ include-package-data = true
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
21
+ exclude = ["*.tests", "*.tests.*"]
22
+
23
+ [tool.splent]
24
+ cli_version = "1.11.0"
25
+
26
+ # ── Feature Contract (auto-generated) ────────────────────────────────────────
27
+ # Do not edit manually — re-run `splent feature:contract --write` to refresh.
28
+ # Preserved across refreshes: description, category, tags, env,
29
+ # requires.features_manual (dependencies invisible to static analysis)
30
+ # and provides.routes_manual (routes built from configuration at
31
+ # registration time, which have no literal path to find in source).
32
+ [tool.splent.contract]
33
+ description = "People directory (organisers, faculty, speakers) with public list/detail and admin CRUD"
34
+ archetype = "full"
35
+
36
+ [tool.splent.contract.provides]
37
+ routes = ["/admin/team", "/admin/team/<int:member_id>/delete", "/admin/team/<int:member_id>/edit", "/admin/team/new", "/admin/team/roles", "/admin/team/roles/<int:role_id>/delete", "/admin/team/roles/<int:role_id>/edit", "/team", "/team/<slug>"]
38
+ blueprints = []
39
+ models = ["Role", "TeamMember"]
40
+ commands = ["hello"]
41
+ hooks = ["layout.authenticated_sidebar"]
42
+ services = ["TeamService"]
43
+ signals = []
44
+ translations = ["es"]
45
+ docker = []
46
+
47
+ [tool.splent.contract.requires]
48
+ features = ["media"]
49
+ features_manual = ["media"]
50
+ env_vars = []
51
+ signals = []
52
+
53
+ [tool.splent.contract.extensible]
54
+ services = ["TeamService"]
55
+ templates = ["team/admin/form.html", "team/admin/list.html", "team/admin/roles.html", "team/detail.html", "team/index.html", "team/list.html"]
56
+ models = ["Role", "TeamMember"]
57
+ hooks = ["layout.authenticated_sidebar"]
58
+ routes = false
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: splent_feature_team
3
+ Version: 0.3.2
4
+ Requires-Python: >=3.13
5
+ Description-Content-Type: text/markdown
@@ -0,0 +1,78 @@
1
+ MANIFEST.in
2
+ pyproject.toml
3
+ src/splent_feature_team.egg-info/PKG-INFO
4
+ src/splent_feature_team.egg-info/SOURCES.txt
5
+ src/splent_feature_team.egg-info/dependency_links.txt
6
+ src/splent_feature_team.egg-info/top_level.txt
7
+ src/splent_io/splent_feature_team/__init__.py
8
+ src/splent_io/splent_feature_team/commands.py
9
+ src/splent_io/splent_feature_team/config.py
10
+ src/splent_io/splent_feature_team/forms.py
11
+ src/splent_io/splent_feature_team/hooks.py
12
+ src/splent_io/splent_feature_team/models.py
13
+ src/splent_io/splent_feature_team/repositories.py
14
+ src/splent_io/splent_feature_team/routes.py
15
+ src/splent_io/splent_feature_team/seeders.py
16
+ src/splent_io/splent_feature_team/services.py
17
+ src/splent_io/splent_feature_team/signals.py
18
+ src/splent_io/splent_feature_team/assets/css/team.css
19
+ src/splent_io/splent_feature_team/assets/dist/splent_feature_team.bundle.js
20
+ src/splent_io/splent_feature_team/assets/dist/splent_feature_team.bundle.js.map
21
+ src/splent_io/splent_feature_team/assets/js/scripts.js
22
+ src/splent_io/splent_feature_team/assets/js/webpack.config.js
23
+ src/splent_io/splent_feature_team/migrations/alembic.ini
24
+ src/splent_io/splent_feature_team/migrations/env.py
25
+ src/splent_io/splent_feature_team/migrations/script.py.mako
26
+ src/splent_io/splent_feature_team/migrations/versions/1bb39441d98d_splent_feature_team.py
27
+ src/splent_io/splent_feature_team/migrations/versions/3f76830b5164_splent_feature_team.py
28
+ src/splent_io/splent_feature_team/migrations/versions/tm0003_roles_m2m.py
29
+ src/splent_io/splent_feature_team/seed_media/alberto-valenzuela-munoz.jpg
30
+ src/splent_io/splent_feature_team/seed_media/ana-eva-chacon-luna.jpg
31
+ src/splent_io/splent_feature_team/seed_media/antonio-german-marquez-trujillo.jpg
32
+ src/splent_io/splent_feature_team/seed_media/antonio-j-dominguez.jpg
33
+ src/splent_io/splent_feature_team/seed_media/antonio-jimenez-ortega.jpg
34
+ src/splent_io/splent_feature_team/seed_media/antonio-manuel-guiterrez-fernandez.webp
35
+ src/splent_io/splent_feature_team/seed_media/asmaa-hasan-sbaih.jpg
36
+ src/splent_io/splent_feature_team/seed_media/cristian-vidal-silva.jpg
37
+ src/splent_io/splent_feature_team/seed_media/daniel-molina-torres.jpg
38
+ src/splent_io/splent_feature_team/seed_media/david-benavides.jpg
39
+ src/splent_io/splent_feature_team/seed_media/david-romero-organvidez.jpg
40
+ src/splent_io/splent_feature_team/seed_media/esther-rocio-valladolid-ortiz.jpg
41
+ src/splent_io/splent_feature_team/seed_media/f-j-dominguez-mayo.jpg
42
+ src/splent_io/splent_feature_team/seed_media/felipe-escalera-gonzalez.jpg
43
+ src/splent_io/splent_feature_team/seed_media/francisco-sebastian-benitez-ruis-diaz.jpg
44
+ src/splent_io/splent_feature_team/seed_media/guillermo-ciria-gonzalez.jpg
45
+ src/splent_io/splent_feature_team/seed_media/isabel-mylonas.jpg
46
+ src/splent_io/splent_feature_team/seed_media/javier-aponte.jpg
47
+ src/splent_io/splent_feature_team/seed_media/jesennia-cardenas-cobo.jpg
48
+ src/splent_io/splent_feature_team/seed_media/jesus-fernandez-romero.jpg
49
+ src/splent_io/splent_feature_team/seed_media/jesus-moreno-leon.jpg
50
+ src/splent_io/splent_feature_team/seed_media/jorge-garcia.jpg
51
+ src/splent_io/splent_feature_team/seed_media/jorge-rodas-silva.jpg
52
+ src/splent_io/splent_feature_team/seed_media/jose-a-galindo.jpg
53
+ src/splent_io/splent_feature_team/seed_media/jose-a-zamudio.jpg
54
+ src/splent_io/splent_feature_team/seed_media/jose-manuel-sanchez-ruiz.jpg
55
+ src/splent_io/splent_feature_team/seed_media/jose-miguel-horcas.webp
56
+ src/splent_io/splent_feature_team/seed_media/julio-navarro-rodriguez.jpg
57
+ src/splent_io/splent_feature_team/seed_media/mario-rivas-becerra.jpg
58
+ src/splent_io/splent_feature_team/seed_media/mariuxi-vinueza-morales.jpg
59
+ src/splent_io/splent_feature_team/seed_media/megha-bhushan.jpg
60
+ src/splent_io/splent_feature_team/seed_media/miguel-moreno-martin-palomo.png
61
+ src/splent_io/splent_feature_team/seed_media/miguel-olivero.jpg
62
+ src/splent_io/splent_feature_team/seed_media/noelia-lopez-duran.jpg
63
+ src/splent_io/splent_feature_team/seed_media/pablo-neira.png
64
+ src/splent_io/splent_feature_team/seed_media/pablo-pazo.jpg
65
+ src/splent_io/splent_feature_team/seed_media/pablo-ramos-berciano.jpg
66
+ src/splent_io/splent_feature_team/seed_media/rafael-harana-mancilla.png
67
+ src/splent_io/splent_feature_team/seed_media/salvador-munoz-hermoso.jpg
68
+ src/splent_io/splent_feature_team/seed_media/victor-ramirez-de-la-corte.jpg
69
+ src/splent_io/splent_feature_team/seed_media/victor-rea-sanchez.jpg
70
+ src/splent_io/splent_feature_team/templates/team/detail.html
71
+ src/splent_io/splent_feature_team/templates/team/index.html
72
+ src/splent_io/splent_feature_team/templates/team/list.html
73
+ src/splent_io/splent_feature_team/templates/team/admin/form.html
74
+ src/splent_io/splent_feature_team/templates/team/admin/list.html
75
+ src/splent_io/splent_feature_team/templates/team/admin/roles.html
76
+ src/splent_io/splent_feature_team/translations/messages.pot
77
+ src/splent_io/splent_feature_team/translations/es/LC_MESSAGES/messages.mo
78
+ src/splent_io/splent_feature_team/translations/es/LC_MESSAGES/messages.po
@@ -0,0 +1,25 @@
1
+ from splent_framework.blueprints.base_blueprint import create_blueprint
2
+ from splent_framework.nav.nav_registry import register_nav_item
3
+ from splent_framework.services.service_locator import register_service
4
+
5
+ from splent_io.splent_feature_team.services import TeamService
6
+
7
+ team_bp = create_blueprint(__name__)
8
+
9
+
10
+ def init_feature(app):
11
+ from splent_framework.assets.asset_registry import register_asset
12
+
13
+ # Team is managed through its OWN custom admin screens (see routes.py and
14
+ # hooks.py) — the WordPress-plugin pattern — instead of the generic admin
15
+ # resource, so it does not call register_admin_resource.
16
+ register_service(app, "TeamService", TeamService)
17
+ register_nav_item(key="team", label="Team", href="/team", order=40)
18
+ # Public team-member detail stylesheet (token-driven; styled by the skin).
19
+ register_asset(
20
+ "css", "team.assets", order=100, subfolder="css", filename="team.css"
21
+ )
22
+
23
+
24
+ def inject_context_vars(app):
25
+ return {}
@@ -0,0 +1,138 @@
1
+ /* ══════════════════════════════════════════════════════════════════════════
2
+ splent_feature_team — public team-member detail sheet.
3
+ Token-driven: every colour/radius comes from the theme's --brand-* custom
4
+ properties, so a skin restyles it for free. Loaded via register_asset on top
5
+ of the theme's public.css. No inline styles, no CDN.
6
+ ══════════════════════════════════════════════════════════════════════════ */
7
+
8
+ .member-detail {
9
+ max-width: 72ch;
10
+ }
11
+
12
+ .member-detail__photo {
13
+ width: 160px;
14
+ height: 160px;
15
+ border-radius: 50%;
16
+ object-fit: cover;
17
+ margin-bottom: 1rem;
18
+ }
19
+
20
+ .member-roles {
21
+ display: flex;
22
+ flex-wrap: wrap;
23
+ gap: .35rem;
24
+ margin-bottom: 1rem;
25
+ }
26
+
27
+ .member-detail .section-title {
28
+ margin-top: 1.5rem;
29
+ }
30
+
31
+ .member-links {
32
+ display: flex;
33
+ flex-wrap: wrap;
34
+ gap: .5rem;
35
+ }
36
+
37
+ /* ── Email contact row: text label / small mail icon, no emoji ─────────────── */
38
+ .member-contact {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: .5rem;
42
+ margin-top: 1rem;
43
+ }
44
+
45
+ .member-contact__icon {
46
+ flex: none;
47
+ width: 1rem;
48
+ height: 1rem;
49
+ color: var(--brand-primary, #2563eb);
50
+ }
51
+
52
+ .member-contact__label {
53
+ font-size: .72rem;
54
+ font-weight: 700;
55
+ letter-spacing: .04em;
56
+ text-transform: uppercase;
57
+ color: var(--brand-muted, #6b7280);
58
+ }
59
+
60
+ .member-detail__back {
61
+ display: inline-block;
62
+ margin-top: 2rem;
63
+ }
64
+
65
+ /* ── Public team grid: photo-cover cards with a gradient + white text ────────── */
66
+ .member-grid {
67
+ display: grid;
68
+ grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
69
+ gap: 1.25rem;
70
+ margin-bottom: 2.75rem;
71
+ }
72
+
73
+ .member-card {
74
+ position: relative;
75
+ display: block;
76
+ aspect-ratio: 3 / 4;
77
+ border-radius: var(--brand-radius, 8px);
78
+ overflow: hidden;
79
+ text-decoration: none;
80
+ background: var(--brand-surface, #f1f1f3);
81
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .1);
82
+ transition: transform .2s ease, box-shadow .2s ease;
83
+ }
84
+
85
+ .member-card:hover {
86
+ transform: translateY(-4px);
87
+ box-shadow: 0 14px 30px rgba(0, 0, 0, .2);
88
+ }
89
+
90
+ .member-card__photo {
91
+ position: absolute;
92
+ inset: 0;
93
+ width: 100%;
94
+ height: 100%;
95
+ object-fit: cover;
96
+ }
97
+
98
+ .member-card__placeholder {
99
+ position: absolute;
100
+ inset: 0;
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ font-family: var(--brand-font-display, var(--brand-font-heading, inherit));
105
+ font-size: 3.5rem;
106
+ color: var(--brand-muted, #9aa3af);
107
+ }
108
+
109
+ .member-card__overlay {
110
+ position: absolute;
111
+ inset: 0;
112
+ display: flex;
113
+ flex-direction: column;
114
+ justify-content: flex-end;
115
+ padding: 1.1rem 1.2rem;
116
+ background: linear-gradient(
117
+ 180deg,
118
+ transparent 42%,
119
+ rgba(0, 0, 0, .45) 68%,
120
+ rgba(0, 0, 0, .88) 100%
121
+ );
122
+ }
123
+
124
+ /* Scoped under .member-card so a skin's generic h3 colour can't override white. */
125
+ .member-card .member-card__name {
126
+ margin: 0;
127
+ color: #ffffff;
128
+ font-size: 1.15rem;
129
+ line-height: 1.2;
130
+ text-shadow: 0 1px 8px rgba(0, 0, 0, .55);
131
+ }
132
+
133
+ .member-card .member-card__role {
134
+ margin: .2rem 0 0;
135
+ color: rgba(255, 255, 255, .85);
136
+ font-size: .85rem;
137
+ text-shadow: 0 1px 6px rgba(0, 0, 0, .55);
138
+ }
@@ -0,0 +1,20 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /*!*************************************************************************************!*\
3
+ !*** ../splent_feature_team/src/splent_io/splent_feature_team/assets/js/scripts.js ***!
4
+ \*************************************************************************************/
5
+ // Entry point for splent_feature_team frontend assets.
6
+ // Add your JavaScript here. Webpack compiles this into assets/dist/splent_feature_team.bundle.js
7
+ //
8
+ // To load the compiled bundle in the product layout, register it in hooks.py:
9
+ //
10
+ // from splent_framework.hooks.template_hooks import register_template_hook
11
+ // from flask import url_for
12
+ //
13
+ // def team_scripts():
14
+ // return '<script src="' + url_for("team.assets", subfolder="dist", filename="splent_feature_team.bundle.js") + '"></script>'
15
+ //
16
+ // register_template_hook("layout.scripts", team_scripts)
17
+
18
+ /******/ })()
19
+ ;
20
+ //# sourceMappingURL=splent_feature_team.bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splent_feature_team.bundle.js","mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sources":["webpack://diversolab_app/../splent_feature_team/src/splent_io/splent_feature_team/assets/js/scripts.js"],"sourcesContent":["// Entry point for splent_feature_team frontend assets.\n// Add your JavaScript here. Webpack compiles this into assets/dist/splent_feature_team.bundle.js\n//\n// To load the compiled bundle in the product layout, register it in hooks.py:\n//\n// from splent_framework.hooks.template_hooks import register_template_hook\n// from flask import url_for\n//\n// def team_scripts():\n// return '<script src=\"' + url_for(\"team.assets\", subfolder=\"dist\", filename=\"splent_feature_team.bundle.js\") + '\"></script>'\n//\n// register_template_hook(\"layout.scripts\", team_scripts)\n"],"names":[],"sourceRoot":""}
@@ -0,0 +1,12 @@
1
+ // Entry point for splent_feature_team frontend assets.
2
+ // Add your JavaScript here. Webpack compiles this into assets/dist/splent_feature_team.bundle.js
3
+ //
4
+ // To load the compiled bundle in the product layout, register it in hooks.py:
5
+ //
6
+ // from splent_framework.hooks.template_hooks import register_template_hook
7
+ // from flask import url_for
8
+ //
9
+ // def team_scripts():
10
+ // return '<script src="' + url_for("team.assets", subfolder="dist", filename="splent_feature_team.bundle.js") + '"></script>'
11
+ //
12
+ // register_template_hook("layout.scripts", team_scripts)
@@ -0,0 +1,14 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ entry: path.resolve(__dirname, './scripts.js'),
5
+ output: {
6
+ filename: 'splent_feature_team.bundle.js',
7
+ path: path.resolve(__dirname, '../dist'),
8
+ },
9
+ resolve: {
10
+ fallback: {
11
+ }
12
+ },
13
+ mode: 'development',
14
+ };
@@ -0,0 +1,21 @@
1
+ """
2
+ CLI commands contributed by splent_feature_team.
3
+
4
+ These commands are auto-discovered by the framework and exposed in the
5
+ SPLENT CLI under the ``feature:team`` group.
6
+
7
+ Usage::
8
+
9
+ splent feature:team hello
10
+ """
11
+
12
+ import click
13
+
14
+
15
+ @click.command("hello")
16
+ def hello():
17
+ """Example command — replace with your own."""
18
+ click.echo(" Hello from splent_feature_team!")
19
+
20
+
21
+ cli_commands = [hello]
@@ -0,0 +1,19 @@
1
+ """
2
+ team feature configuration.
3
+
4
+ Injects environment variables into Flask app.config.
5
+ Add your feature's env vars here so the framework can track them.
6
+
7
+ To regenerate from source code: splent feature:inject-config splent_feature_team
8
+ """
9
+
10
+ import os # noqa: F401 — used when adding env vars below
11
+
12
+
13
+ def inject_config(app):
14
+ app.config.update(
15
+ {
16
+ # Add your feature's env vars here, e.g.:
17
+ # "MY_VAR": os.getenv("MY_VAR", "default_value"),
18
+ }
19
+ )
@@ -0,0 +1,6 @@
1
+ from flask_wtf import FlaskForm
2
+ from wtforms import SubmitField
3
+
4
+
5
+ class SplentFeatureTeamForm(FlaskForm):
6
+ submit = SubmitField("Save splent_feature_team")
@@ -0,0 +1,23 @@
1
+ from flask import request, url_for
2
+
3
+ from splent_framework.hooks.template_hooks import register_template_hook
4
+
5
+
6
+ def team_admin_link():
7
+ """Sidebar entry for the Team management screen (the WP-plugin pattern)."""
8
+ active = (
9
+ "active"
10
+ if request.endpoint and request.endpoint.startswith("team.admin")
11
+ else ""
12
+ )
13
+ return (
14
+ f'<li class="sidebar-item {active}">'
15
+ f'<a class="sidebar-link" href="{url_for("team.admin_index")}">'
16
+ '<i class="align-middle" data-feather="users"></i> '
17
+ '<span class="align-middle">Team</span>'
18
+ "</a>"
19
+ "</li>"
20
+ )
21
+
22
+
23
+ register_template_hook("layout.authenticated_sidebar", team_admin_link)
@@ -0,0 +1,36 @@
1
+ [alembic]
2
+ script_location = migrations
3
+
4
+ [loggers]
5
+ keys = root,sqlalchemy,alembic
6
+
7
+ [handlers]
8
+ keys = console
9
+
10
+ [formatters]
11
+ keys = generic
12
+
13
+ [logger_root]
14
+ level = WARN
15
+ handlers = console
16
+ qualname =
17
+
18
+ [logger_sqlalchemy]
19
+ level = WARN
20
+ handlers =
21
+ qualname = sqlalchemy.engine
22
+
23
+ [logger_alembic]
24
+ level = INFO
25
+ handlers =
26
+ qualname = alembic
27
+
28
+ [handler_console]
29
+ class = StreamHandler
30
+ args = (sys.stderr,)
31
+ level = NOTSET
32
+ formatter = generic
33
+
34
+ [formatter_generic]
35
+ format = %(levelname)-5.5s [%(name)s] %(message)s
36
+ datefmt = %H:%M:%S
@@ -0,0 +1,9 @@
1
+ """Alembic migration environment for splent_feature_team."""
2
+
3
+ from splent_io.splent_feature_team import models # noqa
4
+ from splent_framework.migrations.feature_env import run_feature_migrations
5
+
6
+ FEATURE_NAME = "splent_feature_team"
7
+ FEATURE_TABLES = set()
8
+
9
+ run_feature_migrations(FEATURE_NAME, FEATURE_TABLES)
@@ -0,0 +1,24 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+ ${imports if imports else ""}
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = ${repr(up_revision)}
14
+ down_revision = ${repr(down_revision)}
15
+ branch_labels = ${repr(branch_labels)}
16
+ depends_on = ${repr(depends_on)}
17
+
18
+
19
+ def upgrade():
20
+ ${upgrades if upgrades else "pass"}
21
+
22
+
23
+ def downgrade():
24
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,46 @@
1
+ """splent_feature_team
2
+
3
+ Revision ID: 1bb39441d98d
4
+ Revises:
5
+ Create Date: 2026-06-27 20:03:41.933507
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "1bb39441d98d"
15
+ down_revision = None
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.create_table(
23
+ "team_member",
24
+ sa.Column("id", sa.Integer(), nullable=False),
25
+ sa.Column("name", sa.String(length=255), nullable=False),
26
+ sa.Column("slug", sa.String(length=255), nullable=False),
27
+ sa.Column("role", sa.String(length=255), nullable=True),
28
+ sa.Column("affiliation", sa.String(length=255), nullable=True),
29
+ sa.Column("group", sa.String(length=128), nullable=True),
30
+ sa.Column("bio", sa.Text(), nullable=True),
31
+ sa.Column("photo", sa.String(length=512), nullable=True),
32
+ sa.Column("email", sa.String(length=255), nullable=True),
33
+ sa.Column("link", sa.String(length=512), nullable=True),
34
+ sa.Column("order", sa.Integer(), nullable=True),
35
+ sa.Column("published", sa.Boolean(), nullable=True),
36
+ sa.PrimaryKeyConstraint("id"),
37
+ )
38
+ op.create_index(op.f("ix_team_member_slug"), "team_member", ["slug"], unique=True)
39
+ # ### end Alembic commands ###
40
+
41
+
42
+ def downgrade():
43
+ # ### commands auto generated by Alembic - please adjust! ###
44
+ op.drop_index(op.f("ix_team_member_slug"), table_name="team_member")
45
+ op.drop_table("team_member")
46
+ # ### end Alembic commands ###
@@ -0,0 +1,29 @@
1
+ """splent_feature_team
2
+
3
+ Revision ID: 3f76830b5164
4
+ Revises: 1bb39441d98d
5
+ Create Date: 2026-06-27 23:28:16.486569
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "3f76830b5164"
15
+ down_revision = "1bb39441d98d"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.add_column("team_member", sa.Column("links", sa.JSON(), nullable=True))
23
+ # ### end Alembic commands ###
24
+
25
+
26
+ def downgrade():
27
+ # ### commands auto generated by Alembic - please adjust! ###
28
+ op.drop_column("team_member", "links")
29
+ # ### end Alembic commands ###