From 2c8bc2ffa6ac667c9e1821ba4944c726d2b08231 Mon Sep 17 00:00:00 2001
From: Hangzhi Yu <hangzhi@protonmail.com>
Date: Wed, 18 Jan 2023 16:58:41 +0100
Subject: [PATCH] Add SPA support

---
 CHANGELOG.rst                                 |   5 +
 aleksis/apps/chronos/frontend/index.js        | 140 ++++++++++++++++++
 .../apps/chronos/frontend/messages/de.json    |   5 +
 .../apps/chronos/frontend/messages/en.json    |  18 +++
 aleksis/apps/chronos/views.py                 |   7 +
 5 files changed, 175 insertions(+)
 create mode 100644 aleksis/apps/chronos/frontend/index.js
 create mode 100644 aleksis/apps/chronos/frontend/messages/de.json
 create mode 100644 aleksis/apps/chronos/frontend/messages/en.json

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d26b06e2..cea77f1f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,11 @@ and this project adheres to `Semantic Versioning`_.
 Unreleased
 ----------
 
+Added
+~~~~~
+
+* Add support for operation with SPA.
+
 Changed
 ~~~~~~~
 
diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js
new file mode 100644
index 00000000..b711136e
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/index.js
@@ -0,0 +1,140 @@
+import { hasPersonValidator } from "aleksis.core/routeValidators";
+
+export default
+  {
+    meta: {
+      inMenu: true,
+      titleKey: "chronos.menu_title",
+      icon: "mdi-school-outline",
+      validators: [
+        hasPersonValidator
+      ]
+    },
+    children: [
+      {
+        path: "",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.allTimetables",
+        meta: {
+          inMenu: true,
+          titleKey: "chronos.timetable.menu_title_all",
+          icon: "mdi-grid",
+          permission: "chronos.view_timetable_overview_rule",
+        },
+      },
+      {
+        path: "timetable/my/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.myTimetable",
+        meta: {
+          inMenu: true,
+          titleKey: "chronos.timetable.menu_title_my",
+          icon: "mdi-account-outline",
+          permission: "chronos.view_my_timetable_rule",
+        },
+      },
+      {
+        path: "timetable/my/:year/:month/:day/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.myTimetableByDate",
+      },
+      {
+        path: "timetable/:type_/:pk/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.timetable",
+      },
+      {
+        path: "timetable/:type_/:pk/:year/:week/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.timetableByWeek",
+      },
+      {
+        path: "timetable/:type_/:pk/print/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.timetablePrint",
+      },
+      {
+        path: "timetable/:type_/:pk/:regular/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.timetableRegular",
+      },
+      {
+        path: "lessons/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.lessonsDay",
+        meta: {
+          inMenu: true,
+          titleKey: "chronos.lessons.menu_title_daily",
+          icon: "mdi-calendar-outline",
+          permission: "chronos.view_lessons_day_rule",
+        },
+      },
+      {
+        path: "lessons/:year/:month/:day/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.lessonsDayByDate",
+      },
+      {
+        path: "lessons/:id_/:week/substitution/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.editSubstitution",
+      },
+      {
+        path: "lessons/:id_/:week/substitution/delete/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.deleteSubstitution",
+      },
+      {
+        path: "substitutions/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.substitutions",
+        meta: {
+          inMenu: true,
+          titleKey: "chronos.substitutions.menu_title",
+          icon: "mdi-update",
+          permission: "chronos.view_substitutions_rule",
+        },
+      },
+      {
+        path: "substitutions/print/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.substitutionsPrint",
+      },
+      {
+        path: "substitutions/:year/:month/:day/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.substitutionsByDate",
+      },
+      {
+        path: "substitutions/:year/:month/:day/print/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.substitutionsPrintByDate",
+      },
+      {
+        path: "supervisions/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.supervisionsDay",
+        meta: {
+          inMenu: true,
+          titleKey: "chronos.supervisions.menu_title_daily",
+          icon: "mdi-calendar-outline",
+          permission: "chronos.view_supervisions_day_rule",
+        },
+      },
+      {
+        path: "supervisions/:year/:month/:day/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.supervisionsDayByDate",
+      },
+      {
+        path: "supervisions/:id_/:week/substitution/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.editSupervisionSubstitution",
+      },
+      {
+        path: "supervisions/:id_/:week/substitution/delete/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "chronos.deleteSupervisionSubstitution",
+      },
+    ],
+  }
\ No newline at end of file
diff --git a/aleksis/apps/chronos/frontend/messages/de.json b/aleksis/apps/chronos/frontend/messages/de.json
new file mode 100644
index 00000000..19c815f9
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/messages/de.json
@@ -0,0 +1,5 @@
+{
+  "chronos": {
+    "menu_title": "Stundenpläne"
+  }
+}
\ No newline at end of file
diff --git a/aleksis/apps/chronos/frontend/messages/en.json b/aleksis/apps/chronos/frontend/messages/en.json
new file mode 100644
index 00000000..77ac18be
--- /dev/null
+++ b/aleksis/apps/chronos/frontend/messages/en.json
@@ -0,0 +1,18 @@
+{
+  "chronos": {
+    "menu_title": "Timetables",
+    "timetable": {
+      "menu_title_all": "All timetables",
+      "menu_title_my": "My timetable"
+    },
+    "lessons": {
+      "menu_title_daily": "Daily lessons"
+    },
+    "substitutions": {
+      "menu_title": "Substitutions"
+    },
+    "supervisions": {
+      "menu_title_daily": "Daily supervisions"
+    }
+  }
+}
\ No newline at end of file
diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py
index f73c0791..fdb6def8 100644
--- a/aleksis/apps/chronos/views.py
+++ b/aleksis/apps/chronos/views.py
@@ -14,6 +14,7 @@ import reversion
 from django_tables2 import RequestConfig
 from rules.contrib.views import permission_required
 
