Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\LearningDashboard;
final class Bootstrap
{
}
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\LearningDashboard\Data;
final class Lesson implements \JsonSerializable
{
/** @var string */
private $name;
/** @var array|Task[] */
private $tasks;
/**
* Lesson constructor.
*
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
/** @param Task $task */
public function addTask(Task $task)
{
$this->tasks[] = $task;
}
public function getProgress()
{
$totalSteps = 0;
$completedSteps = 0;
foreach ($this->tasks as $task) {
$taskProgress = $task->getProgress();
$totalSteps += $taskProgress['total'];
$completedSteps += $taskProgress['complete'];
}
return [
'total' => $totalSteps,
'completed' => $completedSteps,
];
}
/** @return array */
public function jsonSerialize()
{
return [
'name' => $this->name,
'progress' => $this->getProgress(),
'tasks' => $this->tasks,
];
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\LearningDashboard\Data;
final class Tab implements \JsonSerializable
{
/** @var string */
private $name;
/** @var Lesson[] */
private $lessons;
/**
* Tab constructor.
*
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
/** @param Lesson $lesson */
public function addLesson(Lesson $lesson)
{
$this->lessons[] = $lesson;
}
/** @return array */
public function jsonSerialize()
{
$totalSteps = 0;
$completedSteps = 0;
foreach ($this->lessons as $lesson) {
$lessonProgress = $lesson->getProgress();
$totalSteps += $lessonProgress['total'];
$completedSteps += $lessonProgress['completed'];
}
return [
'name' => $this->name,
'progress' => [
'total' => $totalSteps,
'completed' => $completedSteps,
],
'lessons' => $this->lessons,
];
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\LearningDashboard\Data;
final class Task implements \JsonSerializable
{
/** @var array */
private $data;
/**
* Task constructor.
*
* @param array $data
*/
public function __construct(array $data)
{
$this->data = $data;
}
/**
* @return array
*/
public function getData(): array
{
return $this->data;
}
/** @return array */
public function getProgress(): array
{
$stepCount = 0;
$completedStepCount = 0;
foreach ($this->data['step_groups'] as $stepGroupName => $stepGroupContent) {
$stepCount++;
if (isset($stepGroupContent['completed']) && $stepGroupContent['completed'] === true) {
$completedStepCount++;
}
}
return [
'complete' => $completedStepCount,
'total' => $stepCount,
];
}
/** @return array */
public function jsonSerialize()
{
return [
'key' => $this->data['key'],
'title' => $this->data['title'],
'category' => $this->data['category'],
'sub_title' => $this->data['sub_title'],
'description' => $this->data['description'],
'missing_modules' => $this->data['missing_modules'],
'missing_permissions' => $this->data['missing_permissions'],
'link' => "index.php?module=wizard&action=ajax&cmd=set_active_wizard&key={$this->data['key']}",
'progress' => $this->getProgress(),
];
}
}
@@ -0,0 +1,319 @@
/* general
================================== */
#learning-dashboard-container a{
cursor: pointer;
}
#learning-dashboard-container ul {
padding-left: 15px;
}
#learning-dashboard-container,
#learning-dashboard-container h4 {
color: rgba(55, 64, 81, 0.6);
}
#learning-dashboard-container h2,
#learning-dashboard-container h3,
#learning-dashboard-container h4 {
color: #374051;
font-weight: normal;
padding: 0;
margin: 0;
}
#learning-dashboard-container h2 {
font-size: 20px;
}
#learning-dashboard-container h3 {
font-size: 16px;
}
#learning-dashboard-container h4 {
font-size: 14px;
}
#learning-dashboard-container p {
font-size: 12px;
line-height: 18px;
}
#learning-dashboard-container .progress-counter {
font-size: 14px;
align-self: center;
}
@media (max-width: 1500px) {
#learning-dashboard-container .progress-counter {
font-size: 12px;
}
}
/* header
================================== */
#learning-dashboard-container .dashboard-header-content {
font-size: 14px;
line-height: 20px;
}
#learning-dashboard-container .dashboard-header-progress {
font-size: 14px;
line-height: 26px;
justify-content: flex-end;
}
#learning-dashboard-container .progress-completed-tasks {
color: var(--xentral-signature-violet);
}
#learning-dashboard-container .progress-completed-tasks-label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
#learning-dashboard-container .progress-ring-container {
position: relative;
overflow: visible;
}
#learning-dashboard-container .progress-ring-border,
#learning-dashboard-container .progress-ring {
position: absolute;
top: -20px;
}
#learning-dashboard-container .progress-ring-circle {
transition: 0.35s stroke-dashoffset;
transform: rotate(150deg);
transform-origin: 50% 50%;
stroke: var(--xentral-signature-violet);
}
#learning-dashboard-container .progress-ring {
top: -18px;
left: 2px;
stroke-linecap: round;
}
#learning-dashboard-container .progress-ring-border-circle {
transform: rotate(130deg);
transform-origin: 50% 50%;
stroke: rgba(204, 204, 204, 0.5);
stroke-dasharray: 270, 270;
}
#learning-dashboard-container .progress-ring-percentage {
margin-top: 25px;
display: flex;
align-self: center;
justify-content: center;
flex-direction: column;
width: 115px;
text-align: center;
line-height: 19px;
font-size: 12px;
}
#learning-dashboard-container .progress-ring-percentage span {
color: var(--xentral-signature-violet);
font-size: 22px;
text-align: center;
}
/* tab content
================================== */
#learning-dashboard-container .tab-row {
margin-top: 35px;
}
#learning-dashboard-container .tab-row.border-bottom {
border-bottom: 1px solid var(--separator-border);
}
#learning-dashboard-container .dashboard-tabs {
justify-content: flex-start;
flex-direction: row;
}
#learning-dashboard-container .dashboard-tabs .dashboard-tab {
min-width: 60px;
height: 30px;
line-height: 30px;
text-align: center;
font-size: 14px;
padding: 0 20px;
cursor: pointer;
}
#learning-dashboard-container .dashboard-tabs .dashboard-tab.active,
#learning-dashboard-container .dashboard-tabs .dashboard-tab:hover {
font-weight: bold;
border-bottom: 2px solid var(--active-border);
}
#learning-dashboard-container .tab-row #show-completed-input + label {
cursor: pointer;
}
/** Lessons **/
#learning-dashboard-container .dashboard-lessons {
margin-bottom: .7%;
}
#learning-dashboard-container .dashboard-lesson {
cursor: pointer;
border-right: 3px solid transparent;
}
#learning-dashboard-container .dashboard-lesson.active,
#learning-dashboard-container .dashboard-lesson:hover {
background: rgba(91, 100, 238, 0.1);
border-right: 3px solid rgba(91, 100, 238, 0.8);
}
#learning-dashboard-container .dashboard-lesson.active h4 {
color: #374051
}
#learning-dashboard-container .dashboard-lesson.active path {
stroke: rgba(91, 100, 238, 1);
stroke-opacity: 1;
}
#learning-dashboard-container .dashboard-lesson .dashboard-icon {
align-items: center;
justify-content: center;
}
#learning-dashboard-container .dashboard-lesson .progress-bar{
margin-left: 0;
}
#learning-dashboard-container .cta-button {
color: white;
height: 35px;
width: 100%;
border-radius: 3px;
background: var(--button-primary-background);
font-size: 14px;
box-sizing: border-box;
border: none;
}
#learning-dashboard-container .cta-button .prefix {
display: inline-block;
height: 35px;
line-height: 27px;
font-size: 25px;
padding-right: 5px;
}
/** Tasks **/
#learning-dashboard-container .dashboard-tasks {
justify-content: flex-start;
}
#learning-dashboard-container .task {
position: relative;
transition: box-shadow 150ms linear;
display: flex;
flex-direction: column;
}
#learning-dashboard-container .task div:last-child{
margin-top: auto;
}
#learning-dashboard-container .task:hover {
box-shadow: 0 2px 4px rgba(28, 41, 90, 0.2);
}
#learning-dashboard-container .task .button {
height: 25px;
line-height: 23px;
padding: 0;
align-self: center;
text-align: center;
margin: 0;
max-width: 80px;
}
#learning-dashboard-container .task .progress-bar{
margin-bottom: 7px;
margin-left: 0px;
align-self: flex-end;
}
#learning-dashboard-container .task .task-completion-status {
position: absolute;
width: 22px;
height: 22px;
right: 20px;
top: 20px;
background: #D7DFE9;
border-radius: 22px;
line-height: 23px;
text-align: center;
transition: background-color .3s;
}
#learning-dashboard-container .task .task-completion-status.complete {
background: #2DCA73;
}
#learning-dashboard-container .task .task-completion-status.in-progress {
background: #FFA958;
line-height: 25px;
}
#learning-dashboard-container .task .task-completion-status svg {
display: inline-block;
}
#learning-dashboard-container .task .task-icon {
width: 51px;
height: 51px;
background-position: center;
border-radius: 51px;
background-repeat: no-repeat;
}
#learning-dashboard-container .task .task-details{
position: absolute;
width: 100%;
background-color: white;
top:0;
left: 0;
right:0;
bottom: 0;
overflow-y: auto;
}
#learning-dashboard-container .task .task-details svg{
cursor: pointer;
}
#learning-dashboard-container .task-details .task-details-header{
border-bottom: 1px solid #EBEFF2;
}
#learning-dashboard-container .task-details .task-details-content{
padding-top: 0;
}
/* transitions
================================== */
#learning-dashboard-container .fade-enter-active, .fade-leave-active {
transition: opacity .3s;
}
#learning-dashboard-container .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
{
opacity: 0;
}
@@ -0,0 +1,44 @@
/* Progress bar
================================== */
.progress-bar {
margin: 10px;
padding: 0;
height: 4px;
border-radius: 5px;
overflow: visible;
position: relative;
background-color: var(--inactive-grey);
}
.progress-bar .progress {
width: 100%;
margin: 0;
height: 4px;
background: var(--brand-violet);
position: relative;
transition: all .3s;
border-radius: 5px;
}
.progress-bar.complete .progress {
background: var(--xentral-signature-green);
}
.progress-bar.complete .progress-bar-percentage.top{
color: var(--xentral-signature-green);
}
.progress-bar .progress-bar-percentage {
position: absolute;
top: -4px;
color: var(--xentral-signature-violet);
}
.progress-bar .progress-bar-percentage {
left: 110%;
top: -5px;
}
.progress-bar .progress-bar-percentage.top {
left: calc(50% - 13px);
top: -15px;
}
@@ -0,0 +1,80 @@
Vue.component('learning-dashboard-header', {
props: ['tabs'],
template:
' <div class="learning-dashboard-header grid-container"> ' +
' <div class="grid-cell grid-card grid-container grid-3-column grid-padded"> ' +
' <div class="grid-cell grid-separator-right grid-padded cell-2-8"> ' +
' <h2>{{ $wording.header.headline }}</h2> ' +
' <p>{{ $wording.header.subline }}</p> ' +
' </div> ' +
' <div class="grid-cell grid-separator-right grid-padded cell-4-8 dashboard-header-content">{{ $wording.header.content }}</div> ' +
' <div class="grid-cell grid-padded grid-container grid-2-column cell-2-8 dashboard-header-progress"> ' +
' <div v-if="tabs.length > 1" class="grid-cell cell-4-8"> ' +
' <div v-for="(tab, index) in tabs" :key="index">' +
' <div class="grid-container grid-2-column">' +
' <div class="grid-cell cell-6-8 progress-completed-tasks-label">{{ tab.name }}</div> ' +
' <div class="grid-cell cell-2-8 progress-completed-tasks">{{ tab.progress.completed}}</div>' +
' </div>' +
' </div> ' +
' </div> ' +
' <div class="grid-cell cell-4-8 progress-ring-container">' +
' <div class="progress-ring-percentage">' +
' <span>{{ calculateProgress() }}%</span>{{ $wording.header.progress }}' +
' </div>' +
' <svg class="progress-ring-border" width="115" height="115">' +
' <circle class="progress-ring-border-circle" stroke-width="1" fill="transparent" r="55.5" cx="57.5" cy="57.5"/>' +
' </svg>' +
' <svg class="progress-ring" width="110" height="110">' +
' <circle class="progress-ring-circle" ref="circle" stroke-width="4" fill="transparent" r="47" cx="55" cy="55"/>' +
' </svg>' +
' </div> ' +
' </div> ' +
' </div> ' +
' </div>',
mounted: function () {
this.progressCircle();
},
methods: {
/**
* Combines progress of all tabs in percent
*
* @returns {number}
*/
calculateProgress: function () {
var progress = 0,
completedTotal = 0,
total = 0,
i, current;
for (i = 0; i < this.tabs.length; i++) {
current = this.tabs[i];
completedTotal += current.progress.completed;
total += current.progress.total;
}
progress = Math.round(100 * completedTotal / total);
return progress;
},
progressCircle: function () {
var circle = this.$refs.circle,
radius = circle.r.baseVal.value,
progressReduction = 0.66,
circumference = radius * 2 * Math.PI,
progress = this.calculateProgress();
// reduces progress to not make a full circle, since our style says 2/3 = 100%
progress = progress * progressReduction;
if(progress > 0){
circle.style.strokeDasharray = [circumference, circumference];
circle.style.strokeDashoffset = circumference - (progress / 100 * circumference);
} else {
circle.style.stroke = 'transparent';
}
}
}
});
@@ -0,0 +1,21 @@
Vue.component('learning-dashboard-lesson', {
props: ['lesson', 'lessonIndex'],
template:
'<div class="grid-container grid-2-column grid-padded-vertical">' +
' <div class="grid-cell grid-container cell-2-8 dashboard-icon">' +
' <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
' <path d="M20.25 11.25V14.25C20.25 15.0784 19.5784 15.75 18.75 15.75H5.25C4.42157 15.75 3.75 15.0784 3.75 14.25V11.25" stroke="#374051" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' <path fill-rule="evenodd" clip-rule="evenodd" d="M21.7484 23.25H2.2524C1.73242 23.2502 1.24945 22.981 0.976044 22.5387C0.702636 22.0964 0.677798 21.5441 0.910404 21.079L2.7464 17.408C3.25463 16.3919 4.29327 15.75 5.4294 15.75H18.5714C19.7075 15.75 20.7462 16.3919 21.2544 17.408L23.0904 21.079C23.323 21.5441 23.2982 22.0964 23.0248 22.5387C22.7514 22.981 22.2684 23.2502 21.7484 23.25Z" stroke="#374051" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' <path d="M10.5 20.25H13.5" stroke="#374051" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' <path fill-rule="evenodd" clip-rule="evenodd" d="M21.148 0.75H2.852C2.50038 0.749942 2.19579 0.993864 2.119 1.337L0.75 6C0.75 7.24264 1.75736 8.25 3 8.25C4.24264 8.25 5.25 7.24264 5.25 6C5.25 7.24264 6.25736 8.25 7.5 8.25C8.74264 8.25 9.75 7.24264 9.75 6C9.75 7.24264 10.7574 8.25 12 8.25C13.2426 8.25 14.25 7.24264 14.25 6C14.25 7.24264 15.2574 8.25 16.5 8.25C17.7426 8.25 18.75 7.24264 18.75 6C18.75 7.24264 19.7574 8.25 21 8.25C22.2426 8.25 23.25 7.24264 23.25 6L21.88 1.337C21.804 0.993808 21.4995 0.749658 21.148 0.75Z" stroke="#374051" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' </svg>' +
' </div>' +
' <div class="grid-cell cell-6-8">' +
' <h4>{{ lesson.name }}</h4>' +
' <progress-bar class="grid-cell cell-3-8" ' +
' :value="lesson.progress.completed" ' +
' :max="lesson.progress.total">' +
' </progress-bar>' +
' </div>' +
'</div>'
});
@@ -0,0 +1,12 @@
Vue.component('learning-dashboard-main', {
props: ['tabs'],
template:
'<div id="learning-dashboard" class="grid-wrapper grid-padded">' +
' <learning-dashboard-header ' +
' :tabs="tabs">' +
' </learning-dashboard-header>' +
' <learning-dashboard-tabs' +
' :tabs="tabs">' +
' </learning-dashboard-tabs>' +
'</div>'
});
@@ -0,0 +1,123 @@
Vue.component('learning-dashboard-tabs', {
props: ['tabs'],
data: function () {
return {
showCompleted: true,
activeTabIndex: null,
activeLessonIndex: null
};
},
template:
'<div>' +
'<div class="grid-container tab-row" :class="{\'border-bottom\': tabs.length > 1 }">' +
' <div class="dashboard-tabs grid-container">' +
' <div v-if="tabs.length > 1" class="dashboard-tab noselect" v-for="(tab, tabIndex) in tabs" :key="tabIndex" :class="{\'active\': activeTabIndex === tabIndex }" @click="setActiveTab(tabIndex)">{{ tab.name }}</div>' +
' </div>' +
' <div class="grid-container">' +
' <input type="checkbox" @change="showCompleted = !showCompleted" id="show-completed-input"/>' +
' <label class="grid-padded-horizontal noselect" for="show-completed-input">{{ $wording.tabs.hideCompleted}}</label>' +
' </div>' +
'</div>' +
'<transition name="fade" mode="out-in">' +
'<div v-for="(tab, tabIndex) in tabs" :key="tabIndex" v-if="tabIndex === activeTabIndex" class="grid-container grid-2-column grid-padded-vertical">' +
' <div class="grid-container grid-cell cell-2-8 grid-padded-right dashboard-lessons">' +
' <div class="grid-card">' +
' <div class="grid-padded-s">' +
' <button class="cta-button" v-if="tab.cta">' +
' <span class="prefix">{{ tab.cta.prefix }}</span>' +
' {{ tab.cta.text }}' +
' </button>' +
' </div>' +
' <learning-dashboard-lesson' +
' v-for="(lesson, lessonIndex) in tab.lessons" ' +
' class="dashboard-lesson"' +
' :lesson="lesson" ' +
' :lessonIndex="lessonIndex"' +
' :key="lessonIndex"' +
' :class="{\'active\': lessonIndex === activeLessonIndex }"' +
' @click.native="setActiveLesson(lessonIndex)">' +
' </learning-dashboard-lesson>' +
' </div>' +
' </div>' +
' <div class="grid-container grid-cell tasks cell-6-8 grid-padded-left">' +
' <div v-for="(lesson, lessonIndex) in tab.lessons" :key="lessonIndex" class="grid-cell grid-container grid-3-column dashboard-tasks">' +
' <learning-dashboard-task' +
' v-if="lessonIndex === activeLessonIndex" ' +
' v-for="(task, taskIndex) in filterCompletedTasks(lesson.tasks)" ' +
' :task="task" ' +
' :activeTabIndex="activeTabIndex" ' +
' :activeLessonIndex="activeLessonIndex"' +
' :showCompleted="showCompleted"' +
' :key="taskIndex">' +
' </learning-dashboard-task>' +
' </div>' +
' </div>' +
'</div>' +
'</transition>' +
'</div>',
mounted: function () {
this.findActiveTabAndLesson();
},
methods: {
/**
*
* @param {Number} index
*/
setActiveTab: function (index) {
this.activeTabIndex = index;
},
/**
*
* @param {Number} index
*/
setActiveLesson: function (index) {
this.activeLessonIndex = index;
},
/**
*
* @param {Object} tasks
*
* @returns {Boolean}
*/
filterCompletedTasks: function (tasks) {
var self = this;
return tasks.filter(function (task) {
if (!self.showCompleted) {
if (task.progress.complete === task.progress.total) {
return false;
}
}
return true;
});
},
/**
* finds active Tab and active Lesson
*/
findActiveTabAndLesson: function () {
for (var i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].active === true) {
this.setActiveTab(i);
for (var j = 0; j < this.tabs[i].lessons.length; j++) {
if (this.tabs[i].lessons[j].active === true) {
this.setActiveLesson(j);
}
}
}
}
if(this.activeLessonIndex === null){
this.activeLessonIndex = 0;
}
if(this.activeTabIndex === null){
this.activeTabIndex = 0;
}
}
}
});
@@ -0,0 +1,67 @@
Vue.component('learning-dashboard-task', {
props: ['task', 'activeTabIndex', 'activeLessonIndex'],
data: function () {
return {
complete: false,
percentageValue: 0,
showDetails: false
};
},
template:
' <div class="grid-cell grid-card grid-padded task grid-margin-m">' +
' <div v-if="task.progress.complete === task.progress.total" class="task-completion-status complete">' +
' <svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">' +
' <path d="M1 5.2L4.2 8.2L10.6 1" stroke="white" stroke-width="1.5"/>' +
' </svg>' +
' </div>' +
' <div v-else-if="task.progress.complete > 0" class="task-completion-status in-progress">' +
' <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">' +
' <path d="M4.39733 7.27575L6.07047 6.02091V3.46477C6.07047 3.20775 6.27822 3 6.53523 3C6.79225 3 7 3.20775 7 3.46477V6.25331C7 6.3997 6.93121 6.53775 6.81409 6.62512L4.95508 8.0194C4.87143 8.08214 4.77381 8.11235 4.67669 8.11235C4.53494 8.11235 4.39551 8.04867 4.3044 7.92598C4.15007 7.72099 4.1919 7.42959 4.39733 7.27575Z" fill="white"/>' +
' <path d="M6 0C9.3086 0 12 2.6914 12 6C12 9.3086 9.3086 12 6 12C2.6914 12 0 9.3086 0 6C0 2.6914 2.6914 0 6 0ZM6 11.0705C8.79551 11.0705 11.0705 8.79551 11.0705 6C11.0705 3.20449 8.79551 0.929508 6 0.929508C3.20402 0.929508 0.929508 3.20449 0.929508 6C0.929508 8.79551 3.20449 11.0705 6 11.0705Z" fill="white"/>' +
' </svg>'+
' </div>'+
' <div>' +
' <div class="task-icon" :class="\'app-category-icon-\'+task.category"></div>' +
' <div class="grid-padded-vertical">' +
' <h3>{{ task.title }}</h3>' +
' <p v-html="task.description">{{ task.description}}</p>' +
' </div>' +
' </div>' +
' <div v-if="task.missing_permissions.length === 0 && task.missing_modules.length === 0" class="grid-container grid-3-column">' +
' <progress-bar ' +
' class="grid-cell cell-3-8 grid-padded-horizontal" ' +
' :value="task.progress.complete" ' +
' :max="task.progress.total" ' +
' percentagePosition="top">' +
' </progress-bar>' +
' <a v-if="task.progress.complete === task.progress.total" class="grid-cell cell-3-8 button button-tertiary" :href="task.link">{{$wording.task.cta.completed}}</a>' +
' <a v-else class="grid-cell cell-3-8 button button-primary" role="button" :href="task.link">{{$wording.task.cta.incomplete}}</a>' +
' </div>' +
' <div v-else class="grid-container grid-3-column">' +
' <div class="grid-container grid-column grid-cell cell-5-8">' +
' </div>' +
' <button @click="showDetails = true" class="grid-cell cell-3-8 button button-tertiary">{{$wording.task.cta.incomplete}}</button>' +
' </div>' +
' <div v-if="showDetails" class="task-details">' +
' <div class="task-details-header grid-padded-s">' +
' <svg @click="showDetails = false" width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">' +
' <path d="M12.52 8.99854H1" stroke="#94979E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' <path d="M10.1204 6.49854L12.5204 8.99854L10.1204 11.4985" stroke="#94979E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' <path d="M15.3999 1.49854V16.4985" stroke="#374051" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
' </svg>' +
' </div>' +
' <div class="task-details-content grid-padded">' +
' <p>{{$wording.task.details.info}}</p>' +
' <h4 v-if="task.missing_modules.length > 0">{{$wording.task.details.missing.modules.headline}}</h4>' +
' <ul v-if="task.missing_modules.length > 0">' +
' <li v-for="(missingModule, missingModulesIndex) in task.missing_modules" :key="missingModulesIndex"><a :href="\'index.php?module=appstore&action=list&cmd=detail&app=\' + missingModule" target="_blank">{{ missingModule }}</a></li>' +
' </ul>' +
' <h4 v-if="task.missing_permissions.length > 0">{{$wording.task.details.missing.permissions.headline}}</h4>' +
' <ul v-if="task.missing_permissions.length > 0">' +
' <li v-for="(missingPermission, missingPermissionIndex) in task.missing_permissions" :key="missingPermissionIndex">{{ missingPermission }}</li>' +
' </ul>' +
' </div>' +
' </div>'+
' </div>'
});
@@ -0,0 +1,47 @@
Vue.component('progress-bar', {
props: ['value', 'max', 'percentagePosition'],
data: function () {
return {
complete: false,
percentageValue: 0
};
},
watch: {
value: function(){
this.percentageValue = this.calculatePercentage();
},
max: function () {
this.percentageValue = this.calculatePercentage();
}
},
template:
'<div class="progress-bar" :class="[complete ? \'complete\': \'\']">' +
' <div class="progress" :style="{ width: percentageValue + \'%\'}"></div>' +
' <div class="progress-bar-percentage" :class="percentagePosition"> {{ percentageValue }}%</div>' +
'</div>',
mounted: function () {
this.percentageValue = this.calculatePercentage();
},
updated: function () {
this.percentageValue = this.calculatePercentage();
if (this.percentageValue !== 100) {
this.complete = false;
}
},
methods: {
calculatePercentage: function () {
if (!Number.isInteger(this.value) || !Number.isInteger(this.max)) {
return 0;
}
var percentage = Math.round((100 * this.value) / this.max);
if (percentage === 100) {
this.complete = true;
}
return percentage;
}
}
});
@@ -0,0 +1,66 @@
var LearningDashboard = function ($) {
'use strict';
var me = {
settings: {
frontendTechnology: 'vue'
},
storage: {
dashboardData: null
},
elem: {
container: '#learning-dashboard-container'
},
init: function () {
me.getDashboardData().then(me.runDashboard);
},
getDashboardData: function () {
return $.ajax({
url: 'index.php?module=learningdashboard&action=ajax&cmd=get_content',
method: 'get',
success: function (data) {
me.storage.dashboardData = data;
},
error: function (xhr, status, httpStatus) {
$(me.elem.container).html('Fehler beim Abrufen der Daten: ' + httpStatus);
}
});
},
runDashboard: function () {
if (me.storage.dashboardData === null) {
$(me.elem.container).html('No data - dashboard could not be loaded');
return;
}
if (me.settings.frontendTechnology === 'vue') {
var vueConstructor = {
el: me.elem.container,
data: me.storage.dashboardData,
created: function () {
if (this.wording === undefined) {
return;
}
Vue.prototype.$wording = this.wording;
}
};
new Vue(vueConstructor);
}
}
};
return {
init: me.init
};
}(jQuery);
$(document).ready(function () {
LearningDashboard.init();
});