Compare commits
2 commits
b71af55b1a
...
efa4d7613b
Author | SHA1 | Date | |
---|---|---|---|
|
efa4d7613b | ||
|
c9d90283eb |
13 changed files with 290 additions and 47 deletions
3
db.py
3
db.py
|
@ -5,7 +5,8 @@ import collections
|
|||
|
||||
USER_ID_TO_EMAIL_MAPPING = {}
|
||||
USER_ID_TO_AUTHKEY_MAPPING = {}
|
||||
ALL_EXERCICES = []
|
||||
ALL_EXERCISES = []
|
||||
SELECTED_EXERCISES = []
|
||||
INJECT_BY_UUID = {}
|
||||
INJECT_SEQUENCE_BY_INJECT_UUID = {}
|
||||
INJECT_REQUIREMENTS_BY_INJECT_UUID = {}
|
||||
|
|
43
exercise.py
43
exercise.py
|
@ -14,11 +14,9 @@ ACTIVE_EXERCISES_DIR = "active_exercises"
|
|||
|
||||
|
||||
def load_exercises() -> bool:
|
||||
db.ALL_EXERCICES = read_exercise_dir()
|
||||
db.ALL_EXERCISES = read_exercise_dir()
|
||||
init_inject_flow()
|
||||
init_exercises_tasks()
|
||||
# mark_task_completed(10, "4703a4b2-0ae4-47f3-9dc3-91250be60156", "e2216993-6192-4e7c-ae30-97cfe9de61b4") # filtering - past 48h
|
||||
# mark_task_completed(10, "29324587-db6c-4a73-a209-cf8c79871629", "a6b5cf88-ba93-4c3f-8265-04e00d53778e") # Data - Event creation
|
||||
return True
|
||||
|
||||
|
||||
|
@ -35,12 +33,12 @@ def read_exercise_dir():
|
|||
|
||||
|
||||
def init_inject_flow():
|
||||
for exercise in db.ALL_EXERCICES:
|
||||
for exercise in db.ALL_EXERCISES:
|
||||
for inject in exercise['injects']:
|
||||
inject['exercise_uuid'] = exercise['exercise']['uuid']
|
||||
db.INJECT_BY_UUID[inject['uuid']] = inject
|
||||
|
||||
for exercise in db.ALL_EXERCICES:
|
||||
for exercise in db.ALL_EXERCISES:
|
||||
for inject_flow in exercise['inject_flow']:
|
||||
db.INJECT_REQUIREMENTS_BY_INJECT_UUID[inject_flow['inject_uuid']] = inject_flow['requirements']
|
||||
db.INJECT_SEQUENCE_BY_INJECT_UUID[inject_flow['inject_uuid']] = []
|
||||
|
@ -49,7 +47,7 @@ def init_inject_flow():
|
|||
|
||||
|
||||
def init_exercises_tasks():
|
||||
for exercise in db.ALL_EXERCICES:
|
||||
for exercise in db.ALL_EXERCISES:
|
||||
tasks = {}
|
||||
for inject in exercise['injects']:
|
||||
tasks[inject['uuid']] = {
|
||||
|
@ -65,8 +63,8 @@ def init_exercises_tasks():
|
|||
|
||||
|
||||
def get_exercises():
|
||||
exercices = []
|
||||
for exercise in db.ALL_EXERCICES:
|
||||
exercises = []
|
||||
for exercise in db.ALL_EXERCISES:
|
||||
tasks = []
|
||||
for inject in exercise['injects']:
|
||||
score = 0
|
||||
|
@ -82,7 +80,7 @@ def get_exercises():
|
|||
"score": score,
|
||||
}
|
||||
)
|
||||
exercices.append(
|
||||
exercises.append(
|
||||
{
|
||||
"name": exercise['exercise']['name'],
|
||||
"uuid": exercise['exercise']['uuid'],
|
||||
|
@ -92,8 +90,28 @@ def get_exercises():
|
|||
"tasks": tasks,
|
||||
}
|
||||
)
|
||||
exercices = sorted(exercices, key=lambda d: d['priority'])
|
||||
return exercices
|
||||
exercises = sorted(exercises, key=lambda d: d['priority'])
|
||||
return exercises
|
||||
|
||||
|
||||
def get_selected_exercises():
|
||||
return db.SELECTED_EXERCISES
|
||||
|
||||
|
||||
def change_exercise_selection(exercise_uuid: str, selected: bool):
|
||||
if selected:
|
||||
if exercise_uuid not in db.SELECTED_EXERCISES:
|
||||
db.SELECTED_EXERCISES.append(exercise_uuid)
|
||||
else:
|
||||
if exercise_uuid in db.SELECTED_EXERCISES:
|
||||
db.SELECTED_EXERCISES.remove(exercise_uuid)
|
||||
|
||||
|
||||
def resetAllExerciseProgress():
|
||||
for user_id in db.USER_ID_TO_EMAIL_MAPPING.keys():
|
||||
for exercise_status in db.EXERCISES_STATUS.values():
|
||||
for task in exercise_status['tasks'].values():
|
||||
mark_task_incomplete(user_id, exercise_status['uuid'], task['uuid'])
|
||||
|
||||
|
||||
def get_completed_tasks_for_user(user_id: int):
|
||||
|
@ -338,6 +356,9 @@ def check_active_tasks(user_id: int, data: dict, context: dict) -> bool:
|
|||
available_tasks = get_available_tasks_for_user(user_id)
|
||||
for task_uuid in available_tasks:
|
||||
inject = db.INJECT_BY_UUID[task_uuid]
|
||||
if inject['exercise_uuid'] not in db.SELECTED_EXERCISES:
|
||||
print(f'exercise not active for this inject {inject['name']}')
|
||||
continue
|
||||
print(f'checking: {inject['name']}')
|
||||
completed = check_inject(user_id, inject, data, context)
|
||||
if completed:
|
||||
|
|
45
package-lock.json
generated
45
package-lock.json
generated
|
@ -20,6 +20,7 @@
|
|||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"daisyui": "^4.12.10",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"postcss": "^8.4.38",
|
||||
|
@ -1419,6 +1420,16 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-selector-tokenizer": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
|
||||
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"fastparse": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
|
@ -1436,6 +1447,34 @@
|
|||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/culori": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz",
|
||||
"integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/daisyui": {
|
||||
"version": "4.12.10",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.12.10.tgz",
|
||||
"integrity": "sha512-jp1RAuzbHhGdXmn957Z2XsTZStXGHzFfF0FgIOZj3Wv9sH7OZgLfXTRZNfKVYxltGUOBsG1kbWAdF5SrqjebvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"css-selector-tokenizer": "^0.8",
|
||||
"culori": "^3",
|
||||
"picocolors": "^1",
|
||||
"postcss-js": "^4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/daisyui"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
|
||||
|
@ -1856,6 +1895,12 @@
|
|||
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fastparse": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"daisyui": "^4.12.10",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"postcss": "^8.4.38",
|
||||
|
|
16
server.py
16
server.py
|
@ -40,6 +40,14 @@ def disconnect(sid):
|
|||
def get_exercises(sid):
|
||||
return exercise_model.get_exercises()
|
||||
|
||||
@sio.event
|
||||
def get_selected_exercises(sid):
|
||||
return exercise_model.get_selected_exercises()
|
||||
|
||||
@sio.event
|
||||
def change_exercise_selection(sid, payload):
|
||||
return exercise_model.change_exercise_selection(payload['exercise_uuid'], payload['selected'])
|
||||
|
||||
@sio.event
|
||||
def get_progress(sid):
|
||||
return exercise_model.get_progress()
|
||||
|
@ -50,11 +58,15 @@ def get_notifications(sid):
|
|||
|
||||
@sio.event
|
||||
def mark_task_completed(sid, payload):
|
||||
return exercise_model.mark_task_completed(payload['user_id'], payload['exercise_uuid'], payload['task_uuid'])
|
||||
return exercise_model.mark_task_completed(int(payload['user_id']), payload['exercise_uuid'], payload['task_uuid'])
|
||||
|
||||
@sio.event
|
||||
def mark_task_incomplete(sid, payload):
|
||||
return exercise_model.mark_task_incomplete(payload['user_id'], payload['exercise_uuid'], payload['task_uuid'])
|
||||
return exercise_model.mark_task_incomplete(int(payload['user_id']), payload['exercise_uuid'], payload['task_uuid'])
|
||||
|
||||
@sio.event
|
||||
def reset_all_exercise_progress(sid):
|
||||
return exercise_model.resetAllExerciseProgress()
|
||||
|
||||
@sio.on('*')
|
||||
def any_event(event, sid, data={}):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import TheDahboard from './TheDahboard.vue'
|
||||
import TheThemeButton from './components/TheThemeButton.vue'
|
||||
import TheAdminPanel from './components/TheAdminPanel.vue'
|
||||
import TheSocketConnectionState from './components/TheSocketConnectionState.vue'
|
||||
import TheDahboard from './TheDahboard.vue'
|
||||
import { socketConnected } from "@/socket";
|
||||
|
||||
|
||||
|
@ -16,9 +17,12 @@ onMounted(() => {
|
|||
<template>
|
||||
<main>
|
||||
<div class="absolute top-1 right-1">
|
||||
<div class="flex gap-2">
|
||||
<TheThemeButton></TheThemeButton>
|
||||
<TheAdminPanel></TheAdminPanel>
|
||||
<TheSocketConnectionState></TheSocketConnectionState>
|
||||
</div>
|
||||
</div>
|
||||
<TheDahboard></TheDahboard>
|
||||
</main>
|
||||
</template>
|
||||
|
|
71
src/components/TheAdminPanel.vue
Normal file
71
src/components/TheAdminPanel.vue
Normal file
|
@ -0,0 +1,71 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { exercises, selected_exercises, resetAllExerciseProgress, changeExerciseSelection } from "@/socket";
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { faScrewdriverWrench, faTrash } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
const admin_modal = ref(null)
|
||||
|
||||
function changeSelectionState(state_enabled, exec_uuid) {
|
||||
changeExerciseSelection(exec_uuid, state_enabled);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
@click="admin_modal.showModal()"
|
||||
class="px-2 py-1 rounded-md focus-outline font-semibold bg-blue-600 text-slate-200 hover:bg-blue-700"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faScrewdriverWrench" class="mr-1"></FontAwesomeIcon>
|
||||
Admin panel
|
||||
</button>
|
||||
|
||||
<dialog ref="admin_modal" class="modal">
|
||||
<div class="modal-box w-11/12 max-w-6xl top-24 absolute bg-slate-200 dark:bg-slate-600 text-slate-700 dark:text-slate-200">
|
||||
<h2 class="text-2xl font-bold">
|
||||
<FontAwesomeIcon :icon="faScrewdriverWrench" class="mr-1"></FontAwesomeIcon>
|
||||
Admin panel
|
||||
</h2>
|
||||
<div class="modal-action">
|
||||
<form method="dialog">
|
||||
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<div class="flex mb-5">
|
||||
<button
|
||||
@click="resetAllExerciseProgress()"
|
||||
class="px-2 py-1 rounded-md focus-outline font-semibold bg-red-600 text-slate-200 hover:bg-red-700"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faTrash" class="mr-1"></FontAwesomeIcon>
|
||||
Reset All Exercises
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h3 class="text-lg font-semibold">Selected Exercises</h3>
|
||||
<div
|
||||
v-for="(exercise) in exercises"
|
||||
:key="exercise.name"
|
||||
class="form-control pl-3"
|
||||
>
|
||||
<label class="label cursor-pointer justify-start">
|
||||
<input
|
||||
@change="changeSelectionState($event.target.checked, exercise.uuid)"
|
||||
type="checkbox"
|
||||
:checked="selected_exercises.includes(exercise.uuid)"
|
||||
:value="exercise.uuid"
|
||||
:class="`checkbox ${selected_exercises.includes(exercise.uuid) ? 'checkbox-success' : ''}`"
|
||||
/>
|
||||
<span class="font-mono font-semibold text-base ml-3">{{ exercise.name }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</template>
|
|
@ -57,7 +57,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="Object.keys(notifications).length == 0">
|
||||
<tr v-if="notifications.length == 0">
|
||||
<td
|
||||
colspan="5"
|
||||
class="text-center border-b border-slate-100 dark:border-slate-700 text-slate-600 dark:text-slate-400 p-3 pl-6"
|
||||
|
@ -89,6 +89,10 @@
|
|||
class="p-1 rounded-md font-bold text-xs mr-2 w-10 inline-block text-center
|
||||
dark:bg-amber-600 dark:text-neutral-100 bg-amber-600 text-neutral-100"
|
||||
>POST</span>
|
||||
<span v-else-if="notification.http_method == 'PUT'"
|
||||
class="p-1 rounded-md font-bold text-xs mr-2 w-10 inline-block text-center
|
||||
dark:bg-amber-600 dark:text-neutral-100 bg-amber-600 text-neutral-100"
|
||||
>PUT</span>
|
||||
<span v-else-if="notification.http_method == 'DELETE'"
|
||||
class="p-1 rounded-md font-bold text-xs mr-2 w-10 inline-block text-center
|
||||
dark:bg-red-600 dark:text-neutral-100 bg-red-600 text-neutral-100"
|
||||
|
|
|
@ -1,23 +1,17 @@
|
|||
<script setup>
|
||||
import { exercises, progresses } from "@/socket";
|
||||
import { computed } from "vue";
|
||||
import { active_exercises as exercises, progresses, setCompletedState } from "@/socket";
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { faCheck, faTimes, faGraduationCap } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
||||
function toggle_completed(completed, user_id, exec_uuid, task_uuid) {
|
||||
const payload = {
|
||||
user_id: user_id,
|
||||
exercise_uuid: exec_uuid,
|
||||
task_uuid: task_uuid,
|
||||
}
|
||||
const event_name = !completed ? "mark_task_completed": "mark_task_incomplete"
|
||||
socket.emit(event_name, payload, () => {
|
||||
socket.emit("get_progress", (all_progress) => {
|
||||
socketState.progresses = all_progress
|
||||
})
|
||||
})
|
||||
function toggleCompleted(completed, user_id, exec_uuid, task_uuid) {
|
||||
setCompletedState(completed, user_id, exec_uuid, task_uuid)
|
||||
}
|
||||
|
||||
const hasExercises = computed(() => exercises.value.length > 0)
|
||||
const hasProgress = computed(() => Object.keys(progresses.value).length > 0)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -25,6 +19,13 @@
|
|||
<FontAwesomeIcon :icon="faGraduationCap"></FontAwesomeIcon>
|
||||
Active Exercises
|
||||
</h3>
|
||||
|
||||
<div
|
||||
v-if="!hasExercises"
|
||||
class="text-center text-slate-600 dark:text-slate-400 p-3 pl-6"
|
||||
>
|
||||
<i>- No Exercise available -</i>
|
||||
</div>
|
||||
<table
|
||||
v-for="(exercise, exercise_index) in exercises"
|
||||
:key="exercise.name"
|
||||
|
@ -63,7 +64,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="Object.keys(progresses).length == 0">
|
||||
<tr v-if="!hasProgress">
|
||||
<td
|
||||
:colspan="2 + exercise.tasks.length"
|
||||
class="text-center border-b border-slate-100 dark:border-slate-700 text-slate-600 dark:text-slate-400 p-3 pl-6"
|
||||
|
@ -85,8 +86,8 @@
|
|||
class="text-center border-b border-slate-100 dark:border-slate-700 text-slate-500 dark:text-slate-400 p-3"
|
||||
>
|
||||
<span
|
||||
class="select-none cursor-pointer"
|
||||
@click="toggle_completed(progress.exercises[exercise.uuid].tasks_completion[task.uuid], user_id, exercise.uuid, task.uuid)"
|
||||
class="select-none cursor-pointer text-nowrap"
|
||||
@click="toggleCompleted(progress.exercises[exercise.uuid].tasks_completion[task.uuid], user_id, exercise.uuid, task.uuid)"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
:icon="progress.exercises[exercise.uuid].tasks_completion[task.uuid] ? faCheck : faTimes"
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
<script setup>
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons'
|
||||
import { socketConnected } from "@/socket";
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="text-slate-900 dark:text-slate-400 shadow-blue-500/50">
|
||||
<span :class="{
|
||||
'px-2 py-1 rounded-md inline-block w-48': true,
|
||||
'text-slate-900 dark:text-slate-400': socketConnected,
|
||||
'text-slate-50 bg-red-600': !socketConnected,
|
||||
}">
|
||||
<span class="mr-1">Socket.IO:</span>
|
||||
<span v-show="socketConnected" class="font-semibold text-green-600 dark:text-green-400">Connected</span>
|
||||
<span v-show="!socketConnected" class="font-semibold text-red-500">Disconnected</span>
|
||||
<span v-show="!socketConnected" class="font-semibold text-slate-50">Disconnected</span>
|
||||
</span>
|
||||
</template>
|
|
@ -15,12 +15,41 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
<div class="flex">
|
||||
<label class="grid cursor-pointer place-items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
@click="darkMode = !darkMode"
|
||||
class="mr-3 px-2 py-1 rounded-md focus-outline font-semibold bg-blue-600 text-slate-200 hover:bg-blue-700"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faSun" class="mr-1" v-show="!darkMode"></FontAwesomeIcon>
|
||||
<FontAwesomeIcon :icon="faMoon" class="mr-1" v-show="darkMode"></FontAwesomeIcon>
|
||||
{{ darkMode ? 'Dark' : 'Light'}}
|
||||
</button>
|
||||
:checked="darkMode"
|
||||
class="toggle theme-controller bg-slate-400 col-span-2 col-start-1 row-start-1 [--tglbg:#e2e8f0]" />
|
||||
<svg
|
||||
class="stroke-base-100 fill-base-100 col-start-1 row-start-1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<path
|
||||
d="M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4" />
|
||||
</svg>
|
||||
<svg
|
||||
class="stroke-base-100 fill-base-100 col-start-2 row-start-1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
|
@ -10,6 +10,7 @@ const initial_state = {
|
|||
notificationCounter: 0,
|
||||
notificationAPICounter: 0,
|
||||
exercises: [],
|
||||
selected_exercises: [],
|
||||
progresses: {},
|
||||
}
|
||||
|
||||
|
@ -19,6 +20,8 @@ const connectionState = reactive({
|
|||
})
|
||||
|
||||
export const exercises = computed(() => state.exercises)
|
||||
export const selected_exercises = computed(() => state.selected_exercises)
|
||||
export const active_exercises = computed(() => state.exercises.filter((exercise) => state.selected_exercises.includes(exercise.uuid)))
|
||||
export const progresses = computed(() => state.progresses)
|
||||
export const notifications = computed(() => state.notificationEvents)
|
||||
export const notificationCounter = computed(() => state.notificationCounter)
|
||||
|
@ -34,6 +37,9 @@ export function fullReload() {
|
|||
socket.emit("get_exercises", (all_exercises) => {
|
||||
state.exercises = all_exercises
|
||||
})
|
||||
socket.emit("get_selected_exercises", (all_selected_exercises) => {
|
||||
state.selected_exercises = all_selected_exercises
|
||||
})
|
||||
socket.emit("get_notifications", (all_notifications) => {
|
||||
state.notificationEvents = all_notifications
|
||||
})
|
||||
|
@ -42,6 +48,40 @@ export function fullReload() {
|
|||
})
|
||||
}
|
||||
|
||||
export function setCompletedState(completed, user_id, exec_uuid, task_uuid) {
|
||||
const payload = {
|
||||
user_id: user_id,
|
||||
exercise_uuid: exec_uuid,
|
||||
task_uuid: task_uuid,
|
||||
}
|
||||
const event_name = !completed ? "mark_task_completed": "mark_task_incomplete"
|
||||
socket.emit(event_name, payload, () => {
|
||||
socket.emit("get_progress", (all_progress) => {
|
||||
state.progresses = all_progress
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function resetAllExerciseProgress() {
|
||||
socket.emit("reset_all_exercise_progress", () => {
|
||||
socket.emit("get_progress", (all_progress) => {
|
||||
state.progresses = all_progress
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function changeExerciseSelection(exec_uuid, state_enabled) {
|
||||
const payload = {
|
||||
exercise_uuid: exec_uuid,
|
||||
selected: state_enabled,
|
||||
}
|
||||
socket.emit("change_exercise_selection", payload, () => {
|
||||
socket.emit("get_selected_exercises", (all_selected_exercises) => {
|
||||
state.selected_exercises = all_selected_exercises
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const socket = io(URL, {
|
||||
autoConnect: true
|
||||
|
|
|
@ -11,6 +11,18 @@ export default {
|
|||
} ,
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [
|
||||
require('daisyui'),
|
||||
],
|
||||
darkMode: ['selector'],
|
||||
daisyui: {
|
||||
themes: false, // false: only light + dark | true: all themes | array: specific themes like this ["light", "dark", "cupcake"]
|
||||
darkTheme: "dark", // name of one of the included themes for dark mode
|
||||
base: false, // applies background color and foreground color for root element by default
|
||||
styled: true, // include daisyUI colors and design decisions for all components
|
||||
utils: false, // adds responsive and modifier utility classes
|
||||
prefix: "", // prefix for daisyUI classnames (components, modifiers and responsive class names. Not colors)
|
||||
logs: false, // Shows info about daisyUI version and used config in the console when building your CSS
|
||||
themeRoot: ":root", // The element that receives theme color CSS variables
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue