fix: refactor components for create modal

This commit is contained in:
raizasafeel
2026-03-17 03:10:25 +05:30
parent 29d11a42df
commit 674512444e
7 changed files with 122 additions and 34 deletions

View File

@@ -9,7 +9,11 @@
nullable
v-slot="{ open: isComboboxOpen }"
>
<Popover class="w-full" v-model:show="showOptions" :matchTargetWidth="true">
<Popover
class="w-full"
v-model:show="showOptions"
:matchTargetWidth="true"
>
<template #target="{ open: openPopover, togglePopover }">
<slot name="target" v-bind="{ open: openPopover, togglePopover }">
<div class="w-full">
@@ -105,7 +109,12 @@
name="item-label"
v-bind="{ active, selected, option }"
>
<div class="flex flex-col px-1" :class="optionLines(option).secondary ? 'gap-0.5' : ''">
<div
class="flex flex-col px-1"
:class="
optionLines(option).secondary ? 'gap-0.5' : ''
"
>
<div class="text-base font-medium text-ink-gray-8">
{{ optionLines(option).primary }}
</div>
@@ -246,17 +255,16 @@ function filterOptions(options) {
}
function optionLines(option) {
const primary = option.label
let secondary = null
if (option.description && option.description !== primary) {
secondary = option.description
} else if (option.value && option.value !== primary) {
secondary = option.value
}
return { primary, secondary }
const primary = option.label
let secondary = null
if (option.description && option.description !== primary) {
secondary = option.description
} else if (option.value && option.value !== primary) {
secondary = option.value
}
return { primary, secondary }
}
function displayValue(option) {
if (typeof option === 'string') {
let allOptions = groups.value.flatMap((group) => group.items)

View File

@@ -88,6 +88,7 @@ const props = defineProps({
const show = defineModel()
const emailTemplates = defineModel('emailTemplates')
const emit = defineEmits(['created'])
const template = reactive({
name: '',
subject: '',
@@ -113,6 +114,7 @@ const createNewTemplate = (close) => {
{
onSuccess() {
emailTemplates.value.reload()
emit('created', template.name)
refreshForm(close)
toast.success(__('Email Template created successfully'))
},

View File

@@ -72,10 +72,11 @@
/>
<Link
v-model="batchDetail.doc.category"
doctype="LMS Category"
:label="__('Category')"
v-model="batchDetail.doc.category"
:onCreate="(value, close) => openSettings('Categories', close)"
:inlineCreate="true"
:onCreate="createCategory"
/>
</div>
</div>
@@ -156,12 +157,14 @@
class="mb-4"
/>
<Link
ref="emailTemplateLinkRef"
doctype="Email Template"
:label="__('Enrollment Confirmation Email Template')"
v-model="batchDetail.doc.confirmation_email_template"
:onCreate="
(value, close) => {
openSettings('Email Templates', close)
if (close) close()
showEmailTemplateModal = true
}
"
/>
@@ -280,6 +283,12 @@
:defaultRoles="['batch_evaluator']"
@created="onInstructorCreated"
/>
<EmailTemplateModal
v-model="showEmailTemplateModal"
v-model:emailTemplates="emailTemplates"
templateID="new"
@created="onEmailTemplateCreated"
/>
</template>
<script setup>
import {
@@ -300,8 +309,10 @@ import {
createDocumentResource,
toast,
call,
createListResource,
} from 'frappe-ui'
import {
createLMSCategory,
escapeHTML,
getMetaInfo,
openSettings,
@@ -317,6 +328,7 @@ import Link from '@/components/Controls/Link.vue'
import BatchCourses from '@/pages/Batches/components/BatchCourses.vue'
import Assessments from '@/pages/Batches/components/Assessments.vue'
import NewMemberModal from '@/components/Modals/NewMemberModal.vue'
import EmailTemplateModal from '@/components/Modals/EmailTemplateModal.vue'
const router = useRouter()
const user = inject('$user')
@@ -329,6 +341,29 @@ const { $dialog } = app.appContext.config.globalProperties
const isDirty = ref(false)
const originalDoc = ref(null)
const showMemberModal = ref(false)
const showEmailTemplateModal = ref(false)
const emailTemplateLinkRef = ref(null)
const emailTemplates = createListResource({
doctype: 'Email Template',
fields: ['name', 'subject', 'use_html', 'response', 'response_html'],
auto: true,
orderBy: 'modified desc',
cache: 'email-templates',
})
const onEmailTemplateCreated = (name) => {
batchDetail.doc.confirmation_email_template = name
emailTemplateLinkRef.value?.reload()
}
const createCategory = (name, done) => {
createLMSCategory(name).then((categoryName) => {
if (!categoryName) return
batchDetail.doc.category = categoryName
done()
})
}
const onInstructorCreated = (user) => {
instructors.value = [...instructors.value, user.name]

View File

@@ -46,16 +46,10 @@
autocomplete="off"
/>
<Link
doctype="LMS Category"
v-model="batch.category"
doctype="LMS Category"
:label="__('Category')"
:allowCreate="true"
:onCreate="
() => {
openSettings('Categories')
show = false
}
"
:onCreate="createCategory"
/>
<FormControl
v-model="batch.seat_count"
@@ -126,7 +120,7 @@ import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
import { computed, inject, onMounted, onBeforeUnmount, ref } from 'vue'
import { useRouter } from 'vue-router'
import { cleanError, openSettings, sanitizeHTML, escapeHTML } from '@/utils'
import { sanitizeHTML, escapeHTML, createLMSCategory } from '@/utils'
import MultiSelect from '@/components/Controls/MultiSelect.vue'
import Link from '@/components/Controls/Link.vue'
import NewMemberModal from '@/components/Modals/NewMemberModal.vue'
@@ -172,6 +166,14 @@ const batch = ref<Batch>({
medium: null,
})
const createCategory = (name: string, done: () => void) => {
createLMSCategory(name).then((categoryName: string) => {
if (!categoryName) return
batch.value.category = categoryName
done()
})
}
const onInstructorCreated = (user: any) => {
batch.value.instructors = [...batch.value.instructors, user.name]
}

View File

@@ -18,10 +18,11 @@
@input="makeFormDirty()"
/>
<Link
doctype="LMS Category"
v-model="courseResource.doc.category"
doctype="LMS Category"
:label="__('Category')"
:onCreate="(value, close) => openSettings('Categories', close)"
:inlineCreate="true"
:onCreate="createCategory"
@update:modelValue="makeFormDirty()"
/>
</div>
@@ -253,6 +254,7 @@
</div>
<div v-if="courseResource.doc.paid_certificate" class="space-y-5">
<Link
ref="evaluatorLinkRef"
doctype="Course Evaluator"
v-model="courseResource.doc.evaluator"
:label="__('Evaluator')"
@@ -336,9 +338,10 @@ import {
import {
escapeHTML,
getMetaInfo,
openSettings,
sanitizeHTML,
updateMetaInfo,
createLMSCategory,
cleanError,
} from '@/utils'
import { Trash2, X } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
@@ -360,6 +363,7 @@ const app = getCurrentInstance()
const { $dialog } = app.appContext.config.globalProperties
const isDirty = ref(false)
const showMemberModal = ref(false)
const evaluatorLinkRef = ref(null)
const memberModalRoles = ref(['course_creator'])
const props = defineProps({
@@ -432,6 +436,7 @@ const submitCourse = () => {
const onMemberCreated = (user) => {
if (memberModalRoles.value.includes('batch_evaluator')) {
courseResource.doc.evaluator = user.name
evaluatorLinkRef.value?.reload()
makeFormDirty()
} else {
instructors.value = [...instructors.value, user.name]
@@ -555,6 +560,15 @@ const checkPermission = () => {
}
}
const createCategory = (name, done) => {
createLMSCategory(name).then((categoryName) => {
if (!categoryName) return
courseResource.doc.category = categoryName
done()
makeFormDirty()
})
}
const makeFormDirty = () => {
isDirty.value = true
}

View File

@@ -16,15 +16,11 @@
autocomplete="off"
/>
<Link
doctype="LMS Category"
v-model="course.category"
doctype="LMS Category"
:label="__('Category')"
:onCreate="
() => {
openSettings('Categories')
show = false
}
"
:inlineCreate="true"
:onCreate="createCategory"
/>
<MultiSelect
v-model="course.instructors"
@@ -86,8 +82,13 @@ import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
import { inject, onMounted, onBeforeUnmount, ref } from 'vue'
import { useRouter } from 'vue-router'
import { cleanError, openSettings, sanitizeHTML, escapeHTML } from '@/utils'
import Link from '@/components/Controls/Link.vue'
import {
cleanError,
sanitizeHTML,
escapeHTML,
createLMSCategory,
} from '@/utils'
import MultiSelect from '@/components/Controls/MultiSelect.vue'
import Uploader from '@/components/Controls/Uploader.vue'
import NewMemberModal from '@/components/Modals/NewMemberModal.vue'
@@ -122,6 +123,14 @@ const course = ref<Course>({
image: null,
})
const createCategory = (name: string, done: () => void) => {
createLMSCategory(name).then((categoryName: string) => {
if (!categoryName) return
course.value.category = categoryName
done()
})
}
const onInstructorCreated = (user: any) => {
course.value.instructors = [...course.value.instructors, user.name]
}

View File

@@ -824,6 +824,24 @@ const extractYouTubeId = (url) => {
}
}
export const createLMSCategory = (name) => {
return call('frappe.client.insert', {
doc: {
doctype: 'LMS Category',
category: name,
},
})
.then((data) => {
toast.success(__('Category created successfully'))
return data.name
})
.catch((err) => {
toast.error(
cleanError(err.messages?.[0]) || __('Unable to create category')
)
})
}
export const openSettings = (category, close = null) => {
const settingsStore = useSettings()
if (close) {