DEV Community

Hama
Hama

Posted on

sqdsqd

sggs


Enter fullscreen mode Exit fullscreen mode



<!-- Header -->
:type="learnModule?.status === 'draft' ? 'draft' : 'edit'"
saveMode="auto"
:menu-items="menuItems"
:publishText="$t('Publish')"
@preview-module="modalPreviewModule.modalSkeleton.dialog = true"
@toggle-ai="openAiSection = !openAiSection"
@publish-module="openPublishModuleDialog"
@close-and-go-back="handleClose"
/>

<modal-preview-learn-module
  v-if="learnModule"
  ref="modalPreviewModule"
  :title="title"
  :cover="learnModule?.cover_url?.['500']"
  :themes="learnModule?.themes"
  :duration="learnModule?.duration"
  :editor-data="editorContentData"
  :reactions="learnModule?.user_reactions"
  :has-learn-pieces-quiz="learnModule?.has_learn_pieces_quiz"
  :inputs="inputs"
  :learn-module="learnModule"
>
  <template #button>
    <div class="hidden opacity-0" />
  </template>
</modal-preview-learn-module>

<div class="px-5 pt-4 w-full flex flex-col gap-6 mx-auto md:!px-0 md:!max-w-[752px] lg:!max-w-[752px] xl:!max-w-[752px]">
  <div class="flex flex-col gap-1">
    <svn-pro-text body-medium regular color="onSurfaceVariant">
      {{ $t('Training banner (Accepts only .jpg, .png and .jpeg file formats)') }}
    </svn-pro-text>

    <bkt-image-cover-position
      :mode="mode"
      :url="learnModule?.cover_url ? learnModule?.cover_url?.['1000'] : null"
      v-model:coordinates="coordinates"
      @update:coordinates="updateModule({id: route?.params?.id, cover_offset_left: $event.left, cover_offset_top: $event.top})"
      @file-upload="uploadImage"
      @error="errorUploading"
      @change-mode="mode = $event"
    />
  </div>

  <div class="w-full flex flex-col gap-6 ">
    <div id="module-title">
      <!-- Training title -->
      <svn-pro-text-field
        :label="$t('Module title*')"
        :error="warningTitle && !title"
        v-model="title"
        :max-length="70"
        counter
        class="w-full"
        @input="updateLearnModuleTitle"
      />
    </div>
    <div class="flex justify-start gap-4 flex-col">
      <div class="flex gap-1 flex-col">
        <svn-pro-title h6 medium>
          {{ $t('Estimated duration') }}
        </svn-pro-title>

        <svn-pro-text subtitle-medium regular>
          {{ $t('It’s required to acquire this module.') }}
        </svn-pro-text>
      </div>

      <div class="flex gap-2 w-full">
        <!-- Time -->
        <div class="w-[140px] flex-items-center">
          <svn-pro-text-field
            v-model="time"
            :label="$t('Value')"
            type="number"
            min="1"
            @input="updateLearnModuleDurationTime"
          />
        </div>

        <!-- Time unit -->
        <div class="w-[220px]">
          <svn-pro-select
            v-model="timeUnit"
            :label="$t('Unity')"
            item-title="text"
            item-value="value"
            :items="timeUnits"
            @update:model-value="updateLearnModuleDurationTimeUnit"
          />
        </div>
      </div>
    </div>
    <svn-pro-divider color="[#767680]" class="border-opacity-100" />

    <div class="flex justify-start gap-4 flex-col">
      <div class="flex gap-1 flex-col">
        <svn-pro-title h6 medium>
          {{ $t('Themes') }}
        </svn-pro-title>

        <svn-pro-text subtitle-medium regular>
          {{ $t('You can describe your module using themes.') }}
        </svn-pro-text>
      </div>

      <!-- Time unit -->
      <div v-if="learnThemes?.length" class="min-w-[120px] max-w-[520px]">

        <svn-pro-combobox
          v-model="entityThemes"
          :items="learnThemes"
          :key="generation"
          item-title="name"
          :label="$t('Themes')"
          :clearable="false"
          multiple
          return-object
          @update:search="searchThemeText = $event"
          @update:model-value="updateOrCreateTheme"
        />
      </div>

      <!-- Input to add a new theme -->
      <div
        v-if="entityThemes?.length"
        class="flex flex-wrap gap-2"
      >
        <svn-pro-chip
          v-for="theme in entityThemes"
          key="entityTag.id"
          class=""
          :text="theme.name"
          is-slot-append="true"
        >
          <template #append>
            <Icon
              icon="mingcute:close-line"
              width="18"
              height="18"
              class="ml-2 cursor-pointer"
              @click="deleteTheme(theme)"
            />
          </template>
        </svn-pro-chip>
      </div>
    </div>

    <svn-pro-divider color="[#767680]" class="border-opacity-100" />

    <div class="flex justify-start gap-4 flex-col">
      <svn-pro-title h6 medium>
        {{ $t('Content') }}
      </svn-pro-title>

      <!-- Rich text editor -->
      <svn-tiptap
        v-if="editorContentData?.blocks?.length"
        :create-image-url="`/api/v1/editor_contents/${editorContentId}/upload_image`"
        :html-data="editorContentData?.blocks"
        :extension-selection="AllTipTapPlugins"
        :extension-slash-command="AllTipTapPlugins"
        :extension-left-menu="true"
        @on-save="debounceEditorContentUpdate"
      />
    </div>

    <svn-pro-divider color="[#767680]" class="border-opacity-100" />

    <div id="module-evaluation" class="flex justify-start gap-4 flex-col" >
      <svn-pro-title h6 medium>
        {{ $t('Evaluation') }}
      </svn-pro-title>

      <svn-pro-card
        :class="learnModule.piece_id ? '' : warningPieceClass"
        :elevation="learnModule.piece_id ? 2 : 0"
        :link="false"
        class="flex justify-start p-6 gap-8 flex-col"
      >
        <div class="flex flex-col gap-3">
          <svn-pro-title h6 medium>
            {{ $t('Evaluation type') }}
          </svn-pro-title>

          <v-item-group
            v-model="learnModule.submission_type"
            mandatory
            @update:model-value="changeEvaluationType"
          >
            <div class="flex flex-col gap-4 lg:grid lg:grid-cols-2">
              <svn-pro-extended-radio-button
                v-for="evaluationItem in evaluationItems"
                :value="evaluationItem?.type"
                :icon="evaluationItem?.icon"
                :title="evaluationItem?.title"
                :subtitle="evaluationItem?.subtitle"
              />
            </div>
          </v-item-group>
        </div>

        <!-- Description -->
        <svn-pro-text-area
          id="module-approval-description"
          v-if="learnModule?.has_learn_pieces_approval"
          :label="$t('Learning objectives*')"
          v-model="learnApprovalInput.text"
          :error="warningAprovalDescription && !learnApprovalInput.text"
          :rows="6"
          :max-rows="6"
          class="w-full"
          @update:model-value="updateApprovalInputText"
          :errorMessages="warningAprovalDescription && !learnApprovalInput.text ? 'Required*' : ''"
        />

        <!-- Evaluation type is Face to face -->
        <div
          v-if="learnModule?.learn_pieces_face_to_face_evaluation?.id && inputsFaceToFace?.length"
          v-for="question in inputsFaceToFace"
          :key="question.id"
          class="flex flex-col gap-8"
        >
          <template-header-question
            v-if="question.type == 'Learn::Inputs::OpenQuestion'"
            @delete="removeOpenQuestion(question)"
            @move-up="getListAfterDragFaceToFace(question, 'moveUp')"
            @move-down="getListAfterDragFaceToFace(question, 'moveDown')"
            @copy-bkt="duplicateOpenQuestion(question)"
            width="100%"
            :isDeleteDisabled="inputsFaceToFace?.length <= 1"
          >
            <template #body>
              <learn-edit-open-question-block
                v-if="question.type === LearnInputType.OPEN"
                :question="question"
                :can-remove-question="inputsFaceToFace.filter(input =>
                  input.type === LearnInputType.OPEN)?.length > 1"
                @update-question="inputChannel?.update"
                @duplicate-question="duplicateOpenQuestion(question)"
                @remove-question="removeOpenQuestion(question)"
              />
            </template>
          </template-header-question>

          <learn-add-question-block
            @click="addFaceToFaceOpenQuestionBlock((question?.position || 0) + 1)"
            face-to-face
          />
        </div>

        <learn-add-question-block
          v-if="learnModule?.learn_pieces_face_to_face_evaluation?.id && !inputsFaceToFace?.length"
          @click="addFaceToFaceOpenQuestionBlock(1)"
          face-to-face
        />

        <!-- Evaluation type is Quiz -->
        <div
          v-if="learnModule?.learn_pieces_quiz?.id && inputs?.length"
          v-for="question in inputs"
          :key="question.id"
          class="flex flex-col gap-8"
        >
          <template-header-question
            v-if="question.type === LearnInputType.CHECKBOX"
            @delete="removeInput(question)"
            @move-up="getListAfterDrag(question, 'moveUp')"
            @move-down="getListAfterDrag(question, 'moveDown')"
            @copy-bkt="duplicateOption(question)"
            :isDeleteDisabled="inputs?.length <= 1"
            width=""
          >
            <template #body>
              <learn-edit-question-block
                :input="question"
                :can-remove-input="inputs.filter(input => input.type === LearnInputType.CHECKBOX).length > 1"
                @update-input="inputChannel?.update"
                @remove-option="removeOption($event)"
                @add-option="addOption($event)"
                @remove-input="removeInput(question)"
                @duplicate-option="duplicateOption(question)"
              />
            </template>
          </template-header-question>

          <learn-add-question-block
            @click="addQuizQuestionBlock((question?.position || 0) + 1)"
            face-to-face
          />
        </div>


        <learn-add-question-block
          v-if="learnModule?.learn_pieces_quiz?.id && !inputs?.length"
          @click="addQuizQuestionBlock((question?.position || 0) + 1)"
          face-to-face
        />
      </svn-pro-card>
    </div>
  </div>