+from aleksis.core.decorators import pwa_cache
 from aleksis.core.models import Announcement
 from aleksis.core.util import messages
 from aleksis.core.util.core_helpers import has_person
@@ -39,6 +40,7 @@ from .util.date import CalendarWeek, get_weeks_for_year, week_weekday_to_date
 from .util.js import date_unix
 
 
+@pwa_cache
 @permission_required("chronos.view_timetable_overview_rule")
 def all_timetables(request: HttpRequest) -> HttpResponse:
     """View all timetables for persons, groups and rooms."""
@@ -54,6 +56,7 @@ def all_timetables(request: HttpRequest) -> HttpResponse:
     return render(request, "chronos/all.html", context)
 
 
+@pwa_cache
 @permission_required("chronos.view_my_timetable_rule")
 def my_timetable(
     request: HttpRequest,
@@ -115,6 +118,7 @@ def my_timetable(
         return redirect("all_timetables")
 
 
+@pwa_cache
 @permission_required("chronos.view_timetable_rule", fn=get_el_by_pk)
 def timetable(
     request: HttpRequest,
@@ -206,6 +210,7 @@ def timetable(
         return render(request, "chronos/timetable.html", context)
 
 
+@pwa_cache
 @permission_required("chronos.view_lessons_day_rule")
 def lessons_day(
     request: HttpRequest,
@@ -325,6 +330,7 @@ def delete_substitution(request: HttpRequest, id_: int, week: int) -> HttpRespon
     return redirect("lessons_day_by_date", year=date.year, month=date.month, day=date.day)
 
 
+@pwa_cache
 @permission_required("chronos.view_substitutions_rule")
 def substitutions(
     request: HttpRequest,
@@ -341,6 +347,7 @@ def substitutions(
         return render_pdf(request, "chronos/substitutions_print.html", context)
 
 
+@pwa_cache
 @permission_required("chronos.view_supervisions_day_rule")
 def supervisions_day(
     request: HttpRequest,
-- 
GitLab