From c4e2019aefc42e99192dd28d1e7fae74d37fd54c Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+github@gmail.com>
Date: Fri, 31 May 2024 17:13:49 +0200
Subject: [PATCH] refactor more vue apps

---
 app/controllers/admin/tree.php                | 42 ++++++++++++++++++-
 app/controllers/search/courses.php            | 15 +++++++
 app/views/admin/tree/batch_assign_semtree.php | 10 +++--
 app/views/admin/tree/rangetree.php            |  9 ----
 app/views/admin/tree/semtree.php              | 10 -----
 app/views/search/courses/index.php            | 16 -------
 lib/classes/VueApp.php                        | 39 +++++++++--------
 .../bootstrap/responsive-navigation.js        | 10 -----
 .../assets/javascripts/bootstrap/treeview.js  | 13 ------
 resources/assets/javascripts/bootstrap/vue.js |  5 ++-
 resources/assets/javascripts/entry-base.js    |  2 -
 templates/header.php                          | 12 +++---
 12 files changed, 92 insertions(+), 91 deletions(-)
 delete mode 100644 app/views/admin/tree/rangetree.php
 delete mode 100644 app/views/admin/tree/semtree.php
 delete mode 100644 app/views/search/courses/index.php
 delete mode 100644 resources/assets/javascripts/bootstrap/responsive-navigation.js
 delete mode 100644 resources/assets/javascripts/bootstrap/treeview.js

diff --git a/app/controllers/admin/tree.php b/app/controllers/admin/tree.php
index c8f2a8f1c4a..ec35368c262 100644
--- a/app/controllers/admin/tree.php
+++ b/app/controllers/admin/tree.php
@@ -7,10 +7,28 @@ class Admin_TreeController extends AuthenticatedController
         $GLOBALS['perm']->check('root');
         Navigation::activateItem('/admin/locations/range_tree');
         PageLayout::setTitle(_('Einrichtungshierarchie bearbeiten'));
-        $this->startId = Request::get('node_id', 'RangeTreeNode_root');
+
         $this->semester = Request::option('semester', Semester::findCurrent()->id);
         $this->classname = RangeTreeNode::class;
         $this->setupSidebar();
+
+        $this->render_vue_app(
+            Studip\VueApp::create('tree/StudipTree')
+                ->withProps([
+                    'breadcrumb-icon'              => 'institute',
+                    'create-url'                   => $this->createURL(),
+                    'delete-url'                   => $this->deleteURL(),
+                    'edit-url'                     => $this->editURL(),
+                    'editable'                     => true,
+                    'semester'                     => $this->semester,
+                    'show-structure-as-navigation' => true,
+                    'start-id'                     => Request::get('node_id', 'RangeTreeNode_root'),
+                    'title'                        => _('Einrichtungshierarchie bearbeiten'),
+                    'view-type'                    => 'table',
+                    'visible-children-only'        => false,
+                    'with-courses'                 => true,
+                ])
+        );
     }
 
     public function semtree_action()
@@ -18,10 +36,30 @@ class Admin_TreeController extends AuthenticatedController
         $GLOBALS['perm']->check('root');
         Navigation::activateItem('/admin/locations/sem_tree');
         PageLayout::setTitle(_('Veranstaltungshierarchie bearbeiten'));
-        $this->startId = Request::get('node_id', 'StudipStudyArea_root');
+
+
         $this->semester = Request::option('semester', Semester::findCurrent()->id);
         $this->classname = StudipStudyArea::class;
         $this->setupSidebar();
+
+        $this->render_vue_app(
+            Studip\VueApp::create('tree/StudipTree')
+                ->withProps([
+                    'breadcrumb-icon'              => 'literature',
+                    'create-url'                   => $this->createURL(),
+                    'delete-url'                   => $this->deleteURL(),
+                    'edit-url'                     => $this->editURL(),
+                    'editable'                     => true,
+                    'semester'                     => $this->semester,
+                    'show-structure-as-navigation' => true,
+                    'start-id'                     => Request::get('node_id', 'StudipStudyArea_root'),
+                    'title'                        => _('Veranstaltungshierarchie bearbeiten'),
+                    'view-type'                    => 'table',
+                    'visible-children-only'        => false,
+                    'with-course-assign'           => true,
+                    'with-courses'                 => true,
+                ])
+        );
     }
 
     /**
diff --git a/app/controllers/search/courses.php b/app/controllers/search/courses.php
index a6a4d272d11..76e3320e2bd 100644
--- a/app/controllers/search/courses.php
+++ b/app/controllers/search/courses.php
@@ -59,6 +59,21 @@ class Search_CoursesController extends AuthenticatedController
 
         $this->setupSidebar();
         PageLayout::setTitle($title);
+
+        $this->render_vue_app(
+            Studip\VueApp::create('tree/StudipTree')
+                ->withProps([
+                    'breadcrumb-icon' => $this->breadcrumbIcon,
+                    'sem-class'       => $this->semClass,
+                    'semester'        => $this->semester,
+                    'start-id'        => $this->startId,
+                    'title'           => $this->treeTitle,
+                    'view-type'       => $this->show_as,
+                    'with-courses'    => true,
+                    'with-export'     => true,
+                    'with-search'     => true,
+                ])
+        );
     }
 
     private function setupSidebar()
diff --git a/app/views/admin/tree/batch_assign_semtree.php b/app/views/admin/tree/batch_assign_semtree.php
index 4b1a4ff0649..6b7df0e2bbd 100644
--- a/app/views/admin/tree/batch_assign_semtree.php
+++ b/app/views/admin/tree/batch_assign_semtree.php
@@ -2,10 +2,12 @@
     <?= CSRFProtection::tokenTag() ?>
     <fieldset>
         <legend><?= _('Studienbereichszuordnungen der ausgewählten Veranstaltungen bearbeiten') ?></legend>
-        <div data-studip-tree>
-            <studip-tree start-id="StudipStudyArea_root" :with-info="false" :open-levels="1"
-                         :assignable="true"></studip-tree>
-        </div>
+        <?= Studip\VueApp::create('tree/StudipTree')->withProps([
+            'assignable'  => true,
+            'open-levels' => 1,
+            'start-id'    => 'StudipStudyArea_root',
+            'with-info'   => false,
+        ]) ?>
     </fieldset>
     <fieldset>
         <legend><?= _('Diese Veranstaltungen werden zugewiesen') ?></legend>
diff --git a/app/views/admin/tree/rangetree.php b/app/views/admin/tree/rangetree.php
deleted file mode 100644
index 1e3e9453f53..00000000000
--- a/app/views/admin/tree/rangetree.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<div data-studip-tree>
-    <studip-tree start-id="<?= htmlReady($startId) ?>" view-type="table" breadcrumb-icon="institute"
-                 :with-search="false" :visible-children-only="false"
-                 :editable="true" edit-url="<?= $controller->url_for('admin/tree/edit') ?>"
-                 create-url="<?= $controller->url_for('admin/tree/create') ?>"
-                 delete-url="<?= $controller->url_for('admin/tree/delete') ?>"
-                 :with-courses="true" semester="<?= htmlReady($semester) ?>" :show-structure-as-navigation="true"
-                 title="<?= _('Einrichtungshierarchie bearbeiten') ?>"></studip-tree>
-</div>
diff --git a/app/views/admin/tree/semtree.php b/app/views/admin/tree/semtree.php
deleted file mode 100644
index 0c48245a6ed..00000000000
--- a/app/views/admin/tree/semtree.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<div data-studip-tree>
-    <studip-tree start-id="<?= htmlReady($startId) ?>" view-type="table" breadcrumb-icon="literature"
-                 :with-search="false" :visible-children-only="false"
-                 :editable="true" edit-url="<?= $controller->url_for('admin/tree/edit') ?>"
-                 create-url="<?= $controller->url_for('admin/tree/create') ?>"
-                 delete-url="<?= $controller->url_for('admin/tree/delete') ?>"
-                 :show-structure-as-navigation="true" :with-course-assign="true"
-                 :with-courses="true" semester="<?= htmlReady($semester) ?>"
-                 title="<?= _('Veranstaltungshierarchie bearbeiten') ?>"></studip-tree>
-</div>
diff --git a/app/views/search/courses/index.php b/app/views/search/courses/index.php
deleted file mode 100644
index 500c31e7f69..00000000000
--- a/app/views/search/courses/index.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-/**
- * @var String $startId
- * @var String $show_as
- * @var String $treeTitle
- * @var String $breadcrumIcon
- * @var String $semester
- * @var String $semClass
- */
-?>
-<div data-studip-tree>
-    <studip-tree start-id="<?= htmlReady($startId) ?>" view-type="<?= htmlReady($show_as) ?>" :visible-children-only="true"
-                 title="<?= htmlReady($treeTitle) ?>" breadcrumb-icon="<?= htmlReady($breadcrumbIcon) ?>"
-                 :with-search="true" :with-export="true" :with-courses="true" semester="<?= htmlReady($semester) ?>"
-                 :sem-class="<?= htmlReady($semClass) ?>" :with-export="true"></studip-tree>
-</div>
diff --git a/lib/classes/VueApp.php b/lib/classes/VueApp.php
index 16ebf545ac5..972c4c6d7e9 100644
--- a/lib/classes/VueApp.php
+++ b/lib/classes/VueApp.php
@@ -6,27 +6,17 @@ use Stringable;
 
 final class VueApp implements Stringable
 {
-    public static function create(string $base_component, array $props = []): VueApp
+    public static function create(string $base_component): VueApp
     {
-        return new self($base_component, $props);
+        return new self($base_component);
     }
 
     private array $components = [];
+    private array $props = [];
     private array $stores = [];
     private array $storeData = [];
 
-    private function __construct(
-        private string  $base_component,
-        private array $props = []
-    ) {
-    }
-
-    public function withBaseComponent(string $base_component): VueApp
-    {
-        $clone = clone $this;
-        $clone->base_component = $base_component;
-
-        return $clone;
+    private function __construct(private readonly string $base_component) {
     }
 
     public function withComponents(string ...$components): VueApp
@@ -92,17 +82,30 @@ final class VueApp implements Stringable
         $template = $GLOBALS['template_factory']->open('vue-app.php');
         $template->attributes = [
             'data-vue-app' => json_encode([
-                'components' => [$this->base_component, ...$this->components],
-                'stores'     => $this->stores,
+                'components' => [
+                    $this->base_component,
+                    ...$this->components
+                ],
+                'stores' => $this->stores,
             ]),
-            'is' => $this->base_component,
+            'is' => basename($this->base_component),
 
-            ...$this->props,
+            ...$this->getPreparedProps(),
         ];
         $template->storeData = $this->storeData;
         return $template;
     }
 
+    private function getPreparedProps(): array
+    {
+        $result = [];
+        foreach ($this->props as $name => $value) {
+            $name = ltrim($name, ':');
+            $result[":{$name}"] = json_encode($value);
+        }
+        return $result;
+    }
+
     public function render(): string
     {
         return $this->getTemplate()->render();
diff --git a/resources/assets/javascripts/bootstrap/responsive-navigation.js b/resources/assets/javascripts/bootstrap/responsive-navigation.js
deleted file mode 100644
index ad39d2be092..00000000000
--- a/resources/assets/javascripts/bootstrap/responsive-navigation.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import ResponsiveNavigation from '../../../vue/components/responsive/ResponsiveNavigation.vue';
-
-STUDIP.domReady(() => {
-    STUDIP.Vue.load().then(({ createApp }) => {
-        createApp({
-            el: '#responsive-menu',
-            components: { ResponsiveNavigation }
-        });
-    });
-});
diff --git a/resources/assets/javascripts/bootstrap/treeview.js b/resources/assets/javascripts/bootstrap/treeview.js
deleted file mode 100644
index d132775a335..00000000000
--- a/resources/assets/javascripts/bootstrap/treeview.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import StudipTree from '../../../vue/components/tree/StudipTree.vue'
-
-STUDIP.ready(() => {
-    document.querySelectorAll('[data-studip-tree]:not(.vueified)').forEach(element => {
-        element.classList.add('vueified');
-        STUDIP.Vue.load().then(({ createApp }) => {
-            createApp({
-                el: element,
-                components: { StudipTree }
-            })
-        })
-    });
-});
diff --git a/resources/assets/javascripts/bootstrap/vue.js b/resources/assets/javascripts/bootstrap/vue.js
index 2c64b1095f6..eb51f3c4189 100644
--- a/resources/assets/javascripts/bootstrap/vue.js
+++ b/resources/assets/javascripts/bootstrap/vue.js
@@ -17,7 +17,8 @@ STUDIP.ready(() => {
 
         let components = {};
         config.components.forEach(component => {
-            components[component] = () => import(`../../../vue/components/${component}.vue`);
+            const name = component.split('/').reverse()[0];
+            components[name] = () => import(`../../../vue/components/${component}.vue`);
         });
 
         STUDIP.Vue.load().then(async ({createApp, store}) => {
@@ -31,6 +32,8 @@ STUDIP.ready(() => {
                         Object.keys(data).forEach(command => {
                             store.commit(`${index}/${command}`, data[command]);
                         });
+
+                        dataElement.remove();
                     }
                 });
 
diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js
index 0b10750cd13..9c24c574310 100644
--- a/resources/assets/javascripts/entry-base.js
+++ b/resources/assets/javascripts/entry-base.js
@@ -78,8 +78,6 @@ import "./bootstrap/cache-admin.js"
 import "./bootstrap/oer.js"
 import "./bootstrap/courseware.js"
 import "./bootstrap/contentmodules.js"
-import "./bootstrap/responsive-navigation.js"
-import "./bootstrap/treeview.js"
 import "./bootstrap/stock-images.js"
 import "./bootstrap/external_pages.js"
 
diff --git a/templates/header.php b/templates/header.php
index 711d51785d2..1be47abd629 100644
--- a/templates/header.php
+++ b/templates/header.php
@@ -66,14 +66,14 @@ if ($navigation) {
                     'username' => $user->username,
                     'perm' => $GLOBALS['perm']->get_perm()
                 ];
-                ?>
-            <? } else {
+            } else {
                 $me = ['username' => 'nobody'];
             } ?>
-            <responsive-navigation :me="<?= htmlReady(json_encode($me)) ?>"
-                                   context="<?= htmlReady(Context::get() ? Context::get()->getFullName() : '') ?>"
-                                   :navigation="<?= htmlReady(json_encode(ResponsiveHelper::getNavigationObject($_COOKIE['responsive-navigation-hash'] ?? null))) ?>"
-            ></responsive-navigation>
+            <?= Studip\VueApp::create('responsive/ResponsiveNavigation')->withProps([
+                'context' => Context::get()?->getFullName() ?? '',
+                'me' => $me,
+                'navigation' => ResponsiveHelper::getNavigationObject($_COOKIE['responsive-navigation-hash'] ?? null),
+            ]) ?>
         </div>
         <div id="site-title">
             <?= htmlReady(Config::get()->UNI_NAME_CLEAN) ?>
-- 
GitLab