</div>

<!-- Drawer Large -->
<div
  class="h-full overflow-y-auto flex flex-col transition-all relative"
  :class="drawerLarge ? 'w-[344px]' : 'w-0'"
>
  <a-i-drawer
    :is-mobile="false"
    :drawer-large="openAiSection"
    :response="response"
    @ask-a-i-question="askAIQuestion"
    @close-drawer="openAiSection = false"
  />
</div>

<!-- Scroll to top button -->
to-top
size="small"
color="primary"
variant="tonal"
:rounded="'lg'"
class="fixed bottom-4 right-4 bg-white"
icon="custom:mingcute:arrow-to-up-line"
/>

<!-- Dialog edit Playlist -->
v-if="learnModule?.learn_trainings?.length"
ref="deleteModuleDialog"
:items="learnModule?.learn_trainings"
:title="$t(`Module will be deleted with their trainings`)"
:description="$t(`If this module is the only content of a training, the training will be deleted. Training(s) containing only this module :`)"
@delete-content="deleteLearnModule"
/>

<svn-pro-dialog-validation
v-else-if="learnModule?.status === 'draft'"
ref="deleteModuleDialog"
icon="noto:warning"F
:action-two-title="$t('Cancel')"
:action-one-title="$t('Delete')"
:title="$t(Module will be deleted)"
:content-text="$t('This is a permanent action.')"
@click-primary-button="deleteLearnModule"

