314 lines
7.3 KiB
Vue
314 lines
7.3 KiB
Vue
<template>
|
|
<header
|
|
class="sticky flex items-center justify-between top-0 z-10 border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
|
>
|
|
<Breadcrumbs :items="breadcrumbs" />
|
|
<router-link
|
|
v-if="user.data?.is_moderator"
|
|
:to="{
|
|
name: 'BatchForm',
|
|
params: { batchName: 'new' },
|
|
}"
|
|
>
|
|
<Button variant="solid">
|
|
<template #prefix>
|
|
<Plus class="h-4 w-4 stroke-1.5" />
|
|
</template>
|
|
{{ __('New') }}
|
|
</Button>
|
|
</router-link>
|
|
</header>
|
|
<div class="p-5 pb-10">
|
|
<div
|
|
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5"
|
|
>
|
|
<div class="text-lg text-ink-gray-9 font-semibold">
|
|
{{ __('All Batches') }}
|
|
</div>
|
|
<div
|
|
class="flex flex-col space-y-2 lg:space-y-0 lg:flex-row lg:items-center lg:space-x-4"
|
|
>
|
|
<TabButtons
|
|
v-if="user.data"
|
|
:buttons="batchTabs"
|
|
v-model="currentTab"
|
|
/>
|
|
<FormControl
|
|
v-model="certification"
|
|
:label="__('Certification')"
|
|
type="checkbox"
|
|
@change="updateBatches()"
|
|
/>
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<FormControl
|
|
v-model="title"
|
|
:placeholder="__('Search by Title')"
|
|
type="text"
|
|
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
|
@input="updateBatches()"
|
|
/>
|
|
<div class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40">
|
|
<Select
|
|
v-if="categories.length"
|
|
v-model="currentCategory"
|
|
:options="categories"
|
|
:placeholder="__('Category')"
|
|
@change="updateBatches()"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="batches.data?.length"
|
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5"
|
|
>
|
|
<router-link
|
|
v-for="batch in batches.data"
|
|
:to="{ name: 'BatchDetail', params: { batchName: batch.name } }"
|
|
>
|
|
<BatchCard :batch="batch" />
|
|
</router-link>
|
|
</div>
|
|
<div
|
|
v-else-if="!batches.list.loading"
|
|
class="flex flex-col items-center justify-center text-sm text-ink-gray-5 italic mt-48"
|
|
>
|
|
<BookOpen class="size-10 mx-auto stroke-1 text-ink-gray-4" />
|
|
<div class="text-lg font-medium mb-1">
|
|
{{ __('No batches found') }}
|
|
</div>
|
|
<div class="leading-5 w-2/5 text-center">
|
|
{{
|
|
__(
|
|
'There are no batches matching the criteria. Keep an eye out, fresh learning experiences are on the way soon!'
|
|
)
|
|
}}
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="!batches.list.loading && batches.hasNextPage"
|
|
class="flex justify-center mt-5"
|
|
>
|
|
<Button @click="batches.next()">
|
|
{{ __('Load More') }}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script setup>
|
|
import {
|
|
Breadcrumbs,
|
|
Button,
|
|
createListResource,
|
|
FormControl,
|
|
Select,
|
|
TabButtons,
|
|
} from 'frappe-ui'
|
|
import { useRouteQuery } from '@vueuse/router'
|
|
import { computed, inject, onMounted, ref, watch } from 'vue'
|
|
import { BookOpen, Plus } from 'lucide-vue-next'
|
|
import { updateDocumentTitle } from '@/utils'
|
|
import BatchCard from '@/components/BatchCard.vue'
|
|
|
|
const user = inject('$user')
|
|
const dayjs = inject('$dayjs')
|
|
const start = ref(0)
|
|
const pageLength = ref(20)
|
|
const categories = ref([])
|
|
const currentCategory = ref(null)
|
|
const title = ref('')
|
|
const certification = ref(false)
|
|
const filters = ref({})
|
|
const currentTab = useRouteQuery(
|
|
'tab',
|
|
user.data?.is_student ? 'All' : 'Upcoming'
|
|
)
|
|
const orderBy = ref('start_date')
|
|
|
|
onMounted(() => {
|
|
setFiltersFromQuery()
|
|
updateBatches()
|
|
categories.value = [
|
|
{
|
|
label: '',
|
|
value: null,
|
|
},
|
|
]
|
|
})
|
|
|
|
const setFiltersFromQuery = () => {
|
|
let queries = new URLSearchParams(location.search)
|
|
title.value = queries.get('title') || ''
|
|
currentCategory.value = queries.get('category') || null
|
|
certification.value = queries.get('certification') || false
|
|
}
|
|
|
|
const batches = createListResource({
|
|
doctype: 'LMS Batch',
|
|
url: 'lms.lms.utils.get_batches',
|
|
cache: ['batches', user.data?.name],
|
|
pageLength: pageLength.value,
|
|
start: start.value,
|
|
onSuccess(data) {
|
|
let allCategories = data.map((batch) => batch.category)
|
|
allCategories = allCategories.filter(
|
|
(category, index) => allCategories.indexOf(category) === index && category
|
|
)
|
|
if (categories.value.length <= allCategories.length) {
|
|
updateCategories(data)
|
|
}
|
|
},
|
|
})
|
|
|
|
const updateBatches = () => {
|
|
updateFilters()
|
|
batches.update({
|
|
filters: filters.value,
|
|
orderBy: orderBy.value,
|
|
})
|
|
batches.reload()
|
|
}
|
|
|
|
const updateFilters = () => {
|
|
updateCategoryFilter()
|
|
updateTitleFilter()
|
|
updateCertificationFilter()
|
|
updateTabFilter()
|
|
updateStudentFilter()
|
|
setQueryParams()
|
|
}
|
|
|
|
const updateCategoryFilter = () => {
|
|
if (currentCategory.value) {
|
|
filters.value['category'] = currentCategory.value
|
|
} else {
|
|
delete filters.value['category']
|
|
}
|
|
}
|
|
|
|
const updateTitleFilter = () => {
|
|
if (title.value) {
|
|
filters.value['title'] = ['like', `%${title.value}%`]
|
|
} else {
|
|
delete filters.value['title']
|
|
}
|
|
}
|
|
|
|
const updateCertificationFilter = () => {
|
|
if (certification.value) {
|
|
filters.value['certification'] = 1
|
|
} else {
|
|
delete filters.value['certification']
|
|
}
|
|
}
|
|
|
|
const updateTabFilter = () => {
|
|
orderBy.value = 'start_date'
|
|
if (!user.data) {
|
|
return
|
|
}
|
|
if (currentTab.value == 'Enrolled' && user.data?.is_student) {
|
|
filters.value['enrolled'] = 1
|
|
delete filters.value['start_date']
|
|
delete filters.value['published']
|
|
orderBy.value = 'start_date desc'
|
|
} else if (user.data?.is_student) {
|
|
delete filters.value['enrolled']
|
|
} else {
|
|
delete filters.value['start_date']
|
|
delete filters.value['published']
|
|
orderBy.value = 'start_date desc'
|
|
if (currentTab.value == 'Upcoming') {
|
|
filters.value['start_date'] = ['>=', dayjs().format('YYYY-MM-DD')]
|
|
filters.value['published'] = 1
|
|
orderBy.value = 'start_date'
|
|
} else if (currentTab.value == 'Archived') {
|
|
filters.value['start_date'] = ['<=', dayjs().format('YYYY-MM-DD')]
|
|
} else if (currentTab.value == 'Unpublished') {
|
|
filters.value['published'] = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
const updateStudentFilter = () => {
|
|
if (!user.data || (user.data?.is_student && currentTab.value != 'Enrolled')) {
|
|
filters.value['start_date'] = ['>=', dayjs().format('YYYY-MM-DD')]
|
|
filters.value['published'] = 1
|
|
}
|
|
}
|
|
|
|
const setQueryParams = () => {
|
|
let queries = new URLSearchParams(location.search)
|
|
let filterKeys = {
|
|
title: title.value,
|
|
category: currentCategory.value,
|
|
certification: certification.value,
|
|
}
|
|
|
|
Object.keys(filterKeys).forEach((key) => {
|
|
if (filterKeys[key]) {
|
|
queries.set(key, filterKeys[key])
|
|
} else {
|
|
queries.delete(key)
|
|
}
|
|
})
|
|
|
|
let queryString = ''
|
|
if (queries.toString()) {
|
|
queryString = `?${queries.toString()}`
|
|
}
|
|
|
|
history.replaceState({}, '', `${location.pathname}${queryString}`)
|
|
}
|
|
|
|
const updateCategories = (data) => {
|
|
data.forEach((batch) => {
|
|
if (
|
|
batch.category &&
|
|
!categories.value.find((category) => category.value === batch.category)
|
|
)
|
|
categories.value.push({
|
|
label: batch.category,
|
|
value: batch.category,
|
|
})
|
|
})
|
|
}
|
|
|
|
watch(currentTab, () => {
|
|
updateBatches()
|
|
})
|
|
|
|
const batchTabs = computed(() => {
|
|
let tabs = [
|
|
{
|
|
label: __('All'),
|
|
},
|
|
]
|
|
if (user.data?.is_student) {
|
|
tabs.push({ label: __('Enrolled') })
|
|
} else {
|
|
tabs.push({ label: __('Upcoming') })
|
|
tabs.push({ label: __('Archived') })
|
|
tabs.push({ label: __('Unpublished') })
|
|
}
|
|
return tabs
|
|
})
|
|
|
|
const breadcrumbs = computed(() => [
|
|
{
|
|
label: __('Batches'),
|
|
route: { name: 'Batches' },
|
|
},
|
|
])
|
|
|
|
const pageMeta = computed(() => {
|
|
return {
|
|
title: 'Batches',
|
|
description: 'All upcoming batches.',
|
|
}
|
|
})
|
|
|
|
updateDocumentTitle(pageMeta)
|
|
</script>
|