diff --git a/aleksis/apps/chronos/frontend/components/Timetable.vue b/aleksis/apps/chronos/frontend/components/Timetable.vue
index 5c0f66811aa7fc88098863b7fb71f53975e272b0..78947059bd524bcd829077d790dec3581bfe42a2 100644
--- a/aleksis/apps/chronos/frontend/components/Timetable.vue
+++ b/aleksis/apps/chronos/frontend/components/Timetable.vue
@@ -1,9 +1,84 @@
 <script setup>
 import TimetableWrapper from "./TimetableWrapper.vue";
 </script>
+
 <script>
+import { DateTime } from "luxon";
+
 export default {
   name: "Timetable",
+  data() {
+    return {
+      calendarFocus: "",
+      calendarType: "week",
+      initialRouteFocusSet: false,
+    };
+  },
+  methods: {
+    setCalendarFocus(val) {
+      this.calendarFocus = val;
+    },
+    setCalendarType(val) {
+      this.calendarType = val;
+    },
+    setInnerFocusAndType() {
+      if (this.$route.name === "chronos.timetableWithId") {
+        this.$refs.calendarWithControls.setCalendarFocus(
+          DateTime.now().toISODate(),
+        );
+        this.$refs.calendarWithControls.setCalendarType(
+          this.$vuetify.breakpoint.mdAndDown ? "day" : "week",
+        );
+      } else {
+        this.initialRouteFocusSet = true;
+        this.$refs.calendarWithControls.setCalendarFocus(
+          [
+            this.$route.params.year,
+            this.$route.params.month,
+            this.$route.params.day,
+          ].join("-"),
+        );
+        this.$refs.calendarWithControls.setCalendarType(
+          this.$route.params.view,
+        );
+      }
+    },
+  },
+  watch: {
+    calendarFocus(newValue, oldValue) {
+      // Do not redirect on first page load
+      if (oldValue === "") return;
+
+      // Do not redirect when calendar focus was just set with route param values
+      if (this.initialRouteFocusSet) {
+        this.initialRouteFocusSet = false;
+        return;
+      }
+
+      const [year, month, day] = newValue.split("-");
+      this.$router.push({
+        name: "chronos.timetableWithIdAndParams",
+        params: {
+          view: this.calendarType,
+          year,
+          month,
+          day,
+        },
+      });
+    },
+    calendarType(newValue) {
+      const [year, month, day] = this.calendarFocus.split("-");
+      this.$router.push({
+        name: "chronos.timetableWithIdAndParams",
+        params: {
+          view: newValue,
+          year,
+          month,
+          day,
+        },
+      });
+    },
+  },
 };
 </script>
 
@@ -17,6 +92,10 @@ export default {
           { name: 'holidays' },
         ]"
         :params="{ type: selected.type, id: selected.objId }"
+        ref="calendarWithControls"
+        @changeCalendarFocus="setCalendarFocus"
+        @changeCalendarType="setCalendarType"
+        @calendarReady="setInnerFocusAndType"
       />
     </template>
   </timetable-wrapper>
diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js
index 9aeb78188d6d3993d3b7d04f64fd0d8f087956fc..6c75eb1ac79d983a1584a81cadd0c579c74513d1 100644
--- a/aleksis/apps/chronos/frontend/index.js
+++ b/aleksis/apps/chronos/frontend/index.js
@@ -8,7 +8,7 @@ export default {
     titleKey: "chronos.menu_title",
     icon: "mdi-school-outline",
     iconActive: "mdi-school",
-    validators: [hasPersonValidator],
+    permission: "chronos.view_menu_rule",
   },
   children: [
     {
@@ -32,6 +32,17 @@ export default {
         permission: "chronos.view_timetable_overview_rule",
         fullWidth: true,
       },
+      children: [
+        {
+          path: ":view(month|week|day)/:year(\\d\\d\\d\\d)/:month(\\d\\d)/:day(\\d\\d)/",
+          component: () => import("./components/Timetable.vue"),
+          name: "chronos.timetableWithIdAndParams",
+          meta: {
+            permission: "chronos.view_timetable_overview_rule",
+            fullWidth: true,
+          },
+        },
+      ],
     },
     {
       path: "substitution_overview/",
diff --git a/aleksis/apps/chronos/rules.py b/aleksis/apps/chronos/rules.py
index df0166222f7921b49189e20494086ff3f8a094ae..82938a1704d89d6bb787e0c0490d1f00aac27be6 100644
--- a/aleksis/apps/chronos/rules.py
+++ b/aleksis/apps/chronos/rules.py
@@ -102,3 +102,7 @@ add_perm("chronos.delete_supervision_substitution_rule", delete_supervision_subs
 # View room (timetable)
 view_room_predicate = has_person & has_room_timetable_perm
 add_perm("chronos.view_room_rule", view_room_predicate)
+
+# View parent menu entry
+view_menu_predicate = has_person & (view_timetable_overview_predicate)
+add_perm("chronos.view_menu_rule", view_menu_predicate)
diff --git a/pyproject.toml b/pyproject.toml
index 0e8dc2244f8d364a90b8f923dc48df22228444e4..6b5fa8bab2f96835c25717717148b7203eb2f94a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "AlekSIS-App-Chronos"
-version = "4.0.0.dev6"
+version = "4.0.0.dev7"
 packages = [
     { include = "aleksis" }
 ]