<template #activator="{ props }">
  <div class="hidden"/>
</template>

<svn-pro-dialog-validation
v-else
ref="deleteModuleDialog"
icon="noto:warning"F
:action-two-title="$t('Cancel')"
:action-one-title="$t('Delete')"
:title="$t(Module will be deleted)"
:content-text="$t('Deleted modules are stored for 30 days. After this period, they will be permanently deleted.')"
@click-primary-button="deleteLearnModule"

<template #activator="{ props }">
  <div class="hidden"/>
</template>

<svn-pro-dialog-validation
ref="publishModuleDialog"
:action-two-title="$t('Cancel')"
:action-one-title="$t('Publish')"
:title="$t(Module will be published)"
:content-text="$t('Your module will be added to the Catalog and will be visible to everyone.')"
@click-primary-button="publishLearnModule"
:width="412"

<template #activator="{ props }">
  <div class="hidden"/>
</template>

<!-- Dialog duplicate module -->
ref="duplicateModuleDialog"
:moduleTitle="learnModule.title"
@duplicate-module="duplicateLearnModule"
/>

import {Icon} from "@iconify/vue";
import {onMounted, ref, computed, onBeforeUnmount} from "vue";
import ModalPreviewLearnModule from "@/components/BktPopUp/Modals/learn/ModalPreviewLearnModule.vue";
import LearnAddQuestionBlock from "@/components/learnApp/moduleBlock/createBlock/LearnAddQuestionBlock.vue";
import LearnEditQuestionBlock from "@/components/learnApp/moduleBlock/editBlock/LearnEditQuestionBlock.vue";
import { useLearnModuleStore } from "@/store/learn-module";
import DialogDuplicateModule from '@/components/BktPopUp/Dialogs/learn/DialogDuplicateModule.vue';
import { useLearnThemesStore } from "@/store/learn-themes";
import { useMobileStore } from "@/store/mobile";
import { storeToRefs } from "pinia";
import { useSnackbar } from "@/store/snackbar";
import { useRouter, useRoute } from "vue-router";
import { debounce } from "lodash";
import { useActionCable } from "@/store/cable.js";
import { useLearnTrainingStore } from "@/store/learn-trainings.js";
import { useToastStore } from "@/store/toast.js";
import { useUserStore } from "@/store/user.js";
import i18n from "@/plugins/i18n.js";
import BktImageCoverPosition from "@/components/image/bkt-image-cover-position.vue";
import LearnEditOpenQuestionBlock from "../../../../../components/learnApp/moduleBlock/editBlock/LearnEditOpenQuestionBlock.vue";
import { LearnInputType } from '@/constants/types';
import { AllTipTapPlugins } from 'svn-ui-library/extensions';
import TemplateHeaderQuestion from "@/components/interviewApp/template/Edit/TemplateHeaderQuestion.vue";
import DialogDeleteContent from '@/components/BktPopUp/Dialogs/learn/DialogDeleteContent.vue';
import AIDrawer from '@/components/trainingApp/AIDrawer.vue';

