codex-django-cli 0.2.0__py3-none-any.whl

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 (212) hide show
  1. codex_django_cli/__init__.py +12 -0
  2. codex_django_cli/blueprints/apps/default/__init__.py +0 -0
  3. codex_django_cli/blueprints/apps/default/admin/__init__.py +0 -0
  4. codex_django_cli/blueprints/apps/default/apps.py.j2 +0 -0
  5. codex_django_cli/blueprints/apps/default/forms/__init__.py +0 -0
  6. codex_django_cli/blueprints/apps/default/models/__init__.py +0 -0
  7. codex_django_cli/blueprints/apps/default/modules/__init__.py +0 -0
  8. codex_django_cli/blueprints/apps/default/selector/__init__.py +0 -0
  9. codex_django_cli/blueprints/apps/default/services/__init__.py +0 -0
  10. codex_django_cli/blueprints/apps/default/tests/__init__.py +0 -0
  11. codex_django_cli/blueprints/apps/default/translations.py.j2 +0 -0
  12. codex_django_cli/blueprints/apps/default/urls.py.j2 +0 -0
  13. codex_django_cli/blueprints/apps/default/views/__init__.py +0 -0
  14. codex_django_cli/blueprints/deploy/shared/.dockerignore.j2 +24 -0
  15. codex_django_cli/blueprints/deploy/shared/.env.example.j2 +64 -0
  16. codex_django_cli/blueprints/deploy/shared/backend/Dockerfile.j2 +36 -0
  17. codex_django_cli/blueprints/deploy/shared/backend/entrypoint.sh.j2 +20 -0
  18. codex_django_cli/blueprints/deploy/shared/nginx/Dockerfile.local.j2 +14 -0
  19. codex_django_cli/blueprints/deploy/shared/nginx/nginx-main.conf.j2 +67 -0
  20. codex_django_cli/blueprints/deploy/shared/nginx/site-local.conf.j2 +66 -0
  21. codex_django_cli/blueprints/deploy/shared/worker/Dockerfile.j2 +25 -0
  22. codex_django_cli/blueprints/deploy/stack/docker/base.Dockerfile.j2 +38 -0
  23. codex_django_cli/blueprints/deploy/stack/docker/django.Dockerfile.j2 +27 -0
  24. codex_django_cli/blueprints/deploy/stack/docker/entrypoint.sh.j2 +19 -0
  25. codex_django_cli/blueprints/deploy/stack/docker-compose.apps.yml.j2 +41 -0
  26. codex_django_cli/blueprints/deploy/stack/docker-compose.infra.yml.j2 +80 -0
  27. codex_django_cli/blueprints/deploy/stack/docker-compose.local.apps.yml.j2 +41 -0
  28. codex_django_cli/blueprints/deploy/stack/docker-compose.local.infra.yml.j2 +94 -0
  29. codex_django_cli/blueprints/deploy/stack/docker-compose.test.yml.j2 +63 -0
  30. codex_django_cli/blueprints/deploy/stack/nginx/Dockerfile.j2 +12 -0
  31. codex_django_cli/blueprints/deploy/stack/nginx/conf.d/default.conf.j2 +68 -0
  32. codex_django_cli/blueprints/deploy/stack/nginx/site.conf.template.j2 +81 -0
  33. codex_django_cli/blueprints/deploy/stack_workflows/ci-develop.yml.j2 +42 -0
  34. codex_django_cli/blueprints/deploy/stack_workflows/ci-main.yml.j2 +165 -0
  35. codex_django_cli/blueprints/deploy/stack_workflows/deploy-cluster.yml.j2 +112 -0
  36. codex_django_cli/blueprints/deploy/standalone/docker-compose.prod.yml.j2 +118 -0
  37. codex_django_cli/blueprints/deploy/standalone/docker-compose.test.yml.j2 +86 -0
  38. codex_django_cli/blueprints/deploy/standalone/docker-compose.yml.j2 +151 -0
  39. codex_django_cli/blueprints/deploy/standalone/nginx/Dockerfile.j2 +18 -0
  40. codex_django_cli/blueprints/deploy/standalone/nginx/site.conf.template.j2 +168 -0
  41. codex_django_cli/blueprints/deploy/standalone_workflows/ci-develop.yml.j2 +42 -0
  42. codex_django_cli/blueprints/deploy/standalone_workflows/ci-main.yml.j2 +113 -0
  43. codex_django_cli/blueprints/deploy/standalone_workflows/deploy-production-tag.yml.j2 +211 -0
  44. codex_django_cli/blueprints/features/booking/booking/__init__.py.j2 +0 -0
  45. codex_django_cli/blueprints/features/booking/booking/admin.py.j2 +52 -0
  46. codex_django_cli/blueprints/features/booking/booking/apps.py.j2 +8 -0
  47. codex_django_cli/blueprints/features/booking/booking/models.py.j2 +142 -0
  48. codex_django_cli/blueprints/features/booking/booking/selectors.py.j2 +102 -0
  49. codex_django_cli/blueprints/features/booking/booking/urls.py.j2 +12 -0
  50. codex_django_cli/blueprints/features/booking/booking/views.py.j2 +174 -0
  51. codex_django_cli/blueprints/features/booking/booking/wiki.md.j2 +142 -0
  52. codex_django_cli/blueprints/features/booking/cabinet/templates/cabinet/booking/my_bookings.html +249 -0
  53. codex_django_cli/blueprints/features/booking/cabinet/views/booking.py.j2 +70 -0
  54. codex_django_cli/blueprints/features/booking/system/admin/booking_settings.py.j2 +31 -0
  55. codex_django_cli/blueprints/features/booking/system/models/booking_settings.py.j2 +7 -0
  56. codex_django_cli/blueprints/features/booking/templates/booking/booking_page.html +137 -0
  57. codex_django_cli/blueprints/features/booking/templates/booking/partials/step_confirm.html +143 -0
  58. codex_django_cli/blueprints/features/booking/templates/booking/partials/step_date.html +184 -0
  59. codex_django_cli/blueprints/features/booking/templates/booking/partials/step_service.html +78 -0
  60. codex_django_cli/blueprints/features/booking/templates/booking/partials/step_time.html +89 -0
  61. codex_django_cli/blueprints/features/client_cabinet/cabinet/adapters.py.j2 +21 -0
  62. codex_django_cli/blueprints/features/client_cabinet/cabinet/templates/cabinet/client/my_appointments.html +58 -0
  63. codex_django_cli/blueprints/features/client_cabinet/cabinet/templates/cabinet/client/profile.html +107 -0
  64. codex_django_cli/blueprints/features/client_cabinet/cabinet/templates/cabinet/client/settings.html +106 -0
  65. codex_django_cli/blueprints/features/client_cabinet/cabinet/templates/cabinet/client/settings_notifications.html +106 -0
  66. codex_django_cli/blueprints/features/client_cabinet/cabinet/templates/cabinet/client/settings_privacy.html +135 -0
  67. codex_django_cli/blueprints/features/client_cabinet/cabinet/views/client.py.j2 +49 -0
  68. codex_django_cli/blueprints/features/client_cabinet/system/models/user_profile.py.j2 +22 -0
  69. codex_django_cli/blueprints/features/notifications/arq/client.j2 +22 -0
  70. codex_django_cli/blueprints/features/notifications/feature/models/email_content.j2 +15 -0
  71. codex_django_cli/blueprints/features/notifications/feature/selectors/email_content.j2 +23 -0
  72. codex_django_cli/blueprints/features/notifications/feature/services/notification.j2 +66 -0
  73. codex_django_cli/blueprints/project/cabinet/__init__.py +0 -0
  74. codex_django_cli/blueprints/project/cabinet/admin/__init__.py +0 -0
  75. codex_django_cli/blueprints/project/cabinet/apps.py.j2 +11 -0
  76. codex_django_cli/blueprints/project/cabinet/cabinet.py.j2 +33 -0
  77. codex_django_cli/blueprints/project/cabinet/forms/__init__.py +0 -0
  78. codex_django_cli/blueprints/project/cabinet/mock.py.j2 +110 -0
  79. codex_django_cli/blueprints/project/cabinet/models/__init__.py +0 -0
  80. codex_django_cli/blueprints/project/cabinet/modules/__init__.py +0 -0
  81. codex_django_cli/blueprints/project/cabinet/selector/__init__.py +3 -0
  82. codex_django_cli/blueprints/project/cabinet/selector/users.py.j2 +25 -0
  83. codex_django_cli/blueprints/project/cabinet/services/__init__.py +0 -0
  84. codex_django_cli/blueprints/project/cabinet/static/cabinet/css/base.css +11 -0
  85. codex_django_cli/blueprints/project/cabinet/static/cabinet/css/compiler_config.json +5 -0
  86. codex_django_cli/blueprints/project/cabinet/static/cabinet/css/theme/tokens.css +30 -0
  87. codex_django_cli/blueprints/project/cabinet/static/cabinet/js/app/cabinet.js +37 -0
  88. codex_django_cli/blueprints/project/cabinet/static/cabinet/js/compiler_config.json +7 -0
  89. codex_django_cli/blueprints/project/cabinet/templates/cabinet/users/detail.html +91 -0
  90. codex_django_cli/blueprints/project/cabinet/templates/cabinet/users/index.html +97 -0
  91. codex_django_cli/blueprints/project/cabinet/tests/__init__.py +0 -0
  92. codex_django_cli/blueprints/project/cabinet/translations.py.j2 +2 -0
  93. codex_django_cli/blueprints/project/cabinet/urls.py.j2 +22 -0
  94. codex_django_cli/blueprints/project/cabinet/views/__init__.py +3 -0
  95. codex_django_cli/blueprints/project/cabinet/views/users.py.j2 +17 -0
  96. codex_django_cli/blueprints/project/core/__init__.py.j2 +1 -0
  97. codex_django_cli/blueprints/project/core/apps.py.j2 +15 -0
  98. codex_django_cli/blueprints/project/core/asgi.py.j2 +7 -0
  99. codex_django_cli/blueprints/project/core/logger.py.j2 +57 -0
  100. codex_django_cli/blueprints/project/core/redis.py.j2 +4 -0
  101. codex_django_cli/blueprints/project/core/settings/__init__.py.j2 +4 -0
  102. codex_django_cli/blueprints/project/core/settings/base.py.j2 +67 -0
  103. codex_django_cli/blueprints/project/core/settings/dev.py.j2 +56 -0
  104. codex_django_cli/blueprints/project/core/settings/modules/__init__.py.j2 +1 -0
  105. codex_django_cli/blueprints/project/core/settings/modules/admin.py.j2 +72 -0
  106. codex_django_cli/blueprints/project/core/settings/modules/apps.py.j2 +64 -0
  107. codex_django_cli/blueprints/project/core/settings/modules/cache.py.j2 +49 -0
  108. codex_django_cli/blueprints/project/core/settings/modules/codex.py.j2 +39 -0
  109. codex_django_cli/blueprints/project/core/settings/modules/database.py.j2 +49 -0
  110. codex_django_cli/blueprints/project/core/settings/modules/internationalization.py.j2 +43 -0
  111. codex_django_cli/blueprints/project/core/settings/modules/logging.py.j2 +45 -0
  112. codex_django_cli/blueprints/project/core/settings/modules/middleware.py.j2 +17 -0
  113. codex_django_cli/blueprints/project/core/settings/modules/security.py.j2 +53 -0
  114. codex_django_cli/blueprints/project/core/settings/modules/sitemap.py.j2 +14 -0
  115. codex_django_cli/blueprints/project/core/settings/modules/static.py.j2 +36 -0
  116. codex_django_cli/blueprints/project/core/settings/modules/templates.py.j2 +29 -0
  117. codex_django_cli/blueprints/project/core/settings/prod.py.j2 +77 -0
  118. codex_django_cli/blueprints/project/core/settings/test.py.j2 +40 -0
  119. codex_django_cli/blueprints/project/core/sitemaps.py.j2 +26 -0
  120. codex_django_cli/blueprints/project/core/urls.py.j2 +71 -0
  121. codex_django_cli/blueprints/project/core/wsgi.py.j2 +7 -0
  122. codex_django_cli/blueprints/project/features/__init__.py.j2 +0 -0
  123. codex_django_cli/blueprints/project/features/main/admin/__init__.py +0 -0
  124. codex_django_cli/blueprints/project/features/main/apps.py.j2 +8 -0
  125. codex_django_cli/blueprints/project/features/main/forms/__init__.py +0 -0
  126. codex_django_cli/blueprints/project/features/main/models/__init__.py +0 -0
  127. codex_django_cli/blueprints/project/features/main/sitemaps.py.j2 +23 -0
  128. codex_django_cli/blueprints/project/features/main/tests/__init__.py +0 -0
  129. codex_django_cli/blueprints/project/features/main/translations.py.j2 +2 -0
  130. codex_django_cli/blueprints/project/features/main/urls.py.j2 +8 -0
  131. codex_django_cli/blueprints/project/features/main/views/__init__.py.j2 +9 -0
  132. codex_django_cli/blueprints/project/manage.py.j2 +39 -0
  133. codex_django_cli/blueprints/project/static/css/base/components.css +88 -0
  134. codex_django_cli/blueprints/project/static/css/base/footer.css +43 -0
  135. codex_django_cli/blueprints/project/static/css/base/header.css +76 -0
  136. codex_django_cli/blueprints/project/static/css/base/layout.css +58 -0
  137. codex_django_cli/blueprints/project/static/css/base/reset.css +65 -0
  138. codex_django_cli/blueprints/project/static/css/base/tokens.css +45 -0
  139. codex_django_cli/blueprints/project/static/css/base.css +31 -0
  140. codex_django_cli/blueprints/project/static/css/compiler_config.json +10 -0
  141. codex_django_cli/blueprints/project/static/css/pages/contacts.css +42 -0
  142. codex_django_cli/blueprints/project/static/css/pages/errors.css +11 -0
  143. codex_django_cli/blueprints/project/static/css/pages/home.css +120 -0
  144. codex_django_cli/blueprints/project/static/js/app/main.js +8 -0
  145. codex_django_cli/blueprints/project/static/js/vendor/alpine.min.js +9 -0
  146. codex_django_cli/blueprints/project/static/js/vendor/htmx.min.js +9 -0
  147. codex_django_cli/blueprints/project/static/manifest.json +15 -0
  148. codex_django_cli/blueprints/project/static/robots.txt +4 -0
  149. codex_django_cli/blueprints/project/system/__init__.py.j2 +1 -0
  150. codex_django_cli/blueprints/project/system/admin/__init__.py.j2 +15 -0
  151. codex_django_cli/blueprints/project/system/admin/seo.py.j2 +28 -0
  152. codex_django_cli/blueprints/project/system/admin/settings.py.j2 +65 -0
  153. codex_django_cli/blueprints/project/system/admin/static.py.j2 +18 -0
  154. codex_django_cli/blueprints/project/system/apps.py.j2 +9 -0
  155. codex_django_cli/blueprints/project/system/forms/__init__.py.j2 +1 -0
  156. codex_django_cli/blueprints/project/system/management/__init__.py +0 -0
  157. codex_django_cli/blueprints/project/system/management/commands/__init__.py +0 -0
  158. codex_django_cli/blueprints/project/system/management/commands/dev.py.j2 +5 -0
  159. codex_django_cli/blueprints/project/system/management/commands/menu.py.j2 +81 -0
  160. codex_django_cli/blueprints/project/system/management/commands/runserver_plus.py.j2 +46 -0
  161. codex_django_cli/blueprints/project/system/management/commands/update_all_content.py.j2 +17 -0
  162. codex_django_cli/blueprints/project/system/management/commands/update_site_settings.py.j2 +80 -0
  163. codex_django_cli/blueprints/project/system/models/__init__.py.j2 +15 -0
  164. codex_django_cli/blueprints/project/system/models/seo.py.j2 +37 -0
  165. codex_django_cli/blueprints/project/system/models/settings.py.j2 +31 -0
  166. codex_django_cli/blueprints/project/system/models/static.py.j2 +13 -0
  167. codex_django_cli/blueprints/project/system/services/__init__.py.j2 +1 -0
  168. codex_django_cli/blueprints/project/system/urls.py.j2 +7 -0
  169. codex_django_cli/blueprints/project/system/views/__init__.py.j2 +1 -0
  170. codex_django_cli/blueprints/project/system/views/errors.py.j2 +13 -0
  171. codex_django_cli/blueprints/project/templates/base.html.j2 +53 -0
  172. codex_django_cli/blueprints/project/templates/errors/400.html +9 -0
  173. codex_django_cli/blueprints/project/templates/errors/403.html +9 -0
  174. codex_django_cli/blueprints/project/templates/errors/404.html +16 -0
  175. codex_django_cli/blueprints/project/templates/errors/500.html +16 -0
  176. codex_django_cli/blueprints/project/templates/includes/_analytics_body.html +2 -0
  177. codex_django_cli/blueprints/project/templates/includes/_analytics_head.html +2 -0
  178. codex_django_cli/blueprints/project/templates/includes/_cookie_consent.html +2 -0
  179. codex_django_cli/blueprints/project/templates/includes/_critical_css.html +38 -0
  180. codex_django_cli/blueprints/project/templates/includes/_footer.html +57 -0
  181. codex_django_cli/blueprints/project/templates/includes/_header.html.j2 +75 -0
  182. codex_django_cli/blueprints/project/templates/includes/_hreflang_tags.html.j2 +11 -0
  183. codex_django_cli/blueprints/project/templates/includes/_meta.html +44 -0
  184. codex_django_cli/blueprints/project/templates/main/contacts.html +38 -0
  185. codex_django_cli/blueprints/project/templates/main/home.html +128 -0
  186. codex_django_cli/blueprints/project/templates/main/home.html.j2 +129 -0
  187. codex_django_cli/blueprints/project/templates/sitemap.xml +16 -0
  188. codex_django_cli/blueprints/repo/.env.example.j2 +15 -0
  189. codex_django_cli/blueprints/repo/.github/workflows/.gitkeep +1 -0
  190. codex_django_cli/blueprints/repo/.gitignore +38 -0
  191. codex_django_cli/blueprints/repo/README.md.j2 +32 -0
  192. codex_django_cli/blueprints/repo/docs/README.md +5 -0
  193. codex_django_cli/blueprints/repo/pyproject.toml.j2 +46 -0
  194. codex_django_cli/blueprints/repo/tools/dev/check.py.j2 +23 -0
  195. codex_django_cli/blueprints/repo/tools/dev/generate_project_tree.py +15 -0
  196. codex_django_cli/commands/__init__.py +1 -0
  197. codex_django_cli/commands/add_app.py +56 -0
  198. codex_django_cli/commands/booking.py +92 -0
  199. codex_django_cli/commands/client_cabinet.py +66 -0
  200. codex_django_cli/commands/deploy.py +96 -0
  201. codex_django_cli/commands/init.py +216 -0
  202. codex_django_cli/commands/notifications.py +59 -0
  203. codex_django_cli/commands/quality.py +116 -0
  204. codex_django_cli/engine.py +113 -0
  205. codex_django_cli/main.py +470 -0
  206. codex_django_cli/prompts.py +258 -0
  207. codex_django_cli/py.typed +0 -0
  208. codex_django_cli/utils.py +25 -0
  209. codex_django_cli-0.2.0.dist-info/METADATA +66 -0
  210. codex_django_cli-0.2.0.dist-info/RECORD +212 -0
  211. codex_django_cli-0.2.0.dist-info/WHEEL +4 -0
  212. codex_django_cli-0.2.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,17 @@
