Merge pull request #1807 from rehanrehman389/program-fix
Misc Programs Issues
This commit is contained in:
@@ -58,15 +58,15 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
v-if="programCourses.data.length > 0"
|
v-if="program.program_courses.length > 0"
|
||||||
:columns="courseColumns"
|
:columns="courseColumns"
|
||||||
:rows="programCourses.data"
|
:rows="program.program_courses"
|
||||||
:options="{
|
:options="{
|
||||||
selectable: true,
|
selectable: true,
|
||||||
resizeColumn: true,
|
resizeColumn: true,
|
||||||
showTooltip: false,
|
showTooltip: false,
|
||||||
}"
|
}"
|
||||||
rowKey="name"
|
:rowKey="programName === 'new' ? 'course' : 'name'"
|
||||||
>
|
>
|
||||||
<ListHeader
|
<ListHeader
|
||||||
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
|
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
|
||||||
@@ -75,8 +75,8 @@
|
|||||||
</ListHeader>
|
</ListHeader>
|
||||||
<ListRows>
|
<ListRows>
|
||||||
<Draggable
|
<Draggable
|
||||||
:list="programCourses.data"
|
:list="program.program_courses"
|
||||||
item-key="name"
|
:item-key="programName === 'new' ? 'course' : 'name'"
|
||||||
group="items"
|
group="items"
|
||||||
@end="updateOrder"
|
@end="updateOrder"
|
||||||
class="cursor-move"
|
class="cursor-move"
|
||||||
@@ -133,14 +133,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
v-if="programMembers.data.length > 0"
|
v-if="program.program_members.length > 0"
|
||||||
:columns="memberColumns"
|
:columns="memberColumns"
|
||||||
:rows="programMembers.data"
|
:rows="program.program_members"
|
||||||
:options="{
|
:options="{
|
||||||
selectable: true,
|
selectable: true,
|
||||||
resizeColumn: true,
|
resizeColumn: true,
|
||||||
}"
|
}"
|
||||||
rowKey="name"
|
:rowKey="programName === 'new' ? 'member' : 'name'"
|
||||||
>
|
>
|
||||||
<ListHeader
|
<ListHeader
|
||||||
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
|
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
<ListHeaderItem :item="item" v-for="item in memberColumns" />
|
<ListHeaderItem :item="item" v-for="item in memberColumns" />
|
||||||
</ListHeader>
|
</ListHeader>
|
||||||
<ListRows>
|
<ListRows>
|
||||||
<ListRow :row="row" v-for="row in programMembers.data" />
|
<ListRow :row="row" v-for="row in program.program_members" />
|
||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner>
|
<ListSelectBanner>
|
||||||
<template #actions="{ unselectAll, selections }">
|
<template #actions="{ unselectAll, selections }">
|
||||||
@@ -217,13 +217,12 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ close }">
|
<template #actions="{ close }">
|
||||||
<div class="flex justify-end space-x-2 group">
|
<div class="flex justify-end space-x-2">
|
||||||
<Button
|
<Button
|
||||||
v-if="programName != 'new'"
|
v-if="programName != 'new'"
|
||||||
@click="deleteProgram(close)"
|
@click="deleteProgram(close)"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
theme="red"
|
theme="red"
|
||||||
class="invisible group-hover:visible"
|
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Trash2 class="size-4 stroke-1.5" />
|
<Trash2 class="size-4 stroke-1.5" />
|
||||||
@@ -252,7 +251,7 @@ import {
|
|||||||
ListRow,
|
ListRow,
|
||||||
toast,
|
toast,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch, getCurrentInstance } from 'vue'
|
||||||
import { Plus, Trash2, TrendingUp } from 'lucide-vue-next'
|
import { Plus, Trash2, TrendingUp } from 'lucide-vue-next'
|
||||||
import { Programs, Program } from '@/types/programs'
|
import { Programs, Program } from '@/types/programs'
|
||||||
import { escapeHTML, openSettings } from '@/utils'
|
import { escapeHTML, openSettings } from '@/utils'
|
||||||
@@ -269,6 +268,9 @@ const member = ref<string>('')
|
|||||||
const showProgressDialog = ref(false)
|
const showProgressDialog = ref(false)
|
||||||
const dirty = ref(false)
|
const dirty = ref(false)
|
||||||
|
|
||||||
|
const app = getCurrentInstance()
|
||||||
|
const { $dialog } = app.appContext.config.globalProperties
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
programName: string | null
|
programName: string | null
|
||||||
@@ -427,25 +429,22 @@ const addCourse = (close: () => void) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
programCourses.insert.submit(
|
const existingCourse = program.value.program_courses.find(
|
||||||
{
|
(c) => c.course === course.value
|
||||||
parent: props.programName,
|
|
||||||
parenttype: 'LMS Program',
|
|
||||||
parentfield: 'program_courses',
|
|
||||||
course: course.value,
|
|
||||||
idx: programCourses.data.length + 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess() {
|
|
||||||
updateCounts('course', 'add')
|
|
||||||
close()
|
|
||||||
toast.success(__('Course added to program successfully'))
|
|
||||||
},
|
|
||||||
onError(err: any) {
|
|
||||||
toast.warning(__(err.messages?.[0] || err))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if (!existingCourse) {
|
||||||
|
program.value.program_courses.push({
|
||||||
|
course: course.value,
|
||||||
|
idx: program.value.program_courses.length + 1,
|
||||||
|
})
|
||||||
|
if (props.programName !== 'new') {
|
||||||
|
dirty.value = true
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
toast.success(__('Course added to program successfully'))
|
||||||
|
} else {
|
||||||
|
toast.warning(__('Course already added to program'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addMember = (close: () => void) => {
|
const addMember = (close: () => void) => {
|
||||||
@@ -454,24 +453,21 @@ const addMember = (close: () => void) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
programMembers.insert.submit(
|
const existingMember = program.value.program_members.find(
|
||||||
{
|
(m) => m.member === member.value
|
||||||
parent: props.programName,
|
|
||||||
parenttype: 'LMS Program',
|
|
||||||
parentfield: 'program_members',
|
|
||||||
member: member.value,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess() {
|
|
||||||
updateCounts('member', 'add')
|
|
||||||
close()
|
|
||||||
toast.success(__('Member added to program successfully'))
|
|
||||||
},
|
|
||||||
onError(err: any) {
|
|
||||||
toast.warning(__(err.messages?.[0] || err))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if (!existingMember) {
|
||||||
|
program.value.program_members.push({
|
||||||
|
member: member.value,
|
||||||
|
})
|
||||||
|
if (props.programName !== 'new') {
|
||||||
|
dirty.value = true
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
toast.success(__('Member added to program successfully'))
|
||||||
|
} else {
|
||||||
|
toast.warning(__('Member already added to program'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCounts = async (
|
const updateCounts = async (
|
||||||
@@ -509,57 +505,83 @@ const updateCounts = async (
|
|||||||
const updateOrder = async (e: DragEvent) => {
|
const updateOrder = async (e: DragEvent) => {
|
||||||
let sourceIdx = e.from.dataset.idx
|
let sourceIdx = e.from.dataset.idx
|
||||||
let targetIdx = e.to.dataset.idx
|
let targetIdx = e.to.dataset.idx
|
||||||
let courses = programCourses.data
|
|
||||||
courses.splice(targetIdx, 0, courses.splice(sourceIdx, 1)[0])
|
|
||||||
|
|
||||||
for (const [index, course] of courses.entries()) {
|
if (props.programName === 'new') {
|
||||||
programCourses.setValue.submit(
|
let courses = program.value.program_courses
|
||||||
{
|
courses.splice(targetIdx, 0, courses.splice(sourceIdx, 1)[0])
|
||||||
name: course.name,
|
courses.forEach((course, index) => {
|
||||||
idx: index + 1,
|
course.idx = index + 1
|
||||||
},
|
})
|
||||||
{
|
dirty.value = true
|
||||||
onError(err: any) {
|
} else {
|
||||||
toast.warning(__(err.messages?.[0] || err))
|
let courses = programCourses.data
|
||||||
|
courses.splice(targetIdx, 0, courses.splice(sourceIdx, 1)[0])
|
||||||
|
|
||||||
|
for (const [index, course] of courses.entries()) {
|
||||||
|
programCourses.setValue.submit(
|
||||||
|
{
|
||||||
|
name: course.name,
|
||||||
|
idx: index + 1,
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
)
|
onError(err: any) {
|
||||||
await wait(100)
|
toast.warning(__(err.messages?.[0] || err))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await wait(100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const wait = (ms: number) => new Promise((res) => setTimeout(res, ms))
|
const wait = (ms: number) => new Promise((res) => setTimeout(res, ms))
|
||||||
|
|
||||||
const remove = async (
|
const remove = (
|
||||||
selections: string[],
|
selections: string[],
|
||||||
unselectAll: () => void,
|
unselectAll: () => void,
|
||||||
type: string
|
type: string
|
||||||
) => {
|
) => {
|
||||||
selections = Array.from(selections)
|
const selectionsArray = Array.from(selections)
|
||||||
for (const selection of selections) {
|
if (type === 'courses') {
|
||||||
if (type == 'courses') {
|
program.value.program_courses = program.value.program_courses.filter(
|
||||||
await programCourses.delete.submit(selection)
|
(c) => !selectionsArray.includes(c.name || c.course)
|
||||||
await updateCounts('course', 'remove')
|
)
|
||||||
} else {
|
} else {
|
||||||
await programMembers.delete.submit(selection)
|
program.value.program_members = program.value.program_members.filter(
|
||||||
await updateCounts('member', 'remove')
|
(m) => !selectionsArray.includes(m.name || m.member)
|
||||||
}
|
)
|
||||||
await programs.value.reload()
|
|
||||||
await wait(100)
|
|
||||||
}
|
}
|
||||||
|
dirty.value = true
|
||||||
unselectAll()
|
unselectAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteProgram = (close: () => void) => {
|
const deleteProgram = (close: () => void) => {
|
||||||
if (props.programName == 'new') return
|
if (props.programName == 'new') return
|
||||||
programs.value?.delete.submit(props.programName, {
|
$dialog({
|
||||||
onSuccess() {
|
title: __('Delete Program'),
|
||||||
toast.success(__('Program deleted successfully'))
|
message: __(
|
||||||
close()
|
'Are you sure you want to delete this program? This action cannot be undone.'
|
||||||
},
|
),
|
||||||
onError(err: any) {
|
actions: [
|
||||||
toast.warning(__(err.messages?.[0] || err))
|
{
|
||||||
},
|
label: __('Delete'),
|
||||||
|
theme: 'red',
|
||||||
|
variant: 'solid',
|
||||||
|
onClick(closeDialog) {
|
||||||
|
programs.value?.delete.submit(props.programName, {
|
||||||
|
onSuccess() {
|
||||||
|
toast.success(__('Program deleted successfully'))
|
||||||
|
close()
|
||||||
|
closeDialog()
|
||||||
|
},
|
||||||
|
onError(err: any) {
|
||||||
|
toast.warning(__(err.messages?.[0] || err))
|
||||||
|
closeDialog()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +589,7 @@ const courseColumns = computed(() => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: 'Title',
|
label: 'Title',
|
||||||
key: 'course_title',
|
key: props.programName === 'new' ? 'course' : 'course_title',
|
||||||
width: 1,
|
width: 1,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class LMSProgram(Document):
|
|||||||
duplicates = {course for course in courses if courses.count(course) > 1}
|
duplicates = {course for course in courses if courses.count(course) > 1}
|
||||||
if len(duplicates):
|
if len(duplicates):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Course {0} has already been added to this batch.").format(
|
_("Course {0} has already been added to this program.").format(
|
||||||
frappe.bold(next(iter(duplicates)))
|
frappe.bold(next(iter(duplicates)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -27,7 +27,7 @@ class LMSProgram(Document):
|
|||||||
duplicates = {member for member in members if members.count(member) > 1}
|
duplicates = {member for member in members if members.count(member) > 1}
|
||||||
if len(duplicates):
|
if len(duplicates):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Member {0} has already been added to this batch.").format(
|
_("Member {0} has already been added to this program.").format(
|
||||||
frappe.bold(next(iter(duplicates)))
|
frappe.bold(next(iter(duplicates)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user