fix: delete batch and pass fields prop to brand settings
This commit is contained in:
@@ -65,6 +65,10 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Button variant="solid" class="w-full mt-4">
|
<Button variant="solid" class="w-full mt-4">
|
||||||
|
<template #prefix>
|
||||||
|
<Settings v-if="isModerator" class="size-4 stroke-1.5" />
|
||||||
|
<LogIn v-else class="size-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
<span>
|
<span>
|
||||||
{{ isModerator ? __('Manage Batch') : __('Visit Batch') }}
|
{{ isModerator ? __('Manage Batch') : __('Visit Batch') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -85,6 +89,9 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Button v-if="!isStudent" class="w-full mt-4" variant="solid">
|
<Button v-if="!isStudent" class="w-full mt-4" variant="solid">
|
||||||
|
<template #prefix>
|
||||||
|
<CreditCard class="size-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
<span>
|
<span>
|
||||||
{{ __('Register Now') }}
|
{{ __('Register Now') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -100,6 +107,9 @@
|
|||||||
"
|
"
|
||||||
@click="enrollInBatch()"
|
@click="enrollInBatch()"
|
||||||
>
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<GraduationCap class="size-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
{{ __('Enroll Now') }}
|
{{ __('Enroll Now') }}
|
||||||
</Button>
|
</Button>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -112,6 +122,9 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Button class="w-full mt-2">
|
<Button class="w-full mt-2">
|
||||||
|
<template #prefix>
|
||||||
|
<Pencil class="size-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
<span>
|
<span>
|
||||||
{{ __('Edit') }}
|
{{ __('Edit') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -122,8 +135,17 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { inject, computed } from 'vue'
|
import { inject, computed } from 'vue'
|
||||||
import { Badge, Button, createResource, toast } from 'frappe-ui'
|
import { Button, createResource, toast } from 'frappe-ui'
|
||||||
import { BookOpen, Clock, Globe } from 'lucide-vue-next'
|
import {
|
||||||
|
BookOpen,
|
||||||
|
Clock,
|
||||||
|
CreditCard,
|
||||||
|
Globe,
|
||||||
|
GraduationCap,
|
||||||
|
LogIn,
|
||||||
|
Pencil,
|
||||||
|
Settings,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
import { formatNumberIntoCurrency, formatTime } from '@/utils'
|
import { formatNumberIntoCurrency, formatTime } from '@/utils'
|
||||||
import DateRange from '@/components/Common/DateRange.vue'
|
import DateRange from '@/components/Common/DateRange.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|||||||
@@ -7,17 +7,17 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #body-content>
|
<template #body-content>
|
||||||
<div class="flex justify-between space-x-10 text-base">
|
<div class="flex justify-between space-x-10 text-base mt-10">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="flex items-center justify-between space-x-5 mb-4">
|
<div class="flex items-center justify-between space-x-5 mb-4">
|
||||||
<div class="text-xl font-semibold text-ink-gray-6">
|
<!-- <div class="text-xl font-semibold text-ink-gray-6">
|
||||||
{{ __('{0} Members').format(memberCount) }}
|
{{ __('{0} Members').format(memberCount) }}
|
||||||
</div>
|
</div> -->
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="searchFilter"
|
v-model="searchFilter"
|
||||||
:label="__('Search by Member Name')"
|
:placeholder="__('Search by Member Name')"
|
||||||
type="text"
|
type="text"
|
||||||
class="w-1/2"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-h-[70vh] overflow-y-auto">
|
<div class="max-h-[70vh] overflow-y-auto">
|
||||||
@@ -90,13 +90,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 self-start w-full space-y-5">
|
<div class="mb-4 self-start w-full space-y-5">
|
||||||
<NumberChart
|
<div class="flex items-center space-x-4">
|
||||||
class="border rounded-md"
|
<NumberChart
|
||||||
:config="{
|
class="border rounded-md w-full"
|
||||||
title: __('Average Progress %'),
|
:config="{
|
||||||
value: chartDetails.data?.average_progress || 0,
|
title: __('Enrollments'),
|
||||||
}"
|
value: memberCount || 0,
|
||||||
/>
|
}"
|
||||||
|
/>
|
||||||
|
<NumberChart
|
||||||
|
class="border rounded-md w-full"
|
||||||
|
:config="{
|
||||||
|
title: __('Average Progress %'),
|
||||||
|
value: chartDetails.data?.average_progress || 0,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<DonutChart
|
<DonutChart
|
||||||
:config="{
|
:config="{
|
||||||
data: chartDetails.data?.progress_distribution || [],
|
data: chartDetails.data?.progress_distribution || [],
|
||||||
|
|||||||
@@ -88,9 +88,31 @@ const update = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(branding, (newData) => {
|
watch(branding, (updatedDoc) => {
|
||||||
if (newData && !isDirty.value) {
|
let textFields = []
|
||||||
isDirty.value = true
|
let imageFields = []
|
||||||
}
|
|
||||||
|
props.fields.forEach((f) => {
|
||||||
|
if (f.type === 'Upload') {
|
||||||
|
imageFields.push(f.name)
|
||||||
|
} else {
|
||||||
|
textFields.push(f.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
textFields.forEach((field) => {
|
||||||
|
if (updatedDoc.data[field] != updatedDoc.previousData[field]) {
|
||||||
|
isDirty.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
imageFields.forEach((field) => {
|
||||||
|
if (
|
||||||
|
updatedDoc.data[field] &&
|
||||||
|
updatedDoc.data[field].file_url != updatedDoc.previousData[field].file_url
|
||||||
|
) {
|
||||||
|
isDirty.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -37,8 +37,13 @@
|
|||||||
<component
|
<component
|
||||||
v-if="activeTab.template"
|
v-if="activeTab.template"
|
||||||
:is="activeTab.template"
|
:is="activeTab.template"
|
||||||
:label="activeTab.label"
|
v-bind="{
|
||||||
:description="activeTab.description"
|
label: activeTab.label,
|
||||||
|
description: activeTab.description,
|
||||||
|
...(activeTab.label === 'Branding'
|
||||||
|
? { fields: activeTab.fields }
|
||||||
|
: {}),
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<PaymentSettings
|
<PaymentSettings
|
||||||
v-else-if="activeTab.label === 'Payment Gateway'"
|
v-else-if="activeTab.label === 'Payment Gateway'"
|
||||||
|
|||||||
@@ -4,9 +4,16 @@
|
|||||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||||
<Button variant="solid" @click="saveBatch()">
|
<div class="flex items-center space-x-2">
|
||||||
{{ __('Save') }}
|
<Button v-if="batchDetail.data?.name" @click="deleteBatch">
|
||||||
</Button>
|
<template #icon>
|
||||||
|
<Trash2 class="size-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
<Button variant="solid" @click="saveBatch()">
|
||||||
|
{{ __('Save') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="py-5">
|
<div class="py-5">
|
||||||
<div class="px-20 pb-5 space-y-5 border-b mb-5">
|
<div class="px-20 pb-5 space-y-5 border-b mb-5">
|
||||||
@@ -303,10 +310,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
onMounted,
|
getCurrentInstance,
|
||||||
inject,
|
inject,
|
||||||
reactive,
|
onMounted,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
|
reactive,
|
||||||
ref,
|
ref,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import {
|
import {
|
||||||
@@ -318,9 +326,11 @@ import {
|
|||||||
createResource,
|
createResource,
|
||||||
usePageMeta,
|
usePageMeta,
|
||||||
toast,
|
toast,
|
||||||
|
call,
|
||||||
|
Toast,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { Image } from 'lucide-vue-next'
|
import { Image, Trash2 } from 'lucide-vue-next'
|
||||||
import { capture } from '@/telemetry'
|
import { capture } from '@/telemetry'
|
||||||
import { useOnboarding } from 'frappe-ui/frappe'
|
import { useOnboarding } from 'frappe-ui/frappe'
|
||||||
import { sessionStore } from '../stores/session'
|
import { sessionStore } from '../stores/session'
|
||||||
@@ -338,6 +348,8 @@ const user = inject('$user')
|
|||||||
const { brand } = sessionStore()
|
const { brand } = sessionStore()
|
||||||
const { updateOnboardingStep } = useOnboarding('learning')
|
const { updateOnboardingStep } = useOnboarding('learning')
|
||||||
const instructors = ref([])
|
const instructors = ref([])
|
||||||
|
const app = getCurrentInstance()
|
||||||
|
const { $dialog } = app.appContext.config.globalProperties
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
batchName: {
|
batchName: {
|
||||||
@@ -539,6 +551,38 @@ const editBatchDetails = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteBatch = () => {
|
||||||
|
$dialog({
|
||||||
|
title: __('Confirm your action to delete'),
|
||||||
|
message: __(
|
||||||
|
'Deleting this batch will also delete all its data including enrolled students, linked courses, assessments, feedback and discussions. Are you sure you want to continue?'
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: __('Delete'),
|
||||||
|
theme: 'red',
|
||||||
|
variant: 'solid',
|
||||||
|
onClick({ close }) {
|
||||||
|
trashBatch(close)
|
||||||
|
close()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const trashBatch = (close) => {
|
||||||
|
call('lms.lms.api.delete_batch', {
|
||||||
|
batch: props.batchName,
|
||||||
|
}).then(() => {
|
||||||
|
toast.success(__('Batch deleted successfully'))
|
||||||
|
close()
|
||||||
|
router.push({
|
||||||
|
name: 'Batches',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const saveImage = (file) => {
|
const saveImage = (file) => {
|
||||||
batch.image = file
|
batch.image = file
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -990,7 +990,30 @@ def delete_course(course):
|
|||||||
frappe.delete_doc("LMS Course", course)
|
frappe.delete_doc("LMS Course", course)
|
||||||
|
|
||||||
|
|
||||||
def give_dicussions_permission():
|
@frappe.whitelist()
|
||||||
|
def delete_batch(batch):
|
||||||
|
frappe.db.delete("LMS Batch Enrollment", {"batch": batch})
|
||||||
|
frappe.db.delete("Batch Course", {"parent": batch, "parenttype": "LMS Batch"})
|
||||||
|
frappe.db.delete("LMS Assessment", {"parent": batch, "parenttype": "LMS Batch"})
|
||||||
|
frappe.db.delete("LMS Batch Timetable", {"parent": batch, "parenttype": "LMS Batch"})
|
||||||
|
frappe.db.delete("LMS Batch Feedback", {"batch": batch})
|
||||||
|
delete_batch_discussions(batch)
|
||||||
|
frappe.db.delete("LMS Batch", batch)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_batch_discussions(batch):
|
||||||
|
topics = frappe.get_all(
|
||||||
|
"Discussion Topic",
|
||||||
|
{"reference_doctype": "LMS Batch", "reference_docname": batch},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
|
||||||
|
for topic in topics:
|
||||||
|
frappe.db.delete("Discussion Reply", {"topic": topic})
|
||||||
|
frappe.db.delete("Discussion Topic", topic)
|
||||||
|
|
||||||
|
|
||||||
|
def give_discussions_permission():
|
||||||
doctypes = ["Discussion Topic", "Discussion Reply"]
|
doctypes = ["Discussion Topic", "Discussion Reply"]
|
||||||
roles = ["LMS Student", "Course Creator", "Moderator", "Batch Evaluator"]
|
roles = ["LMS Student", "Course Creator", "Moderator", "Batch Evaluator"]
|
||||||
for doctype in doctypes:
|
for doctype in doctypes:
|
||||||
|
|||||||
Reference in New Issue
Block a user