Merge pull request #1422 from pateljannat/issues-89

fix: don't update onboarding status if user is not system manager
This commit is contained in:
Jannat Patel
2025-04-10 15:55:32 +05:30
committed by GitHub
17 changed files with 115 additions and 55 deletions

View File

@@ -118,6 +118,23 @@ import { ref, watch, reactive, inject } from 'vue'
import { RefreshCw, Plus, X } from 'lucide-vue-next' import { RefreshCw, Plus, X } from 'lucide-vue-next'
import { useOnboarding } from 'frappe-ui/frappe' import { useOnboarding } from 'frappe-ui/frappe'
interface User {
data: {
email: string
name: string
enabled: boolean
user_image: string
full_name: string
user_type: ['System User', 'Website User']
username: string
is_moderator: boolean
is_system_manager: boolean
is_evaluator: boolean
is_instructor: boolean
is_fc_site: boolean
}
}
const router = useRouter() const router = useRouter()
const show = defineModel('show') const show = defineModel('show')
const search = ref('') const search = ref('')
@@ -126,6 +143,7 @@ const memberList = ref([])
const hasNextPage = ref(false) const hasNextPage = ref(false)
const showForm = ref(false) const showForm = ref(false)
const dayjs = inject('$dayjs') const dayjs = inject('$dayjs')
const user = inject<User | null>('$user')
const { updateOnboardingStep } = useOnboarding('learning') const { updateOnboardingStep } = useOnboarding('learning')
const member = reactive({ const member = reactive({
@@ -187,7 +205,9 @@ const newMember = createResource({
auto: false, auto: false,
onSuccess(data) { onSuccess(data) {
show.value = false show.value = false
updateOnboardingStep('invite_students')
if (user?.data?.is_system_manager) updateOnboardingStep('invite_students')
router.push({ router.push({
name: 'Profile', name: 'Profile',
params: { params: {

View File

@@ -32,7 +32,7 @@
</template> </template>
<script setup> <script setup>
import { Dialog, createResource } from 'frappe-ui' import { Dialog, createResource } from 'frappe-ui'
import { ref } from 'vue' import { ref, inject } from 'vue'
import Link from '@/components/Controls/Link.vue' import Link from '@/components/Controls/Link.vue'
import { showToast } from '@/utils' import { showToast } from '@/utils'
import { useOnboarding } from 'frappe-ui/frappe' import { useOnboarding } from 'frappe-ui/frappe'
@@ -41,6 +41,7 @@ import { useSettings } from '@/stores/settings'
const show = defineModel() const show = defineModel()
const course = ref(null) const course = ref(null)
const evaluator = ref(null) const evaluator = ref(null)
const user = inject('$user')
const courses = defineModel('courses') const courses = defineModel('courses')
const { updateOnboardingStep } = useOnboarding('learning') const { updateOnboardingStep } = useOnboarding('learning')
const settingsStore = useSettings() const settingsStore = useSettings()
@@ -73,9 +74,11 @@ const addCourse = (close) => {
{}, {},
{ {
onSuccess() { onSuccess() {
courses.value.reload() if (user.data?.is_system_manager)
updateOnboardingStep('add_batch_course') updateOnboardingStep('add_batch_course')
close() close()
courses.value.reload()
course.value = null course.value = null
evaluator.value = null evaluator.value = null
}, },

View File

@@ -77,7 +77,7 @@ import {
FormControl, FormControl,
Switch, Switch,
} from 'frappe-ui' } from 'frappe-ui'
import { reactive, watch } from 'vue' import { reactive, watch, inject } from 'vue'
import { showToast, getFileSize } from '@/utils/' import { showToast, getFileSize } from '@/utils/'
import { capture } from '@/telemetry' import { capture } from '@/telemetry'
import { FileText, X } from 'lucide-vue-next' import { FileText, X } from 'lucide-vue-next'
@@ -85,6 +85,7 @@ import { useOnboarding } from 'frappe-ui/frappe'
const show = defineModel() const show = defineModel()
const outline = defineModel('outline') const outline = defineModel('outline')
const user = inject('$user')
const { updateOnboardingStep } = useOnboarding('learning') const { updateOnboardingStep } = useOnboarding('learning')
const props = defineProps({ const props = defineProps({
@@ -139,8 +140,10 @@ const addChapter = async (close) => {
return validateChapter() return validateChapter()
}, },
onSuccess: (data) => { onSuccess: (data) => {
if (user.data?.is_system_manager)
updateOnboardingStep('create_first_chapter')
capture('chapter_created') capture('chapter_created')
updateOnboardingStep('create_first_chapter')
chapterReference.submit( chapterReference.submit(
{ name: data.name }, { name: data.name },
{ {

View File

@@ -106,7 +106,7 @@
</template> </template>
<script setup> <script setup>
import { Dialog, FormControl, TextEditor, createResource } from 'frappe-ui' import { Dialog, FormControl, TextEditor, createResource } from 'frappe-ui'
import { computed, watch, reactive, ref } from 'vue' import { computed, watch, reactive, ref, inject } from 'vue'
import Link from '@/components/Controls/Link.vue' import Link from '@/components/Controls/Link.vue'
import { showToast } from '@/utils' import { showToast } from '@/utils'
import { useOnboarding } from 'frappe-ui/frappe' import { useOnboarding } from 'frappe-ui/frappe'
@@ -115,6 +115,7 @@ const show = defineModel()
const quiz = defineModel('quiz') const quiz = defineModel('quiz')
const questionType = ref(null) const questionType = ref(null)
const editMode = ref(false) const editMode = ref(false)
const user = inject('$user')
const { updateOnboardingStep } = useOnboarding('learning') const { updateOnboardingStep } = useOnboarding('learning')
const existingQuestion = reactive({ const existingQuestion = reactive({
@@ -262,8 +263,10 @@ const addQuestionRow = (question, close) => {
}, },
{ {
onSuccess() { onSuccess() {
if (user.data?.is_system_manager)
updateOnboardingStep('create_first_quiz')
show.value = false show.value = false
updateOnboardingStep('create_first_quiz')
showToast(__('Success'), __('Question added successfully'), 'check') showToast(__('Success'), __('Question added successfully'), 'check')
quiz.value.reload() quiz.value.reload()
close() close()

View File

@@ -26,13 +26,14 @@
</template> </template>
<script setup> <script setup>
import { Dialog, createResource } from 'frappe-ui' import { Dialog, createResource } from 'frappe-ui'
import { ref } from 'vue' import { ref, inject } from 'vue'
import Link from '@/components/Controls/Link.vue' import Link from '@/components/Controls/Link.vue'
import { showToast } from '@/utils' import { showToast } from '@/utils'
import { useOnboarding } from 'frappe-ui/frappe' import { useOnboarding } from 'frappe-ui/frappe'
const students = defineModel('reloadStudents') const students = defineModel('reloadStudents')
const student = ref() const student = ref()
const user = inject('$user')
const { updateOnboardingStep } = useOnboarding('learning') const { updateOnboardingStep } = useOnboarding('learning')
const show = defineModel() const show = defineModel()
@@ -61,9 +62,11 @@ const addStudent = (close) => {
{}, {},
{ {
onSuccess() { onSuccess() {
if (user.data?.is_system_manager)
updateOnboardingStep('add_batch_student')
students.value.reload() students.value.reload()
student.value = null student.value = null
updateOnboardingStep('add_batch_student')
close() close()
}, },
onError(err) { onError(err) {

View File

@@ -430,10 +430,13 @@ const createNewBatch = () => {
{}, {},
{ {
onSuccess(data) { onSuccess(data) {
if (user.data?.is_system_manager) {
updateOnboardingStep('create_first_batch', true, false, () => {
localStorage.setItem('firstBatch', data.name)
})
}
capture('batch_created') capture('batch_created')
updateOnboardingStep('create_first_batch', true, false, () => {
localStorage.setItem('firstBatch', data.name)
})
router.push({ router.push({
name: 'BatchDetail', name: 'BatchDetail',
params: { params: {

View File

@@ -3,15 +3,11 @@
<div class="grid md:grid-cols-[70%,30%] h-full"> <div class="grid md:grid-cols-[70%,30%] h-full">
<div> <div>
<header <header
class="sticky top-0 z-10 group flex flex-col md:flex-row md:items-center justify-between border-b bg-surface-white px-3 py-2.5 sm:px-5" class="sticky top-0 z-10 flex flex-col md:flex-row md: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" />
<div class="flex items-center mt-3 md:mt-0"> <div class="flex items-center mt-3 md:mt-0">
<Button <Button v-if="courseResource.data?.name" @click="trashCourse()">
v-if="courseResource.data?.name"
@click="trashCourse()"
class="invisible group-hover:visible"
>
<template #icon> <template #icon>
<Trash2 class="w-4 h-4 stroke-1.5" /> <Trash2 class="w-4 h-4 stroke-1.5" />
</template> </template>
@@ -265,7 +261,7 @@ import {
watch, watch,
getCurrentInstance, getCurrentInstance,
} from 'vue' } from 'vue'
import { showToast, updateDocumentTitle } from '@/utils' import { showToast } from '@/utils'
import { Image, Trash2, X } from 'lucide-vue-next' import { Image, Trash2, X } from 'lucide-vue-next'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { capture } from '@/telemetry' import { capture } from '@/telemetry'
@@ -404,7 +400,7 @@ const courseResource = createResource({
'paid_course', 'paid_course',
'featured', 'featured',
'enable_certification', 'enable_certification',
'paid_certifiate', 'paid_certificate',
] ]
for (let idx in checkboxes) { for (let idx in checkboxes) {
let key = checkboxes[idx] let key = checkboxes[idx]
@@ -447,11 +443,14 @@ const submitCourse = () => {
} else { } else {
courseCreationResource.submit(course, { courseCreationResource.submit(course, {
onSuccess(data) { onSuccess(data) {
if (user.data?.is_system_manager) {
updateOnboardingStep('create_first_course', true, false, () => {
localStorage.setItem('firstCourse', data.name)
})
}
capture('course_created') capture('course_created')
showToast('Success', 'Course created successfully', 'check') showToast('Success', 'Course created successfully', 'check')
updateOnboardingStep('create_first_course', true, false, () => {
localStorage.setItem('firstCourse', data.name)
})
router.push({ router.push({
name: 'CourseForm', name: 'CourseForm',
params: { courseName: data.name }, params: { courseName: data.name },

View File

@@ -4,7 +4,7 @@
> >
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
<router-link <router-link
v-if="user.data?.is_moderator" v-if="canCreateCourse()"
:to="{ :to="{
name: 'CourseForm', name: 'CourseForm',
params: { courseName: 'new' }, params: { courseName: 'new' },
@@ -105,6 +105,7 @@ import {
import { computed, inject, onMounted, ref, watch } from 'vue' import { computed, inject, onMounted, ref, watch } from 'vue'
import { BookOpen, Plus } from 'lucide-vue-next' import { BookOpen, Plus } from 'lucide-vue-next'
import { sessionStore } from '@/stores/session' import { sessionStore } from '@/stores/session'
import { canCreateCourse } from '@/utils'
import CourseCard from '@/components/CourseCard.vue' import CourseCard from '@/components/CourseCard.vue'
const user = inject('$user') const user = inject('$user')

View File

@@ -25,7 +25,7 @@
</router-link> </router-link>
</header> </header>
<div> <div>
<div class="p-5"> <div v-if="jobs.data?.length" class="p-5">
<div <div
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5" class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5"
> >
@@ -58,10 +58,7 @@
</div> </div>
</div> </div>
<div <div class="grid grid-cols-1 lg:grid-cols-3 gap-5">
v-if="jobs.data?.length"
class="grid grid-cols-1 lg:grid-cols-3 gap-5"
>
<router-link <router-link
v-for="job in jobs.data" v-for="job in jobs.data"
:to="{ :to="{
@@ -73,8 +70,21 @@
<JobCard :job="job" /> <JobCard :job="job" />
</router-link> </router-link>
</div> </div>
<div v-else class="text-ink-gray-7 italic p-5 w-fit mx-auto"> </div>
{{ __('No jobs posted') }} <div
v-else
class="flex flex-col items-center justify-center text-sm text-ink-gray-5 mt-48"
>
<Laptop class="size-10 mx-auto stroke-1 text-ink-gray-4" />
<div class="text-lg font-medium mb-1">
{{ __('No jobs found') }}
</div>
<div class="leading-5 w-2/5 text-center">
{{
__(
'There are no jobs available at the moment. Open a job opportunity or check here again later.'
)
}}
</div> </div>
</div> </div>
</div> </div>
@@ -88,7 +98,7 @@ import {
FormControl, FormControl,
usePageMeta, usePageMeta,
} from 'frappe-ui' } from 'frappe-ui'
import { Plus, Search } from 'lucide-vue-next' import { Laptop, Plus, Search } from 'lucide-vue-next'
import { sessionStore } from '../stores/session' import { sessionStore } from '../stores/session'
import { inject, computed, ref, onMounted } from 'vue' import { inject, computed, ref, onMounted } from 'vue'
import JobCard from '@/components/JobCard.vue' import JobCard from '@/components/JobCard.vue'

View File

@@ -402,8 +402,10 @@ const createNewLesson = () => {
{ lesson: data.name }, { lesson: data.name },
{ {
onSuccess() { onSuccess() {
if (user.data?.is_system_manager)
updateOnboardingStep('create_first_lesson')
capture('lesson_created') capture('lesson_created')
updateOnboardingStep('create_first_lesson')
showToast('Success', 'Lesson created successfully', 'check') showToast('Success', 'Lesson created successfully', 'check')
lessonDetails.reload() lessonDetails.reload()
}, },

View File

@@ -9,15 +9,9 @@ export const useSettings = defineStore('settings', () => {
const activeTab = ref(null) const activeTab = ref(null)
const learningPaths = createResource({ const learningPaths = createResource({
url: 'frappe.client.get_single_value', url: 'lms.lms.api.is_learning_path_enabled',
makeParams(values) { auto: true,
return { cache: ['learningPath'],
doctype: 'LMS Settings',
field: 'enable_learning_paths',
}
},
auto: isLoggedIn ? true : false,
cache: ['learningPaths'],
}) })
const allowGuestAccess = createResource({ const allowGuestAccess = createResource({
@@ -26,12 +20,6 @@ export const useSettings = defineStore('settings', () => {
cache: ['allowGuestAccess'], cache: ['allowGuestAccess'],
}) })
/* const onboardingDetails = createResource({
url: 'lms.lms.utils.is_onboarding_complete',
auto: isLoggedIn ? true : false,
cache: ['onboardingDetails'],
}) */
return { return {
isSettingsOpen, isSettingsOpen,
activeTab, activeTab,

View File

@@ -14,6 +14,7 @@ import dayjs from '@/utils/dayjs'
import Embed from '@editorjs/embed' import Embed from '@editorjs/embed'
import SimpleImage from '@editorjs/simple-image' import SimpleImage from '@editorjs/simple-image'
import Table from '@editorjs/table' import Table from '@editorjs/table'
import { usersStore } from '../stores/user'
export function createToast(options) { export function createToast(options) {
toast({ toast({
@@ -567,3 +568,8 @@ export const escapeHTML = (text) => {
(char) => escape_html_mapping[char] || char (char) => escape_html_mapping[char] || char
) )
} }
export const canCreateCourse = () => {
const { userResource } = usersStore()
return userResource.data?.is_instructor || userResource.data?.is_moderator
}

View File

@@ -25,7 +25,7 @@ export default defineConfig({
}), }),
], ],
server: { server: {
allowedHosts: ['fs', 'onb1'], allowedHosts: ['fs', 'onb2'],
}, },
resolve: { resolve: {
alias: { alias: {

View File

@@ -1259,6 +1259,11 @@ def is_guest_allowed():
return frappe.get_cached_value("LMS Settings", None, "allow_guest_access") return frappe.get_cached_value("LMS Settings", None, "allow_guest_access")
@frappe.whitelist(allow_guest=True)
def is_learning_path_enabled():
return frappe.get_cached_value("LMS Settings", None, "enable_learning_paths")
@frappe.whitelist() @frappe.whitelist()
def cancel_evaluation(evaluation): def cancel_evaluation(evaluation):
evaluation = frappe._dict(evaluation) evaluation = frappe._dict(evaluation)

View File

@@ -161,7 +161,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-11-14 13:46:56.838659", "modified": "2025-04-10 15:19:22.400932",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "Course Lesson", "name": "Course Lesson",
@@ -189,14 +189,28 @@
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "LMS Student", "role": "Course Creator",
"select": 1,
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Moderator",
"select": 1, "select": 1,
"share": 1, "share": 1,
"write": 1 "write": 1
} }
], ],
"row_format": "Dynamic",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -4,7 +4,7 @@
<br> <br>
<p> {{ _(" Please evaluate and grade it.") }} </p> <p> {{ _(" Please evaluate and grade it.") }} </p>
<br>` <br>`
<a href="/assignment-submission/{{ assignment_name }}/{{ submission_name }}"> <a href="/lms/assignment-submission/{{ assignment_name }}/{{ submission_name }}">
{{ _("Open Assignment") }} {{ _("Open Assignment") }}
</a> </a>