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 (7)

Collapse
 
hama99o profile image
Hama

test

Collapse
 
hama99o profile image
Hama

text

Collapse
 
hama99o profile image
Hama

testing text

Thread Thread
 
hama99o profile image
Hama

test

Thread Thread
 
hama99o profile image
Hama

test

Thread Thread
 
hama99o profile image
Hama

test

Thread Thread
 
hama99o profile image
Hama

test