const { addToast } = useToastStore();
const { id: userId } = storeToRefs(useUserStore())

const {
updateModule, publishModule, duplicateModule, addThemeToModule,
removeThemeFromModule,
fetchModule,
deleteModule,
deleteModulePermanently,
fetchInputs,
fetchInputsFaceToFace,
updateInputs,
updateInputsFaceToFace,
postInputs,
postInputsFaceToFace,
postParagraph,
postParagraphFaceToFace,
deleteInputQuestion,
deleteOpenQuestion,
getEditorContent,
updateModuleImage,
resetStates,
changeSubmissionPieceType,
updateApprovalInput
} = useLearnModuleStore()

const {
learnModule,
inputs,
inputsFaceToFace,
editorContentId,
editorContentData,
learnApprovalInput
} = storeToRefs(useLearnModuleStore());
const {cable} = storeToRefs(useActionCable());

const learnThemesStore = useLearnThemesStore()
const { fetchThemes } = learnThemesStore
const { learnThemes } = storeToRefs(learnThemesStore)

const learnTrainingStore = useLearnTrainingStore()
const { learnTraining } = storeToRefs(learnTrainingStore)
const { updateModuleAndPlaylistData, fetchTraining } = learnTrainingStore

const mobileStore = useMobileStore()

const { isMobile } = storeToRefs(mobileStore)

