Skip to content

Commit 5dba4d1

Browse files
authored
Merge pull request #1537 from frappe/develop
chore: merge 'develop' into 'main'
2 parents de240e4 + e4f1e7b commit 5dba4d1

57 files changed

Lines changed: 8128 additions & 8222 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cypress/e2e/course_creation.cy.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
describe("Course Creation", () => {
22
it("creates a new course", () => {
33
cy.login();
4-
cy.wait(1000);
4+
cy.wait(500);
55
cy.visit("/lms/courses");
66

7+
// Close onboarding modal
8+
cy.closeOnboardingModal();
9+
710
// Create a course
811
cy.get("button").contains("New").click();
9-
cy.wait(1000);
12+
cy.wait(500);
1013
cy.url().should("include", "/courses/new/edit");
1114

1215
cy.get("label").contains("Title").type("Test Course");
@@ -96,7 +99,8 @@ describe("Course Creation", () => {
9699
// View Course
97100
cy.wait(1000);
98101
cy.visit("/lms");
99-
cy.wait(500);
102+
cy.closeOnboardingModal();
103+
100104
cy.url().should("include", "/lms/courses");
101105
cy.get(".grid a:first").within(() => {
102106
cy.get("div").contains("Test Course");

cypress/support/commands.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
2626

2727
import "cypress-file-upload";
28+
import "cypress-real-events";
2829

2930
Cypress.Commands.add("login", (email, password) => {
3031
if (!email) {
@@ -68,3 +69,11 @@ Cypress.Commands.add("paste", { prevSubject: true }, (subject, text) => {
6869
element.dispatchEvent(event);
6970
});
7071
});
72+
73+
Cypress.Commands.add("closeOnboardingModal", () => {
74+
cy.wait(500);
75+
cy.get('[class*="z-50"]')
76+
.find('button:has(svg[class*="feather-x"])')
77+
.realClick();
78+
cy.wait(1000);
79+
});

frontend/components.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,14 @@ declare module 'vue' {
4747
Discussions: typeof import('./src/components/Discussions.vue')['default']
4848
EditCoverImage: typeof import('./src/components/Modals/EditCoverImage.vue')['default']
4949
EditProfile: typeof import('./src/components/Modals/EditProfile.vue')['default']
50+
EmailTemplateModal: typeof import('./src/components/Modals/EmailTemplateModal.vue')['default']
51+
EmailTemplates: typeof import('./src/components/EmailTemplates.vue')['default']
5052
EmptyState: typeof import('./src/components/EmptyState.vue')['default']
5153
EvaluationModal: typeof import('./src/components/Modals/EvaluationModal.vue')['default']
5254
Evaluators: typeof import('./src/components/Evaluators.vue')['default']
5355
Event: typeof import('./src/components/Modals/Event.vue')['default']
5456
ExplanationVideos: typeof import('./src/components/Modals/ExplanationVideos.vue')['default']
57+
FeedbackModal: typeof import('./src/components/Modals/FeedbackModal.vue')['default']
5558
FrappeCloudIcon: typeof import('./src/components/Icons/FrappeCloudIcon.vue')['default']
5659
IconPicker: typeof import('./src/components/Controls/IconPicker.vue')['default']
5760
IndicatorIcon: typeof import('./src/components/Icons/IndicatorIcon.vue')['default']

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"codemirror-editor-vue3": "^2.8.0",
2828
"dayjs": "^1.11.6",
2929
"feather-icons": "^4.28.0",
30-
"frappe-ui": "^0.1.143",
30+
"frappe-ui": "^0.1.147",
3131
"highlight.js": "^11.11.1",
3232
"lucide-vue-next": "^0.383.0",
3333
"markdown-it": "^14.0.0",

frontend/src/App.vue

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,14 @@
99
<script setup>
1010
import { FrappeUIProvider } from 'frappe-ui'
1111
import { Dialogs } from '@/utils/dialogs'
12-
import { computed, onMounted, onUnmounted, ref } from 'vue'
12+
import { computed, onUnmounted, ref } from 'vue'
1313
import { useScreenSize } from './utils/composables'
1414
import DesktopLayout from './components/DesktopLayout.vue'
1515
import MobileLayout from './components/MobileLayout.vue'
1616
import NoSidebarLayout from './components/NoSidebarLayout.vue'
17-
import { stopSession } from '@/telemetry'
18-
import { init as initTelemetry } from '@/telemetry'
19-
import { usersStore } from '@/stores/user'
2017
import { useRouter } from 'vue-router'
2118
2219
const screenSize = useScreenSize()
23-
let { userResource } = usersStore()
2420
const router = useRouter()
2521
const noSidebar = ref(false)
2622
@@ -39,13 +35,9 @@ const Layout = computed(() => {
3935
}
4036
if (screenSize.width < 640) {
4137
return MobileLayout
42-
} else {
43-
return DesktopLayout
4438
}
45-
})
4639
47-
onMounted(async () => {
48-
if (userResource.data) await initTelemetry()
40+
return DesktopLayout
4941
})
5042
5143
onUnmounted(() => {

frontend/src/components/AppSidebar.vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@
181181
import UserDropdown from '@/components/UserDropdown.vue'
182182
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
183183
import SidebarLink from '@/components/SidebarLink.vue'
184-
import { useStorage } from '@vueuse/core'
185184
import { ref, onMounted, inject, watch, reactive, markRaw, h } from 'vue'
186185
import { getSidebarLinks } from '../utils'
187186
import { usersStore } from '@/stores/user'
@@ -244,6 +243,7 @@ const iconProps = {
244243
onMounted(() => {
245244
addNotifications()
246245
setSidebarLinks()
246+
setUpOnboarding()
247247
socket.on('publish_lms_notifications', (data) => {
248248
unreadNotifications.reload()
249249
})
@@ -388,10 +388,6 @@ const deletePage = (link) => {
388388
)
389389
}
390390
391-
const getSidebarFromStorage = () => {
392-
return useStorage('sidebar_is_collapsed', false)
393-
}
394-
395391
const toggleSidebar = () => {
396392
sidebarStore.isSidebarCollapsed = !sidebarStore.isSidebarCollapsed
397393
localStorage.setItem(
@@ -438,6 +434,7 @@ const steps = reactive([
438434
title: __('Add your first chapter'),
439435
icon: markRaw(h(FolderTree, iconProps)),
440436
completed: false,
437+
dependsOn: 'create_first_course',
441438
onClick: async () => {
442439
minimize.value = true
443440
let course = await getFirstCourse()
@@ -453,6 +450,7 @@ const steps = reactive([
453450
title: __('Add your first lesson'),
454451
icon: markRaw(h(FileText, iconProps)),
455452
completed: false,
453+
dependsOn: 'create_first_chapter',
456454
onClick: async () => {
457455
minimize.value = true
458456
let course = await getFirstCourse()
@@ -471,6 +469,7 @@ const steps = reactive([
471469
title: __('Create your first quiz'),
472470
icon: markRaw(h(CircleHelp, iconProps)),
473471
completed: false,
472+
dependsOn: 'create_first_course',
474473
onClick: () => {
475474
minimize.value = true
476475
router.push({ name: 'Quizzes' })
@@ -502,6 +501,7 @@ const steps = reactive([
502501
title: __('Add students to your batch'),
503502
icon: markRaw(h(UserPlus, iconProps)),
504503
completed: false,
504+
dependsOn: 'create_first_batch',
505505
onClick: async () => {
506506
minimize.value = true
507507
let batch = await getFirstBatch()
@@ -522,6 +522,7 @@ const steps = reactive([
522522
title: __('Add courses to your batch'),
523523
icon: markRaw(h(BookText, iconProps)),
524524
completed: false,
525+
dependsOn: 'create_first_batch',
525526
onClick: async () => {
526527
minimize.value = true
527528
let batch = await getFirstBatch()

frontend/src/components/BatchFeedback.vue

Lines changed: 50 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,49 @@
11
<template>
22
<div v-if="user.data?.is_student">
3-
<div
4-
v-if="feedbackList.data?.length"
5-
class="bg-surface-blue-2 text-blue-700 p-2 rounded-md mb-5"
6-
>
7-
{{ __('Thank you for providing your feedback!') }}
8-
</div>
9-
<div v-else class="flex justify-between items-center mb-5">
10-
<div class="text-lg font-semibold">
11-
{{ __('Help Us Improve') }}
3+
<div>
4+
<div class="leading-5 mb-4">
5+
<div v-if="readOnly">
6+
{{ __('Thank you for providing your feedback.') }}
7+
<span
8+
@click="showFeedbackForm = !showFeedbackForm"
9+
class="underline cursor-pointer"
10+
>{{ __('Click here') }}</span
11+
>
12+
{{ __('to view your feedback.') }}
13+
</div>
14+
<div v-else>
15+
{{ __('Help us improve by providing your feedback.') }}
16+
</div>
1217
</div>
13-
<Button @click="submitFeedback()">
14-
{{ __('Submit') }}
15-
</Button>
16-
</div>
17-
<div class="space-y-8">
18-
<div class="flex items-center justify-between">
19-
<Rating
20-
v-for="key in ratingKeys"
21-
v-model="feedback[key]"
22-
:label="__(convertToTitleCase(key))"
18+
<div class="space-y-4" :class="showFeedbackForm ? 'block' : 'hidden'">
19+
<div class="space-y-4">
20+
<Rating
21+
v-for="key in ratingKeys"
22+
v-model="feedback[key]"
23+
:label="__(convertToTitleCase(key))"
24+
:readonly="readOnly"
25+
/>
26+
</div>
27+
<FormControl
28+
v-model="feedback.feedback"
29+
type="textarea"
30+
:label="__('Feedback')"
31+
:rows="9"
2332
:readonly="readOnly"
2433
/>
34+
<Button v-if="!readOnly" @click="submitFeedback">
35+
{{ __('Submit Feedback') }}
36+
</Button>
2537
</div>
26-
<FormControl
27-
v-model="feedback.feedback"
28-
type="textarea"
29-
:label="__('Feedback')"
30-
:rows="7"
31-
:readonly="readOnly"
32-
/>
3338
</div>
3439
</div>
3540

3641
<div v-else-if="feedbackList.data?.length">
37-
<div class="text-lg font-semibold mb-5">
38-
{{ __('Average of Feedback Received') }}
42+
<div class="leading-5 text-sm mb-2 mt-5">
43+
{{ __('Average Feedback Received') }}
3944
</div>
4045

41-
<div class="flex items-center justify-between mb-10">
46+
<div class="space-y-4">
4247
<Rating
4348
v-for="key in ratingKeys"
4449
v-model="average[key]"
@@ -47,81 +52,32 @@
4752
/>
4853
</div>
4954

50-
<div class="text-lg font-semibold mb-5">
51-
{{ __('All Feedback') }}
52-
</div>
53-
<ListView
54-
:columns="feedbackColumns"
55-
:rows="feedbackList.data"
56-
row-key="name"
57-
:options="{
58-
showTooltip: false,
59-
rowHeight: 'h-16',
60-
selectable: false,
61-
}"
62-
>
63-
<ListHeader
64-
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
65-
></ListHeader>
66-
<ListRows>
67-
<ListRow
68-
:row="row"
69-
v-for="row in feedbackList.data"
70-
class="group cursor-pointer feedback-list"
71-
>
72-
<template #default="{ column, item }">
73-
<ListRowItem
74-
:item="row[column.key]"
75-
:align="column.align"
76-
class="text-sm"
77-
>
78-
<template #prefix>
79-
<div v-if="column.key == 'member_name'">
80-
<Avatar
81-
class="flex"
82-
:image="row['member_image']"
83-
:label="item"
84-
size="sm"
85-
/>
86-
</div>
87-
</template>
88-
<div v-if="ratingKeys.includes(column.key)">
89-
<Rating v-model="row[column.key]" :readonly="true" />
90-
</div>
91-
<div v-else class="leading-5">
92-
{{ row[column.key] }}
93-
</div>
94-
</ListRowItem>
95-
</template>
96-
</ListRow>
97-
</ListRows>
98-
</ListView>
55+
<Button variant="outline" class="mt-5" @click="showAllFeedback = true">
56+
{{ __('View all feedback') }}
57+
</Button>
9958
</div>
100-
<div v-else class="text-sm italic text-center text-ink-gray-7 mt-5">
59+
<div v-else class="text-ink-gray-7 mt-5 leading-5">
10160
{{ __('No feedback received yet.') }}
10261
</div>
62+
<FeedbackModal
63+
v-if="feedbackList.data?.length"
64+
v-model="showAllFeedback"
65+
:feedbackList="feedbackList.data"
66+
/>
10367
</template>
10468
<script setup>
105-
import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
69+
import { inject, onMounted, reactive, ref, watch } from 'vue'
10670
import { convertToTitleCase } from '@/utils'
107-
import {
108-
Avatar,
109-
Button,
110-
createListResource,
111-
FormControl,
112-
ListView,
113-
ListHeader,
114-
ListRows,
115-
ListRow,
116-
ListRowItem,
117-
Rating,
118-
} from 'frappe-ui'
71+
import { Button, createListResource, FormControl, Rating } from 'frappe-ui'
72+
import FeedbackModal from '@/components/Modals/FeedbackModal.vue'
11973
12074
const user = inject('$user')
12175
const ratingKeys = ['content', 'instructors', 'value']
12276
const readOnly = ref(false)
12377
const average = reactive({})
12478
const feedback = reactive({})
79+
const showFeedbackForm = ref(true)
80+
const showAllFeedback = ref(false)
12581
12682
const props = defineProps({
12783
batch: {
@@ -167,6 +123,7 @@ watch(
167123
if (feedbackList.data.length) {
168124
let data = feedbackList.data
169125
readOnly.value = true
126+
showFeedbackForm.value = false
170127
171128
ratingKeys.forEach((key) => {
172129
average[key] = 0
@@ -201,40 +158,11 @@ const submitFeedback = () => {
201158
{
202159
onSuccess: () => {
203160
feedbackList.reload()
161+
showFeedbackForm.value = false
204162
},
205163
}
206164
)
207165
}
208-
209-
const feedbackColumns = computed(() => {
210-
return [
211-
{
212-
label: 'Member',
213-
key: 'member_name',
214-
width: '10rem',
215-
},
216-
{
217-
label: 'Feedback',
218-
key: 'feedback',
219-
width: '15rem',
220-
},
221-
{
222-
label: 'Content',
223-
key: 'content',
224-
width: '9rem',
225-
},
226-
{
227-
label: 'Instructors',
228-
key: 'instructors',
229-
width: '9rem',
230-
},
231-
{
232-
label: 'Value',
233-
key: 'value',
234-
width: '9rem',
235-
},
236-
]
237-
})
238166
</script>
239167
<style>
240168
.feedback-list > button > div {

0 commit comments

Comments
 (0)