chore: fixed merge conflicts
This commit is contained in:
@@ -179,6 +179,9 @@
|
||||
"
|
||||
:editable="true"
|
||||
:fixedMenu="true"
|
||||
:uploadArgs="{
|
||||
private: true,
|
||||
}"
|
||||
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
{{ __('Enroll Now') }}
|
||||
</Button>
|
||||
<router-link
|
||||
v-if="isModerator"
|
||||
v-if="canEditBatch"
|
||||
:to="{
|
||||
name: 'BatchForm',
|
||||
params: {
|
||||
@@ -209,7 +209,19 @@ const isEvaluator = computed(() => {
|
||||
return user.data?.is_evaluator
|
||||
})
|
||||
|
||||
const isInstructor = computed(() => {
|
||||
return (
|
||||
props.batch.data?.instructors?.filter(
|
||||
(instructor) => instructor.name === user.data?.name
|
||||
).length > 0
|
||||
)
|
||||
})
|
||||
|
||||
const canAccessBatch = computed(() => {
|
||||
return isModerator.value || isStudent.value || isEvaluator.value
|
||||
})
|
||||
|
||||
const canEditBatch = computed(() => {
|
||||
return isModerator.value || isInstructor.value
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
:style="
|
||||
modelValue
|
||||
? {
|
||||
backgroundColor:
|
||||
theme.backgroundColor[modelValue.toLowerCase()][400],
|
||||
backgroundColor: getColor(
|
||||
modelValue.toLowerCase(),
|
||||
400
|
||||
),
|
||||
}
|
||||
: {}
|
||||
"
|
||||
@@ -55,8 +57,7 @@
|
||||
:key="color"
|
||||
class="size-5 rounded-full cursor-pointer"
|
||||
:style="{
|
||||
backgroundColor:
|
||||
theme.backgroundColor[color.toLowerCase()][400],
|
||||
backgroundColor: getColor(color.toLowerCase(), 400),
|
||||
}"
|
||||
@click="
|
||||
(e) => {
|
||||
@@ -79,7 +80,7 @@
|
||||
import { Button, FormControl, Popover } from 'frappe-ui'
|
||||
import { computed } from 'vue'
|
||||
import { Palette, X } from 'lucide-vue-next'
|
||||
import { theme } from '@/utils/theme'
|
||||
import { getColor } from '@/utils'
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
class="w-4 h-4 text-ink-gray-7 stroke-1.5"
|
||||
:is="icons.Folder"
|
||||
/>
|
||||
<span v-if="selectedIcon">
|
||||
<span v-if="selectedIcon" class="text-ink-gray-7">
|
||||
{{ selectedIcon }}
|
||||
</span>
|
||||
<span v-else class="text-ink-gray-5">
|
||||
|
||||
@@ -28,10 +28,12 @@
|
||||
class="mt-1 rounded-lg bg-surface-white py-1 text-base border-2"
|
||||
>
|
||||
<ComboboxOptions
|
||||
class="my-1 min-h-[6rem] max-h-[12rem] overflow-y-auto px-1.5"
|
||||
class="my-1 max-h-[12rem] overflow-y-auto px-1.5"
|
||||
:class="options.length ? 'min-h-[6rem]' : 'min-h-[3.8rem]'"
|
||||
static
|
||||
>
|
||||
<ComboboxOption
|
||||
v-if="options.length"
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
:value="option"
|
||||
@@ -53,7 +55,9 @@
|
||||
</div>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<div class="h-10"></div>
|
||||
<div v-else class="text-ink-gray-7 px-4">
|
||||
{{ __('No results found') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="attrs.onCreate"
|
||||
class="absolute bottom-2 left-1 w-[95%] pt-2 bg-white border-t"
|
||||
|
||||
@@ -136,11 +136,11 @@
|
||||
import { Award, BookOpen, GraduationCap, Star, Users } from 'lucide-vue-next'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { Tooltip } from 'frappe-ui'
|
||||
import { theme } from '@/utils/theme'
|
||||
import { formatAmount } from '@/utils'
|
||||
import CourseInstructors from '@/components/CourseInstructors.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import ProgressBar from '@/components/ProgressBar.vue'
|
||||
import colors from '@/utils/frappe-ui-colors.json'
|
||||
|
||||
const { user } = sessionStore()
|
||||
|
||||
@@ -152,8 +152,10 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const getGradientColor = () => {
|
||||
let theme =
|
||||
localStorage.getItem('theme') == 'light' ? 'lightMode' : 'darkMode'
|
||||
let color = props.course.card_gradient?.toLowerCase() || 'blue'
|
||||
let colorMap = theme.backgroundColor[color]
|
||||
let colorMap = colors[theme][color]
|
||||
return `linear-gradient(to top right, black, ${colorMap[400]})`
|
||||
/* return `bg-gradient-to-br from-${color}-100 via-${color}-200 to-${color}-400` */
|
||||
/* return `linear-gradient(to bottom right, ${colorMap[100]}, ${colorMap[400]})` */
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import AppSidebar from './AppSidebar.vue'
|
||||
import AppSidebar from '@/components/Sidebar/AppSidebar.vue'
|
||||
</script>
|
||||
|
||||
@@ -114,11 +114,11 @@
|
||||
categoryColumn: 'category',
|
||||
valueColumn: 'count',
|
||||
colors: [
|
||||
theme.colors.red['400'],
|
||||
theme.colors.amber['400'],
|
||||
theme.colors.pink['400'],
|
||||
theme.colors.blue['400'],
|
||||
theme.colors.green['400'],
|
||||
getColor('red', 400),
|
||||
getColor('amber', 400),
|
||||
getColor('pink', 400),
|
||||
getColor('blue', 400),
|
||||
getColor('green', 400),
|
||||
],
|
||||
}"
|
||||
/>
|
||||
@@ -146,7 +146,7 @@ import {
|
||||
NumberChart,
|
||||
} from 'frappe-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { theme } from '@/utils/theme'
|
||||
import { getColor } from '@/utils'
|
||||
|
||||
const show = defineModel<boolean>({ default: false })
|
||||
const searchFilter = ref<string | null>(null)
|
||||
|
||||
@@ -222,8 +222,8 @@ watch(
|
||||
|
||||
watch(
|
||||
() => profile.language,
|
||||
(newVal, oldVal) => {
|
||||
if (newVal !== oldVal) {
|
||||
() => {
|
||||
if (profile.language !== props.profile.data.language) {
|
||||
hasLanguageChanged.value = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +66,18 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Dialog, createResource, Select, FormControl, toast } from 'frappe-ui'
|
||||
import {
|
||||
dayjs,
|
||||
Dialog,
|
||||
createResource,
|
||||
Select,
|
||||
FormControl,
|
||||
toast,
|
||||
} from 'frappe-ui'
|
||||
import { reactive, watch, inject } from 'vue'
|
||||
import { formatTime } from '@/utils/'
|
||||
|
||||
const user = inject('$user')
|
||||
const dayjs = inject('$dayjs')
|
||||
const show = defineModel()
|
||||
const evaluations = defineModel('reloadEvals')
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
class="text-base"
|
||||
:options="{
|
||||
title: __('Add web page to sidebar'),
|
||||
size: 'lg',
|
||||
@@ -17,15 +16,17 @@
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<Link
|
||||
v-model="page.webpage"
|
||||
doctype="Web Page"
|
||||
:label="__('Web Page')"
|
||||
:filters="{
|
||||
published: 1,
|
||||
}"
|
||||
/>
|
||||
<IconPicker v-model="page.icon" :label="__('Icon')" class="mt-4" />
|
||||
<div class="text-base">
|
||||
<Link
|
||||
v-model="page.webpage"
|
||||
doctype="Web Page"
|
||||
:label="__('Web Page')"
|
||||
:filters="{
|
||||
published: 1,
|
||||
}"
|
||||
/>
|
||||
<IconPicker v-model="page.icon" :label="__('Icon')" class="mt-4" />
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
@@ -117,32 +117,27 @@ const props = defineProps({
|
||||
watch(
|
||||
() => props.accountID,
|
||||
(val) => {
|
||||
if (val != 'new') {
|
||||
zoomAccounts.value?.data.forEach((acc) => {
|
||||
if (acc.name === val) {
|
||||
account.name = acc.name
|
||||
account.enabled = acc.enabled || false
|
||||
account.member = acc.member
|
||||
account.account_id = acc.account_id
|
||||
account.client_id = acc.client_id
|
||||
account.client_secret = acc.client_secret
|
||||
}
|
||||
})
|
||||
if (val === 'new') {
|
||||
account.name = ''
|
||||
account.enabled = false
|
||||
account.member = user?.data?.name || ''
|
||||
account.account_id = ''
|
||||
account.client_id = ''
|
||||
account.client_secret = ''
|
||||
} else if (val && val !== 'new') {
|
||||
const acc = zoomAccounts.value?.data.find((acc) => acc.name === val)
|
||||
if (acc) {
|
||||
account.name = acc.name
|
||||
account.enabled = acc.enabled || false
|
||||
account.member = acc.member
|
||||
account.account_id = acc.account_id
|
||||
account.client_id = acc.client_id
|
||||
account.client_secret = acc.client_secret
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(show, (val) => {
|
||||
if (!val) {
|
||||
account.name = ''
|
||||
account.enabled = false
|
||||
account.member = user?.data?.name || ''
|
||||
account.account_id = ''
|
||||
account.client_id = ''
|
||||
account.client_secret = ''
|
||||
}
|
||||
})
|
||||
|
||||
const saveAccount = (close: () => void) => {
|
||||
if (props.accountID == 'new') {
|
||||
createAccount(close)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<span
|
||||
class="size-3 rounded-full"
|
||||
:style="{
|
||||
backgroundColor: theme.backgroundColor[color.toLowerCase()][400],
|
||||
backgroundColor: getColor(color.toLowerCase(), 400),
|
||||
}"
|
||||
></span>
|
||||
<span>
|
||||
@@ -55,9 +55,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, ref, watch } from 'vue'
|
||||
import { NotepadText, Trash2 } from 'lucide-vue-next'
|
||||
import { theme } from '@/utils/theme'
|
||||
import type { Note, Notes } from '@/components/Notes/types'
|
||||
import { blockQuotesClick, highlightText } from '@/utils'
|
||||
import { blockQuotesClick, getColor, highlightText } from '@/utils'
|
||||
|
||||
const user = inject<any>('$user')
|
||||
const show = defineModel()
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
:placeholder="__('Make notes for quick revision. Press / for menu.')"
|
||||
@change="(val: string) => updateNoteText(val)"
|
||||
:editable="true"
|
||||
:uploadArgs="{
|
||||
private: true,
|
||||
}"
|
||||
editorClass="prose prose-sm min-h-[200px] max-w-none"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<div
|
||||
v-if="activeTab && data.doc"
|
||||
:key="activeTab.label"
|
||||
class="flex flex-1 flex-col p-8 bg-surface-modal"
|
||||
class="flex flex-1 flex-col p-8 bg-surface-modal overflow-x-auto"
|
||||
>
|
||||
<component
|
||||
v-if="activeTab.template"
|
||||
@@ -71,7 +71,7 @@ import { Dialog, createDocumentResource } from 'frappe-ui'
|
||||
import { computed, markRaw, ref, watch } from 'vue'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
import SettingDetails from '@/components/Settings/SettingDetails.vue'
|
||||
import SidebarLink from '@/components/SidebarLink.vue'
|
||||
import SidebarLink from '@/components/Sidebar/SidebarLink.vue'
|
||||
import Members from '@/components/Settings/Members.vue'
|
||||
import Evaluators from '@/components/Settings/Evaluators.vue'
|
||||
import Categories from '@/components/Settings/Categories.vue'
|
||||
@@ -181,6 +181,53 @@ const tabsStructure = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Lists',
|
||||
hideLabel: false,
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
description:
|
||||
'Add new members or manage roles and permissions of existing members',
|
||||
icon: 'UserRoundPlus',
|
||||
template: markRaw(Members),
|
||||
},
|
||||
{
|
||||
label: 'Evaluators',
|
||||
description: '',
|
||||
icon: 'UserCheck',
|
||||
description:
|
||||
'Add new evaluators or check the slots existing evaluators',
|
||||
template: markRaw(Evaluators),
|
||||
},
|
||||
{
|
||||
label: 'Zoom Accounts',
|
||||
description:
|
||||
'Manage zoom accounts to conduct live classes from batches',
|
||||
icon: 'Video',
|
||||
template: markRaw(ZoomSettings),
|
||||
},
|
||||
{
|
||||
label: 'Badges',
|
||||
description:
|
||||
'Create badges and assign them to students to acknowledge their achievements',
|
||||
icon: 'Award',
|
||||
template: markRaw(Badges),
|
||||
},
|
||||
{
|
||||
label: 'Categories',
|
||||
description: 'Double click to edit the category',
|
||||
icon: 'Network',
|
||||
template: markRaw(Categories),
|
||||
},
|
||||
{
|
||||
label: 'Email Templates',
|
||||
description: 'Manage the email templates for your learning system',
|
||||
icon: 'MailPlus',
|
||||
template: markRaw(EmailTemplates),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Payment',
|
||||
hideLabel: false,
|
||||
@@ -242,53 +289,6 @@ const tabsStructure = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Lists',
|
||||
hideLabel: false,
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
description:
|
||||
'Add new members or manage roles and permissions of existing members',
|
||||
icon: 'UserRoundPlus',
|
||||
template: markRaw(Members),
|
||||
},
|
||||
{
|
||||
label: 'Evaluators',
|
||||
description: '',
|
||||
icon: 'UserCheck',
|
||||
description:
|
||||
'Add new evaluators or check the slots existing evaluators',
|
||||
template: markRaw(Evaluators),
|
||||
},
|
||||
{
|
||||
label: 'Zoom Accounts',
|
||||
description:
|
||||
'Manage zoom accounts to conduct live classes from batches',
|
||||
icon: 'Video',
|
||||
template: markRaw(ZoomSettings),
|
||||
},
|
||||
{
|
||||
label: 'Badges',
|
||||
description:
|
||||
'Create badges and assign them to students to acknowledge their achievements',
|
||||
icon: 'Award',
|
||||
template: markRaw(Badges),
|
||||
},
|
||||
{
|
||||
label: 'Categories',
|
||||
description: 'Double click to edit the category',
|
||||
icon: 'Network',
|
||||
template: markRaw(Categories),
|
||||
},
|
||||
{
|
||||
label: 'Email Templates',
|
||||
description: 'Manage the email templates for your learning system',
|
||||
icon: 'MailPlus',
|
||||
template: markRaw(EmailTemplates),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Customize',
|
||||
hideLabel: false,
|
||||
|
||||
@@ -187,11 +187,12 @@ import { usersStore } from '@/stores/user'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { useSidebar } from '@/stores/sidebar'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
import { Button, call, createResource, Tooltip } from 'frappe-ui'
|
||||
import { Button, call, createResource, Tooltip, toast } from 'frappe-ui'
|
||||
import PageModal from '@/components/Modals/PageModal.vue'
|
||||
import { capture } from '@/telemetry'
|
||||
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import InviteIcon from '@/components/Icons/InviteIcon.vue'
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
@@ -456,21 +457,13 @@ const openPageModal = (link) => {
|
||||
}
|
||||
|
||||
const deletePage = (link) => {
|
||||
createResource({
|
||||
url: 'lms.lms.api.delete_sidebar_item',
|
||||
makeParams(values) {
|
||||
return {
|
||||
webpage: link.web_page,
|
||||
}
|
||||
},
|
||||
}).submit(
|
||||
{},
|
||||
{
|
||||
onSuccess() {
|
||||
sidebarSettings.reload()
|
||||
},
|
||||
}
|
||||
)
|
||||
call('lms.lms.api.delete_documents', {
|
||||
doctype: 'LMS Sidebar Item',
|
||||
documents: [link.name],
|
||||
}).then(() => {
|
||||
sidebarSettings.reload()
|
||||
toast.success(__('Page deleted successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const toggleSidebar = () => {
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<Popover placement="right-start" class="flex w-full">
|
||||
<Popover placement="right-start" trigger="hover" class="flex w-full">
|
||||
<template #target="{ togglePopover }">
|
||||
<button
|
||||
:class="[
|
||||
'group w-full flex h-7 items-center justify-between rounded px-2 text-base text-ink-gray-7 hover:bg-surface-gray-2',
|
||||
]"
|
||||
@click.prevent="togglePopover()"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<LayoutGrid class="size-4 stroke-1.5" />
|
||||
26
frontend/src/components/Sidebar/Configuration.vue
Normal file
26
frontend/src/components/Sidebar/Configuration.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-3 justify-between bg-surface-white">
|
||||
<div key="name" class="py-1 px-2 hover:bg-surface-gray-2 rounded">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'DataImportList',
|
||||
query: {
|
||||
step: 'list',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<div class="flex flex-col items-center space-y-1">
|
||||
<ArrowDownToLine
|
||||
class="size-9 text-ink-gray-7 p-2 bg-surface-gray-2 rounded-md"
|
||||
/>
|
||||
<div class="text-sm text-ink-gray-7">
|
||||
{{ __('Import') }}
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowDownToLine } from 'lucide-vue-next'
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<Dropdown :options="userDropdownOptions">
|
||||
<template v-slot="{ open }">
|
||||
<template v-slot="{ open, close }">
|
||||
<button
|
||||
class="flex h-12 py-2 items-center rounded-md duration-300 ease-in-out"
|
||||
:class="
|
||||
@@ -64,18 +64,19 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { Dropdown } from 'frappe-ui'
|
||||
import Apps from '@/components/Apps.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { convertToTitleCase } from '@/utils'
|
||||
import { usersStore } from '@/stores/user'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
import { markRaw, watch, ref, onMounted, computed } from 'vue'
|
||||
import { createDialog } from '@/utils/dialogs'
|
||||
import SettingsModal from '@/components/Settings/Settings.vue'
|
||||
import Apps from '@/components/Sidebar/Apps.vue'
|
||||
import Configuration from '@/components/Sidebar/Configuration.vue'
|
||||
import FrappeCloudIcon from '@/components/Icons/FrappeCloudIcon.vue'
|
||||
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
||||
import SettingsModal from '@/components/Settings/Settings.vue'
|
||||
import {
|
||||
ChevronDown,
|
||||
LogIn,
|
||||
@@ -84,6 +85,7 @@ import {
|
||||
User,
|
||||
Settings,
|
||||
Sun,
|
||||
Wrench,
|
||||
Zap,
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
@@ -168,6 +170,18 @@ const userDropdownOptions = computed(() => {
|
||||
return userResource.data?.is_moderator
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Configuration',
|
||||
icon: Wrench,
|
||||
submenu: [
|
||||
{
|
||||
component: markRaw(Configuration),
|
||||
},
|
||||
],
|
||||
condition: () => {
|
||||
return userResource.data?.is_moderator
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: FrappeCloudIcon,
|
||||
label: 'Login to Frappe Cloud',
|
||||
@@ -17,7 +17,7 @@
|
||||
import { createResource } from 'frappe-ui'
|
||||
import { computed, inject, onMounted, ref, watch } from 'vue'
|
||||
import ApexChart from 'vue3-apexcharts'
|
||||
import { theme } from '@/utils/theme'
|
||||
import { getColor } from '@/utils'
|
||||
|
||||
const user = inject('$user')
|
||||
const labels = ref([])
|
||||
@@ -81,11 +81,11 @@ const chartOptions = computed(() => {
|
||||
enableShades: true,
|
||||
colorScale: {
|
||||
ranges: [
|
||||
{ from: 0, to: 0, color: theme.colors.gray[400] },
|
||||
{ from: 1, to: 5, color: theme.colors.green[200] },
|
||||
{ from: 6, to: 15, color: theme.colors.green[500] },
|
||||
{ from: 16, to: 30, color: theme.colors.green[700] },
|
||||
{ from: 31, to: 100, color: theme.colors.green[800] },
|
||||
{ from: 0, to: 0, color: getColor('green', 400) },
|
||||
{ from: 1, to: 5, color: getColor('green', 200) },
|
||||
{ from: 6, to: 15, color: getColor('green', 500) },
|
||||
{ from: 16, to: 30, color: getColor('green', 700) },
|
||||
{ from: 31, to: 100, color: getColor('green', 800) },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user