Skip to content

Commit 6745124

Browse files
committed
fix: refactor components for create modal
1 parent 29d11a4 commit 6745124

7 files changed

Lines changed: 122 additions & 34 deletions

File tree

frontend/src/components/Controls/Autocomplete.vue

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
nullable
1010
v-slot="{ open: isComboboxOpen }"
1111
>
12-
<Popover class="w-full" v-model:show="showOptions" :matchTargetWidth="true">
12+
<Popover
13+
class="w-full"
14+
v-model:show="showOptions"
15+
:matchTargetWidth="true"
16+
>
1317
<template #target="{ open: openPopover, togglePopover }">
1418
<slot name="target" v-bind="{ open: openPopover, togglePopover }">
1519
<div class="w-full">
@@ -105,7 +109,12 @@
105109
name="item-label"
106110
v-bind="{ active, selected, option }"
107111
>
108-
<div class="flex flex-col px-1" :class="optionLines(option).secondary ? 'gap-0.5' : ''">
112+
<div
113+
class="flex flex-col px-1"
114+
:class="
115+
optionLines(option).secondary ? 'gap-0.5' : ''
116+
"
117+
>
109118
<div class="text-base font-medium text-ink-gray-8">
110119
{{ optionLines(option).primary }}
111120
</div>
@@ -246,17 +255,16 @@ function filterOptions(options) {
246255
}
247256
248257
function optionLines(option) {
249-
const primary = option.label
250-
let secondary = null
251-
if (option.description && option.description !== primary) {
252-
secondary = option.description
253-
} else if (option.value && option.value !== primary) {
254-
secondary = option.value
255-
}
256-
return { primary, secondary }
258+
const primary = option.label
259+
let secondary = null
260+
if (option.description && option.description !== primary) {
261+
secondary = option.description
262+
} else if (option.value && option.value !== primary) {
263+
secondary = option.value
264+
}
265+
return { primary, secondary }
257266
}
258267
259-
260268
function displayValue(option) {
261269
if (typeof option === 'string') {
262270
let allOptions = groups.value.flatMap((group) => group.items)

frontend/src/components/Modals/EmailTemplateModal.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const props = defineProps({
8888
8989
const show = defineModel()
9090
const emailTemplates = defineModel('emailTemplates')
91+
const emit = defineEmits(['created'])
9192
const template = reactive({
9293
name: '',
9394
subject: '',
@@ -113,6 +114,7 @@ const createNewTemplate = (close) => {
113114
{
114115
onSuccess() {
115116
emailTemplates.value.reload()
117+
emit('created', template.name)
116118
refreshForm(close)
117119
toast.success(__('Email Template created successfully'))
118120
},

frontend/src/pages/Batches/BatchForm.vue

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@
7272
/>
7373

7474
<Link
75+
v-model="batchDetail.doc.category"
7576
doctype="LMS Category"
7677
:label="__('Category')"
77-
v-model="batchDetail.doc.category"
78-
:onCreate="(value, close) => openSettings('Categories', close)"
78+
:inlineCreate="true"
79+
:onCreate="createCategory"
7980
/>
8081
</div>
8182
</div>
@@ -156,12 +157,14 @@
156157
class="mb-4"
157158
/>
158159
<Link
160+
ref="emailTemplateLinkRef"
159161
doctype="Email Template"
160162
:label="__('Enrollment Confirmation Email Template')"
161163
v-model="batchDetail.doc.confirmation_email_template"
162164
:onCreate="
163165
(value, close) => {
164-
openSettings('Email Templates', close)
166+
if (close) close()
167+
showEmailTemplateModal = true
165168
}
166169
"
167170
/>
@@ -280,6 +283,12 @@
280283
:defaultRoles="['batch_evaluator']"
281284
@created="onInstructorCreated"
282285
/>
286+
<EmailTemplateModal
287+
v-model="showEmailTemplateModal"
288+
v-model:emailTemplates="emailTemplates"
289+
templateID="new"
290+
@created="onEmailTemplateCreated"
291+
/>
283292
</template>
284293
<script setup>
285294
import {
@@ -300,8 +309,10 @@ import {
300309
createDocumentResource,
301310
toast,
302311
call,
312+
createListResource,
303313
} from 'frappe-ui'
304314
import {
315+
createLMSCategory,
305316
escapeHTML,
306317
getMetaInfo,
307318
openSettings,
@@ -317,6 +328,7 @@ import Link from '@/components/Controls/Link.vue'
317328
import BatchCourses from '@/pages/Batches/components/BatchCourses.vue'
318329
import Assessments from '@/pages/Batches/components/Assessments.vue'
319330
import NewMemberModal from '@/components/Modals/NewMemberModal.vue'
331+
import EmailTemplateModal from '@/components/Modals/EmailTemplateModal.vue'
320332
321333
const router = useRouter()
322334
const user = inject('$user')
@@ -329,6 +341,29 @@ const { $dialog } = app.appContext.config.globalProperties
329341
const isDirty = ref(false)
330342
const originalDoc = ref(null)
331343
const showMemberModal = ref(false)
344+
const showEmailTemplateModal = ref(false)
345+
const emailTemplateLinkRef = ref(null)
346+
347+
const emailTemplates = createListResource({
348+
doctype: 'Email Template',
349+
fields: ['name', 'subject', 'use_html', 'response', 'response_html'],
350+
auto: true,
351+
orderBy: 'modified desc',
352+
cache: 'email-templates',
353+
})
354+
355+
const onEmailTemplateCreated = (name) => {
356+
batchDetail.doc.confirmation_email_template = name
357+
emailTemplateLinkRef.value?.reload()
358+
}
359+
360+
const createCategory = (name, done) => {
361+
createLMSCategory(name).then((categoryName) => {
362+
if (!categoryName) return
363+
batchDetail.doc.category = categoryName
364+
done()
365+
})
366+
}
332367
333368
const onInstructorCreated = (user) => {
334369
instructors.value = [...instructors.value, user.name]

frontend/src/pages/Batches/components/NewBatchModal.vue

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,10 @@
4646
autocomplete="off"
4747
/>
4848
<Link
49-
doctype="LMS Category"
5049
v-model="batch.category"
50+
doctype="LMS Category"
5151
:label="__('Category')"
52-
:allowCreate="true"
53-
:onCreate="
54-
() => {
55-
openSettings('Categories')
56-
show = false
57-
}
58-
"
52+
:onCreate="createCategory"
5953
/>
6054
<FormControl
6155
v-model="batch.seat_count"
@@ -126,7 +120,7 @@ import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
126120
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
127121
import { computed, inject, onMounted, onBeforeUnmount, ref } from 'vue'
128122
import { useRouter } from 'vue-router'
129-
import { cleanError, openSettings, sanitizeHTML, escapeHTML } from '@/utils'
123+
import { sanitizeHTML, escapeHTML, createLMSCategory } from '@/utils'
130124
import MultiSelect from '@/components/Controls/MultiSelect.vue'
131125
import Link from '@/components/Controls/Link.vue'
132126
import NewMemberModal from '@/components/Modals/NewMemberModal.vue'
@@ -172,6 +166,14 @@ const batch = ref<Batch>({
172166
medium: null,
173167
})
174168
169+
const createCategory = (name: string, done: () => void) => {
170+
createLMSCategory(name).then((categoryName: string) => {
171+
if (!categoryName) return
172+
batch.value.category = categoryName
173+
done()
174+
})
175+
}
176+
175177
const onInstructorCreated = (user: any) => {
176178
batch.value.instructors = [...batch.value.instructors, user.name]
177179
}

frontend/src/pages/Courses/CourseForm.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
@input="makeFormDirty()"
1919
/>
2020
<Link
21-
doctype="LMS Category"
2221
v-model="courseResource.doc.category"
22+
doctype="LMS Category"
2323
:label="__('Category')"
24-
:onCreate="(value, close) => openSettings('Categories', close)"
24+
:inlineCreate="true"
25+
:onCreate="createCategory"
2526
@update:modelValue="makeFormDirty()"
2627
/>
2728
</div>
@@ -253,6 +254,7 @@
253254
</div>
254255
<div v-if="courseResource.doc.paid_certificate" class="space-y-5">
255256
<Link
257+
ref="evaluatorLinkRef"
256258
doctype="Course Evaluator"
257259
v-model="courseResource.doc.evaluator"
258260
:label="__('Evaluator')"
@@ -336,9 +338,10 @@ import {
336338
import {
337339
escapeHTML,
338340
getMetaInfo,
339-
openSettings,
340341
sanitizeHTML,
341342
updateMetaInfo,
343+
createLMSCategory,
344+
cleanError,
342345
} from '@/utils'
343346
import { Trash2, X } from 'lucide-vue-next'
344347
import { useRouter } from 'vue-router'
@@ -360,6 +363,7 @@ const app = getCurrentInstance()
360363
const { $dialog } = app.appContext.config.globalProperties
361364
const isDirty = ref(false)
362365
const showMemberModal = ref(false)
366+
const evaluatorLinkRef = ref(null)
363367
const memberModalRoles = ref(['course_creator'])
364368
365369
const props = defineProps({
@@ -432,6 +436,7 @@ const submitCourse = () => {
432436
const onMemberCreated = (user) => {
433437
if (memberModalRoles.value.includes('batch_evaluator')) {
434438
courseResource.doc.evaluator = user.name
439+
evaluatorLinkRef.value?.reload()
435440
makeFormDirty()
436441
} else {
437442
instructors.value = [...instructors.value, user.name]
@@ -555,6 +560,15 @@ const checkPermission = () => {
555560
}
556561
}
557562
563+
const createCategory = (name, done) => {
564+
createLMSCategory(name).then((categoryName) => {
565+
if (!categoryName) return
566+
courseResource.doc.category = categoryName
567+
done()
568+
makeFormDirty()
569+
})
570+
}
571+
558572
const makeFormDirty = () => {
559573
isDirty.value = true
560574
}

frontend/src/pages/Courses/NewCourseModal.vue

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@
1616
autocomplete="off"
1717
/>
1818
<Link
19-
doctype="LMS Category"
2019
v-model="course.category"
20+
doctype="LMS Category"
2121
:label="__('Category')"
22-
:onCreate="
23-
() => {
24-
openSettings('Categories')
25-
show = false
26-
}
27-
"
22+
:inlineCreate="true"
23+
:onCreate="createCategory"
2824
/>
2925
<MultiSelect
3026
v-model="course.instructors"
@@ -86,8 +82,13 @@ import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
8682
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
8783
import { inject, onMounted, onBeforeUnmount, ref } from 'vue'
8884
import { useRouter } from 'vue-router'
89-
import { cleanError, openSettings, sanitizeHTML, escapeHTML } from '@/utils'
9085
import Link from '@/components/Controls/Link.vue'
86+
import {
87+
cleanError,
88+
sanitizeHTML,
89+
escapeHTML,
90+
createLMSCategory,
91+
} from '@/utils'
9192
import MultiSelect from '@/components/Controls/MultiSelect.vue'
9293
import Uploader from '@/components/Controls/Uploader.vue'
9394
import NewMemberModal from '@/components/Modals/NewMemberModal.vue'
@@ -122,6 +123,14 @@ const course = ref<Course>({
122123
image: null,
123124
})
124125
126+
const createCategory = (name: string, done: () => void) => {
127+
createLMSCategory(name).then((categoryName: string) => {
128+
if (!categoryName) return
129+
course.value.category = categoryName
130+
done()
131+
})
132+
}
133+
125134
const onInstructorCreated = (user: any) => {
126135
course.value.instructors = [...course.value.instructors, user.name]
127136
}

frontend/src/utils/index.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,24 @@ const extractYouTubeId = (url) => {
824824
}
825825
}
826826

827+
export const createLMSCategory = (name) => {
828+
return call('frappe.client.insert', {
829+
doc: {
830+
doctype: 'LMS Category',
831+
category: name,
832+
},
833+
})
834+
.then((data) => {
835+
toast.success(__('Category created successfully'))
836+
return data.name
837+
})
838+
.catch((err) => {
839+
toast.error(
840+
cleanError(err.messages?.[0]) || __('Unable to create category')
841+
)
842+
})
843+
}
844+
827845
export const openSettings = (category, close = null) => {
828846
const settingsStore = useSettings()
829847
if (close) {

0 commit comments

Comments
 (0)