1
+ from typing import ClassVar
2
+
3
+ from codex_django.system.management.base_commands import BaseUpdateAllContentCommand
4
+
5
+
6
+ class Command(BaseUpdateAllContentCommand):
7
+ """
8
+ Run all content update commands and clear cache.
9
+ Useful to run during entrypoint initialization or deployments.
10
+ """
11
+
12
+ help = "Run all content update commands and clear cache"
13
+
14
+ commands_to_run: ClassVar[list[str]] = [
15
+ "update_site_settings",
16
+ # Add other specific commands here
17
+ ]
@@ -0,0 +1,80 @@
1
+ import json
2
+
3
+ from codex_django.system.management.base_commands import BaseHashProtectedCommand
4
+ from django.conf import settings
5
+ from loguru import logger as log
6
+
7
+ from system.models.settings import SiteSettings
8
+
9
+
10
+ class Command(BaseHashProtectedCommand):
11
+ """
12
+ Management command to update Site Settings from JSON fixture.
13
+ Checks a hash to avoid double loading.
14
+ Usage: python manage.py update_site_settings
15
+ """
16
+
17
+ help = "Update Site Settings from JSON fixture (system/fixtures/content/site_settings.json)"
18
+ fixture_key = "site_settings"
19
+
20
+ def get_fixture_paths(self):
21
+ return [settings.BASE_DIR / "system" / "fixtures" / "content" / "site_settings.json"]
22
+
23
+ def handle_import(self, *args, **options):
24
+ log.info("Command: update_site_settings | Action: Start")
25
+
26
+ fixture_paths = self.get_fixture_paths()
27
+ if not fixture_paths:
28
+ return False
29
+
30
+ fixture_path = fixture_paths[0]
31
+ try:
32
+ with open(fixture_path, encoding="utf-8") as f:
33
+ data = json.load(f)
34
+ except json.JSONDecodeError as e:
35
+ log.error(f"Command: update_site_settings | Action: Failed | error=JSONDecodeError | message={e}")
36
+ self.stdout.write(self.style.ERROR(f"Error decoding JSON: {e}"))
37
+ return False
38
+
39
+ if not data or not isinstance(data, list) or len(data) == 0:
40
+ log.error("Command: update_site_settings | Action: Failed | error=Invalid format")
41
+ self.stdout.write(self.style.ERROR("Invalid fixture format"))
42
+ return False
43
+
44
+ fixture_data = data[0]["fields"]
45
+
46
+ # Load current settings using Singleton get() method
47
+ site_settings = SiteSettings.load()
48
+
49
+ # Compare and update
50
+ updated_fields = []
51
+ for field_name, new_value in fixture_data.items():
52
+ if hasattr(site_settings, field_name):
53
+ current_value = getattr(site_settings, field_name)
54
+
55
+ # Convert Decimal fields to string for comparison
56
+ if hasattr(current_value, "__str__") and field_name in ["latitude", "longitude"]:
57
+ current_value = str(current_value)
58
+
59
+ if str(current_value) != str(new_value):
60
+ setattr(site_settings, field_name, new_value)
61
+ updated_fields.append(field_name)
62
+ log.debug(f"Action: FieldUpdate | field={field_name} | value={new_value}")
63
+ self.stdout.write(self.style.SUCCESS(f" [UPDATE] {field_name}: {new_value}"))
64
+
65
+ # Save if there are changes
66
+ if updated_fields:
67
+ site_settings.save()
68
+ log.info(f"Command: update_site_settings | Action: SaveDB | count={len(updated_fields)}")
69
+
70
+ from system.models.settings import get_site_settings_manager
71
+
72
+ manager = get_site_settings_manager()
73
+ manager.save_instance(site_settings)
74
+ self.stdout.write(self.style.SUCCESS(f"\n✓ Site Settings updated ({len(updated_fields)} fields changed)"))
75
+ else:
76
+ log.info("Command: update_site_settings | Action: NoChanges")
77
+ self.stdout.write(self.style.SUCCESS("\n✓ No changes needed (all fields up to date)"))
78
+
79
+ log.info("Command: update_site_settings | Action: Success")
80
+ return True
@@ -0,0 +1,15 @@
1
+ from .seo import StaticPageSeo
2
+ from .settings import SiteSettings
3
+ from .static import StaticTranslation
4
+ {%- if with_booking %}
5
+ from .booking_settings import BookingSettings
6
+ {%- endif %}
7
+
8
+ __all__ = [
9
+ "SiteSettings",
10
+ "StaticTranslation",
11
+ "StaticPageSeo",
12
+ {%- if with_booking %}
13
+ "BookingSettings",
14
+ {%- endif %}
15
+ ]
@@ -0,0 +1,37 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from codex_django.system.mixins.seo import AbstractStaticPageSeo
4
+ from django.db import models
5
+ from django.utils.translation import gettext_lazy as _
6
+
7
+
8
+ class StaticPageSeo(AbstractStaticPageSeo):
9
+ """
10
+ SEO settings for specific static pages of the project.
11
+ """
12
+
13
+ KEY_HOME = "home"
14
+ KEY_CONTACTS = "contacts"
15
+ KEY_IMPRESSUM = "impressum"
16
+ KEY_DATENSCHUTZ = "datenschutz"
17
+
18
+ PAGE_CHOICES: ClassVar[list] = [
19
+ (KEY_HOME, _("Главная страница")),
20
+ (KEY_CONTACTS, _("Контакты")),
21
+ (KEY_IMPRESSUM, _("Impressum")),
22
+ (KEY_DATENSCHUTZ, _("Datenschutz")),
23
+ ]
24
+
25
+ page_key: Any = models.CharField(
26
+ max_length=50,
27
+ choices=PAGE_CHOICES,
28
+ unique=True,
29
+ verbose_name=_("ID Страницы"),
30
+ )
31
+
32
+ class Meta:
33
+ verbose_name = _("SEO статичной страницы")
34
+ verbose_name_plural = _("SEO статичных страниц")
35
+
36
+ def __str__(self):
37
+ return self.get_page_key_display()
@@ -0,0 +1,31 @@
1
+ from codex_django.system.mixins.settings import (
2
+ AbstractSiteSettings,
3
+ SiteContactSettingsMixin,
4
+ SiteEmailSettingsMixin,
5
+ SiteGeoSettingsMixin,
6
+ SiteLegalSettingsMixin,
7
+ SiteMarketingSettingsMixin,
8
+ SiteSocialSettingsMixin,
9
+ SiteTechnicalSettingsMixin,
10
+ )
11
+ from django.utils.translation import gettext_lazy as _
12
+
13
+
14
+ class SiteSettings(
15
+ AbstractSiteSettings,
16
+ SiteContactSettingsMixin,
17
+ SiteGeoSettingsMixin,
18
+ SiteSocialSettingsMixin,
19
+ SiteMarketingSettingsMixin,
20
+ SiteTechnicalSettingsMixin,
21
+ SiteEmailSettingsMixin,
22
+ SiteLegalSettingsMixin
23
+ ):
24
+ """
25
+ Global site settings.
26
+ Automatically synchronized with Redis.
27
+ """
28
+
29
+ class Meta:
30
+ verbose_name = _("Настройки сайта")
31
+ verbose_name_plural = _("Настройки сайта")
@@ -0,0 +1,13 @@
1
+ from codex_django.system.mixins import AbstractStaticTranslation
2
+ from django.utils.translation import gettext_lazy as _
3
+
4
+
5
+ class StaticTranslation(AbstractStaticTranslation):
6
+ """
7
+ Concrete model for static page content/translations.
8
+ Used by context_processor to provide content in templates.
9
+ """
10
+
11
+ class Meta(AbstractStaticTranslation.Meta):
12
+ verbose_name = _("Статический перевод")
13
+ verbose_name_plural = _("Статические переводы")
@@ -0,0 +1 @@
1
+ # Services package initialization
@@ -0,0 +1,7 @@
1
+ from typing import Any
2
+
3
+ app_name = "system"
4
+
5
+ urlpatterns: list[Any] = [
6
+ # Add system routes here (e.g., health-check endpoint)
7
+ ]
@@ -0,0 +1 @@
1
+ # Views package initialization
@@ -0,0 +1,13 @@
1
+ from django.shortcuts import render
2
+
3
+ def handler404(request, exception):
4
+ return render(request, "errors/404.html", status=404)
5
+
6
+ def handler500(request):
7
+ return render(request, "errors/500.html", status=500)
8
+
9
+ def handler403(request, exception):
10
+ return render(request, "errors/403.html", status=403)
11
+
12
+ def handler400(request, exception):
13
+ return render(request, "errors/400.html", status=400)
@@ -0,0 +1,53 @@
1
+ {% raw %}{% load static %}{% endraw %}{% if enable_i18n %}{% raw %}{% load i18n %}{% endraw %}{% endif %}
2
+ <!DOCTYPE html>
3
+ <html lang="{%- if enable_i18n -%}{% raw %}{{ LANGUAGE_CODE }}{% endraw %}{%- else -%}de{%- endif -%}">
4
+ <head>
5
+ {% raw %}
6
+ {% include "includes/_meta.html" %}
7
+ {% include "includes/_analytics_head.html" %}
8
+
9
+ <!-- Core Styles -->
10
+ <link rel="stylesheet" href="{% static 'css/app.css' %}">
11
+
12
+ <!-- Vendor Scripts -->
13
+ <script src="{% static 'js/vendor/htmx.min.js' %}" defer></script>
14
+ <script src="{% static 'js/vendor/alpine.min.js' %}" defer></script>
15
+
16
+ {% block extra_css %}{% endblock extra_css %}
17
+ </head>
18
+ <body class="{% block body_class %}{% endblock body_class %}" {% if site_settings.app_mode_enabled %}x-data="appMode"{% endif %}>
19
+ {% include "includes/_analytics_body.html" %}
20
+
21
+ <div class="site-wrapper">
22
+ {% include "includes/_header.html" %}
23
+
24
+ <!-- Django Messages -->
25
+ {% if messages %}
26
+ <div class="messages-container container">
27
+ {% for message in messages %}
28
+ <div class="alert alert-{{ message.tags }}">
29
+ {{ message }}
30
+ </div>
31
+ {% endfor %}
32
+ </div>
33
+ {% endif %}
34
+
35
+ <main id="main-content">
36
+ {% block content %}
37
+ {% endblock content %}
38
+ </main>
39
+
40
+ {% include "includes/_footer.html" %}
41
+ </div>
42
+
43
+ <!-- Global Components -->
44
+ {% include "includes/_cookie_consent.html" %}
45
+
46
+ <!-- App Scripts -->
47
+ <script src="{% static 'js/app/main.js' %}" defer></script>
48
+
49
+ {% block scripts %}
50
+ {% endblock scripts %}
51
+ </body>
52
+ {% endraw %}
53
+ </html>
@@ -0,0 +1,9 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block content %}
4
+ <div class="error-container">
5
+ <h1>400 - Bad Request</h1>
6
+ <p>Your browser sent a request that this server could not understand.</p>
7
+ <a href="{% url 'home' %}">Return Home</a>
8
+ </div>
9
+ {% endblock %}
@@ -0,0 +1,9 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block content %}
4
+ <div class="error-container">
5
+ <h1>403 - Permission Denied</h1>
6
+ <p>You do not have permission to access this resource.</p>
7
+ <a href="{% url 'home' %}">Return Home</a>
8
+ </div>
9
+ {% endblock %}
@@ -0,0 +1,16 @@
1
+ {% extends "base.html" %}
2
+ {% load i18n %}
3
+
4
+ {% block title %}404 — {% trans "Page Not Found" %}{% endblock title %}
5
+ {% block meta_robots %}<meta name="robots" content="noindex, nofollow">{% endblock meta_robots %}
6
+
7
+ {% block content %}
8
+ <section class="section">
9
+ <div class="container text-center">
10
+ <h1 class="error-code">404</h1>
11
+ <h2>{% trans "Page Not Found" %}</h2>
12
+ <p class="text-muted">{% trans "The page you are looking for does not exist." %}</p>
13
+ <a href="{% url 'home' %}" class="btn btn-primary">{% trans "Go Home" %}</a>
14
+ </div>
15
+ </section>
16
+ {% endblock content %}
@@ -0,0 +1,16 @@
1
+ {% extends "base.html" %}
2
+ {% load i18n %}
3
+
4
+ {% block title %}500 — {% trans "Server Error" %}{% endblock title %}
5
+ {% block meta_robots %}<meta name="robots" content="noindex, nofollow">{% endblock meta_robots %}
6
+
7
+ {% block content %}
8
+ <section class="section">
9
+ <div class="container text-center">
10
+ <h1 class="error-code">500</h1>
11
+ <h2>{% trans "Server Error" %}</h2>
12
+ <p class="text-muted">{% trans "Something went wrong on our end. Please try again later." %}</p>
13
+ <a href="{% url 'home' %}" class="btn btn-primary">{% trans "Go Home" %}</a>
14
+ </div>
15
+ </section>
16
+ {% endblock content %}
@@ -0,0 +1,2 @@
1
+ <!-- Analytics BODY placeholder -->
2
+ <!-- Add your Google Analytics / GTM body scripts here -->
@@ -0,0 +1,2 @@
1
+ <!-- Analytics HEAD placeholder -->
2
+ <!-- Add your Google Analytics / GTM head scripts here -->
@@ -0,0 +1,2 @@
1
+ <!-- Cookie Consent placeholder -->
2
+ <!-- Integrate your cookie consent solution here (e.g. cookieconsent.js) -->
@@ -0,0 +1,38 @@
1
+ <style>
2
+ /* Critical CSS: Minimal styles for first paint */
3
+ :root {
4
+ --bg-body: #f9fafb;
5
+ --text-main: #111827;
6
+ --header-height: 80px;
7
+ }
8
+
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ background-color: var(--bg-body);
13
+ color: var(--text-main);
14
+ font-family: sans-serif;
15
+ overflow-x: hidden;
16
+ }
17
+
18
+ .site-wrapper {
19
+ display: flex;
20
+ flex-direction: column;
21
+ min-height: 100vh;
22
+ }
23
+
24
+ .header {
25
+ height: var(--header-height);
26
+ background: #ffffff;
27
+ border-bottom: 1px solid #e5e7eb;
28
+ }
29
+
30
+ .container {
31
+ width: 100%;
32
+ max-width: 1280px;
33
+ margin: 0 auto;
34
+ padding: 0 20px;
35
+ }
36
+
37
+ [x-cloak] { display: none !important; }
38
+ </style>
@@ -0,0 +1,57 @@
1
+ {% load i18n static %}
2
+
3
+ <footer class="footer">
4
+ <div class="container">
5
+ <div class="footer-grid">
6
+ <!-- Col 1: Brand -->
7
+ <div class="footer-col">
8
+ <h3 class="footer-col-title">{{ site_settings.company_name }}</h3>
9
+ <p class="footer-link" style="margin-bottom: 24px;">
10
+ {{ site_settings.site_description|default:"" }}
11
+ </p>
12
+ <div class="footer-socials">
13
+ {% if site_settings.instagram_url %}
14
+ <a href="{{ site_settings.instagram_url }}" class="footer-link" target="_blank">Instagram</a>
15
+ {% endif %}
16
+ {% if site_settings.facebook_url %}
17
+ <a href="{{ site_settings.facebook_url }}" class="footer-link" target="_blank">Facebook</a>
18
+ {% endif %}
19
+ </div>
20
+ </div>
21
+
22
+ <!-- Col 2: Navigation -->
23
+ <div class="footer-col">
24
+ <h4 class="footer-col-title">{% trans "Navigation" %}</h4>
25
+ <nav class="footer-links">
26
+ <a href="{% url 'home' %}" class="footer-link">{% trans "Home" %}</a>
27
+ <a href="{% url 'contacts' %}" class="footer-link">{% trans "Contacts" %}</a>
28
+ </nav>
29
+ </div>
30
+
31
+ <!-- Col 3: Contacts -->
32
+ <div class="footer-col">
33
+ <h4 class="footer-col-title">{% trans "Contact Us" %}</h4>
34
+ <div class="footer-links">
35
+ <div class="footer-contact-item">
36
+ <span>📞</span>
37
+ <a href="tel:{{ site_settings.phone }}" class="footer-link">{{ site_settings.phone }}</a>
38
+ </div>
39
+ <div class="footer-contact-item">
40
+ <span>✉️</span>
41
+ <a href="mailto:{{ site_settings.email }}" class="footer-link">{{ site_settings.email }}</a>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- Bottom Bar -->
48
+ <div class="footer-bottom">
49
+ <p class="footer-copyright">
50
+ &copy; {% now "Y" %} {{ site_settings.company_name }}. All rights reserved.
51
+ </p>
52
+ <div class="footer-legal">
53
+ <a href="{% url 'account_login' %}" class="footer-link" style="opacity: 0.5; font-size: 12px;">{% trans "Management" %}</a>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </footer>
@@ -0,0 +1,75 @@
1
+ {% raw %}{% load static %}{% endraw %}
2
+ {% raw %}{% load i18n %}{% endraw %}
3
+
4
+ {% raw %}
5
+ <header class="header" x-data="{ mobileMenuOpen: false }">
6
+ <div class="container header-inner">
7
+
8
+ <!-- Logo -->
9
+ <a href="{% url 'home' %}" class="logo-link">
10
+ {% if site_settings.logo_url %}
11
+ <img src="{{ site_settings.logo_url }}" alt="{{ site_settings.company_name }}" class="logo-img">
12
+ {% else %}
13
+ <span class="logo-text">{{ site_settings.company_name }}</span>
14
+ {% endif %}
15
+ </a>
16
+
17
+ <!-- Desktop Navigation -->
18
+ <nav class="nav-desktop">
19
+ <a href="{% url 'home' %}" class="nav-link {% if request.resolver_match.url_name == 'home' %}active{% endif %}">{% trans "Home" %}</a>
20
+ <a href="{% url 'contacts' %}" class="nav-link {% if 'contacts' in request.path %}active{% endif %}">{% trans "Contacts" %}</a>
21
+ </nav>
22
+
23
+ <!-- Actions -->
24
+ <div class="header-actions">
25
+ {% endraw %}
26
+ {%- if enable_i18n %}
27
+ {% raw %}
28
+ <!-- Language Switcher -->
29
+ <div class="lang-switcher" x-data="{ open: false }" @click.away="open = false">
30
+ <button class="btn-link" @click="open = !open">
31
+ {{ LANGUAGE_CODE|upper }}
32
+ </button>
33
+ <div class="lang-dropdown" x-show="open" x-cloak>
34
+ {% get_available_languages as LANGUAGES %}
35
+ {% for lang_code, lang_name in LANGUAGES %}
36
+ {% if lang_code != LANGUAGE_CODE %}
37
+ <form action="{% url 'set_language' %}" method="post">
38
+ {% csrf_token %}
39
+ <input name="next" type="hidden" value="{{ request.path }}">
40
+ <input name="language" type="hidden" value="{{ lang_code }}">
41
+ <button type="submit" class="lang-option">{{ lang_code|upper }}</button>
42
+ </form>
43
+ {% endif %}
44
+ {% endfor %}
45
+ </div>
46
+ </div>
47
+ {% endraw %}
48
+ {%- endif %}
49
+ {% raw %}
50
+ <!-- Burger -->
51
+ <button class="burger-btn" @click="mobileMenuOpen = true" aria-label="Open menu">
52
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
53
+ <line x1="3" y1="12" x2="21" y2="12"></line>
54
+ <line x1="3" y1="6" x2="21" y2="6"></line>
55
+ <line x1="3" y1="18" x2="21" y2="18"></line>
56
+ </svg>
57
+ </button>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Mobile Menu -->
62
+ <template x-teleport="body">
63
+ <div class="mobile-menu" :class="{ 'active': mobileMenuOpen }" x-show="mobileMenuOpen" x-cloak>
64
+ <button class="close-btn" @click="mobileMenuOpen = false" style="position: absolute; top: 20px; right: 20px; background: none; border: none; font-size: 32px;">&times;</button>
65
+ <nav class="flex flex-col gap-lg">
66
+ <a href="{% url 'home' %}" class="mobile-nav-link" @click="mobileMenuOpen = false">{% trans "Home" %}</a>
67
+ <a href="{% url 'contacts' %}" class="mobile-nav-link" @click="mobileMenuOpen = false">{% trans "Contacts" %}</a>
68
+ </nav>
69
+ <div class="mt-auto">
70
+ <a href="tel:{{ site_settings.phone }}" class="btn btn-primary btn-block">{{ site_settings.phone }}</a>
71
+ </div>
72
+ </div>
73
+ </template>
74
+ </header>
75
+ {% endraw %}
@@ -0,0 +1,11 @@
1
+ {%- if enable_i18n %}
2
+ {% raw %}
3
+ {% load i18n %}
4
+ {% get_current_language as LANGUAGE_CODE %}
5
+ {% get_available_languages as LANGUAGES %}
6
+ {% for lang_code, lang_name in LANGUAGES %}
7
+ <link rel="alternate" hreflang="{{ lang_code }}" href="{{ CANONICAL_DOMAIN }}/{{ lang_code }}{{ request.path }}">
8
+ {% endfor %}
9
+ <link rel="alternate" hreflang="x-default" href="{{ CANONICAL_DOMAIN }}{{ request.path }}">
10
+ {% endraw %}
11
+ {%- endif %}
@@ -0,0 +1,44 @@
1
+ {% load static %}
2
+ <meta charset="UTF-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
+
5
+ <title>
6
+ {% block title %}{% firstof seo.seo_title site_settings.company_name 'Project' %}{% endblock %}
7
+ </title>
8
+
9
+ <meta name="description"
10
+ content="{% block meta_description %}{% firstof seo.seo_description site_settings.site_description '' %}{% endblock %}">
11
+
12
+ <meta name="author" content="{{ site_settings.company_name }}">
13
+ <meta name="generator" content="Django">
14
+ <link rel="canonical" href="{{ CANONICAL_DOMAIN }}{{ request.path }}">
15
+
16
+ {% block meta_robots %}
17
+ <meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
18
+ {% endblock %}
19
+
20
+ {% include 'includes/_hreflang_tags.html' %}
21
+
22
+ <!-- Open Graph -->
23
+ <meta property="og:type" content="website">
24
+ <meta property="og:url" content="{{ CANONICAL_DOMAIN }}{{ request.path }}">
25
+ <meta property="og:title" content="{% block og_title %}{% firstof seo.seo_title site_settings.company_name %}{% endblock %}">
26
+ <meta property="og:description" content="{% block og_description %}{% firstof seo.seo_description site_settings.site_description %}{% endblock %}">
27
+ <meta property="og:image" content="{% block og_image %}{% if seo.seo_image %}{{ CANONICAL_DOMAIN }}{{ seo.seo_image.url }}{% else %}{{ CANONICAL_DOMAIN }}{{ site_settings.logo_url }}{% endif %}{% endblock %}">
28
+ <meta property="og:site_name" content="{{ site_settings.company_name }}">
29
+ <meta property="og:locale" content="{{ LANGUAGE_CODE }}">
30
+
31
+ <!-- Twitter -->
32
+ <meta name="twitter:card" content="summary_large_image">
33
+ <meta name="twitter:title" content="{% block twitter_title %}{% firstof seo.seo_title site_settings.company_name %}{% endblock %}">
34
+ <meta name="twitter:description" content="{% block twitter_description %}{% firstof seo.seo_description site_settings.site_description %}{% endblock %}">
35
+ <meta name="twitter:image" content="{% block twitter_image %}{% if seo.seo_image %}{{ CANONICAL_DOMAIN }}{{ seo.seo_image.url }}{% else %}{{ CANONICAL_DOMAIN }}{{ site_settings.logo_url }}{% endif %}{% endblock %}">
36
+
37
+ <!-- Favicons -->
38
+ <link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}">
39
+ <link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/apple-touch-icon.png' %}">
40
+ <link rel="manifest" href="{% static 'manifest.json' %}">
41
+
42
+ <meta name="theme-color" content="#3b82f6">
43
+
44
+ {% include 'includes/_critical_css.html' %}
@@ -0,0 +1,38 @@
1
+ {% extends "base.html" %}
2
+ {% load i18n %}
3
+
4
+ {% block title %}
5
+ {{ site_settings.company_name }} | {% trans "Contacts" %}
6
+ {% endblock title %}
7
+
8
+ {% block content %}
9
+ <section class="section">
10
+ <div class="container">
11
+ <div class="section-header">
12
+ <h1 class="section-title">{% trans "Contact Us" %}</h1>
13
+ <p class="text-muted">{% trans "We'd love to hear from you. Send us a message!" %}</p>
14
+ </div>
15
+ <div class="contact-grid">
16
+ <!-- Contact Info -->
17
+ <div class="contact-info">
18
+ {% if site_settings.phone %}
19
+ <div class="contact-item">
20
+ <span class="contact-icon">📞</span>
21
+ <a href="tel:{{ site_settings.phone }}" class="contact-link">{{ site_settings.phone }}</a>
22
+ </div>
23
+ {% endif %}
24
+ {% if site_settings.email %}
25
+ <div class="contact-item">
26
+ <span class="contact-icon">✉️</span>
27
+ <a href="mailto:{{ site_settings.email }}" class="contact-link">{{ site_settings.email }}</a>
28
+ </div>
29
+ {% endif %}
30
+ </div>
31
+ <!-- Contact Form -->
32
+ <div class="contact-form-wrapper">
33
+ <p class="text-muted">{% trans "Add your contact form here." %}</p>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </section>
38
+ {% endblock content %}