onMounted(async () => {
await resetStates()

try {
await fetchModule(route?.params?.id).then(() => {
entityThemes.value = learnModule.value?.themes
title.value = learnModule.value?.title
if (learnModule.value?.duration) {
time.value = learnModule.value?.duration?.split(' ')?.[0]
timeUnit.value = learnModule.value?.duration?.split(' ')?.[1] === 'h' ? 'hours' : 'minutes'
}
}).then(() => {
updateEditorContent()
})

if (learnModule?.value?.editor_content_id) {
  await getEditorContent(learnModule.value.editor_content_id);
}

if (learnModule?.value?.has_learn_pieces_quiz) {
  await fetchInputs(learnModule.value?.learn_pieces_quiz?.id);
}

if (learnModule?.value?.has_learn_pieces_face_to_face_evaluation) {
  await fetchInputsFaceToFace(learnModule.value?.learn_pieces_face_to_face_evaluation?.id);
}
updateInputQuestion();
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error fetching module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}

try {
await fetchThemes()
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error fetching themes')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
})

const title = ref()
const typeKeyes = ref({
'Learn::Pieces::Approval': 'Approval',
'Learn::Pieces::Quiz': 'Quiz',
'Learn::Pieces::FaceToFaceEvaluation': 'FaceToFaceEvaluation',

})
const evaluationItems = ref([
{
key: 'Approval',
type: 'Learn::Pieces::Approval',
icon: 'noto:check-mark-button',
title: 'Self-assessment',
subtitle: 'The learner must confirm they have fully understood the module before proceeding.',
},
{
key: 'Quiz',
type: 'Learn::Pieces::Quiz',
icon: 'noto:red-question-mark',
title: 'Quiz',
subtitle: 'The learner must pass the quiz with a score of 100% correct answers to successfully complete this section.',
},
{
key: 'FaceToFaceEvaluation',
type: 'Learn::Pieces::FaceToFaceEvaluation',
icon: 'noto:busts-in-silhouette',
title: 'Face-to-face evaluation',
subtitle: "The learner's answers will be evaluated in real time by an expert, ensuring accurate assessment and personalized feedback.",
},
]);
const cannotBePublished = computed(() => {
return (
!title.value ||
!time.value
);
});
const time = ref()
const isDragged = ref(false)
const timeUnit = ref('minutes')
const timeUnits = ref([
'minutes',
'hours',
])
const variant = ref('plain')
const generation = ref(0);
const openAiSection = ref(false);

const mode = ref('edit')
const newTheme = ref(false)
const entityThemes = ref([])
const searchThemeText = ref('')
const warningPieceClass = ref('')
const warningTitle = ref(false)
const warningAprovalDescription = ref(false)
const drag = ref(false)
const snackbar = useSnackbar()
const router = useRouter()
const route = useRoute()
const inputChannel = ref(null)
const editorContentChannel = ref(null)
const duplicateModuleDialog = ref(false)
const deleteModuleDialog = ref(false)
const publishModuleDialog = ref(false)
const menuItems = ref([
{
title: i18n.global.t('Duplicate module'),
value: 'duplicate_module',
onClick: () => openDialogDuplicateModule(learnModule?.value?.id),
},
{
title: i18n.global.t('Delete module'),
value: 'delete_module',
error: true,
onClick: () => openDialogDeleteModude(learnModule?.value?.id),
},
]);
const modalPreviewModule = ref(false);
const coordinates = ref({
left: learnModule?.cover_offset_left,
top: learnModule?.cover_offset_top,
});

const toggleDragState = (even) => {
isDragged.value = even
}

const updateApprovalInputText = debounce(async(even) => {
await updateApprovalInput({text: even})
}, 300)

const addTheme = async () => {
if (searchThemeText.value) {
try {
await addThemeToModule(learnModule?.value?.id, searchThemeText.value)
snackbar.setBgColor('onSurface')
snackbar.setMsg('Theme added to module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
entityThemes.value = learnModule.value.themes
generation.value = generation.value + 1
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error adding theme to module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
} finally {
searchThemeText.value = ''
}
}
}

const deleteTheme = async (theme) => {
try {
await removeThemeFromModule(learnModule?.value?.id, theme?.name);
snackbar.setBgColor('onSurface');
snackbar.setMsg('Theme removed from module');
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]');
snackbar.displaySnackBar();
entityThemes.value = learnModule.value.themes;
generation.value = generation.value + 1
} catch (error) {
snackbar.setBgColor('error');
snackbar.setMsg('Error removing theme from module');
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]');
snackbar.displaySnackBar();
}

}

