From d13e2360b6dad80da567348c1013353a2fc2297e Mon Sep 17 00:00:00 2001
From: alex <alex@alexloehr.net>
Date: Thu, 23 Oct 2025 14:28:10 +0000
Subject: [PATCH] GS-2375

---
 app.js                         |   10 +
 vue/src/pages/KursDetailLp.vue |  150 +++++++++++++++++++++
 vue/src/router.js              |    8 +
 vue/src/pages/KursDetail.vue   |  239 ++++++++++++++++++---------------
 vue/src/lib/api.js             |    5 
 5 files changed, 304 insertions(+), 108 deletions(-)

diff --git a/app.js b/app.js
index 5a2a88d..acfd58f 100644
--- a/app.js
+++ b/app.js
@@ -190,6 +190,16 @@
             return res.code(404).send({status: "error", msg: "not found"})
         }
     })
+    .get("/api/kurs/:refId/lp", async function (req, res) {
+        const {refId} = req.params
+        const {obj_id: objId} = await db.getObjIdFromRefId(refId)
+        let data = await db.getKursLp(objId)
+        if (data) {
+            return res.send(data)
+        } else {
+            return res.code(404).send({status: "error", msg: "not found"})
+        }
+    })
     .get("/api/kurs/:refId/teilnehmerByRole", async function (req, res) {
         const {refId} = req.params
         const {obj_id} = await db.getObjIdFromRefId(refId)
diff --git a/vue/src/lib/api.js b/vue/src/lib/api.js
index 35959cf..26ff72b 100644
--- a/vue/src/lib/api.js
+++ b/vue/src/lib/api.js
@@ -53,6 +53,11 @@
    return await resKurs.json()
 }
 
+export async function getKursLp (kursId) {
+   let resKursLp = await fetch(`${apiBase}/kurs/${kursId}/lp?token=${apiToken.value}`)
+   return await resKursLp.json()
+}
+
 /////// USER ////////////////////////////////////////////////////////////////
 
 export async function getUsers (offset, limit, search) {
diff --git a/vue/src/pages/KursDetail.vue b/vue/src/pages/KursDetail.vue
index 38b4841..e4ebbcf 100644
--- a/vue/src/pages/KursDetail.vue
+++ b/vue/src/pages/KursDetail.vue
@@ -3,13 +3,13 @@
 import {useRoute} from 'vue-router'
 import {onMounted, reactive, ref} from "vue"
 import {
-   getKurs,
-   getKursItems,
-   getKursRoles,
-   getKursTn,
-   getKursTnByRole,
-   iliasBase,
-   popcornBase,
+  getKurs,
+  getKursItems,
+  getKursRoles,
+  getKursTn,
+  getKursTnByRole,
+  iliasBase,
+  popcornBase, routerBase,
 } from "../lib/api.js"
 import LinkExtern from "../components/LinkExtern.vue"
 import dayjs from "dayjs"
@@ -34,27 +34,27 @@
 /////////////////////////////////////////////////////////////////////////
 
 
-async function init () {
+async function init() {
 
-   const dataKurs = await getKurs(kursId)
-   console.log(dataKurs)
-   kurs.value = dataKurs
+  const dataKurs = await getKurs(kursId)
+  console.log(dataKurs)
+  kurs.value = dataKurs
 
-   const dataKursItems = await getKursItems(kursId)
-   console.log(dataKursItems)
-   kursItems.value = dataKursItems
+  const dataKursItems = await getKursItems(kursId)
+  console.log(dataKursItems)
+  kursItems.value = dataKursItems
 
-   const dataKursTn = await getKursTn(kursId)
-   console.log(dataKursTn)
-   kursTn.value = dataKursTn
+  const dataKursTn = await getKursTn(kursId)
+  console.log(dataKursTn)
+  kursTn.value = dataKursTn
 
-   const dataKursTnByRolle = await getKursTnByRole(kursId)
-   console.log(dataKursTnByRolle)
-   kursTnByRolle.value = dataKursTnByRolle
+  const dataKursTnByRolle = await getKursTnByRole(kursId)
+  console.log(dataKursTnByRolle)
+  kursTnByRolle.value = dataKursTnByRolle
 
-   const dataKursRoles = await getKursRoles(kursId)
-   console.log(dataKursRoles)
-   kursRoles.value = dataKursRoles
+  const dataKursRoles = await getKursRoles(kursId)
+  console.log(dataKursRoles)
+  kursRoles.value = dataKursRoles
 
 }
 
@@ -64,108 +64,133 @@
 
 <template>
 
-   <div>
-      <h1>
-         Kurs
-         <small>{{ kurs?.title }}</small>
-      </h1>
-      <p v-if="error">{{ error }}</p>
+  <div>
+<!--    <pre>{{ kurs }}</pre>-->
+    <h1>
+      Kurs
+      <small>{{ kurs?.title }}</small>
+    </h1>
+    <p v-if="error">{{ error }}</p>
 
 
-      <div class="kurs" v-if="kurs">
-
-         <div>
-            <span>ref_id</span>
-            <div>
-               <a :href="`${iliasBase}/goto.php?target=${kurs.type}_${kurs.ref_id}`" target="_blank">
-                  {{ kurs.ref_id }}
-                  <LinkExtern />
-               </a>
-            </div>
-         </div>
-
-         <div>
-            <span>obj_id</span>
-            <div>{{ kurs.obj_id }}</div>
-         </div>
-
-         <div>
-            <span>offline</span>
-           <div :class="{red:kurs.offline===1,green:kurs.offline===0}">
-             {{ kurs.offline }}
-           </div>
-         </div>
-
-         <div class="text-right" style="grid-column: span 1; justify-content: flex-end">
-            <a :href="`${popcornBase}/anmeldungen?query=iliasId:${kurs.ref_id}&terminFilter=alle`" target="_blank">
-               Search in 🍿
-            </a>
-         </div>
+    <div class="kurs" v-if="kurs">
 
 
-         <div style="grid-column: span 2">
-            <span style="">title</span>
-            <div style="">{{ kurs.title }}</div>
-         </div>
-
-         <div style="grid-column: span 2;">
-            <span>description</span>
-            <div style="grid-column: span 3">{{ kurs.description }}</div>
-         </div>
-
+      <div>
+        <span>ref_id</span>
+        <div>
+          <a :href="`${iliasBase}/goto.php?target=${kurs.type}_${kurs.ref_id}`" target="_blank">
+            {{ kurs.ref_id }}
+            <LinkExtern/>
+          </a>
+        </div>
       </div>
 
-      <div class="cols">
-
-         <div>
-            <h2>KursItems <small>({{ kursItems?.length }})</small></h2>
-            <KursItems :kursItems="kursItems" />
-         </div>
-
-         <div>
-            <h2>KursRoles <small>({{ kursRoles?.length }})</small></h2>
-            <KursRoles :kursRoles="kursRoles" />
-         </div>
-
+      <div>
+        <span>obj_id</span>
+        <div>{{ kurs.obj_id }}</div>
       </div>
 
-      <div class="cols">
-         <div>
-            <h2>KursTn by Members <small>({{ kursTn?.length }})</small></h2>
-            <KursMembers :kursTn="kursTn" />
-         </div>
-
-         <div>
-            <h2>KursTnByRole <small>({{ kursTnByRolle?.length }})</small></h2>
-            <KursTnByRole :kursTnByRolle="kursTnByRolle" />
-         </div>
-
+      <div>
+        <span>offline</span>
+        <div :class="{red:kurs.offline===1,green:kurs.offline===0}">
+          {{ kurs.offline }}
+        </div>
       </div>
 
-   </div>
+      <div class="text-right">
+        <div style="display:block; text-align:right;">
+          <a :href="`${popcornBase}/anmeldungen?query=iliasId:${kurs.ref_id}&terminFilter=alle`" target="_blank">
+            Search in 🍿
+          </a>
+        </div>
+      </div>
+
+      <div style="grid-column: span 3">
+        <span style="">title</span>
+        <div style="">{{ kurs.title }}</div>
+      </div>
+
+<!--      <div>-->
+<!--        <span>description</span>-->
+<!--        <div style="grid-column: span 3">{{ kurs.description }}</div>-->
+<!--      </div>-->
+
+      <div>
+        <span>Learning Progress</span>
+        <div>
+          <RouterLink :to="`${routerBase}/ui/kurs/${kurs?.ref_id}/lp`">
+            LP {{ kurs?.ref_id }}
+          </RouterLink>
+        </div>
+      </div>
+
+    </div>
+
+    <div class="cols">
+
+      <div>
+        <h2>KursItems <small>({{ kursItems?.length }})</small></h2>
+        <KursItems :kursItems="kursItems"/>
+      </div>
+
+      <div>
+        <h2>KursRoles <small>({{ kursRoles?.length }})</small></h2>
+        <KursRoles :kursRoles="kursRoles"/>
+      </div>
+
+    </div>
+
+    <div class="cols">
+      <div>
+        <h2>KursTn by Members <small>({{ kursTn?.length }})</small></h2>
+        <KursMembers :kursTn="kursTn"/>
+      </div>
+
+      <div>
+        <h2>KursTnByRole <small>({{ kursTnByRolle?.length }})</small></h2>
+        <KursTnByRole :kursTnByRolle="kursTnByRolle"/>
+      </div>
+
+    </div>
+
+  </div>
 
 </template>
 
 <style scoped lang="stylus">
 
 .cols
-   display flex;
-   gap 2em
+  display flex;
+  gap 2em
 
 .kurs
-   padding 1em
-   border 1px solid #aaa
-   border-radius .33em
-   box-shadow 1px 1px 3px #333 inset, 1px 1px 100px #ddd inset
-   display grid;
-   grid-template-columns 1fr 1fr 1fr 1fr
-   gap 1em
-   span
-      font-variant small-caps
-      color #555
-   &>*
-      display flex
-      gap 1em
+  padding 1em
+  border 1px solid #aaa
+  border-radius .33em
+  box-shadow 1px 1px 3px #333 inset, 1px 1px 100px #ddd inset
+  display flex
+  flex-direction column
+  display grid;
+  grid-template-columns 1fr 1fr 1fr 1fr
+  gap 1em
 
+  span
+    font-variant small-caps
+    font-size 85%
+    color #555
+
+  & > *
+    display flex
+    flex 1 0 auto
+    justify-content start
+    //border 4px solid orange
+    align-items baseline
+    gap .66em
+
+    & > *
+      display flex
+      flex-direction row
+      gap .33em
 
 </style>
diff --git a/vue/src/pages/KursDetailLp.vue b/vue/src/pages/KursDetailLp.vue
new file mode 100644
index 0000000..f903327
--- /dev/null
+++ b/vue/src/pages/KursDetailLp.vue
@@ -0,0 +1,150 @@
+<script setup>
+
+import {useRoute} from 'vue-router'
+import {onMounted, reactive, ref} from "vue"
+import {
+  getKurs,
+  getKursItems, getKursLp,
+  getKursRoles,
+  getKursTn,
+  getKursTnByRole,
+  iliasBase,
+  popcornBase, routerBase,
+} from "../lib/api.js"
+import LinkExtern from "../components/LinkExtern.vue"
+import dayjs from "dayjs"
+import KursItems from '../components/KursItems.vue'
+import KursRoles from '../components/KursRoles.vue'
+import KursMembers from '../components/KursMembers.vue'
+import KursTnByRole from '../components/KursTnByRole.vue'
+
+const route = useRoute()
+const kursId = route.params.kursId
+document.title = `Kurs LP ${kursId} | globus-ilias-rest`
+
+const kurs = ref(null)
+const kursLp = ref(null)
+const error = ref(null)
+
+onMounted(init)
+
+/////////////////////////////////////////////////////////////////////////
+
+
+async function init() {
+
+  const dataKurs = await getKurs(kursId)
+  console.log(dataKurs)
+  kurs.value = dataKurs
+
+  const dataKursLp = await getKursLp(kursId)
+  console.log({dataKursLp})
+  kursLp.value = dataKursLp
+  // kursLp.value = [...dataKursLp,...dataKursLp,...dataKursLp]
+}
+
+</script>
+
+<template>
+
+  <div>
+    <h1>
+      Kurs LP
+      <small>
+        <RouterLink :to="`${routerBase}/ui/kurs/${kurs?.ref_id}`">
+          {{ kurs?.title }}
+        </RouterLink>
+      </small>
+    </h1>
+
+    <p>Der Kurs Lernfortschritt (LP) wird u.a. aus der Tabelle <code>ut_lp_marks</code> gelesen.</p>
+
+    <p v-if="error">{{ error }}</p>
+
+    <br>
+
+    <div class="kurs-lp" v-if="kursLp">
+
+      <!--      <pre>{{ kursLp }}</pre>-->
+
+      <table class="w100p">
+        <thead>
+        <tr>
+<!--          <th>ref_id</th>-->
+<!--          <th>obj_id</th>-->
+          <!--          <th>title</th>-->
+          <th>usr_id</th>
+          <th>login</th>
+          <th>firstname</th>
+          <th>lastname</th>
+          <th>passed</th>
+          <th>status</th>
+          <th>status_changed</th>
+          <th>status_overwrite</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="item in kursLp">
+<!--          <td>{{ item.ref_id }}</td>-->
+<!--          <td>{{ item.obj_id }}</td>-->
+          <!--          <td>{{item.title}}</td>-->
+          <td>
+            <RouterLink :to="`${routerBase}/ui/user/${item.usr_id}`">
+              {{ item.usr_id }}
+            </RouterLink>
+          </td>
+          <td>{{ item.login }}</td>
+          <td>{{ item.firstname }}</td>
+          <td>{{ item.lastname }}</td>
+          <td>{{ item.passed }}</td>
+          <td>{{ item.status }}</td>
+          <td>{{ dayjs(item.status_changed).format("DD.MM.YYYY HH:mm:ss") }}</td>
+          <td>{{ item.status_overwrite }}</td>
+        </tr>
+        </tbody>
+      </table>
+
+
+    </div>
+
+
+  </div>
+
+</template>
+
+<style scoped lang="stylus">
+
+.cols
+  display flex;
+  gap 2em
+
+.kurs
+  padding 1em
+  border 1px solid #aaa
+  border-radius .33em
+  box-shadow 1px 1px 3px #333 inset, 1px 1px 100px #ddd inset
+  display flex
+  flex-direction column
+  display grid;
+  grid-template-columns 1fr 1fr 1fr 1fr
+  gap 1em
+
+  span
+    font-variant small-caps
+    font-size 85%
+    color #555
+
+  & > *
+    display flex
+    flex 1 0 auto
+    justify-content start
+    //border 4px solid orange
+    align-items baseline
+    gap .66em
+
+    & > *
+      display flex
+      flex-direction row
+      gap .33em
+
+</style>
diff --git a/vue/src/router.js b/vue/src/router.js
index 3fd319f..c19a76d 100644
--- a/vue/src/router.js
+++ b/vue/src/router.js
@@ -7,6 +7,7 @@
 import UserDetail from './pages/UserDetail.vue'
 import Kurse from './pages/Kurse.vue'
 import KursDetail from './pages/KursDetail.vue'
+import KursDetailLp from "@/pages/KursDetailLp.vue";
 import {routerBase} from "@/lib/api"
 
 
@@ -37,8 +38,13 @@
       component: KursDetail,
       meta: { title: 'KursDetail | globus-ilias-rest' }
    },
+   {
+      path: `${routerBase}/ui/kurs/:kursId/lp`,
+      component: KursDetailLp,
+      meta: { title: 'KursDetail LP | globus-ilias-rest' }
+   },
 ]
-console.log(routes)
+// console.log(routes)
 
 const router = createRouter({
    history: createWebHistory(),

--
Gitblit v1.8.0