const updateLearnModuleThemes = async () => {
try {
const theme = findDifferentValues(entityThemes.value, learnModule.value.themes)
if (theme) {
if (entityThemes.value.length > learnModule.value.themes.length) {
await addThemeToModule(learnModule?.value?.id, theme)
entityThemes.value = learnModule.value.themes
generation.value = generation.value + 1
snackbar.setBgColor('onSurface')
snackbar.setMsg('Theme added to module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
}
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error updating')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
}

const updateOrCreateTheme = async (themes) => {
let themeToAdd = themes.find(theme => typeof theme === 'string')

if (themeToAdd) {
await addTheme(themeToAdd)
} else {
updateLearnModuleThemes()
}
};

const clearAll = () => {
return null
}

const findDifferentValues = (array1, array2) => {
const entityThemeNames = array1.map(theme => theme.name);
const learnModuleThemeNames = array2.map(theme => theme.name);

// Find the different values
const differentValues = entityThemeNames.filter(name => !learnModuleThemeNames.includes(name));

return differentValues[differentValues.length - 1];
}

const publishLearnModule = async () => {

try {
await publishModule(learnModule?.value?.id)

snackbar.setBgColor('onSurface')
snackbar.setMsg('Module published!')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-10')
snackbar.displaySnackBar()
if (route.query?.training_id &amp;&amp; route.query?.training_id != 'NaN') {
  await fetchTraining(route.query?.training_id)
  const list = learnTraining.value?.learn_contents
  list.push({
    contentable_id: learnModule?.value?.id,
    contentable_type: "Learn::Module"
  })
  await updateModuleAndPlaylistData(list)
  router.push({name: "training_edit", params: {id: learnTraining.value.id}})
} else {
  router.push({name: "module_show", params: {id: learnModule?.value?.id}})
}
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
snackbarMethod('Error publishing module!')
}
}

const openDialogDuplicateModule = () => {
duplicateModuleDialog.value.dialogDuplicateModule = true
}

const openDialogDeleteModude = () => {
if (learnModule?.value?.learn_trainings?.length) {
deleteModuleDialog.value.deleteDialog = true
} else {
deleteModuleDialog.value.dialogRef.dialog = true
}
}

const openPublishModuleDialog = () => {
let canPublish = true
if (!learnModule.value.piece_id) {
canPublish = false
warningPieceClass.value = "!border !border-[#BA1A1A]/100"

setTimeout(() =&gt; {
  document.getElementById('module-evaluation').scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100)

snackbarMethod('Evaluation type must be set.')
Enter fullscreen mode Exit fullscreen mode

}

if (!title.value) {
canPublish = false
warningTitle.value = true

setTimeout(() =&gt; {
  document.getElementById('module-title').scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100)

snackbarMethod('Please fill required fields (*).')
Enter fullscreen mode Exit fullscreen mode

}
if (learnModule.value?.has_learn_pieces_approval && !learnApprovalInput.value.text) {
canPublish = false
warningAprovalDescription.value = true
setTimeout(() => {
document.getElementById('module-approval-description').scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100)

snackbarMethod('Please fill required fields (*).')
Enter fullscreen mode Exit fullscreen mode

}
if(!canPublish) return

publishModuleDialog.value.dialogRef.dialog = true
}

const changeVariant = () => {
if (variant.value === 'plain') {
variant.value = 'outlined'
} else {
variant.value = 'plain'
}
}

const updateLearnModuleTitle = debounce(async () => {
try {
await updateModule({
id: learnModule?.value?.id,
title: title.value
})
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error updating module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
}, 300)

const updateLearnModuleDurationTime = debounce(async () => {
try {
await updateModule({
id: learnModule?.value?.id,
title: title.value,
duration: (time.value + ' ' + timeUnit.value)
})
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error updating module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
}, 300)

const updateLearnModuleDurationTimeUnit = debounce(async () => {
try {
await updateModule({
id: learnModule?.value?.id,
title: title?.value,
duration: ((time?.value !== learnModule?.value?.duration?.split(' ')?.[0] ?
time.value : learnModule?.value?.duration?.split(' ')?.[0]) + ' ' + timeUnit?.value)
})
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error updating module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
}, 300)

const uploadImage = async (blob) => {
try {
await updateModuleImage(route?.params?.id, blob)
snackbar.setBgColor('onSurface')
snackbar.setMsg('Module image changed successfully')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error uploading module image')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}
}

const snackbarMethod = async (text) => {
snackbar.setBgColor('onSurface')
snackbar.setMsg(text)
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-10')
snackbar.displaySnackBar()
}

const addQuizQuestionBlock = debounce(async(position) => {
const data = {
proposals: [{proposal: "", correct: false}],
title: null,
type: LearnInputType.CHECKBOX,
position: position

}
if (learnModule?.value?.piece_id) {
try {
await postInputs(learnModule.value?.piece_id, data);
} catch (error) {
console.log(error) ;
}
}
}, 200);

const addFaceToFaceOpenQuestionBlock = debounce(async(position) => {
const data = {
title: null,
description: null,
position: position
}
if (learnModule?.value?.piece_id) {
try {
await postInputsFaceToFace(learnModule?.value?.piece_id, data)
} catch (error) {
console.log(error);
}
}
}, 200);

const addQuizFreeContentBlock = debounce(async(faceToFace = false) => {
const data = {
title: '',
type: LearnInputType.PARAGRAPH,
}
if (learnModule?.value?.learn_pieces_quiz?.id) {
try {
await postParagraph(learnModule?.value?.learn_pieces_quiz?.id, data)
} catch (error) {
console.log(error);
}
}
if (learnModule?.value?.learn_pieces_face_to_face_evaluation?.id) {
try {
await postParagraphFaceToFace(learnModule?.value?.learn_pieces_face_to_face_evaluation?.id, data);
} catch (error) {
console.log(error);
}
}
}, 200)

const duplicateLearnModule = async (title) => {
try {
const duplicated = await duplicateModule(learnModule?.value?.id, title)

snackbar.setBgColor('onSurface')
snackbar.setMsg('Module duplicated.')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[42px]')
snackbar.displaySnackBar()

duplicateModuleDialog.value.dialogDuplicateModule = false

setTimeout(() =&gt; {
  router.push({name: 'module_show', params: {id: duplicated.id}})
}, 200);
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error duplicating module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[42px]')
snackbar.displaySnackBar()
}
}

const deleteLearnModule = async () => {
try {
if (learnModule?.value.status === 'draft') {
await deleteModulePermanently(learnModule?.value?.id)
} else {
await deleteModule(learnModule?.value?.id)
}

snackbar.setBgColor('onSurface')
snackbar.setMsg('Module has been deleted successfully.')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[42px]')
snackbar.displaySnackBar()

deleteModuleDialog.value.deleteDialog = false

setTimeout(() =&gt; {
  router.push({name: 'catalog'})
}, 200);
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
snackbar.setBgColor('error')
snackbar.setMsg('Error deleting module')
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[42px]')
snackbar.displaySnackBar()
}
}

const getListAfterDrag = async (question, action) => {
let position

if (action === 'moveUp') {
position = question.position - 1
} else if (action === 'moveDown') {
position = question.position + 1
}

await updateInputs(learnModule.value?.learn_pieces_quiz?.id, question.id, { position: position })
await fetchInputs(learnModule?.value?.piece_id)
};

const getListAfterDragFaceToFace = async (question, action) => {
let position

if (action === 'moveUp') {
position = question.position - 1
} else if (action === 'moveDown') {
position = question.position + 1
}
await updateInputsFaceToFace(learnModule.value?.learn_pieces_face_to_face_evaluation?.id, question.id, { position: position })
await fetchInputsFaceToFace(learnModule?.value?.piece_id)
};

const updateInputQuestion = debounce(async () => {
const subscribeOptions =
{
channel: "Learn::InputChannel",
piece_id: learnModule?.value?.submission_type === 'Learn::Pieces::Quiz' ?
learnModule.value?.learn_pieces_quiz?.id : learnModule?.value?.learn_pieces_face_to_face_evaluation?.id
}

inputChannel.value = cable.value.subscriptions.create(subscribeOptions, {
connected: function () {
// Called when the subscription is ready for use on the server
},

disconnected: function () {
  // Called when the subscription has been terminated by the server
},

received: function (data) {
  if (data.status === "update") {

    switch (data.entity.type) {
      case LearnInputType.CHECKBOX:
        const index = inputs.value.findIndex(x =&gt; x.id === data.entity.id);

        inputs.value[index].title = data.entity.title
        inputs.value[index].proposals = data.entity.proposals
        inputs.value[index].position = data.entity.position
        break;
      case LearnInputType.OPEN:
        const indexFaceToFace = inputsFaceToFace.value.findIndex(x =&gt; x.id === data.entity.id);
        inputsFaceToFace.value[indexFaceToFace].title = data.entity.title
        inputsFaceToFace.value[indexFaceToFace].description = data.entity.description
        inputsFaceToFace.value[indexFaceToFace].position = data.entity.position
      break;
    }
  }
},

update: async function (event) {
  let data = {};

  switch (event?.type) {
    case LearnInputType.PARAGRAPH:
      data = {}
      break;
    case LearnInputType.CHECKBOX:
      data = {
        input_id: event?.id,
        title: event?.title,
        proposals: event?.proposals,
      }
      break;
    case LearnInputType.OPEN:
      data = {
        input_id: event?.id,
        title: event?.title,
        description: event?.description,
      }
      break;
  }

  inputChannel.value.perform('update', data);
},
Enter fullscreen mode Exit fullscreen mode

});
}, 300)

const updateEditorContent = debounce(async () => {
const subscribeOptions =
{
channel: "EditorContentChannel", id: learnModule.value.editor_content_id
}

editorContentChannel.value = cable.value.subscriptions.create(subscribeOptions, {
connected: function () {
// Called when the subscription is ready for use on the server
},

disconnected: function () {
  // Called when the subscription has been terminated by the server
},

received: function (data) {
  if (data.status === "update" &amp;&amp; data.current_user.id !== userId.value &amp;&amp; learnModule.value.editor_content_id === data.editor_content_id) {
    addToast(
        'info',
        i18n.global.t(`This module has just been updated !`),
        i18n.global.t(`The lastest version of this content will be visible if you reload this page.`),
        false,
        {
          name: i18n.global.t(`Reload this page`),
          link: '/learns/module/' + learnModule.value.id + '/edit'
        }
    )
  } else {
    editorContentData.value.blocks = data.entity.blocks
  }
},

update: async function (event) {
  const data = {
    blocks: event
  }

  editorContentChannel.value.perform('update', { data });
},
Enter fullscreen mode Exit fullscreen mode

});
}, 100)

const debounceEditorContentUpdate = debounce(e => editorContentChannel?.value?.update(e), 300)

const removeOption = debounce(async (data) => {
data.input.proposals.splice(data.index, 1);
inputChannel.value?.update(data.input);
}, 200);

const removeInput = debounce(async (input) => {
try {
await deleteInputQuestion(learnModule?.value?.learn_pieces_quiz?.id, input.id)
} catch (error) {
console.log(error)
}
}, 200);

const removeFreeContentFaceToFace = debounce(async (input) => {
try {
await deleteOpenQuestion(learnModule?.value?.learn_pieces_face_to_face_evaluation?.id, input.id)
} catch (error) {
console.log(error)
}
}, 200);

const removeOpenQuestion = debounce(async (input) => {
try {
await deleteOpenQuestion(learnModule?.value?.learn_pieces_face_to_face_evaluation?.id, input?.id)
} catch (error) {
console.log(error)
}
}, 200)

const duplicateOption = debounce(async (input) => {
try {
await postInputs(learnModule?.value?.learn_pieces_quiz?.id, input)
} catch (error) {
console.log(error)
}
}, 200);

const duplicateOpenQuestion = debounce(async (input) => {
try {
await postInputsFaceToFace(learnModule?.value?.learn_pieces_face_to_face_evaluation?.id, input)
} catch (error) {
console.log(error)
}
}, 200);

const addOption = debounce((input) => {
input.proposals.push({
correct: false,
proposal: ""
})
inputChannel.value?.update(input)
}, 200);

const changeEvaluationType = debounce(async(submissionType) => {
try {
const type = typeKeyes.value[submissionType]
await changeSubmissionPieceType(type);
const hasCheckboxQuestion = learnModule.value.learn_pieces_quiz?.has_custom_inputs
const hasOpenQuestion = learnModule.value.learn_pieces_face_to_face_evaluation?.has_custom_inputs

  if (type === 'Quiz') {
    inputsFaceToFace.value = []
    if (hasCheckboxQuestion) {
      await fetchInputs(learnModule?.value?.piece_id)
    } else {
      addQuizQuestionBlock();
    }
  } else if (type === 'FaceToFaceEvaluation') {
    inputs.value = []
    if (hasOpenQuestion) {
      await fetchInputsFaceToFace(learnModule?.value?.piece_id)
    } else {
      addFaceToFaceOpenQuestionBlock();
    }
  }
} catch (error) {
  console.log(error)
}
Enter fullscreen mode Exit fullscreen mode

updateInputQuestion();
}, 200);

onBeforeUnmount(() => {
inputChannel?.value?.unsubscribe();
editorContentChannel?.value?.unsubscribe();
})

const handleClose = () => {
router.back();

// if (learnModule.value?.status === 'draft') {
// snackbar.setBgColor('onSurface')
// snackbar.setMsg('Your module has been saved in your drafts.')
// snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[42px]')
// snackbar.displaySnackBar()
// }
}

const errorUploading = (msg) => {
snackbar.setBgColor('error')
snackbar.setMsg(msg)
snackbar.setCustomClass(isMobile.value ? 'mb-[80px]' : 'mb-[25px]')
snackbar.displaySnackBar()
}

const response = ref('');
const loading = ref(false);

const askAIQuestion = async (text) => {
if (!text) {
alert('Please enter a text!');
return;
}

loading.value = true; // Show loading state
response.value = ''; // Clear previous responses

try {
const authHeaders = JSON.parse(window.localStorage.getItem('bktAccess'))
const url = /api/v1/learn/modules/${learnModule.value.id}/ais; // Replace with your POST endpoint
// Using fetch for streaming
const res = await fetch(url, {
method: 'POST', // Use POST method
headers: {
'access-token': authHeaders['access-token'],
'token-type': authHeaders['token-type'],
'client': authHeaders['client'],
'expiry': authHeaders['expiry'],
'uid': authHeaders['uid'],
'Content-Type': 'application/json', // Sending JSON data
},
body: JSON.stringify({ title: text }) // Send text as part of the request body
});

const reader = res.body.getReader();
const decoder = new TextDecoder('utf-8');
let done = false;

while (!done) {
  const { value, done: readerDone } = await reader.read();
  done = readerDone;
  if (value) {
    const chunk = decoder.decode(value, { stream: !readerDone });
    response.value += chunk; // Append the new token to response
  }
}
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('Error fetching data:', error);
} finally {
loading.value = false; // Hide loading state
}
};

Top comments (0)