Compare commits

..

1278 Commits

Author SHA1 Message Date
Nicolai
8ac7eb5a29 refactor(sidebar): remove external URLs from hardcoded items
External web pages (chatgpt-schoolchild, ai-teachers, my-child)
removed from addEnlightCustomItems() — they should be managed
via the new "More +" sidebar page manager in v2.46.0.

Only Vue-router pages remain hardcoded:
- LeaderBoard (/leaderboard)
- MyPoints (/mypoints)
- Role-based profiles (StudentProfile, SchoolchildrenProfile,
  CourseCreatorProfile, ParentProfile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:51:01 +03:00
Nicolai
8bb7a61f56 fix(sidebar): restore custom Enlight sidebar items after v2.46.0 merge
Two bugs fixed:
1. updateSidebarLinks() was called AFTER custom add functions,
   resetting sidebarLinks.value and erasing all custom items
2. Custom items were pushed as flat objects but the template
   expects grouped structure { label, hideLabel, items: [] }

Refactored all custom functions into a single addEnlightCustomItems()
that builds a proper group and pushes it AFTER updateSidebarLinks().
Items: LeaderBoard, MyPoints, ChatGPT (role-based), MyChild, Profile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:43:34 +03:00
Nicolai
ba99a48c88 Merge v2.46.0 into develop — resolve all conflicts
- yarn.lock, components.d.ts, ru.po: accepted upstream v2.46.0
- AppSidebar.vue: kept custom sidebar links (MyPoints, LeaderBoard,
  ChatGPT, MyChild, Profile) + adopted async watch from v2.46.0
- MobileLayout.vue: merged onMounted with sidebarSettings.reload +
  custom addSideBar() for role-based mobile links
- EditProfile.vue: adopted new Dialog options structure (size only)
- Courses/Courses.vue: unified tab values to lowercase (enrolled,
  upcoming, new, created, unpublished)
- CourseDetail.vue (old): removed — logic migrated to Courses/CourseDetail.vue;
  transferred custom tag flex-wrap styling to CourseOverview.vue
- LessonForm.vue: kept Rutube video support
- App.vue: clean (no conflict markers)
- user.py: merged imports + kept custom sign_up params (phone, user_role)
- utils.py: kept render_html (Rutube), is_mentor, is_eligible_to_review;
  added type hints for get_course_progress from v2.46.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:25:58 +03:00
37d408f762 Merge branch 'develop' of https://git.nefor.net/MIDNIGHT/enlight-lms into develop 2026-03-09 11:00:49 +00:00
6d7c91ceeb Before update v2.46.0 2026-03-09 10:54:44 +00:00
9041101505 Обновить README.md 2026-03-09 02:10:09 +03:00
Nicolai
d3fda0be37 StudentProfile new design 2026-03-08 23:31:54 +03:00
Nicolai
41de21201e LaderBoard design update 2026-03-08 12:48:34 +03:00
Frappe PR Bot
5933a59a14 chore(release): Bumped to Version 2.46.0 2026-03-04 05:08:05 +00:00
Jannat Patel
5c3834cbbe Merge pull request #2161 from frappe/main-hotfix
chore: merge 'main-hotfix' into 'main'
2026-03-04 10:37:22 +05:30
Jannat Patel
c77fdf55b3 Merge branch 'main' into main-hotfix 2026-03-04 10:22:29 +05:30
Frappe PR Bot
2c2e8ca112 chore(release): Bumped to Version 2.45.2 2026-03-03 07:19:36 +00:00
Jannat Patel
4771ebbcfd fix: enrollment error during course progress 2026-03-03 12:48:51 +05:30
Jannat Patel
08fbcc963d Merge pull request #2158 from frappe/mergify/bp/main-hotfix/pr-2157
fix: pricing section issue in course form (backport #2157)
2026-03-02 18:32:05 +05:30
Jannat Patel
54e9396fdb fix: pricing section issue in course form
(cherry picked from commit a0d6b2b6b626040572ddefb47f9ef09beec1a801)
2026-03-02 12:54:01 +00:00
Frappe PR Bot
315ec3d655 chore(release): Bumped to Version 2.45.1 2026-02-25 12:36:10 +00:00
Jannat Patel
484c3d7402 Merge pull request #2128 from frappe/mergify/bp/main/pr-2126
fix: permission issue during quiz submission (backport #2126)
2026-02-25 18:05:27 +05:30
Jannat Patel
e7ce850691 fix: removed trailing comma at the end of permission 2026-02-25 16:45:02 +05:30
Jannat Patel
62b5715b98 Merge pull request #2127 from frappe/mergify/bp/main-hotfix/pr-2126
fix: permission issue during quiz submission (backport #2126)
2026-02-25 13:08:14 +05:30
Jannat Patel
593c70affb chore: resolved conflicts 2026-02-25 13:07:05 +05:30
Jannat Patel
3a1a7db386 chore: resolved conflicts 2026-02-25 13:06:37 +05:30
Jannat Patel
a5e948bba8 fix: permission issue during quiz submission
(cherry picked from commit af5bce9e34df945cdc368eceb2a1c3e56023fb34)

# Conflicts:
#	lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
2026-02-25 07:21:47 +00:00
Jannat Patel
e63d83beb5 fix: permission issue during quiz submission
(cherry picked from commit af5bce9e34df945cdc368eceb2a1c3e56023fb34)
2026-02-25 07:21:40 +00:00
Jannat Patel
1ea8705552 Merge pull request #2125 from frappe/develop
chore: merge `develop` into `main-hotfix`
2026-02-25 11:24:11 +05:30
Jannat Patel
61193b71f4 Merge branch 'main-hotfix' into develop 2026-02-25 11:09:48 +05:30
Jannat Patel
2331ddfc67 Merge pull request #2123 from frappe/main-hotfix
chore: merge 'main-hotfix' into 'main'
2026-02-25 10:55:53 +05:30
Jannat Patel
afe9674a6a Merge pull request #2124 from frappe/mergify/bp/main-hotfix/pr-2121
chore: sync translations from crowdin (backport #2121)
2026-02-25 10:47:30 +05:30
Jannat Patel
5b22ef46c0 chore: resolved conflicts 2026-02-25 10:40:06 +05:30
MochaMind
8f1604e237 chore: Persian translations
(cherry picked from commit 63321fe2c8)
2026-02-25 05:08:21 +00:00
MochaMind
a9f4eb1291 chore: Spanish translations
(cherry picked from commit 68848fc642)

# Conflicts:
#	lms/locale/es.po
2026-02-25 05:08:20 +00:00
Jannat Patel
26301c26e9 Merge pull request #2121 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-25 10:37:32 +05:30
MochaMind
63321fe2c8 chore: Persian translations 2026-02-24 22:48:48 +05:30
MochaMind
68848fc642 chore: Spanish translations 2026-02-24 22:48:29 +05:30
Jannat Patel
aa7ec019bc Merge pull request #2120 from pateljannat/issues-192
fix: misc issues
2026-02-24 18:22:57 +05:30
Jannat Patel
eb33155db2 fix: enqueue progress calculation after validating enrollments 2026-02-24 18:13:02 +05:30
Jannat Patel
3088b14d83 fix: recalculate course progress when lesson is inserted or deleted 2026-02-24 17:40:58 +05:30
Jannat Patel
bf89f3ba2f fix: show system timezone in certificate request 2026-02-24 13:01:29 +05:30
Jannat Patel
2198adf902 fix: sidebar settings issue if guest access was not allowed 2026-02-24 12:36:46 +05:30
Jannat Patel
c5145c6c24 Merge pull request #2119 from pateljannat/issues-191
fix: misc issues
2026-02-24 12:17:41 +05:30
Jannat Patel
499bcd5281 chore: resolved conflicts 2026-02-24 12:05:44 +05:30
Jannat Patel
dc4bbdaa55 Merge pull request #2116 from raizasafeel/fix/codesandbox-embed
fix(lesson): render codesandbox
2026-02-24 12:02:52 +05:30
Jannat Patel
bf19ebd3a8 fix: assignment submission issue 2026-02-24 12:02:10 +05:30
Jannat Patel
fa7e59b4ad Merge pull request #2118 from frappe/mergify/bp/main-hotfix/pr-2117
chore: capture more events for analytics (backport #2117)
2026-02-23 16:59:02 +05:30
Jannat Patel
5fcd3ddabe revert: removed new batch modal changes 2026-02-23 16:45:37 +05:30
Jannat Patel
a9dd43d0ea chore: resolved conflicts 2026-02-23 16:44:04 +05:30
Jannat Patel
22e005f19c chore: capture more events for analytics
(cherry picked from commit b3c8fbd833)

# Conflicts:
#	frontend/src/pages/Batches/components/NewBatchModal.vue
#	lms/lms/doctype/lms_course_review/lms_course_review.json
2026-02-23 11:11:44 +00:00
Jannat Patel
5a6a7ff646 Merge pull request #2117 from pateljannat/issues-189
chore: capture more events for analytics
2026-02-23 16:41:23 +05:30
Jannat Patel
b3c8fbd833 chore: capture more events for analytics 2026-02-23 16:31:58 +05:30
Jannat Patel
f828c76a0f Merge pull request #2115 from pateljannat/issues-188
fix: misc issues
2026-02-23 15:13:02 +05:30
raizasafeel
2634a4e316 fix(lesson): render codesandbox properly 2026-02-23 15:12:08 +05:30
Jannat Patel
fb0517caa0 fix: show only instructor tab for admins on home page 2026-02-23 14:54:54 +05:30
Jannat Patel
90151be166 fix: updated app name in workspace and desktop 2026-02-23 12:35:13 +05:30
Jannat Patel
9b0a7f5fa5 Merge pull request #2112 from pateljannat/issues-187
fix: lesson progress issue
2026-02-23 12:03:30 +05:30
Jannat Patel
aa93375e6c Merge pull request #2110 from frappe/mergify/bp/main-hotfix/pr-2109
fix: check permission of session user during batch enrollment (backport #2109)
2026-02-23 11:49:25 +05:30
Jannat Patel
e8edf33be6 fix: lesson progress issue 2026-02-23 11:49:06 +05:30
Jannat Patel
619f02a74b fix: check permission of session user during batch enrollment
(cherry picked from commit c1260edb00)
2026-02-23 06:08:16 +00:00
Jannat Patel
b77c4867e1 Merge pull request #2109 from pateljannat/issues-186
fix: check permission of session user during batch enrollment
2026-02-23 11:36:49 +05:30
Jannat Patel
c1260edb00 fix: check permission of session user during batch enrollment 2026-02-23 11:28:58 +05:30
Jannat Patel
41d5ef5fd5 Merge pull request #2108 from pateljannat/issues-185
fix: misc permission issues
2026-02-23 11:22:52 +05:30
Jannat Patel
14937fd4fc fix: check ptype for permission if not admin 2026-02-23 11:06:34 +05:30
Jannat Patel
58826fe30f fix: removed badge page reference from the router 2026-02-23 10:41:13 +05:30
Jannat Patel
0da9eec0af Merge pull request #2105 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-23 10:29:32 +05:30
Jannat Patel
bb47fd5ba9 fix: misc permission issues 2026-02-23 10:29:13 +05:30
MochaMind
db49cb2d64 chore: Thai translations 2026-02-21 22:13:22 +05:30
MochaMind
58732148e2 chore: Vietnamese translations 2026-02-21 22:13:18 +05:30
MochaMind
08eb7ef17b chore: Dutch translations 2026-02-21 22:13:10 +05:30
MochaMind
8bda7edb7b chore: Spanish translations 2026-02-21 22:13:03 +05:30
Jannat Patel
49d596216d fix: zoom account link issue 2026-02-21 12:58:19 +05:30
Jannat Patel
faa9c94970 fix: zoom account link issue 2026-02-21 12:28:55 +05:30
Jannat Patel
c596d1e215 fix: misc permission issues 2026-02-21 12:25:47 +05:30
Jannat Patel
235958e432 Merge pull request #2103 from raizasafeel/fix/lesson-body-filter
fix(security): remove ignore_xss_filter to enable HTML sanitization
2026-02-20 14:20:07 +05:30
raizasafeel
7f85dbccec fix: added timestamps for bench migrate 2026-02-20 13:52:13 +05:30
Jannat Patel
5b69ddf9b5 Merge pull request #2099 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-20 13:39:16 +05:30
Jannat Patel
dfb7152aa3 fix: video watch time permission and other issues 2026-02-20 13:08:27 +05:30
Raizaaa
2a311bfb6f Merge pull request #2104 from raizasafeel/fix/lms-enrollment
fix: duplicate enrollment validation on update
2026-02-20 12:21:45 +05:30
raizasafeel
0e90627144 fix: duplicate enrollment validation on update 2026-02-20 12:09:08 +05:30
Raizaaa
aac1692058 Merge branch 'frappe:develop' into fix/lesson-body-filter 2026-02-20 11:58:57 +05:30
raizasafeel
d58d362c72 fix(security): remove ignore_xss_filter to enable HTML sanitization 2026-02-20 11:54:17 +05:30
MochaMind
e7f2386d14 chore: Thai translations 2026-02-19 21:23:50 +05:30
MochaMind
79a50d2454 chore: Portuguese, Brazilian translations 2026-02-19 21:23:45 +05:30
MochaMind
936f82c477 chore: Spanish translations 2026-02-19 21:23:24 +05:30
Jannat Patel
133037698c fix: remove read permission on lms settings for lms student 2026-02-19 16:30:13 +05:30
Jannat Patel
07c58251a1 fix: lms certificate request will allow students to read only if they are owner 2026-02-19 16:27:35 +05:30
Jannat Patel
c88d36df1e fix: permission issues on badges 2026-02-19 15:59:03 +05:30
Jannat Patel
08373dc2ab fix: refactored job form and permissions 2026-02-19 15:58:44 +05:30
Jannat Patel
44ca59c64a fix: return profile details only if the profile is of an LMS user 2026-02-19 12:51:30 +05:30
Jannat Patel
c961923fa0 fix: verify enrollment and admin access before returing batch assessment data 2026-02-19 12:43:50 +05:30
Jannat Patel
72cee75474 fix: only allow lms roles to be modified by moderator 2026-02-19 12:39:55 +05:30
Jannat Patel
cb3af6fa63 fix: sanitised badge assignment api 2026-02-19 12:24:47 +05:30
Jannat Patel
0ff14a959d Merge pull request #2096 from pateljannat/issues-184
fix: course form issues
2026-02-19 11:58:09 +05:30
Jannat Patel
35adf49015 fix: course form overflow issue 2026-02-19 11:47:17 +05:30
Jannat Patel
e5f0d55ff0 fix: course form permission issue 2026-02-19 11:46:56 +05:30
Jannat Patel
ba395fe982 Merge pull request #2081 from pateljannat/batch-dashboard-update
Batch dashboard update
2026-02-18 15:45:01 +05:30
Jannat Patel
8ab6776fa9 fix: redirect from batch/details to batch 2026-02-18 15:36:55 +05:30
Jannat Patel
61d13aeb12 Merge pull request #2094 from frappe/develop
chore: merge `develop` into `main-hotfix`
2026-02-18 12:11:10 +05:30
Jannat Patel
24bfe69985 chore: resolved conflicts 2026-02-18 11:16:00 +05:30
Jannat Patel
7b2a4fe24a Merge pull request #2092 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-18 11:10:40 +05:30
Jannat Patel
6f86b822bf fix: mobile view for batch dashboard 2026-02-18 10:58:49 +05:30
MochaMind
c3b2907ebf chore: Portuguese, Brazilian translations 2026-02-17 21:06:06 +05:30
MochaMind
48c5b82c73 chore: Spanish translations 2026-02-17 21:06:04 +05:30
Jannat Patel
af273a9a1c refactor: batch student progress 2026-02-17 19:38:19 +05:30
Jannat Patel
3b80ccd8db Merge pull request #2083 from raizasafeel/fix/dark-mode
fix: dark mode ui
2026-02-17 17:41:40 +05:30
Jannat Patel
6484d551d1 Merge pull request #2091 from frappe/mergify/bp/main-hotfix/pr-2087
chore: project URLs (backport #2087)
2026-02-17 17:41:05 +05:30
Ankush Menat
719d7b5e88 chore: project URLs (#2087)
(cherry picked from commit 3cd9d89f0b)
2026-02-17 11:38:09 +00:00
Ankush Menat
3cd9d89f0b chore: project URLs (#2087) 2026-02-17 15:54:23 +05:30
raizasafeel
6e44da1993 test: update batch creation test to match Badge component 2026-02-17 15:13:13 +05:30
Jannat Patel
44b7a210ce chore: resolved conflicts 2026-02-17 15:04:31 +05:30
Jannat Patel
0e382f77ef Merge pull request #2048 from raizasafeel/fix/video-embedding
fix(lesson): vimeo player rendered for private and unsanitized content
2026-02-17 14:57:00 +05:30
raizasafeel
7a1a247113 Merge upstream/develop into fix/dark-mode 2026-02-17 14:28:46 +05:30
raizasafeel
2c6ab3c331 Merge remote-tracking branch 'upstream/develop' into fix/video-embedding 2026-02-17 14:21:22 +05:30
Jannat Patel
79dba165c5 Merge pull request #2084 from pateljannat/issues-183
fix: misc issues
2026-02-17 13:57:09 +05:30
Jannat Patel
d83f3464cd fix: permission issue when cancelling evaluation 2026-02-17 13:35:15 +05:30
Jannat Patel
e2ef8f732d test: fixed category input selection in course creation test 2026-02-17 12:09:29 +05:30
Jannat Patel
919904a7f1 refactor: autocomplete component 2026-02-17 12:05:40 +05:30
raizasafeel
8453226f29 fix: add vimeo emded URL to extract hash properly 2026-02-17 11:38:26 +05:30
Jannat Patel
03759ca3c3 fix: spacing issue on course overview page 2026-02-16 19:53:33 +05:30
Jannat Patel
c1608f8cc4 refactor: Link component 2026-02-16 19:48:58 +05:30
Jannat Patel
73b20653f0 chore: updated release process with main-hotfix 2026-02-16 18:33:45 +05:30
Jannat Patel
7e683f8b44 fix: permission checks for api 2026-02-16 18:20:02 +05:30
Jannat Patel
eba1815390 fix: permission issues when adding new members 2026-02-16 18:05:53 +05:30
raizasafeel
7564f0418b fix: dark mode text and divider colors in course dashboard 2026-02-16 17:03:35 +05:30
raizasafeel
7e9cca2782 refactor: replace deprecated input with formcontrol in announcement modal 2026-02-16 17:00:18 +05:30
raizasafeel
dbc7e7d6d4 fix: dark mode styling for controls and modals 2026-02-16 16:58:32 +05:30
raizasafeel
ae25cfae6e fix: text editor border color in dark mode across forms and modals 2026-02-16 16:58:32 +05:30
raizasafeel
970635430b fix(settings): dark mode divider and text colors 2026-02-16 16:58:32 +05:30
raizasafeel
fe869a5988 fix(batch): use frappe-ui badge for dark mode compatibility 2026-02-16 16:58:32 +05:30
raizasafeel
7ea8040790 fix(sidebar): configuration popover and dark mode fixes 2026-02-16 16:58:32 +05:30
Jannat Patel
9f6f717585 fix: discussions reply endpoint permission check 2026-02-16 12:18:41 +05:30
Jannat Patel
641d729bd1 refactor: student batch dashboard 2026-02-16 12:17:13 +05:30
Jannat Patel
ee73d8db86 Merge pull request #2077 from pateljannat/issues-182
refactor: MultiSelect field
2026-02-16 09:10:46 +05:30
Jannat Patel
c7b5f9a04d Merge pull request #2078 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-16 09:10:28 +05:30
MochaMind
fa4c3a8ad7 chore: Persian translations 2026-02-15 19:54:51 +05:30
MochaMind
71318bff04 chore: Portuguese translations 2026-02-15 19:54:42 +05:30
MochaMind
186ddc93c8 chore: Portuguese, Brazilian translations 2026-02-14 19:58:13 +05:30
Jannat Patel
2f1d9a8690 refactor: MultiSelect field 2026-02-13 13:04:02 +05:30
Jannat Patel
5fc7c52bfe Merge pull request #2073 from UmakanthKaspa/fix/categories-dark-mode-text
fix: add text color for category names in dark mode
2026-02-13 10:18:29 +05:30
Jannat Patel
d0da6e7401 Merge pull request #2076 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-13 10:17:53 +05:30
UmakanthKaspa
a437c197a5 fix: format code with pre-commit 2026-02-12 21:41:19 +05:30
Jannat Patel
944fd6d013 refactor: new batch quick entry modal 2026-02-12 19:52:46 +05:30
MochaMind
80a9f2abe2 chore: Spanish translations 2026-02-12 19:28:48 +05:30
Jannat Patel
c0298f0a70 Merge branch 'develop' of https://github.com/frappe/lms into batch-dashboard-update 2026-02-12 11:09:19 +05:30
Jannat Patel
c30b21e5ae Merge pull request #2075 from pateljannat/issues-181
fix: issues on home page
2026-02-12 11:07:12 +05:30
Jannat Patel
3e3afa63c2 fix: issues on home page 2026-02-12 10:44:01 +05:30
Jannat Patel
7ef8aad2c8 fix: dirty state of batch form 2026-02-12 10:33:21 +05:30
Jannat Patel
c00cb100a9 Merge pull request #2072 from UmakanthKaspa/fix/job-details-missing-type-hint
fix: add missing type annotation to get_job_details
2026-02-11 21:41:28 +05:30
Jannat Patel
f824ac3c28 Merge pull request #2070 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-11 21:40:28 +05:30
UmakanthKaspa
2dea096fa0 fix: use semantic bg class for MultiSelect "Create New" button 2026-02-11 21:39:30 +05:30
UmakanthKaspa
f1853a3c97 fix: add text color for category names in dark mode 2026-02-11 21:13:48 +05:30
UmakanthKaspa
4995f8e3fd fix: add missing type annotation to get_job_details 2026-02-11 20:38:08 +05:30
Jannat Patel
f59eecd34e fix: circular dependency issues 2026-02-11 19:24:50 +05:30
MochaMind
560ac8d5c4 chore: Burmese translations 2026-02-11 18:52:08 +05:30
Jannat Patel
eab929da47 refactor: batch form 2026-02-11 18:42:42 +05:30
Jannat Patel
d370ca796f Merge pull request #2067 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-10 19:53:48 +05:30
Jannat Patel
e9f0b12550 feat: batch page new look 2026-02-10 19:53:26 +05:30
MochaMind
a4035168be chore: Italian translations 2026-02-10 17:32:47 +05:30
Jannat Patel
70872857d1 fix: show console error if course creation fails 2026-02-10 10:55:59 +05:30
Jannat Patel
332334b556 Merge pull request #2066 from UmakanthKaspa/fix/course-creation-error-toast
fix: show error toast when course creation fails
2026-02-10 10:48:34 +05:30
UmakanthKaspa
1d91baa9c5 fix: show error toast when course creation fails 2026-02-09 13:41:12 +00:00
Jannat Patel
1e8040ef7b Merge pull request #2036 from Aradhya-Tripathi/setup-fix
fix(setup): Add frappe dependency and build utils
2026-02-09 17:21:05 +05:30
Jannat Patel
ad6e0a3b80 Merge pull request #2064 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-09 15:32:59 +05:30
Jannat Patel
8f6810923d fix: only published courses should be added to batch 2026-02-09 14:42:32 +05:30
Jannat Patel
990db83ab3 chore: Italian translations 2026-02-09 13:02:11 +05:30
Jannat Patel
01f08ba449 chore: fixed typing for non mandatory fields of certificate 2026-02-09 12:44:54 +05:30
Jannat Patel
7a3701cc10 Merge pull request #2063 from pateljannat/issues-180
chore: added type hints to all whitelisted functions
2026-02-06 17:29:57 +05:30
Jannat Patel
f021ddd84c chore: fixed type annotations for doc_event methods 2026-02-06 17:08:49 +05:30
Jannat Patel
0e3157c57e chore: fixed type check for batch flows 2026-02-06 16:53:25 +05:30
Raizaaa
22eb8b9f3f Merge branch 'frappe:develop' into fix/video-embedding 2026-02-06 16:51:52 +05:30
Jannat Patel
9609398643 chore: fixed type check for batch flows 2026-02-06 16:18:37 +05:30
Jannat Patel
cd0d4c413d chore: added type hints to all whitelisted functions 2026-02-06 16:01:49 +05:30
Jannat Patel
1bbdff9aaf Merge pull request #2060 from raizasafeel/translations-fix
fix(profile): translations in tab are now rendered
2026-02-06 15:00:20 +05:30
Jannat Patel
8754d0498c Merge pull request #2062 from pateljannat/issues-179
fix: improved the default print format
2026-02-06 14:59:08 +05:30
raizasafeel
395ac52740 fix(assessments): translation render in list heading 2026-02-06 14:46:02 +05:30
Jannat Patel
29cdbe5b8b Merge pull request #2061 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-06 14:38:40 +05:30
Raizaaa
0677c21dc7 Merge branch 'frappe:develop' into translations-fix 2026-02-06 14:37:22 +05:30
Jannat Patel
1a58e2669f fix: improved the default print format 2026-02-06 14:33:24 +05:30
raizasafeel
3fa27024f9 fix(breadcrumbs): translations are now rendered 2026-02-06 12:53:17 +05:30
Jannat Patel
04c4069c75 chore: Serbian (Latin) translations 2026-02-06 12:08:55 +05:30
Jannat Patel
dd77b01ff1 chore: Bosnian translations 2026-02-06 12:08:52 +05:30
Jannat Patel
085614bca6 chore: Croatian translations 2026-02-06 12:08:50 +05:30
Jannat Patel
ef2606c41a chore: Swedish translations 2026-02-06 12:08:41 +05:30
Jannat Patel
1d95361587 chore: Serbian (Cyrillic) translations 2026-02-06 12:08:40 +05:30
raizasafeel
6ead16edf0 fix(profile): translations in tab are now rendered 2026-02-06 10:10:29 +05:30
Jannat Patel
31d21bf689 Merge pull request #2056 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-02-05 13:07:15 +05:30
Jannat Patel
c5ee140551 Merge pull request #2054 from pateljannat/issues-178
feat: student progress in course dashboard
2026-02-05 12:40:26 +05:30
Jannat Patel
8e97b2f5bb chore: codecov config 2026-02-05 12:33:38 +05:30
Jannat Patel
19171a8019 chore: removed files that are no longer used 2026-02-05 12:24:56 +05:30
Jannat Patel
3f49cf0c9c chore: added type hints to course assessment progress functions 2026-02-05 12:24:36 +05:30
Jannat Patel
8f9cc536e2 chore: Esperanto translations 2026-02-05 12:13:55 +05:30
Jannat Patel
573bc74a41 chore: Serbian (Latin) translations 2026-02-05 12:13:53 +05:30
Jannat Patel
bb2552b30c chore: Norwegian Bokmal translations 2026-02-05 12:13:52 +05:30
Jannat Patel
20ac312f57 chore: Bosnian translations 2026-02-05 12:13:50 +05:30
Jannat Patel
f58842438b chore: Burmese translations 2026-02-05 12:13:49 +05:30
Jannat Patel
a34f99ed49 chore: Croatian translations 2026-02-05 12:13:48 +05:30
Jannat Patel
44b7243f75 chore: Thai translations 2026-02-05 12:13:46 +05:30
Jannat Patel
d2f7d80114 chore: Persian translations 2026-02-05 12:13:45 +05:30
Jannat Patel
192b246381 chore: Indonesian translations 2026-02-05 12:13:43 +05:30
Jannat Patel
17d9a3991e chore: Portuguese, Brazilian translations 2026-02-05 12:13:42 +05:30
Jannat Patel
3407a02046 chore: Vietnamese translations 2026-02-05 12:13:41 +05:30
Jannat Patel
be3546e79c chore: Chinese Simplified translations 2026-02-05 12:13:39 +05:30
Jannat Patel
556067de7a chore: Turkish translations 2026-02-05 12:13:38 +05:30
Jannat Patel
4ce08af516 chore: Swedish translations 2026-02-05 12:13:36 +05:30
Jannat Patel
8a4477a01f chore: Serbian (Cyrillic) translations 2026-02-05 12:13:35 +05:30
Jannat Patel
21d868a355 chore: Slovenian translations 2026-02-05 12:13:33 +05:30
Jannat Patel
68a2cc1003 chore: Russian translations 2026-02-05 12:13:32 +05:30
Jannat Patel
5a288836e0 chore: Portuguese translations 2026-02-05 12:13:30 +05:30
Jannat Patel
4eab5e2867 chore: Polish translations 2026-02-05 12:13:29 +05:30
Jannat Patel
6082093fb6 chore: Dutch translations 2026-02-05 12:13:27 +05:30
Jannat Patel
66c26c2a2c chore: Italian translations 2026-02-05 12:13:26 +05:30
Jannat Patel
f7eaf3faaa chore: Hungarian translations 2026-02-05 12:13:24 +05:30
Jannat Patel
82a43b4f24 chore: German translations 2026-02-05 12:13:23 +05:30
Jannat Patel
5bd33a1536 chore: Danish translations 2026-02-05 12:13:21 +05:30
Jannat Patel
a063c0735c chore: Czech translations 2026-02-05 12:13:20 +05:30
Jannat Patel
732db8290d chore: Arabic translations 2026-02-05 12:13:19 +05:30
Jannat Patel
2b1d57f2bc chore: Spanish translations 2026-02-05 12:13:17 +05:30
Jannat Patel
c8c051c1de chore: French translations 2026-02-05 12:13:16 +05:30
Jannat Patel
13139bc2de test: course assessment progress 2026-02-05 12:10:24 +05:30
Jannat Patel
9814abf55f fix: dark mode issues of course dashboard 2026-02-04 16:04:28 +05:30
Jannat Patel
249ecb8c4c Merge pull request #2053 from frappe/develop
chore: merge 'develop' into 'main'
2026-02-04 15:24:23 +05:30
Jannat Patel
582540e7f0 feat: student progress on course dashboard 2026-02-03 21:31:31 +05:30
raizasafeel
2f3fa7c295 fix: added regex anchors to embed urls 2026-02-03 16:22:50 +05:30
raizasafeel
3b49aac1b3 refactor: removed unused functions 2026-02-03 16:14:58 +05:30
raizasafeel
dc25b408e6 fix(vimeo): video player is rendered for private videos and unsanitized vimeo links 2026-02-03 14:51:17 +05:30
raizasafeel
c8d9b97ab7 refactor: reuse function 'escapehtml' from utils 2026-02-03 14:01:48 +05:30
Jannat Patel
754d3cf2ca fix: import permissions 2026-02-03 10:56:22 +05:30
Jannat Patel
e4268d0437 fix: allow attaching payment information for batch enrollment 2026-02-03 10:49:48 +05:30
Jannat Patel
8febe21aa8 fix: dont allow contact us email sending to guest users 2026-02-03 10:48:59 +05:30
Jannat Patel
5384b26610 Merge pull request #2042 from raizasafeel/fix/chapter-deletion
test(chapter): added reindexing test on chapter deletion
2026-02-02 19:20:55 +05:30
Jannat Patel
2a4650e5ed Merge pull request #2044 from raizasafeel/fix/quiz-order
fix(batches): order assessments by their index
2026-02-02 19:12:50 +05:30
Jannat Patel
737993c543 Merge pull request #2043 from raizasafeel/fix/multilanguage-filters
fix: filter tabs not working in non-english languages
2026-02-02 18:34:50 +05:30
raizasafeel
58b49e3608 fix(batches): order assessments by their index 2026-02-02 18:28:06 +05:30
Raizaaa
27553464d6 Merge branch 'frappe:develop' into fix/chapter-deletion 2026-02-02 16:28:06 +05:30
raizasafeel
be76268c70 fix(batches): use constant value for filter instead of translated label 2026-02-02 16:22:37 +05:30
raizasafeel
df2f2e6603 fix(courses): use constant value for filter instead of translated label 2026-02-02 16:21:31 +05:30
Jannat Patel
fb1e1ec2e4 Merge pull request #2040 from pateljannat/issues-177
fix: permissions cleanup
2026-02-02 15:55:22 +05:30
Jannat Patel
ac81d1817b fix: batch dashboard chart visibility 2026-02-02 15:43:36 +05:30
Jannat Patel
da33e1d3bd test: course and batch details 2026-02-02 15:11:16 +05:30
Jannat Patel
24a511f48e fix: do nor return details of unpublished courses and batches via api 2026-02-02 14:44:13 +05:30
Jannat Patel
14e669435f fix: permissions cleanup 2026-02-02 13:14:16 +05:30
Jannat Patel
0407f01016 Merge pull request #2038 from vishwajeet-13/fix/issue-date
fix: issue date not coming from backend
2026-02-02 11:06:53 +05:30
vishwajeet-13
2a63f781ac fix: issue date not coming from backend 2026-01-30 17:37:07 +05:30
Aradhya-Tripathi
3e7555264f fix(setup): Add frappe dependency and build utils 2026-01-30 15:12:23 +05:30
Jannat Patel
ab155ae609 Merge pull request #2017 from Anexus5919/fix-instructor-notes-settings-menu
fix: instructor notes block settings menu overlapped by sidebar
2026-01-30 13:02:07 +05:30
Jannat Patel
2c60521894 Merge pull request #2018 from Anexus5919/fix-lesson-form-stale-state
fix: reindex lessons after deletion to prevent stale form state
2026-01-30 13:01:32 +05:30
Jannat Patel
4fd3e2549a fix: batch access conditions 2026-01-30 13:00:49 +05:30
Jannat Patel
6d3f7ef3c1 chore: resolved conflicts 2026-01-30 12:53:57 +05:30
Jannat Patel
10a9a5230e Merge pull request #2001 from frappe/feat/config-fe-base-path
feat: configurable frontend base path
2026-01-30 12:40:43 +05:30
Jannat Patel
bfda88dfd2 chore: resolved conflicts 2026-01-30 12:30:53 +05:30
Jannat Patel
4be34848a4 fix: allow mark as read notification endpoints in auth 2026-01-30 12:26:29 +05:30
Jannat Patel
c51eae5665 fix: show course progress summary if average progress is greater than 0 2026-01-30 11:19:08 +05:30
Jannat Patel
6c06a86af4 fix: reset the value of multiple of only one correct answer in quiz question 2026-01-30 11:08:53 +05:30
Jannat Patel
8be53d9050 Merge pull request #2035 from pateljannat/issues-176
fix: api permissions
2026-01-29 21:54:32 +05:30
Jannat Patel
015f903d68 fix: api permissions 2026-01-29 21:42:20 +05:30
Jannat Patel
dc06ad6b22 fix: removed tags methods 2026-01-29 20:40:35 +05:30
Jannat Patel
5689fbb455 fix: api permissions 2026-01-29 20:32:02 +05:30
Jannat Patel
62631daafb chore: resolved conflicts 2026-01-29 19:55:13 +05:30
Jannat Patel
a9a322c9af Merge pull request #2015 from pateljannat/enroll-students-in-course-from-ui
feat: course admin dashboard
2026-01-29 19:53:27 +05:30
Jannat Patel
933bc58264 fix: api permissions 2026-01-29 19:52:56 +05:30
Jannat Patel
8896a79c09 Merge branch 'develop' into enroll-students-in-course-from-ui 2026-01-29 16:42:26 +05:30
Jannat Patel
3170c066dc Merge pull request #2012 from raizasafeel/fix/chapter-deletion
fix(chapter): recalculate indices on deletion to prevent duplicate index errors
2026-01-29 16:37:44 +05:30
Jannat Patel
7360780022 Merge pull request #1998 from raizasafeel/fix/course-deletion
fix(course): resolve deletion failure for enrolled courses
2026-01-29 16:35:59 +05:30
Jannat Patel
7fe398fc66 test: fixed course ui tests based on new flow 2026-01-29 16:17:34 +05:30
Jannat Patel
5970540a99 feat: lesson completion rate in course dashboard 2026-01-29 15:24:20 +05:30
raizasafeel
a882432702 test: added test for chapter deletion and renumbering 2026-01-29 13:32:37 +05:30
raizasafeel
f8b6dfc981 Merge branch 'develop' into fix/chapter-deletion 2026-01-29 13:27:29 +05:30
Jannat Patel
486e2b4a37 Merge branch 'develop' of https://github.com/frappe/lms into enroll-students-in-course-from-ui 2026-01-29 12:35:58 +05:30
Jannat Patel
d8891b8a7d fix: brand settings 2026-01-29 12:35:44 +05:30
Jannat Patel
98c5318b66 feat: new course form slow 2026-01-29 10:50:28 +05:30
Jannat Patel
a353635bb9 Merge pull request #2028 from raizasafeel/fix/restrict-batch-student-details
fix(batch): restrict student list in batch details API to authorised roles
2026-01-28 21:20:33 +05:30
Jannat Patel
9eeb948f04 Merge pull request #2029 from raizasafeel/fix/quiz-questions-empty-on-back-navigation
fix(quiz): questions list empty on back navigation from test quiz
2026-01-28 21:19:20 +05:30
raizasafeel
383850aeb8 fix(quiz): questions list empty on back navigation from test quiz 2026-01-28 16:30:36 +05:30
raizasafeel
fb0499acc4 fix(Batch): restrict student list in batch details API 2026-01-28 14:41:10 +05:30
Alexandrina-Kuzeleva
005f85c34f UPD 7
- add links in Mobile
- switch off InstalPromt
2026-01-28 11:06:55 +03:00
Jannat Patel
ff978f5c29 Merge pull request #2022 from raizasafeel/fix/multiselect-last-option-visibility
fix: prevent 'Create New' button from overlapping last dropdown element
2026-01-28 11:33:36 +05:30
Jannat Patel
e8768d5687 Merge pull request #2026 from frappe/develop
chore: merge 'develop' into 'main'
2026-01-28 11:32:28 +05:30
Jannat Patel
fa1b583968 Merge branch 'main' into develop 2026-01-28 11:15:28 +05:30
Jannat Patel
b50d584a5b feat: new course modal 2026-01-28 10:57:58 +05:30
Jannat Patel
5271709094 Merge pull request #2016 from frappe/pot_develop_2026-01-23
chore: update POT file
2026-01-27 14:37:20 +05:30
raizasafeel
b66b603af2 fix(MultiSelect): prevent 'Create New' button from overlapping dropdown option 2026-01-27 14:12:43 +05:30
Adarsh Singh
53c77f9070 fix: reindex lessons after deletion to prevent stale form state
When a lesson was deleted, the remaining lessons' idx values were not resequenced. This caused Add Lesson to navigate to an existing lesson instead of creating a new one, showing stale data in the form.
2026-01-24 17:04:38 +05:30
Jannat Patel
49ed082831 feat: allow moderators to create new payment from settings 2026-01-24 11:31:37 +05:30
Adarsh Singh
05d21cf817 fix: instructor notes block settings menu overlapped by sidebar
PR #1987 fixed the "+" (Add block) menu positioning but missed the
block settings menu (6-dot "Click to tune" icon) which uses a different
parent container (.ce-toolbar__actions instead of .ce-toolbox).

This adds the missing selector to fix both menus.

Related: #1990
2026-01-24 00:25:21 +05:30
frappe-pr-bot
39473f0037 chore: update POT file 2026-01-23 16:05:21 +00:00
Jannat Patel
0e8b232ef1 feat: course admin dashboard 2026-01-23 18:26:09 +05:30
raizasafeel
aeb2724e82 test: use BaseTestUtils in test_auth for consistent test setup 2026-01-23 12:26:03 +05:30
Jannat Patel
b429fb2e47 Merge pull request #1997 from raizasafeel/fix/markdown-paste-render
fix(lesson): ensure markdown text is displayed when pasted
2026-01-23 10:25:25 +05:30
Jannat Patel
14a1c2ac07 Merge pull request #2014 from raizasafeel/fix/lms-import-get-frappe-version
refactor(lms): update get_frappe_version import
2026-01-22 18:25:30 +05:30
raizasafeel
36b0594960 refactor(lms): update get_frappe_version import 2026-01-22 17:42:06 +05:30
raizasafeel
cc3cb7ac8d Merge remote-tracking branch 'upstream/develop' into fix/course-deletion 2026-01-22 16:33:07 +05:30
raizasafeel
8bf896f766 refactor(tests): use BaseTestUtils inheritance for tests 2026-01-22 16:05:48 +05:30
raizasafeel
9e61982dcb refactor(tests): extract shared test utilities into BaseTestUtils 2026-01-22 16:03:01 +05:30
Jannat Patel
59e40d5a83 Merge branch 'develop' into fix/chapter-deletion 2026-01-22 14:00:12 +05:30
Jannat Patel
412bdeb085 Merge pull request #2007 from pateljannat/issues-174
fix: misc issues
2026-01-22 13:59:54 +05:30
Jannat Patel
4ef0bd30d9 test: fixed authentication tests 2026-01-22 13:16:12 +05:30
Jannat Patel
87e1fa8d6c Merge pull request #2013 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-22 12:16:01 +05:30
Jannat Patel
984ea27d59 chore: Serbian (Latin) translations 2026-01-22 01:46:42 +05:30
Jannat Patel
9d2c7a7ce0 chore: Norwegian Bokmal translations 2026-01-22 01:46:41 +05:30
Jannat Patel
3b2768cdcc chore: Bosnian translations 2026-01-22 01:46:39 +05:30
Jannat Patel
9ef5f01a80 chore: Croatian translations 2026-01-22 01:46:37 +05:30
Jannat Patel
fe492d1134 chore: Portuguese, Brazilian translations 2026-01-22 01:46:33 +05:30
Jannat Patel
3a15e132c2 chore: Vietnamese translations 2026-01-22 01:46:32 +05:30
Jannat Patel
2fb20e2db1 chore: Chinese Simplified translations 2026-01-22 01:46:31 +05:30
Jannat Patel
1fd9b4e626 chore: Turkish translations 2026-01-22 01:46:29 +05:30
Jannat Patel
11668235c2 chore: Swedish translations 2026-01-22 01:46:27 +05:30
Jannat Patel
d349af940a chore: Serbian (Cyrillic) translations 2026-01-22 01:46:26 +05:30
Jannat Patel
7eadb6b683 chore: Polish translations 2026-01-22 01:46:22 +05:30
Jannat Patel
904b6c3462 chore: German translations 2026-01-22 01:46:20 +05:30
Jannat Patel
733ab34007 chore: Arabic translations 2026-01-22 01:46:17 +05:30
Jannat Patel
6215817954 chore: Spanish translations 2026-01-22 01:46:15 +05:30
Jannat Patel
1760b01914 chore: French translations 2026-01-22 01:46:14 +05:30
Jannat Patel
d218a4773a chore: Italian translations 2026-01-22 01:46:12 +05:30
Jannat Patel
6fbe32cacd chore: Hungarian translations 2026-01-22 01:46:11 +05:30
Jannat Patel
c10dd9f5b6 chore: Persian translations 2026-01-22 01:46:09 +05:30
Jannat Patel
7eb1237aa0 chore: Russian translations 2026-01-22 01:46:07 +05:30
Jannat Patel
570e8eaf34 chore: Dutch translations 2026-01-22 01:46:06 +05:30
raizasafeel
297ffa5171 fix(chapter): index recalculation on deletion 2026-01-21 14:37:10 +05:30
Jannat Patel
355c752ec1 Merge pull request #2008 from pateljannat/issues-175
fix: misc issues
2026-01-20 22:08:56 +05:30
Jannat Patel
f1031c49a9 fix: allow reset password endpoint 2026-01-20 22:08:39 +05:30
Jannat Patel
043c7902a3 test: updated test for open to work 2026-01-20 22:07:42 +05:30
Jannat Patel
338f46ac17 chore: changed frappe dependency range 2026-01-20 16:57:30 +05:30
Jannat Patel
9987ea1db6 fix: enable pulse only for logged in users 2026-01-20 16:52:42 +05:30
Jannat Patel
d507479bda fix: filters on jobs page 2026-01-20 16:52:20 +05:30
Jannat Patel
afbdb46fe8 fix: open to work patch 2026-01-20 15:47:55 +05:30
Jannat Patel
1308101fe6 fix: parse allowed_endpoints to list if its string 2026-01-20 12:45:28 +05:30
Jannat Patel
c2d6e160d9 fix: renamed open to opportunities to open to work 2026-01-20 12:43:00 +05:30
Jannat Patel
28c88ec519 Merge pull request #2005 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-20 10:58:07 +05:30
Jannat Patel
f77eeafb9e chore: Esperanto translations 2026-01-20 01:43:56 +05:30
Jannat Patel
92b8343db3 chore: Serbian (Latin) translations 2026-01-20 01:43:54 +05:30
Jannat Patel
ac44cdc303 chore: Norwegian Bokmal translations 2026-01-20 01:43:53 +05:30
Jannat Patel
75dcea4c7e chore: Bosnian translations 2026-01-20 01:43:52 +05:30
Jannat Patel
ee03d75b5f chore: Burmese translations 2026-01-20 01:43:50 +05:30
Jannat Patel
31f372629a chore: Croatian translations 2026-01-20 01:43:49 +05:30
Jannat Patel
1b30f476e8 chore: Thai translations 2026-01-20 01:43:47 +05:30
Jannat Patel
e4738e9a50 chore: Tamil translations 2026-01-20 01:43:46 +05:30
Jannat Patel
893f9d34fd chore: Indonesian translations 2026-01-20 01:43:44 +05:30
Jannat Patel
2c17a03d36 chore: Portuguese, Brazilian translations 2026-01-20 01:43:43 +05:30
Jannat Patel
74cbe1bb47 chore: Vietnamese translations 2026-01-20 01:43:41 +05:30
Jannat Patel
88c468068b chore: Chinese Simplified translations 2026-01-20 01:43:40 +05:30
Jannat Patel
6b5e269564 chore: Turkish translations 2026-01-20 01:43:38 +05:30
Jannat Patel
97d9460157 chore: Swedish translations 2026-01-20 01:43:37 +05:30
Jannat Patel
0d614a5919 chore: Serbian (Cyrillic) translations 2026-01-20 01:43:35 +05:30
Jannat Patel
3f6afbf1ff chore: Slovenian translations 2026-01-20 01:43:34 +05:30
Jannat Patel
6d0996ebfd chore: Portuguese translations 2026-01-20 01:43:32 +05:30
Jannat Patel
14e085c826 chore: Polish translations 2026-01-20 01:43:31 +05:30
Jannat Patel
53bf2f1fe8 chore: German translations 2026-01-20 01:43:29 +05:30
Jannat Patel
b06e199432 chore: Danish translations 2026-01-20 01:43:28 +05:30
Jannat Patel
06e1ec38ad chore: Czech translations 2026-01-20 01:43:27 +05:30
Jannat Patel
3063236bfb chore: Arabic translations 2026-01-20 01:43:25 +05:30
Jannat Patel
e3a2eb382d chore: Spanish translations 2026-01-20 01:43:24 +05:30
Jannat Patel
566ef576f5 chore: French translations 2026-01-20 01:43:23 +05:30
Jannat Patel
80ad2da206 chore: Italian translations 2026-01-20 01:43:21 +05:30
Jannat Patel
5d720031b6 chore: Hungarian translations 2026-01-20 01:43:20 +05:30
Jannat Patel
ac719b9e8e chore: Persian translations 2026-01-20 01:43:19 +05:30
Jannat Patel
db9ebb1d86 chore: Russian translations 2026-01-20 01:43:17 +05:30
Jannat Patel
37f6d62101 chore: Dutch translations 2026-01-20 01:43:16 +05:30
Jannat Patel
f7a0fc8b5f build: removed unused inter files 2026-01-19 19:54:06 +05:30
Jannat Patel
0fbe015607 fix: center aligned the load more button on batch student list 2026-01-19 19:49:30 +05:30
Jannat Patel
61909c8498 fix: track cmi.completion_status for SCORM 2004 2026-01-19 19:49:05 +05:30
Jannat Patel
f297f2630b fix: only block endpoints if enabled in site config 2026-01-19 19:03:51 +05:30
Jannat Patel
9425f2aa0e fix: allow custom app endpoints 2026-01-19 18:56:39 +05:30
Jannat Patel
535bb60d69 Merge pull request #2004 from pateljannat/issues-173
fix: allow enabled server script endpoints
2026-01-19 18:33:51 +05:30
Jannat Patel
5175050ed6 fix: allow enabled server script endpoints 2026-01-19 18:33:14 +05:30
Jannat Patel
66486a5a1c fix: do no execute send_notification bg job from batch immediately 2026-01-19 18:17:40 +05:30
Jannat Patel
2d4bf49ab9 Merge pull request #2003 from pateljannat/issues-172
fix: allow social login endpoints
2026-01-19 17:41:10 +05:30
Jannat Patel
50a091a7b0 fix: allow social login endpoints 2026-01-19 17:32:09 +05:30
Jannat Patel
854ebee2e6 Merge branch 'develop' of https://github.com/frappe/lms into issues-172 2026-01-19 17:04:35 +05:30
Jannat Patel
c829ab65d9 fix: payment gateway form 2026-01-19 16:28:16 +05:30
Jannat Patel
1e0e10ca59 Merge branch 'develop' into fix/markdown-paste-render 2026-01-19 15:39:15 +05:30
Jannat Patel
a8cdc76278 Merge branch 'develop' into fix/course-deletion 2026-01-19 15:32:51 +05:30
Jannat Patel
c310460727 Merge pull request #2002 from pateljannat/issues-171
fix: misc issues
2026-01-19 15:31:31 +05:30
Jannat Patel
3e98d962aa test: access to endpoints 2026-01-19 15:06:44 +05:30
Jannat Patel
ad90b89d25 fix: restrict comments in notifications to 3 lines 2026-01-19 12:58:43 +05:30
Jannat Patel
92eac9634a test: fixed evaluator schedule test 2026-01-19 12:46:45 +05:30
Jannat Patel
96015246bd refactor: get certified from certification list will now redirect to certification courses 2026-01-19 12:39:54 +05:30
Jannat Patel
0883aedc1d fix: course and bath category filter issue 2026-01-19 12:38:12 +05:30
Jannat Patel
c97a5e813c fix: only system users can access high level APIs 2026-01-19 11:47:57 +05:30
raizasafeel
2abc243b88 test: refactor course tests and add deletion test 2026-01-19 11:20:33 +05:30
Jannat Patel
62b18e6ffa Merge pull request #2000 from frappe/pot_develop_2026-01-16
chore: update POT file
2026-01-19 10:35:43 +05:30
raizasafeel
36d813b90f feat: add markdown styling for pasted content 2026-01-19 09:53:41 +05:30
Hussain Nagaria
5ce8e8c4ff test: flaky evaluation schedule
* Earlier logic was flaky because the test calculated the last expected date using a 56‑day window, while the production code builds
  schedules for 60 days. Those extra 4 days sometimes include another Monday or Wednesday, so the schedule ends later than the test
  expects.
2026-01-17 23:22:38 +05:30
Hussain Nagaria
3b88892905 refactor: change global variable to function in hooks 2026-01-17 23:14:33 +05:30
Hussain Nagaria
fe1aa3dd40 feat: configurable frontend base path
Co-authored-by: Suraj Shetty <surajshetty3416@users.noreply.github.com>
2026-01-17 23:04:31 +05:30
frappe-pr-bot
ec5e197716 chore: update POT file 2026-01-16 16:04:50 +00:00
Jannat Patel
244b5e445c fix: open course and batch from evaluation event modal 2026-01-16 15:59:16 +05:30
raizasafeel
0f927071c0 fix(course): resolve deletion failure for enrolled courses 2026-01-16 11:37:13 +05:30
Jannat Patel
376de99ef7 Merge pull request #1996 from pateljannat/issues-170
fix: misc issues
2026-01-16 11:34:29 +05:30
Jannat Patel
7a649957dd refactor: list for programming exercises 2026-01-15 19:46:13 +05:30
Jannat Patel
0968c90717 fix: quiz shuffle and limit conditions 2026-01-15 15:09:59 +05:30
Jannat Patel
e22eca9888 Merge pull request #1992 from raizasafeel/improve_batch_performance
feat(batch): add student pagination and optimize dashboard queries
2026-01-15 14:54:48 +05:30
raizasafeel
5890885475 fix(lesson): ensure markdown text is displayed when pasted 2026-01-15 14:49:04 +05:30
Jannat Patel
4a012a99a4 fix: programming exercise test case deletion 2026-01-15 14:47:03 +05:30
raizasafeel
e2c0355821 refactor(batch): simplify dashboard with get_count and conditional rendering 2026-01-15 13:13:40 +05:30
Jannat Patel
bcf27b7150 Merge pull request #1991 from nextchamp-saqib/refactor-lms-telemetry
refactor: telemetry
2026-01-15 10:37:27 +05:30
Jannat Patel
078f18d99c chore: capture quiz creation and certificate creation for analytics 2026-01-15 10:18:09 +05:30
Jannat Patel
19258e263d chore: resolved conflicts 2026-01-15 09:41:48 +05:30
Jannat Patel
7a52a2bf46 Merge pull request #1995 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-15 09:30:49 +05:30
Jannat Patel
a03be5ab4d chore: Serbian (Latin) translations 2026-01-15 01:14:14 +05:30
Jannat Patel
de13c5ddfb chore: Serbian (Cyrillic) translations 2026-01-15 01:14:11 +05:30
Jannat Patel
02564b2e77 chore: Russian translations 2026-01-15 01:14:08 +05:30
Jannat Patel
201e0b96a3 Merge pull request #1993 from pateljannat/issues-169
fix: misc issues
2026-01-14 18:02:33 +05:30
Jannat Patel
e7ccf0a711 fix: sanitize image filename before saving for course and jobs 2026-01-14 17:54:23 +05:30
raizasafeel
c59be28a26 perf(batch): optimise dashboard with query builder 2026-01-14 15:54:54 +05:30
Saqib Ansari
a0ede1dd2a fix: run pre-commit 2026-01-14 14:55:28 +05:30
Saqib Ansari
0aeada4549 refactor: telemetry
* only works with frappe v15.96+
2026-01-14 14:48:05 +05:30
Jannat Patel
6d988eb2b4 chore: changed frappe dependency version 2026-01-14 14:01:21 +05:30
raizasafeel
b58d04c7dc feat(batch): add load more pagination for batch students 2026-01-14 13:56:07 +05:30
Jannat Patel
e2479cd787 Merge pull request #1978 from Omcodes23/develop
fix: Add missing /lms prefix to assignment submission notification links
2026-01-14 12:46:57 +05:30
Jannat Patel
ca30ab8a5e Merge pull request #1987 from pateljannat/issues-168
fix: misc issues
2026-01-14 12:46:39 +05:30
Jannat Patel
da87845a4c chore: changed frappe dependency version 2026-01-14 12:37:49 +05:30
Om vataliya
e7c2ec6965 Merge branch 'develop' into develop 2026-01-14 11:35:38 +05:30
Jannat Patel
a7bcc53e0a fix: dayjs condition for evaluation end date 2026-01-14 10:08:21 +05:30
Jannat Patel
6a5978fed6 fix: instructor notes menu position in lesson form 2026-01-14 09:45:44 +05:30
Jannat Patel
8ff339b7ed fix: misc issues 2026-01-14 09:24:24 +05:30
Jannat Patel
4ace8b2ec0 Merge pull request #1986 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-14 09:01:18 +05:30
Jannat Patel
40c917f255 chore: Esperanto translations 2026-01-14 01:14:55 +05:30
Jannat Patel
bf024af8aa chore: Serbian (Latin) translations 2026-01-14 01:14:53 +05:30
Jannat Patel
675caa380f chore: Norwegian Bokmal translations 2026-01-14 01:14:52 +05:30
Jannat Patel
ad092a71d5 chore: Bosnian translations 2026-01-14 01:14:51 +05:30
Jannat Patel
971fe8fe64 chore: Burmese translations 2026-01-14 01:14:49 +05:30
Jannat Patel
fa63a1d5e5 chore: Croatian translations 2026-01-14 01:14:47 +05:30
Jannat Patel
ece642796a chore: Thai translations 2026-01-14 01:14:46 +05:30
Jannat Patel
ce8f1a5b77 chore: Tamil translations 2026-01-14 01:14:44 +05:30
Jannat Patel
973630059f chore: Indonesian translations 2026-01-14 01:14:43 +05:30
Jannat Patel
0f7e9d2d95 chore: Portuguese, Brazilian translations 2026-01-14 01:14:41 +05:30
Jannat Patel
a7f494f4d8 chore: Vietnamese translations 2026-01-14 01:14:39 +05:30
Jannat Patel
eb37bd1106 chore: Chinese Simplified translations 2026-01-14 01:14:38 +05:30
Jannat Patel
02c1f4c19e chore: Turkish translations 2026-01-14 01:14:36 +05:30
Jannat Patel
00040061f6 chore: Swedish translations 2026-01-14 01:14:35 +05:30
Jannat Patel
cf6292c2c6 chore: Serbian (Cyrillic) translations 2026-01-14 01:14:33 +05:30
Jannat Patel
0cf9984b6b chore: Slovenian translations 2026-01-14 01:14:32 +05:30
Jannat Patel
e793afd063 chore: Portuguese translations 2026-01-14 01:14:30 +05:30
Jannat Patel
6ce655c5b6 chore: Polish translations 2026-01-14 01:14:29 +05:30
Jannat Patel
dd7eb9a9c8 chore: German translations 2026-01-14 01:14:27 +05:30
Jannat Patel
bd8baa6671 chore: Danish translations 2026-01-14 01:14:25 +05:30
Jannat Patel
d387c7b493 chore: Czech translations 2026-01-14 01:14:24 +05:30
Jannat Patel
c7a991e1b0 chore: Arabic translations 2026-01-14 01:14:22 +05:30
Jannat Patel
f7e8707220 chore: Spanish translations 2026-01-14 01:14:21 +05:30
Jannat Patel
27cbc265e2 chore: French translations 2026-01-14 01:14:19 +05:30
Jannat Patel
949ea333eb chore: Italian translations 2026-01-14 01:14:18 +05:30
Jannat Patel
d32a80ef85 chore: Hungarian translations 2026-01-14 01:14:16 +05:30
Jannat Patel
d0ee3fe2d0 chore: Persian translations 2026-01-14 01:14:14 +05:30
Jannat Patel
d4e3676032 chore: Russian translations 2026-01-14 01:14:13 +05:30
Jannat Patel
0265c9dc4c chore: Dutch translations 2026-01-14 01:14:10 +05:30
Jannat Patel
4c81eef80d Merge pull request #1971 from pateljannat/notifications-feed
Notifications feed
2026-01-13 20:38:10 +05:30
Jannat Patel
57c7a8d85c chore: fixed translatable string 2026-01-13 19:16:36 +05:30
Jannat Patel
775d5ecf3a chore: formatting of BrandSettings 2026-01-13 19:13:12 +05:30
Jannat Patel
60fcf0472e fix: mark notification as read when link is visited 2026-01-13 19:07:40 +05:30
Jannat Patel
a09599ec8a feat: notification sent checkbox to prevent resending the notification 2026-01-13 19:06:30 +05:30
Om vataliya
fb5d79fc77 Merge branch 'develop' into develop 2026-01-13 16:55:36 +05:30
Jannat Patel
8ad5288226 chore: resolved merge conflicts 2026-01-13 12:18:30 +05:30
Jannat Patel
65b18641b5 Merge branch 'develop' of https://github.com/frappe/lms into develop 2026-01-13 11:22:40 +05:30
Jannat Patel
78494b9963 chore: added frappe dependency range to pyproject 2026-01-13 11:22:23 +05:30
Jannat Patel
1d83402163 Merge pull request #1984 from frappe/pot_develop_2026-01-13
chore: update POT file
2026-01-13 11:14:47 +05:30
frappe-pr-bot
64bdd85b03 chore: update POT file 2026-01-13 05:34:56 +00:00
Jannat Patel
0f1b6f3eeb chore: upgraded node version for pot file runner 2026-01-13 11:02:16 +05:30
Jannat Patel
566711df22 Merge pull request #1983 from pateljannat/issues-167
fix: certification filters
2026-01-13 10:44:24 +05:30
Jannat Patel
c9c6aef466 fix: certified participants query 2026-01-13 10:36:17 +05:30
Jannat Patel
ad650c73c1 fix: changed order by query for certified participants listing 2026-01-13 10:27:08 +05:30
Jannat Patel
af5bc2b071 chore: upgraded node version for linter and release github actions 2026-01-13 10:26:13 +05:30
Jannat Patel
b5fcfb62de fix: certification filters 2026-01-13 10:18:06 +05:30
Om vataliya
7dccef6b10 fix: Add missing /lms prefix to assignment submission notification links
Fixes #1969

- Added /lms prefix to notification link in lms_assignment_submission.py
- Added /lms prefix to assessment URL in utils.py
- Ensures consistent routing with other notification links (courses, billing, etc.)
- Resolves 404 errors when users click 'View' on assignment submission notifications
2026-01-09 23:45:29 +05:30
Jannat Patel
e1d343528d Merge pull request #1972 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-09 15:47:05 +05:30
Jannat Patel
6eeb688f06 chore: Hungarian translations 2026-01-07 20:03:15 +05:30
Jannat Patel
86e3794d00 Merge pull request #1970 from frappe/develop
chore: merge 'develop' into 'main'
2026-01-07 14:29:49 +05:30
Jannat Patel
45e98b9ddc fix: notification on quiz update and mention 2026-01-07 14:28:56 +05:30
Jannat Patel
aad875f72c Merge pull request #1968 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-07 10:59:45 +05:30
Jannat Patel
1698bf0bca chore: Hungarian translations 2026-01-06 20:01:12 +05:30
Jannat Patel
ea94813d94 feat: system notification for new courses published 2026-01-06 12:36:15 +05:30
Jannat Patel
142b893f2c Merge pull request #1966 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-06 10:26:01 +05:30
Jannat Patel
f88dd84ed3 ci: upgraded python version for pot file generation 2026-01-05 19:55:32 +05:30
Jannat Patel
50660f6720 chore: Persian translations 2026-01-05 19:12:35 +05:30
Jannat Patel
958128060c chore: Dutch translations 2026-01-05 19:12:33 +05:30
Jannat Patel
b2b1d2bb00 feat: email notifications for published courses 2026-01-05 18:18:56 +05:30
Jannat Patel
a3069bd760 Merge pull request #1965 from frappe/l10n_develop2
chore: sync translations from crowdin
2026-01-05 12:52:02 +05:30
Jannat Patel
f39ee39452 chore: Russian translations 2026-01-04 18:25:34 +05:30
Jannat Patel
a43e90e2d0 Merge pull request #1963 from pateljannat/issues-166
fix: payment validation during course enrollment
2026-01-02 16:54:29 +05:30
Jannat Patel
2aa3765ed3 Merge pull request #1962 from rehanrehman389/multiselect
fix: show options on focus and filter selected values
2026-01-02 14:58:17 +05:30
Jannat Patel
45dae0b9d3 fix: payment validation during course enrollment 2026-01-02 14:47:28 +05:30
Jannat Patel
b1789cdcba fix: payment validation during course enrollment 2026-01-02 14:42:42 +05:30
Jannat Patel
e21f0e3a7f refactor(ui): settings 2026-01-02 14:41:17 +05:30
rehanrehman389
ea9975db2c fix: fix build failure 2026-01-02 12:33:09 +05:30
rehanrehman389
b1c8e01bf5 fix: show options on focus and filter selected values 2026-01-02 12:18:03 +05:30
Jannat Patel
1d19389fc0 Merge pull request #1961 from pateljannat/issues-165
fix: misc issues
2026-01-01 14:51:58 +05:30
Jannat Patel
dbd3e17b26 Merge pull request #1912 from royalpinto007/fix/quiz-enter
fix(quizzes): enable enter key to submit quiz creation
2026-01-01 14:28:42 +05:30
Jannat Patel
b17c7ca2db fix: statistics chart based on frappe version 2026-01-01 14:26:34 +05:30
Jannat Patel
e17af04c9a Merge branch 'develop' into fix/quiz-enter 2026-01-01 13:16:34 +05:30
Jannat Patel
638a9abf88 fix: test case table issue in programming exercise form 2026-01-01 13:09:35 +05:30
Jannat Patel
89f9cbd30d Merge pull request #1959 from pateljannat/full-text-search-improvements
feat: search by instructor name from command palette
2026-01-01 12:30:56 +05:30
Jannat Patel
204fb669c0 test: fixed evaluator schedule test 2026-01-01 12:23:31 +05:30
Jannat Patel
7a24a83d9e fix: improved search result sorting 2026-01-01 12:06:15 +05:30
Jannat Patel
edc6007fb6 feat: search by instructor name from command palette 2025-12-31 20:03:44 +05:30
Jannat Patel
27a36540d4 Merge pull request #1957 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-31 12:19:21 +05:30
Frappe PR Bot
6dd1274150 chore(release): Bumped to Version 2.44.0 2025-12-31 06:37:47 +00:00
Jannat Patel
6a80c2ab38 Merge pull request #1952 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-30 10:47:34 +05:30
Jannat Patel
8633444b91 Merge pull request #1953 from pateljannat/open-to-hiring
feat: open to hiring
2025-12-30 10:46:57 +05:30
Jannat Patel
cb014a9507 test: certified participants with opportunities and hiring filters 2025-12-30 10:40:26 +05:30
Jannat Patel
c241abb820 test: fixed evaluator schedule test 2025-12-29 19:37:02 +05:30
Jannat Patel
a497a2d838 fix: mobile layout 2025-12-29 19:14:22 +05:30
Jannat Patel
5e78848d38 chore: Persian translations 2025-12-29 16:39:59 +05:30
Jannat Patel
adf897cc08 Merge pull request #1947 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-29 16:03:14 +05:30
Jannat Patel
5053b4e45f test: certified participants data 2025-12-29 16:02:38 +05:30
Jannat Patel
3151854bfd feat: open to hiring 2025-12-29 13:24:04 +05:30
Jannat Patel
db5868f69a refactor(ui): no permission page 2025-12-29 13:21:13 +05:30
Jannat Patel
9b6f7635bc chore: Persian translations 2025-12-29 06:49:37 +05:30
Jannat Patel
55ff59095c chore: Persian translations 2025-12-28 06:29:21 +05:30
Jannat Patel
3dce1c0930 Merge pull request #1945 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-26 10:45:27 +05:30
Jannat Patel
fd33aa8b70 Merge pull request #1943 from rehanrehman389/python-3.14
build: support Python 3.14
2025-12-26 10:45:06 +05:30
Jannat Patel
05170da762 chore: Serbian (Latin) translations 2025-12-26 05:02:40 +05:30
Jannat Patel
64f0c693d2 chore: Serbian (Cyrillic) translations 2025-12-26 05:02:38 +05:30
Jannat Patel
aac1e2d01f chore: Portuguese, Brazilian translations 2025-12-25 04:55:13 +05:30
Jannat Patel
5472c4d387 chore: Chinese Simplified translations 2025-12-25 04:55:11 +05:30
Jannat Patel
39a31a0baf chore: Turkish translations 2025-12-25 04:55:10 +05:30
Jannat Patel
cffc740ed3 chore: Russian translations 2025-12-25 04:55:05 +05:30
Rehan Ansari
114f3aae6d build(ci): use python 3.14 and node 24 2025-12-24 19:22:23 +05:30
Rehan Ansari
fea7f8f9ae build(deps): bump 2025-12-24 19:21:54 +05:30
Jannat Patel
830f513a06 Merge pull request #1939 from pateljannat/consent-on-billing-page
feat: consent on billing page
2025-12-24 16:50:24 +05:30
Jannat Patel
e76ff45241 Merge pull request #1942 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-24 15:57:13 +05:30
Jannat Patel
66f19d06b0 chore: resolved conflicts 2025-12-24 15:54:55 +05:30
Jannat Patel
987b655976 fix: skip self enrollment check if paid batch 2025-12-24 11:18:54 +05:30
Jannat Patel
8619712d20 Merge pull request #1940 from rehanrehman389/dark-mode-fix
fix: dark mode visibility
2025-12-24 09:09:02 +05:30
Jannat Patel
bc6ca205d5 Merge pull request #1941 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-24 09:06:27 +05:30
Jannat Patel
48d17e88d9 chore: Esperanto translations 2025-12-24 04:55:52 +05:30
Jannat Patel
1e9f1b7661 chore: Serbian (Latin) translations 2025-12-24 04:55:50 +05:30
Jannat Patel
faa6fbf68e chore: Norwegian Bokmal translations 2025-12-24 04:55:49 +05:30
Jannat Patel
789b1016fb chore: Bosnian translations 2025-12-24 04:55:48 +05:30
Jannat Patel
8fb491ac18 chore: Burmese translations 2025-12-24 04:55:46 +05:30
Jannat Patel
3eea872137 chore: Croatian translations 2025-12-24 04:55:45 +05:30
Jannat Patel
e4d98019e7 chore: Thai translations 2025-12-24 04:55:44 +05:30
Jannat Patel
7847f681e9 chore: Tamil translations 2025-12-24 04:55:42 +05:30
Jannat Patel
26d7971d99 chore: Persian translations 2025-12-24 04:55:41 +05:30
Jannat Patel
e25ef7e07b chore: Indonesian translations 2025-12-24 04:55:40 +05:30
Jannat Patel
85fafe7d56 chore: Portuguese, Brazilian translations 2025-12-24 04:55:38 +05:30
Jannat Patel
fdced5c204 chore: Vietnamese translations 2025-12-24 04:55:37 +05:30
Jannat Patel
b21fe69123 chore: Chinese Simplified translations 2025-12-24 04:55:36 +05:30
Jannat Patel
4572d03470 chore: Turkish translations 2025-12-24 04:55:35 +05:30
Jannat Patel
d348fd9f99 chore: Swedish translations 2025-12-24 04:55:33 +05:30
Jannat Patel
4b6e91e81f chore: Serbian (Cyrillic) translations 2025-12-24 04:55:32 +05:30
Jannat Patel
9126e7cd27 chore: Slovenian translations 2025-12-24 04:55:31 +05:30
Jannat Patel
713ee5287f chore: Russian translations 2025-12-24 04:55:29 +05:30
Jannat Patel
79109f1265 chore: Portuguese translations 2025-12-24 04:55:28 +05:30
Jannat Patel
20f16849bf chore: Polish translations 2025-12-24 04:55:27 +05:30
Jannat Patel
99968f5961 chore: Dutch translations 2025-12-24 04:55:25 +05:30
Jannat Patel
0741fbf583 chore: Italian translations 2025-12-24 04:55:24 +05:30
Jannat Patel
784ed37de0 chore: Hungarian translations 2025-12-24 04:55:23 +05:30
Jannat Patel
1c29b4966c chore: German translations 2025-12-24 04:55:21 +05:30
Jannat Patel
a06ea92c8e chore: Danish translations 2025-12-24 04:55:20 +05:30
Jannat Patel
60a47889d2 chore: Czech translations 2025-12-24 04:55:18 +05:30
Jannat Patel
d758039b2c chore: Arabic translations 2025-12-24 04:55:17 +05:30
Jannat Patel
4b507f0706 chore: Spanish translations 2025-12-24 04:55:16 +05:30
Jannat Patel
5986838056 chore: French translations 2025-12-24 04:55:14 +05:30
Rehan Ansari
e02c99bd16 fix: dark theme styling for programs 2025-12-23 22:35:03 +05:30
Rehan Ansari
a3f580a9fb fix: search page dark theme styling 2025-12-23 21:54:07 +05:30
Jannat Patel
43fb0f92df fix: hide consent warning when consent has been provided 2025-12-23 15:05:28 +05:30
Jannat Patel
dd4fbfa8a2 feat: billing consent ui 2025-12-23 15:01:14 +05:30
Jannat Patel
cf2e57ec40 Merge branch 'develop' of https://github.com/frappe/lms into consent-on-billing-page 2025-12-23 13:09:53 +05:30
Jannat Patel
daf2d28f3a Merge pull request #1938 from pateljannat/enrollment-from-batch-main
fix: batch enrollment conditions
2025-12-23 13:05:31 +05:30
Jannat Patel
d5e48f9502 fix: batch enrollment conditions 2025-12-23 12:56:17 +05:30
Jannat Patel
3318a1c599 fix: certification list border style 2025-12-23 12:44:36 +05:30
Jannat Patel
3fa2320fe3 Merge pull request #1937 from pateljannat/enrollment-from-batch
fix: batch enrollment conditions
2025-12-23 12:13:11 +05:30
Jannat Patel
c0c8fb5bf3 fix: skip cancelled slots when checking if slot is already booked 2025-12-23 12:05:43 +05:30
Jannat Patel
acde5ad1d1 fix: batch enrollment conditions 2025-12-23 11:42:45 +05:30
Jannat Patel
0a9b18d04d Merge pull request #1928 from pateljannat/issues-164
fix: misc issues
2025-12-23 11:08:06 +05:30
Jannat Patel
3013372711 fix: don't fetch application count when user is not logged in 2025-12-23 10:58:12 +05:30
Jannat Patel
76979e292d Merge branch 'develop' of https://github.com/frappe/lms into issues-164 2025-12-23 10:40:03 +05:30
Jannat Patel
115d52776f Merge pull request #1936 from rehanrehman389/profile
fix: profile save after image removal
2025-12-23 10:39:36 +05:30
Jannat Patel
f1b392ac9b Merge pull request #1935 from rehanrehman389/guest-issue
fix: job details for guest user
2025-12-23 10:38:55 +05:30
Jannat Patel
00198464e9 Merge pull request #1934 from rehanrehman389/fix/jobs
fix: skip closed jobs API call for guest users
2025-12-23 10:37:49 +05:30
Jannat Patel
1b84f00673 Merge pull request #1927 from frappe/pot_develop_2025-12-19
chore: update POT file
2025-12-23 10:36:03 +05:30
Rehan Ansari
3d9850dc73 fix: profile save after image removal 2025-12-22 23:26:41 +05:30
Rehan Ansari
dfcf295493 revert: job details api 2025-12-22 23:00:53 +05:30
Rehan Ansari
b987bf7f20 revert: job details changes from PR #1890 2025-12-22 22:49:22 +05:30
Rehan Ansari
e1d160a898 fix: skip closed jobs API call for guest users 2025-12-22 21:54:11 +05:30
Jannat Patel
a3fb63cd08 fix: default livecode url 2025-12-22 11:45:52 +05:30
Jannat Patel
1faa697b6c fix: enrollment eligibility conditions 2025-12-22 11:45:36 +05:30
Jannat Patel
01094cd10a fix: enrollment eligibility conditions 2025-12-22 10:55:36 +05:30
Jannat Patel
4141022431 feat: billing consent 2025-12-22 10:50:45 +05:30
Jannat Patel
69d0efbfa7 Merge pull request #1926 from pateljannat/filter-assignment-by-course
feat: filter assignments by course in lesson
2025-12-19 22:54:29 +05:30
frappe-pr-bot
66cc7d392e chore: update POT file 2025-12-19 16:05:21 +00:00
Jannat Patel
8048cb47c5 feat: filter assignments by course in lesson 2025-12-19 17:42:33 +05:30
Jannat Patel
09c668f7ed fix: description on course details 2025-12-19 16:21:22 +05:30
Jannat Patel
984a63c46a fix: description on course details 2025-12-19 16:20:53 +05:30
Jannat Patel
80c978d265 Merge pull request #1924 from pateljannat/issues-163
fix: certification caching issue
2025-12-19 16:12:47 +05:30
Jannat Patel
cde6828c1f fix: certification caching issue 2025-12-19 15:58:46 +05:30
Jannat Patel
51c1b816a1 fix: increase toast duration when evaluation scheduling fails 2025-12-19 14:36:00 +05:30
Jannat Patel
356ce7478c Merge pull request #1923 from pateljannat/improved-evaluation-scheduling
feat: Improved evaluation scheduling
2025-12-19 12:47:49 +05:30
Jannat Patel
2210ef7af7 test: evaluation schedule unavailability 2025-12-19 12:39:39 +05:30
Jannat Patel
0884e1315b test: evaluation schedule 2025-12-19 12:14:10 +05:30
Jannat Patel
58b26b6e32 fix: increased schedule range to 60 days 2025-12-19 10:42:45 +05:30
Jannat Patel
2631681c1d fix: enrollment in restricted courses by admin 2025-12-18 18:01:08 +05:30
Jannat Patel
82d6284b06 Merge pull request #1922 from pateljannat/issues-162
fix: enrollment in restricted courses by admin
2025-12-18 17:56:44 +05:30
Jannat Patel
46d13d65c1 fix: enrollment in restricted courses by admin 2025-12-18 14:59:37 +05:30
Jannat Patel
9da6cff8a5 feat: see all upcoming slots when scheduling evaluation 2025-12-18 14:40:41 +05:30
Jannat Patel
dc724831c3 fix: read permission for certificates 2025-12-17 16:50:51 +05:30
Jannat Patel
776730447a fix: removed vue jsx 2025-12-17 15:30:16 +05:30
Jannat Patel
82c6fdf475 fix: removed vue jsx 2025-12-17 14:58:38 +05:30
Jannat Patel
ca2b175e1c fix: assignment permission to course creator and evaluator 2025-12-17 14:47:47 +05:30
Jannat Patel
ec7e250f96 Merge pull request #1919 from pateljannat/open-to-work
feat: open to opportunities
2025-12-17 11:46:35 +05:30
Jannat Patel
126570fcca fix: save button of profile edit modal 2025-12-17 11:23:10 +05:30
Jannat Patel
a7bbb7f150 Merge pull request #1921 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-17 11:01:30 +05:30
Frappe PR Bot
4345ff18bd chore(release): Bumped to Version 2.43.0 2025-12-17 05:20:08 +00:00
Jannat Patel
26409b0336 Merge pull request #1907 from KerollesFathy/fix-category-on-certified-members
fix: Category dropdown options not visible in the certification list page
2025-12-17 10:49:11 +05:30
Jannat Patel
7653b64353 Merge pull request #1920 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-17 10:45:01 +05:30
Jannat Patel
32dd5832b8 chore: Swedish translations 2025-12-17 02:53:41 +05:30
Jannat Patel
59072c3580 chore: Slovenian translations 2025-12-17 02:53:39 +05:30
Jannat Patel
d121cd3526 chore: Hungarian translations 2025-12-17 02:53:28 +05:30
KerollesFathy
cfbac5f2c4 fix: use update:modelvalue instead of using watch 2025-12-16 21:05:04 +00:00
Jannat Patel
371c72b96c Merge branch 'develop' of https://github.com/frappe/lms into open-to-work 2025-12-16 20:37:38 +05:30
Jannat Patel
55a02004bd fix: updated course and batch category filters 2025-12-16 20:36:53 +05:30
Jannat Patel
b3119f5295 feat: open to opportunities 2025-12-16 20:27:00 +05:30
Jannat Patel
f57b64531c fix: misc home page issues 2025-12-16 14:33:35 +05:30
Jannat Patel
94a3afdc9b Merge pull request #1918 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-16 11:09:00 +05:30
Jannat Patel
b65102e6c8 fix: course card gradient color 2025-12-16 11:06:03 +05:30
Jannat Patel
0f1b48b3e3 chore: Esperanto translations 2025-12-16 02:50:54 +05:30
Jannat Patel
0a62da02fe chore: Serbian (Latin) translations 2025-12-16 02:50:52 +05:30
Jannat Patel
ae54e39b69 chore: Norwegian Bokmal translations 2025-12-16 02:50:50 +05:30
Jannat Patel
ea565d7334 chore: Bosnian translations 2025-12-16 02:50:49 +05:30
Jannat Patel
d11f625ecb chore: Burmese translations 2025-12-16 02:50:48 +05:30
Jannat Patel
ab6761a6ce chore: Croatian translations 2025-12-16 02:50:46 +05:30
Jannat Patel
081bc0eaa6 chore: Thai translations 2025-12-16 02:50:45 +05:30
Jannat Patel
30748b7287 chore: Tamil translations 2025-12-16 02:50:43 +05:30
Jannat Patel
ad928eb2a6 chore: Persian translations 2025-12-16 02:50:42 +05:30
Jannat Patel
bdb281dfa3 chore: Indonesian translations 2025-12-16 02:50:41 +05:30
Jannat Patel
8d26800a5d chore: Portuguese, Brazilian translations 2025-12-16 02:50:39 +05:30
Jannat Patel
f0d85b391c chore: Vietnamese translations 2025-12-16 02:50:37 +05:30
Jannat Patel
ac205b6944 chore: Chinese Simplified translations 2025-12-16 02:50:36 +05:30
Jannat Patel
c6979a2f61 chore: Turkish translations 2025-12-16 02:50:35 +05:30
Jannat Patel
0bbf4a9925 chore: Swedish translations 2025-12-16 02:50:33 +05:30
Jannat Patel
16b047bb73 chore: Serbian (Cyrillic) translations 2025-12-16 02:50:32 +05:30
Jannat Patel
f674fdcc0d chore: Slovenian translations 2025-12-16 02:50:30 +05:30
Jannat Patel
05b40ae47e chore: Russian translations 2025-12-16 02:50:29 +05:30
Jannat Patel
ce5413f622 chore: Portuguese translations 2025-12-16 02:50:28 +05:30
Jannat Patel
fbf4971f52 chore: Polish translations 2025-12-16 02:50:26 +05:30
Jannat Patel
a2e2de2ec3 chore: Dutch translations 2025-12-16 02:50:25 +05:30
Jannat Patel
9937851146 chore: Italian translations 2025-12-16 02:50:23 +05:30
Jannat Patel
a0bdb84d3f chore: German translations 2025-12-16 02:50:22 +05:30
Jannat Patel
21fca61fab chore: Danish translations 2025-12-16 02:50:21 +05:30
Jannat Patel
1157e6a007 chore: Czech translations 2025-12-16 02:50:19 +05:30
Jannat Patel
6d9db8ef46 chore: Arabic translations 2025-12-16 02:50:18 +05:30
Jannat Patel
8c23510622 chore: Spanish translations 2025-12-16 02:50:16 +05:30
Jannat Patel
660945b8fa chore: French translations 2025-12-16 02:50:15 +05:30
Jannat Patel
6550e1c926 chore: Hungarian translations 2025-12-16 02:50:13 +05:30
KerollesFathy
49bc5750a1 fix: prevent duplicate certification categories 2025-12-15 18:30:01 +00:00
Jannat Patel
163e4b8b1e fix: course card gradient color 2025-12-15 16:32:42 +05:30
Jannat Patel
52fb5e2ad8 Merge pull request #1915 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-15 16:02:38 +05:30
Jannat Patel
2596b85eb2 Merge pull request #1913 from rehanrehman389/cleanup-cohort
chore: Remove deprecated doctype and related links
2025-12-15 15:44:24 +05:30
Jannat Patel
1210a6aa87 chore: beautifulsoup dependency management 2025-12-15 15:21:44 +05:30
Jannat Patel
d3a27e8bc9 fix: enrollment eligibility validation 2025-12-15 15:21:22 +05:30
Jannat Patel
6cabb4eed7 chore: resolved conflicts 2025-12-15 15:18:08 +05:30
Jannat Patel
38e6320d8f Merge pull request #1890 from pateljannat/tests-1
test: utils
2025-12-15 15:05:39 +05:30
Jannat Patel
8d41a3d688 test: certificate and rating test 2025-12-15 14:51:37 +05:30
Jannat Patel
7bf6311a90 Merge branch 'develop' of https://github.com/frappe/lms into tests-1 2025-12-15 12:13:19 +05:30
Jannat Patel
41660a1dfe Merge pull request #1895 from pateljannat/full-text-search
feat: Full Text Search
2025-12-15 12:07:04 +05:30
Jannat Patel
46b467847e Merge pull request #1908 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-15 11:22:02 +05:30
Jannat Patel
810635d964 Merge pull request #1906 from frappe/pot_develop_2025-12-12
chore: update POT file
2025-12-15 11:21:48 +05:30
Rehan Ansari
b0a96641ef chore: Remove deprecated doctype and related links 2025-12-14 21:16:02 +05:30
royalpinto007
f2846da4ad fix(quizzes): enable enter key to submit quiz creation
Signed-off-by: royalpinto007 <royalpinto007@gmail.com>
2025-12-14 15:28:29 +05:30
Jannat Patel
e150225226 chore: Hungarian translations 2025-12-14 02:05:58 +05:30
KerollesFathy
4580ab0181 fix: add watcher for currentCategory to update participants 2025-12-13 15:18:37 +00:00
KerollesFathy
f8c10d1807 fix: update certification categories to include label and value 2025-12-13 14:22:08 +00:00
frappe-pr-bot
f783c6a62f chore: update POT file 2025-12-12 16:04:35 +00:00
Jannat Patel
1bc610bd76 feat: search jobs from command palette 2025-12-12 19:14:25 +05:30
Jannat Patel
f49bb98b92 fix: sidebar improvements 2025-12-12 16:25:45 +05:30
Jannat Patel
819318de37 feat: broke down sidebar into categories 2025-12-12 12:30:16 +05:30
Jannat Patel
3ebff2143a Merge pull request #1903 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-11 17:28:55 +05:30
Frappe PR Bot
80deed2be7 chore(release): Bumped to Version 2.42.0 2025-12-11 11:48:42 +00:00
Jannat Patel
8de3996d36 Merge pull request #1866 from muhamiyan/issues-1
fix: open dialog directly to create quiz and assignment
2025-12-11 17:15:47 +05:30
Jannat Patel
b56fd01f39 Merge pull request #1899 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-11 16:36:05 +05:30
Jannat Patel
820ea7e2a4 feat: search page functionality 2025-12-11 14:56:28 +05:30
Jannat Patel
3de5fb0622 Merge branch 'develop' of https://github.com/frappe/lms into full-text-search 2025-12-11 11:45:36 +05:30
Jannat Patel
a5f112ff16 fix: remove course filters from certificate evaluation form 2025-12-11 11:33:29 +05:30
Jannat Patel
32b3fceb3b Merge pull request #1900 from pateljannat/issues-161
fix: exclude cancelled slots when fetching available slots
2025-12-11 11:27:58 +05:30
Jannat Patel
1d7c88674d fix: exclude cancelled slots when fetching available slots 2025-12-11 11:25:59 +05:30
Jannat Patel
ce158692d4 Merge pull request #1898 from pateljannat/issues-160
fix: misc issues
2025-12-11 10:39:02 +05:30
Jannat Patel
9845498f76 chore: Croatian translations 2025-12-11 00:59:38 +05:30
Jannat Patel
7b8250056b chore: Serbian (Cyrillic) translations 2025-12-11 00:59:37 +05:30
Jannat Patel
80a217e646 feat: configuration to disable pwa 2025-12-10 18:05:50 +05:30
Jannat Patel
0877e32e1b fix: sanitize html of all description fields 2025-12-10 17:25:56 +05:30
Jannat Patel
316e739dd6 Merge pull request #1897 from pateljannat/issues-159
fix: misc issues
2025-12-10 16:41:22 +05:30
Jannat Patel
43efebe3a7 fix: removed filter of an old field from LMS Certificate Evaluation 2025-12-10 16:14:27 +05:30
Jannat Patel
ca849da815 fix: validate url starts with http or https for job form 2025-12-10 14:51:16 +05:30
Jannat Patel
9470cc192c fix: batch validations 2025-12-10 14:25:10 +05:30
Jannat Patel
631008832c fix: no need to check if not in test before sending certificate email 2025-12-10 14:11:08 +05:30
Jannat Patel
7fc066679d fix: validate url starts with http or https for job form 2025-12-10 14:06:17 +05:30
Jannat Patel
73ee1b2f09 Merge pull request #1893 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-10 11:19:02 +05:30
Jannat Patel
4c98282335 feat: search page 2025-12-10 11:17:49 +05:30
Jannat Patel
7e3c5beaea fix: filter search records based on roles 2025-12-10 11:17:21 +05:30
Jannat Patel
59d27e06a0 chore: Serbian (Latin) translations 2025-12-10 00:25:38 +05:30
Jannat Patel
2caddafd0b chore: Russian translations 2025-12-10 00:25:22 +05:30
Jannat Patel
cbfc10c08b chore: Hungarian translations 2025-12-10 00:25:17 +05:30
muhamiyan
ae8ffd4cbd fix: linter issues 2025-12-09 19:41:12 +07:00
muhamiyan
e768d5d55c fix: reset QuizForm.vue 2025-12-09 19:40:51 +07:00
Jannat Patel
829245f373 Merge pull request #1891 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-09 11:29:29 +05:30
Jannat Patel
c901a15969 test: utils 2025-12-09 11:24:35 +05:30
muhamiyan
017798ce89 fix: url with query string 2025-12-09 12:07:58 +07:00
muhamiyan
d16874da7c fix: open dialog directly to create assignment 2025-12-09 12:01:10 +07:00
muhamiyan
d8c9204b61 fix: open dialog directly to create quiz 2025-12-09 11:19:51 +07:00
muhamiyan
1ab51b423f Revert "fix: remove redirect to create new quiz and new assignment"
This reverts commit fc82ec8070.
2025-12-09 09:54:16 +07:00
Jannat Patel
ce110e8758 chore: Esperanto translations 2025-12-09 00:28:23 +05:30
Jannat Patel
e257a3f757 chore: French translations 2025-12-09 00:28:21 +05:30
Jannat Patel
49a6305dec chore: Serbian (Latin) translations 2025-12-09 00:28:20 +05:30
Jannat Patel
1b30df0c41 chore: Norwegian Bokmal translations 2025-12-09 00:28:19 +05:30
Jannat Patel
edb2369967 chore: Bosnian translations 2025-12-09 00:28:17 +05:30
Jannat Patel
4f93908ab1 chore: Burmese translations 2025-12-09 00:28:16 +05:30
Jannat Patel
dc95c63c62 chore: Croatian translations 2025-12-09 00:28:14 +05:30
Jannat Patel
28a0cb5b12 chore: Thai translations 2025-12-09 00:28:13 +05:30
Jannat Patel
a4cbcc2f32 chore: Tamil translations 2025-12-09 00:28:12 +05:30
Jannat Patel
0add20e637 chore: Persian translations 2025-12-09 00:28:10 +05:30
Jannat Patel
50a8550a0c chore: Indonesian translations 2025-12-09 00:28:09 +05:30
Jannat Patel
dbd6ac10e9 chore: Portuguese, Brazilian translations 2025-12-09 00:28:07 +05:30
Jannat Patel
1252234eed chore: Vietnamese translations 2025-12-09 00:28:06 +05:30
Jannat Patel
5ee7a6efb6 chore: Chinese Simplified translations 2025-12-09 00:28:04 +05:30
Jannat Patel
a4bfe3752b chore: Turkish translations 2025-12-09 00:28:03 +05:30
Jannat Patel
dd034ec7ae chore: Swedish translations 2025-12-09 00:28:02 +05:30
Jannat Patel
c93b189ede chore: Serbian (Cyrillic) translations 2025-12-09 00:28:00 +05:30
Jannat Patel
2819531d57 chore: Slovenian translations 2025-12-09 00:27:59 +05:30
Jannat Patel
dc1ce8e55e chore: Russian translations 2025-12-09 00:27:57 +05:30
Jannat Patel
2e0266a265 chore: Portuguese translations 2025-12-09 00:27:56 +05:30
Jannat Patel
bcd7aca2ff chore: Polish translations 2025-12-09 00:27:55 +05:30
Jannat Patel
8c66558b63 chore: Dutch translations 2025-12-09 00:27:53 +05:30
Jannat Patel
a263bcd8c3 chore: Italian translations 2025-12-09 00:27:52 +05:30
Jannat Patel
97a873c6b0 chore: Hungarian translations 2025-12-09 00:27:50 +05:30
Jannat Patel
fc43259dcb chore: German translations 2025-12-09 00:27:49 +05:30
Jannat Patel
73116446e2 chore: Danish translations 2025-12-09 00:27:48 +05:30
Jannat Patel
f0d3439071 chore: Czech translations 2025-12-09 00:27:46 +05:30
Jannat Patel
31a1aa7cac chore: Arabic translations 2025-12-09 00:27:45 +05:30
Jannat Patel
34cd751114 chore: Spanish translations 2025-12-09 00:27:43 +05:30
Jannat Patel
196d4a835f test: utils 2025-12-08 21:10:13 +05:30
Jannat Patel
d82517f402 test: utils 2025-12-08 20:22:55 +05:30
Jannat Patel
bcda74a455 Merge pull request #1889 from pateljannat/code-coverage
chore: configured codecov for CI
2025-12-08 15:59:53 +05:30
Jannat Patel
1f65439efd chore: upload coverage data codecov 2025-12-08 15:52:41 +05:30
Jannat Patel
f95b62a6a6 chore: configured codecov for CI 2025-12-08 15:23:18 +05:30
Jannat Patel
0dc0794add Merge pull request #1888 from pateljannat/version-16-changes
chore: removed cohort related doctypes
2025-12-08 14:54:31 +05:30
Jannat Patel
552b5845ea chore: fixed merge conflicts 2025-12-08 14:54:14 +05:30
Jannat Patel
b05739257d chore: removed cohort related doctypes 2025-12-08 14:47:02 +05:30
Jannat Patel
61215cf0ad Merge pull request #1887 from pateljannat/issues-158
fix: charts and dashboards as per version 16
2025-12-08 13:43:16 +05:30
Jannat Patel
bc84e46e09 fix: charts and dashboards as per version 16 2025-12-08 13:23:42 +05:30
Jannat Patel
645581e202 Merge pull request #1886 from pateljannat/issues-157
fix: misc issues
2025-12-08 12:46:51 +05:30
Jannat Patel
4dddb9f2e1 fix: exclude frappe-ui from optimiseDeps only on local 2025-12-08 12:37:04 +05:30
Jannat Patel
6e93f952ab refactor: markdown editor 2025-12-08 12:33:38 +05:30
Jannat Patel
93ffcdb8f9 Merge branch 'develop' of https://github.com/frappe/lms into issues-157 2025-12-08 10:37:45 +05:30
Jannat Patel
040d74c20a fix: use frappe-ui theme colors and fixed tailwind config 2025-12-08 10:37:31 +05:30
Jannat Patel
5825bcf9b3 fix: use frappe-ui theme colors and fixed tailwind config 2025-12-08 10:37:08 +05:30
Jannat Patel
114d183524 Merge pull request #1884 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-08 10:35:55 +05:30
Jannat Patel
de356c4e64 Merge pull request #1883 from frappe/pot_develop_2025-12-05
chore: update POT file
2025-12-08 10:35:42 +05:30
Jannat Patel
fedd5e6a14 chore: Hungarian translations 2025-12-05 22:58:03 +05:30
frappe-pr-bot
29217ab2bb chore: update POT file 2025-12-05 16:04:48 +00:00
Alexandrina-Kuzeleva
6bb6125e81 Update ParentProfile.vue 2025-12-05 16:45:49 +03:00
Alexandrina-Kuzeleva
4da2b844e8 Update ParentProfile.vue 2025-12-05 16:44:39 +03:00
Alexandrina-Kuzeleva
eba1923b7c Update ParentProfile.vue 2025-12-05 16:41:05 +03:00
Alexandrina-Kuzeleva
f28823dbe9 TEST UPD
-add parentprofile
2025-12-05 16:33:32 +03:00
Alexandrina-Kuzeleva
5d122bca7d upd 2025-12-05 16:22:23 +03:00
Alexandrina-Kuzeleva
ef4321586c TEST UPD 2025-12-05 16:13:38 +03:00
Alexandrina-Kuzeleva
336511dcd5 TEST UPD
- add traslation
- bug of doesntexist
2025-12-05 13:17:01 +03:00
Alexandrina-Kuzeleva
d73b6f9026 TEST UPD
- /api/method/frappe.client.get DoesNotExistError
2025-12-05 12:51:37 +03:00
Alexandrina-Kuzeleva
e959c0172d TEST UPD
-schoolchildprofile bug of learn_subjects
-front
2025-12-05 11:59:43 +03:00
Jannat Patel
95f37f7120 Merge pull request #1882 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-05 12:16:51 +05:30
Frappe PR Bot
87a7b93334 chore(release): Bumped to Version 2.41.0 2025-12-05 06:34:16 +00:00
Jannat Patel
0d430ad86c Merge pull request #1881 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-05 12:02:53 +05:30
Jannat Patel
308a108e60 Merge pull request #1880 from pateljannat/issues-155
fix: misc permission issues
2025-12-05 12:02:32 +05:30
Jannat Patel
17401a330d fix: settings issue 2025-12-05 11:34:49 +05:30
Jannat Patel
f9d7463710 test: removed tests for old batch and exercises 2025-12-05 11:03:38 +05:30
Jannat Patel
097c53c7b1 chore: Bosnian translations 2025-12-04 22:37:08 +05:30
Jannat Patel
9ae2e5babb chore: Croatian translations 2025-12-04 22:37:05 +05:30
Jannat Patel
1f29f7a282 chore: Slovenian translations 2025-12-04 22:36:52 +05:30
Jannat Patel
1d1fcd5f6d chore: Russian translations 2025-12-04 22:36:51 +05:30
Jannat Patel
b6f8f87923 chore: Hungarian translations 2025-12-04 22:36:45 +05:30
Alexandrina-Kuzeleva
a15767c14f TEST UPD
-some traslation
-trest schoolchildrenprofile
-frontworks
2025-12-04 18:57:34 +03:00
Alexandrina-Kuzeleva
20b1743223 TEST UPD
- course creator front
- tg icon
2025-12-04 16:06:39 +03:00
Alexandrina-Kuzeleva
a89930fae6 TEST UPD
-translation
-front works
2025-12-04 15:29:50 +03:00
Jannat Patel
466b248c30 fix: validate course details before enrolling 2025-12-04 17:48:39 +05:30
Alexandrina-Kuzeleva
60e81a921e TEST UPD
- translate leader board
2025-12-04 15:13:11 +03:00
Alexandrina-Kuzeleva
36f75beea9 TEST UPD
-test bio
2025-12-04 15:10:29 +03:00
Jannat Patel
7c6747aeb0 test: enroll user in course before creating certificate 2025-12-04 17:35:08 +05:30
Jannat Patel
836a6d1203 fix: misc permission issues 2025-12-04 17:15:14 +05:30
Alexandrina-Kuzeleva
01a9eab73d TEST UPD
- front works
2025-12-04 14:17:57 +03:00
Jannat Patel
a3bd9d2706 Merge pull request #1879 from pateljannat/issues-156
fix: use dayjs from frappe-ui in evaluation modal
2025-12-04 11:24:49 +05:30
Jannat Patel
2cdd45da96 fix: use dayjs from frappe-ui in evaluation modal 2025-12-04 11:06:34 +05:30
Jannat Patel
0367c1db72 fix: validate course and batch before a reply is added 2025-12-04 09:25:07 +05:30
Jannat Patel
5f17802ab8 fix: only moderators should be allowed to delete sidebar pages 2025-12-04 09:24:27 +05:30
Alexandrina-Kuzeleva
a4eff5ae38 TEST UPD 2025-12-03 22:52:16 +03:00
Alexandrina-Kuzeleva
46b5495167 TEST UPD
- tranlation My Points
- front works
2025-12-03 22:44:25 +03:00
Alexandrina-Kuzeleva
7c9ef2a702 TEST UPD
-front works for student profile
2025-12-03 22:10:43 +03:00
Jannat Patel
b3e90c7f2f fix: validate before enrolling in batch 2025-12-03 15:44:19 +05:30
Jannat Patel
6c2978306c Merge branch 'develop' of https://github.com/frappe/lms into develop 2025-12-03 14:18:20 +05:30
Jannat Patel
b951e1567c chore: upgraded dependencies 2025-12-03 14:17:49 +05:30
Jannat Patel
97cdb57406 Merge pull request #1878 from pateljannat/issues-154
fix: increased the rate limit
2025-12-03 10:56:05 +05:30
Jannat Patel
651300b043 Merge pull request #1877 from frappe/develop
chore: merge 'develop' into 'main'
2025-12-03 10:48:37 +05:30
Jannat Patel
b537e2789d fix: increased the rate limit 2025-12-03 10:43:19 +05:30
Jannat Patel
3cd766bc74 Merge pull request #1876 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-03 10:38:46 +05:30
Jannat Patel
30ee06c0ab Merge pull request #1874 from pateljannat/closed-job-applications
feat: Closed job applications
2025-12-03 10:38:34 +05:30
Jannat Patel
74a1f1dc77 chore: Bosnian translations 2025-12-02 21:42:12 +05:30
Jannat Patel
7a6f6d2c7c chore: Croatian translations 2025-12-02 21:42:11 +05:30
Jannat Patel
de8868ec68 chore: Persian translations 2025-12-02 21:42:09 +05:30
Jannat Patel
7105d6271f fix: allow moderators to edit all jobs 2025-12-02 18:34:46 +05:30
Jannat Patel
b04a3de201 feat: closed jobs 2025-12-02 17:59:04 +05:30
Jannat Patel
0107032ee3 Merge pull request #1873 from pateljannat/issues-153
fix: misc issues
2025-12-02 16:41:05 +05:30
Jannat Patel
34e07b0083 fix: fetch correct application count 2025-12-02 16:31:54 +05:30
Jannat Patel
6c16516e89 fix: evaluators can only edit batches where they are instructors 2025-12-02 16:14:17 +05:30
Jannat Patel
7bc6dff6ea fix: misc issues 2025-12-02 15:41:42 +05:30
Jannat Patel
1c0be8a2ec Merge pull request #1870 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-02 11:37:50 +05:30
Jannat Patel
4a9f1197fb Merge pull request #1861 from pateljannat/data-import-fixes
fix: data import issues
2025-12-02 11:34:45 +05:30
Jannat Patel
ad4e7496cb chore: Slovenian translations 2025-12-01 21:28:42 +05:30
Jannat Patel
d321b81a64 chore: Esperanto translations 2025-12-01 21:28:40 +05:30
Jannat Patel
3691e3f240 chore: Serbian (Latin) translations 2025-12-01 21:28:38 +05:30
Jannat Patel
62e2fe56d0 chore: Norwegian Bokmal translations 2025-12-01 21:28:37 +05:30
Jannat Patel
4a6a98c533 chore: Bosnian translations 2025-12-01 21:28:35 +05:30
Jannat Patel
4e5d44c464 chore: Burmese translations 2025-12-01 21:28:34 +05:30
Jannat Patel
b2e8fbd84d chore: Croatian translations 2025-12-01 21:28:32 +05:30
Jannat Patel
41dde09a6a chore: Thai translations 2025-12-01 21:28:30 +05:30
Jannat Patel
7bbfb85b0e chore: Tamil translations 2025-12-01 21:28:29 +05:30
Jannat Patel
c99d05cf6c chore: Persian translations 2025-12-01 21:28:27 +05:30
Jannat Patel
f856aaaacd chore: Indonesian translations 2025-12-01 21:28:26 +05:30
Jannat Patel
d029d4e371 chore: Portuguese, Brazilian translations 2025-12-01 21:28:24 +05:30
Jannat Patel
04f3744624 chore: Vietnamese translations 2025-12-01 21:28:23 +05:30
Jannat Patel
4e1d00afff chore: Chinese Simplified translations 2025-12-01 21:28:21 +05:30
Jannat Patel
ccfa281490 chore: Turkish translations 2025-12-01 21:28:19 +05:30
Jannat Patel
8979b5ef0c chore: Swedish translations 2025-12-01 21:28:17 +05:30
Jannat Patel
765bd8bfab chore: Serbian (Cyrillic) translations 2025-12-01 21:28:16 +05:30
Jannat Patel
4e7f371c49 chore: Russian translations 2025-12-01 21:28:14 +05:30
Jannat Patel
5ca5624f99 chore: Portuguese translations 2025-12-01 21:28:13 +05:30
Jannat Patel
6e5066022b chore: Polish translations 2025-12-01 21:28:11 +05:30
Jannat Patel
b16d8cbd6d chore: Dutch translations 2025-12-01 21:28:10 +05:30
Jannat Patel
030b3095a9 chore: Italian translations 2025-12-01 21:28:08 +05:30
Jannat Patel
d9febdaf82 chore: Hungarian translations 2025-12-01 21:28:06 +05:30
Jannat Patel
3143fc8b2a chore: German translations 2025-12-01 21:28:05 +05:30
Jannat Patel
d8396d13b4 chore: Danish translations 2025-12-01 21:28:03 +05:30
Jannat Patel
6a474b25ef chore: Czech translations 2025-12-01 21:28:02 +05:30
Jannat Patel
c8494f3246 chore: Arabic translations 2025-12-01 21:28:00 +05:30
Jannat Patel
1d66f9695a chore: Spanish translations 2025-12-01 21:27:59 +05:30
Jannat Patel
053a9cd3a9 chore: French translations 2025-12-01 21:27:57 +05:30
Jannat Patel
b7546fd2f4 chore: fixed linters 2025-12-01 17:54:47 +05:30
Jannat Patel
61fc0a9ce7 chore: upgraded frappe-ui 2025-12-01 17:54:15 +05:30
Jannat Patel
251abad821 Merge pull request #1865 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-12-01 12:12:02 +05:30
Jannat Patel
f581e7894d Merge pull request #1867 from frappe/pot_develop_2025-11-28
chore: update POT file
2025-12-01 12:11:49 +05:30
Jannat Patel
c16b81bfa2 chore: Slovenian translations 2025-11-30 20:15:46 +05:30
Jannat Patel
4fcb19010c chore: Persian translations 2025-11-30 20:15:39 +05:30
Jannat Patel
7f1e2a18ea chore: Slovenian translations 2025-11-29 20:09:06 +05:30
Jannat Patel
7f4bb9e05d chore: Persian translations 2025-11-29 20:08:59 +05:30
frappe-pr-bot
58d9dd3c26 chore: update POT file 2025-11-28 16:04:30 +00:00
Jannat Patel
609ed3cb09 chore: Slovenian translations 2025-11-28 20:12:18 +05:30
Jannat Patel
67dd8ac151 Merge pull request #1864 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-28 16:19:37 +05:30
muhamiyan
fc82ec8070 fix: remove redirect to create new quiz and new assignment 2025-11-28 17:21:29 +07:00
Alexandrina-Kuzeleva
ee9aed6bbc Update Test.vue 2025-11-28 11:00:45 +03:00
Alexandrina-Kuzeleva
eb4cf6e2db Update Test.vue 2025-11-28 10:57:54 +03:00
Alexandrina-Kuzeleva
c6ad6b495c Update api.py 2025-11-28 10:50:48 +03:00
Alexandrina-Kuzeleva
5499a86854 Update Test.vue 2025-11-28 10:46:51 +03:00
Alexandrina-Kuzeleva
627ccd8214 Update Test.vue 2025-11-28 10:42:35 +03:00
Alexandrina-Kuzeleva
e760d59d9f Update Test.vue
-front works
- fix bonus points
2025-11-28 10:39:04 +03:00
Alexandrina-Kuzeleva
e76858121f TEST UPD
- front works
2025-11-28 10:27:03 +03:00
Alexandrina-Kuzeleva
34f1d02803 TEST UPD
- add roles permision in api method
2025-11-28 10:11:30 +03:00
Jannat Patel
f218230ad7 chore: Slovenian translations 2025-11-27 19:55:31 +05:30
Jannat Patel
f8380226ee chore: Croatian translations 2025-11-27 19:55:29 +05:30
Alexandrina-Kuzeleva
64610050ca TEST UPD
- current user card
2025-11-27 16:50:33 +03:00
Jannat Patel
b087faeb90 fix: batch instructors selection issue 2025-11-27 16:19:17 +05:30
Jannat Patel
a333b0b754 chore: fixed conflicts 2025-11-27 16:15:58 +05:30
Jannat Patel
2614fbc94c chore: upgraded frappe-ui to latest version 2025-11-27 14:34:08 +05:30
Jannat Patel
66c70dd233 Merge pull request #1860 from pateljannat/issues-152
fix: round the amount after exchange rate calculation
2025-11-27 12:45:03 +05:30
Jannat Patel
8d1c0a7bd1 Merge pull request #1862 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-27 12:42:18 +05:30
Jannat Patel
eae74dacae fix: redirect to courses if user is not moderator on data import 2025-11-27 12:41:39 +05:30
Alexandrina-Kuzeleva
f5bd52a94d Update Test.vue 2025-11-26 18:17:17 +03:00
Alexandrina-Kuzeleva
ce603cac1e Update Test.vue 2025-11-26 18:09:45 +03:00
Alexandrina-Kuzeleva
3108235521 TEST UPD 2025-11-26 17:51:42 +03:00
Alexandrina-Kuzeleva
280aaecf76 Update Test.vue 2025-11-26 17:41:18 +03:00
Alexandrina-Kuzeleva
ba0bb1eabc Update Test.vue 2025-11-26 17:36:32 +03:00
Alexandrina-Kuzeleva
73d0755249 Update Test.vue 2025-11-26 17:27:33 +03:00
Alexandrina-Kuzeleva
4d93dcb9b4 TEST 2025-11-26 17:21:35 +03:00
Alexandrina-Kuzeleva
1fc9b8e279 TEST 2025-11-26 17:15:09 +03:00
Jannat Patel
ca0fed9f17 chore: Slovenian translations 2025-11-26 19:26:44 +05:30
Jannat Patel
cfc5d94711 chore: upgraded frappe-ui to latest version 2025-11-26 16:06:03 +05:30
Jannat Patel
924a11e4f4 chore: frappe-ui tree shaking changes 2025-11-26 15:06:21 +05:30
Jannat Patel
3be3124951 fix: profile update form 2025-11-26 15:05:55 +05:30
Jannat Patel
c846e36032 fix: data import issues 2025-11-26 14:14:09 +05:30
Jannat Patel
cf9e5f861b Merge pull request #1858 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-26 11:32:53 +05:30
Jannat Patel
219139e45b fix: round the amount after exchange rate calculation 2025-11-26 11:21:18 +05:30
Jannat Patel
eab43a66cf feat: improved search results in command palette 2025-11-25 19:44:31 +05:30
Jannat Patel
faa18d6a88 chore: Slovenian translations 2025-11-25 19:10:19 +05:30
Jannat Patel
470e446ae6 chore: Persian translations 2025-11-25 19:10:08 +05:30
Jannat Patel
af3f1a5fc3 chore: Turkish translations 2025-11-25 19:10:02 +05:30
Jannat Patel
3b925246ee chore: German translations 2025-11-25 19:09:51 +05:30
Jannat Patel
e2d7b409bd chore: Arabic translations 2025-11-25 19:09:47 +05:30
Jannat Patel
731e242974 chore: Spanish translations 2025-11-25 19:09:46 +05:30
Jannat Patel
9614f8eb9d chore: French translations 2025-11-25 19:09:44 +05:30
Alexandrina-Kuzeleva
c6d05111cc Update LeaderBoard.vue 2025-11-25 15:34:35 +03:00
Alexandrina-Kuzeleva
8fa3d8ba4a Update LeaderBoard.vue 2025-11-25 15:30:22 +03:00
Alexandrina-Kuzeleva
c5317beb3f TEST UPD 2025-11-25 15:28:23 +03:00
Alexandrina-Kuzeleva
7d82e36790 Update Test.vue 2025-11-25 15:18:58 +03:00
Alexandrina-Kuzeleva
f39867b0e2 Update Test.vue 2025-11-25 15:12:07 +03:00
Alexandrina-Kuzeleva
54cef503ad TEST UPD
- add test page
2025-11-25 15:08:07 +03:00
Alexandrina-Kuzeleva
ce51371e62 TEST UPD
- roles
2025-11-25 13:14:13 +03:00
Alexandrina-Kuzeleva
7aabbbd497 Update LeaderBoard.vue 2025-11-25 13:07:26 +03:00
Alexandrina-Kuzeleva
02b89ea137 TEST UPD
- debug
- user role
2025-11-25 13:00:10 +03:00
Alexandrina-Kuzeleva
119a48f3a3 Update LeaderBoard.vue 2025-11-25 12:37:50 +03:00
Alexandrina-Kuzeleva
3146a0354c Update LeaderBoard.vue 2025-11-25 12:34:06 +03:00
Alexandrina-Kuzeleva
8e895a9890 TEST UPD
- debug
2025-11-25 11:55:43 +03:00
Alexandrina-Kuzeleva
ac436cbf79 TEST UPD
- roles
2025-11-25 11:23:15 +03:00
Alexandrina-Kuzeleva
4363aa7734 TEST UPD
- дурацкие названия файлов ненавижу
2025-11-25 11:10:29 +03:00
Alexandrina-Kuzeleva
a65cb073b5 TEST UPD
- try to add imgs
2025-11-25 10:58:57 +03:00
Alexandrina-Kuzeleva
a3b9e4f7b2 TEST UPD
- add front
2025-11-25 10:51:18 +03:00
Alexandrina-Kuzeleva
ebde8a0171 TEST UPD
- try to do anthore logic of script
2025-11-25 10:26:39 +03:00
Jannat Patel
a98e8025c4 Merge pull request #1854 from rehanrehman389/fix-zoom
fix: data persistence issue
2025-11-25 10:42:10 +05:30
Jannat Patel
397b7ee032 Merge pull request #1807 from rehanrehman389/program-fix
Misc Programs Issues
2025-11-25 10:40:59 +05:30
Jannat Patel
22b041d252 Merge pull request #1852 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-25 10:40:30 +05:30
Jannat Patel
5f20d8ad63 chore: Slovenian translations 2025-11-24 19:11:54 +05:30
Jannat Patel
d3879a9d11 Merge pull request #1853 from frappe/pot_develop_2025-11-21
chore: update POT file
2025-11-24 10:01:27 +05:30
Jannat Patel
0d0b6dcd36 Merge pull request #1785 from pateljannat/data-import
feat: data import
2025-11-24 10:01:10 +05:30
Jannat Patel
588f796069 chore: Slovenian translations 2025-11-23 18:37:06 +05:30
Jannat Patel
440c51f1ca chore: Swedish translations 2025-11-23 18:36:52 +05:30
Rehan Ansari
b99b2bd123 refactor: simplify zoom account modal form handling 2025-11-22 23:29:35 +05:30
Jannat Patel
e502ff3491 chore: Slovenian translations 2025-11-22 18:07:54 +05:30
Jannat Patel
1ae0f87f9c chore: Serbian (Latin) translations 2025-11-22 18:07:53 +05:30
Jannat Patel
5a3e72eaaf chore: Croatian translations 2025-11-22 18:07:52 +05:30
Jannat Patel
41c8384ef5 chore: Serbian (Cyrillic) translations 2025-11-22 18:07:50 +05:30
frappe-pr-bot
12a5641cb4 chore: update POT file 2025-11-21 16:05:05 +00:00
Jannat Patel
e8cd305171 chore: upgraded frappe-ui to latest version 2025-11-21 20:22:32 +05:30
Jannat Patel
880b799df5 chore: upgraded frappe-ui to latest data import commit 2025-11-21 20:00:20 +05:30
Alexandrina-Kuzeleva
684299ac3b Update LeaderBoard.vue 2025-11-21 16:47:34 +03:00
Alexandrina-Kuzeleva
c449aef7ae Update LeaderBoard.vue 2025-11-21 16:41:05 +03:00
Alexandrina-Kuzeleva
879a27ed0a TEST UPD
- debug
2025-11-21 16:37:03 +03:00
Alexandrina-Kuzeleva
107e7a4e31 TEST UPD
- test leader board
2025-11-21 16:28:33 +03:00
Alexandrina-Kuzeleva
fa0325106a Update MyPoints.vue 2025-11-21 15:57:41 +03:00
Alexandrina-Kuzeleva
bbfce9363f TEST UPD
- font works
2025-11-21 15:47:58 +03:00
Alexandrina-Kuzeleva
10a6280b78 TEST UPD
- change weekly sum
2025-11-21 15:37:08 +03:00
Jannat Patel
4c6dc25589 chore: Slovenian translations 2025-11-21 18:01:52 +05:30
Jannat Patel
10a301eebc chore: Persian translations 2025-11-21 18:01:44 +05:30
Jannat Patel
e6e50c96e4 chore: Portuguese, Brazilian translations 2025-11-21 18:01:41 +05:30
Jannat Patel
fab2ee8420 chore: Polish translations 2025-11-21 18:01:33 +05:30
Jannat Patel
58eb3ccacb chore: Spanish translations 2025-11-21 18:01:25 +05:30
Alexandrina-Kuzeleva
08e8724b4c UPD 6.1
- delete func
2025-11-21 15:29:07 +03:00
Alexandrina-Kuzeleva
555c7e4e2d TEST UPD
- add today weekly points
2025-11-21 15:18:41 +03:00
Jannat Patel
aacd5ab7a1 chore: upgraded frappe-ui to latest data import commit 2025-11-21 17:40:18 +05:30
Alexandrina-Kuzeleva
3673026a33 TEST UPD
- add load more
2025-11-21 14:50:00 +03:00
Jannat Patel
38b0b9ceb1 Merge branch 'develop' of https://github.com/frappe/lms into data-import 2025-11-21 17:13:27 +05:30
Jannat Patel
9fb2a169e8 chore: downgraded vite-pwa-plugin 2025-11-21 16:46:31 +05:30
Jannat Patel
5977be4a14 chore: upgraded frappe-ui to latest data import commit 2025-11-21 15:53:04 +05:30
Jannat Patel
5b5c53bebc chore: added runtime caching for PWA 2025-11-21 15:02:42 +05:30
Jannat Patel
9b38e62eaf chore: upgraded node version in CI 2025-11-21 14:49:39 +05:30
Jannat Patel
7a490b19bd chore: downgraded vite-pwa-plugin 2025-11-21 14:29:56 +05:30
Jannat Patel
4d34e9e702 Merge pull request #1773 from HUMENTH/patch-2
fix: beautifulsoap4 version upgrade to match with Frappe Framework
2025-11-21 11:32:28 +05:30
Jannat Patel
6fbd504e39 Merge branch 'develop' into patch-2 2025-11-21 10:48:55 +05:30
Alexandrina-Kuzeleva
cd565ec160 Update MyPoints.vue 2025-11-20 16:44:05 +03:00
Alexandrina-Kuzeleva
bdcbae03ef TEST UPD
- front
2025-11-20 16:40:42 +03:00
Alexandrina-Kuzeleva
296234a093 TEST UPD
- remove upd
2025-11-20 16:36:39 +03:00
Alexandrina-Kuzeleva
10c0955c6c TEST UPD
- add load more
- add front
2025-11-20 16:28:21 +03:00
Alexandrina-Kuzeleva
8ba2bfda63 TEXT UPD
- front
2025-11-20 16:17:10 +03:00
Jannat Patel
b0b79f1d19 test: click on dropdown option to open course or batch form 2025-11-20 18:30:52 +05:30
Alexandrina-Kuzeleva
cb06cc53c2 TEST UPD
- front works
2025-11-20 15:53:36 +03:00
Alexandrina-Kuzeleva
826828ba30 TEST UPD
- text-white remove
2025-11-20 15:34:24 +03:00
Jannat Patel
3d7a3ecfc5 refactor: new data import flow 2025-11-20 18:02:02 +05:30
Jannat Patel
9b7d763d52 refactor: new data import flow 2025-11-20 18:01:36 +05:30
Alexandrina-Kuzeleva
22de38c72b TEST UPD
- try to fix bug of sum points
2025-11-20 15:23:59 +03:00
Alexandrina-Kuzeleva
0037c01beb TEST UPD
- problem of date
2025-11-20 13:35:11 +03:00
Alexandrina-Kuzeleva
fb17c666a9 TEST UPD
- add my points
2025-11-20 13:29:09 +03:00
Alexandrina-Kuzeleva
2c32fac1f2 TEST UPD
- try to get resource
2025-11-20 13:17:23 +03:00
Alexandrina-Kuzeleva
3f0b00decd TEST UPD
- another logic of download user and data
2025-11-20 13:06:55 +03:00
Alexandrina-Kuzeleva
160c7863f0 TEST UPD
- My points page
2025-11-20 12:56:28 +03:00
Alexandrina-Kuzeleva
c4d185f2d6 UPD 6
- add locale file, ru
- AI in Quizes, now it is off
- bug of translation in Courses
- fronted of tags in Course Detail in Mobile ver
- another locale bugs
2025-11-20 12:44:53 +03:00
Jannat Patel
aae8624269 Merge pull request #1851 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-19 21:44:59 +05:30
Jannat Patel
7fd16915c0 chore: Persian translations 2025-11-19 18:02:16 +05:30
Jannat Patel
eacb9fe356 Merge pull request #1846 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-18 19:43:28 +05:30
Jannat Patel
a7305b679e Merge branch 'develop' into l10n_develop2 2025-11-18 19:09:55 +05:30
Jannat Patel
d48405d440 chore: Esperanto translations 2025-11-18 18:04:28 +05:30
Jannat Patel
35157c0b58 chore: Serbian (Latin) translations 2025-11-18 18:04:27 +05:30
Jannat Patel
ee7aacd776 chore: Norwegian Bokmal translations 2025-11-18 18:04:25 +05:30
Jannat Patel
2c08b94c3a chore: Bosnian translations 2025-11-18 18:04:23 +05:30
Jannat Patel
d34cfbc327 chore: Burmese translations 2025-11-18 18:04:22 +05:30
Jannat Patel
d9933e6933 chore: Croatian translations 2025-11-18 18:04:20 +05:30
Jannat Patel
7dbb215a18 chore: Thai translations 2025-11-18 18:04:19 +05:30
Jannat Patel
132f8be21c chore: Tamil translations 2025-11-18 18:04:17 +05:30
Jannat Patel
5fac2198cd chore: Persian translations 2025-11-18 18:04:15 +05:30
Jannat Patel
f94b4d1205 chore: Indonesian translations 2025-11-18 18:04:14 +05:30
Jannat Patel
fc75f92e89 chore: Portuguese, Brazilian translations 2025-11-18 18:04:13 +05:30
Jannat Patel
998049872d chore: Vietnamese translations 2025-11-18 18:04:11 +05:30
Jannat Patel
f9ed0bab5e chore: Chinese Simplified translations 2025-11-18 18:04:10 +05:30
Jannat Patel
b87497411b chore: Turkish translations 2025-11-18 18:04:08 +05:30
Jannat Patel
b6be206630 chore: Swedish translations 2025-11-18 18:04:07 +05:30
Jannat Patel
f7a4350fe8 chore: Serbian (Cyrillic) translations 2025-11-18 18:04:05 +05:30
Jannat Patel
802a104a49 chore: Russian translations 2025-11-18 18:04:03 +05:30
Jannat Patel
35aea2dd77 chore: Portuguese translations 2025-11-18 18:04:02 +05:30
Jannat Patel
6ef7dd75e9 chore: Polish translations 2025-11-18 18:04:00 +05:30
Jannat Patel
115ccdb26a chore: Dutch translations 2025-11-18 18:03:59 +05:30
Jannat Patel
6f888bcf4a chore: Italian translations 2025-11-18 18:03:58 +05:30
Jannat Patel
d55de747f5 chore: Hungarian translations 2025-11-18 18:03:56 +05:30
Jannat Patel
ba45c57cc6 chore: German translations 2025-11-18 18:03:54 +05:30
Jannat Patel
8632f81237 chore: Danish translations 2025-11-18 18:03:53 +05:30
Jannat Patel
f7c2ec7fa6 chore: Czech translations 2025-11-18 18:03:51 +05:30
Jannat Patel
6617c1ef54 chore: Arabic translations 2025-11-18 18:03:49 +05:30
Jannat Patel
229c537731 chore: Spanish translations 2025-11-18 18:03:48 +05:30
Jannat Patel
d0060d828f chore: French translations 2025-11-18 18:03:46 +05:30
Jannat Patel
98f9778464 Merge pull request #1845 from frappe/pot_develop_2025-11-14
chore: update POT file
2025-11-17 20:01:25 +05:30
Jannat Patel
1f6a0194f7 chore: resolved conflicts 2025-11-17 10:12:26 +05:30
Jannat Patel
c7915e2c3d feat: launch command palette 2025-11-17 10:10:26 +05:30
Jannat Patel
41f7979eb4 chore: Norwegian Bokmal translations 2025-11-16 17:56:52 +05:30
Jannat Patel
343ed8d22f chore: Bosnian translations 2025-11-16 17:56:51 +05:30
Jannat Patel
1c038d3334 chore: Persian translations 2025-11-16 17:56:46 +05:30
Jannat Patel
1c3cdec563 chore: Indonesian translations 2025-11-16 17:56:44 +05:30
Jannat Patel
6c37f0f4fe chore: Turkish translations 2025-11-16 17:56:40 +05:30
Jannat Patel
f9967bff2e chore: Swedish translations 2025-11-16 17:56:39 +05:30
Jannat Patel
96a9e34487 chore: Italian translations 2025-11-16 17:56:34 +05:30
Jannat Patel
2eb1574131 chore: German translations 2025-11-16 17:56:32 +05:30
Jannat Patel
583871912b chore: Danish translations 2025-11-16 17:56:30 +05:30
Jannat Patel
f4483d7973 chore: Arabic translations 2025-11-16 17:56:28 +05:30
Jannat Patel
21f49690bd chore: Spanish translations 2025-11-16 17:56:27 +05:30
Jannat Patel
18ebac2130 chore: French translations 2025-11-16 17:56:25 +05:30
frappe-pr-bot
1deee7e396 chore: update POT file 2025-11-14 16:05:00 +00:00
Jannat Patel
0a72f0a9a9 Merge pull request #1844 from pateljannat/issues-150
fix: misc issues
2025-11-14 18:14:00 +05:30
Jannat Patel
1932338660 test: open students tab before adding to batch 2025-11-14 18:06:23 +05:30
Jannat Patel
a481bcd974 fix: test case table ux 2025-11-14 17:46:31 +05:30
Jannat Patel
d86fd0f6f6 fix: misc issues 2025-11-14 12:48:46 +05:30
Jannat Patel
bca70e0842 Merge pull request #1843 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-13 19:15:14 +05:30
Jannat Patel
cf659f93d8 Merge branch 'develop' into l10n_develop2 2025-11-13 17:42:48 +05:30
Jannat Patel
8bfc2a5297 Merge pull request #1777 from JoeBrar/feature/coupons
feat: coupon code discount
2025-11-13 17:42:34 +05:30
Jannat Patel
783f0ed750 chore: German translations 2025-11-13 17:11:59 +05:30
Jannat Patel
90e4097fa3 fix: update redemption count when payment received 2025-11-13 14:50:55 +05:30
Jannat Patel
2aac558d4a fix: don't escape batch details field 2025-11-13 14:22:50 +05:30
Jannat Patel
9d3714eb90 Merge pull request #1841 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-13 11:47:06 +05:30
Jannat Patel
46f5808fdb refactor: transactions changes based on coupon code 2025-11-13 11:46:35 +05:30
Alexandrina-Kuzeleva
6b13b1231a UPD 5
- add Profiles for Student, Schoolchildren, Parent, Course Creator
2025-11-12 17:18:03 +03:00
Jannat Patel
8dbc85d03d chore: Bosnian translations 2025-11-12 17:15:50 +05:30
Jannat Patel
f4be59f958 chore: Croatian translations 2025-11-12 17:15:48 +05:30
Jannat Patel
4d38f0637c chore: Persian translations 2025-11-12 17:15:46 +05:30
Jannat Patel
feb7758830 chore: Swedish translations 2025-11-12 17:15:45 +05:30
Jannat Patel
915be8dbdc Merge pull request #1835 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-12 10:51:02 +05:30
Alexandrina-Kuzeleva
661137d500 UPD 4.1
- error
2025-11-11 17:38:06 +03:00
Alexandrina-Kuzeleva
962dcc1ce9 UPD 4
- add MobileLayout changes
2025-11-11 17:27:09 +03:00
Jannat Patel
3a6d0998c4 chore: Esperanto translations 2025-11-11 17:19:40 +05:30
Jannat Patel
048bc7e421 chore: Serbian (Latin) translations 2025-11-11 17:19:39 +05:30
Jannat Patel
3b0643da47 chore: Norwegian Bokmal translations 2025-11-11 17:19:36 +05:30
Jannat Patel
f873b396b6 chore: Bosnian translations 2025-11-11 17:19:35 +05:30
Jannat Patel
5ec67115ba chore: Burmese translations 2025-11-11 17:19:33 +05:30
Jannat Patel
2a21714ed1 chore: Croatian translations 2025-11-11 17:19:31 +05:30
Jannat Patel
1913529bf0 chore: Thai translations 2025-11-11 17:19:30 +05:30
Jannat Patel
419f47d36e chore: Tamil translations 2025-11-11 17:19:28 +05:30
Jannat Patel
fba2b1ea9b chore: Persian translations 2025-11-11 17:19:27 +05:30
Jannat Patel
9f53e2c8da chore: Indonesian translations 2025-11-11 17:19:25 +05:30
Jannat Patel
13f2a79ebb chore: Portuguese, Brazilian translations 2025-11-11 17:19:24 +05:30
Jannat Patel
800f624d1d chore: Vietnamese translations 2025-11-11 17:19:22 +05:30
Jannat Patel
91e7b18506 chore: Chinese Simplified translations 2025-11-11 17:19:21 +05:30
Jannat Patel
cc3a46b1ff chore: Turkish translations 2025-11-11 17:19:19 +05:30
Jannat Patel
a4aed7b61f chore: Swedish translations 2025-11-11 17:19:17 +05:30
Jannat Patel
1aee97c64f chore: Serbian (Cyrillic) translations 2025-11-11 17:19:16 +05:30
Jannat Patel
d33980a8c6 chore: Russian translations 2025-11-11 17:19:14 +05:30
Jannat Patel
c589def0de chore: Portuguese translations 2025-11-11 17:19:12 +05:30
Jannat Patel
c70ef1e2e5 chore: Polish translations 2025-11-11 17:19:11 +05:30
Jannat Patel
60b6a73bd7 chore: Dutch translations 2025-11-11 17:19:09 +05:30
Jannat Patel
b755f3b29b chore: Italian translations 2025-11-11 17:19:07 +05:30
Jannat Patel
be4eeeb9d7 chore: Hungarian translations 2025-11-11 17:19:06 +05:30
Jannat Patel
31beae63fd chore: German translations 2025-11-11 17:19:04 +05:30
Jannat Patel
16a530fb50 chore: Danish translations 2025-11-11 17:19:03 +05:30
Jannat Patel
b8c5b7f479 chore: Czech translations 2025-11-11 17:19:01 +05:30
Jannat Patel
f64475b793 chore: Arabic translations 2025-11-11 17:18:59 +05:30
Jannat Patel
8e6de04e23 chore: Spanish translations 2025-11-11 17:18:58 +05:30
Jannat Patel
25f6440b1b chore: French translations 2025-11-11 17:18:56 +05:30
Jannat Patel
c1bdfe33f0 Merge branch 'develop' of https://github.com/frappe/lms into feature/coupons 2025-11-11 16:28:07 +05:30
Jannat Patel
d7de538345 Merge pull request #1834 from pateljannat/issues-149
fix: clear user cache after updating roles
2025-11-11 15:42:16 +05:30
Jannat Patel
d2950bc0b5 fix: clear user cache after updating roles 2025-11-11 15:32:44 +05:30
Jannat Patel
6333f58f56 Merge pull request #1831 from pateljannat/issues-148
fix: misc
2025-11-11 14:30:50 +05:30
Jannat Patel
bc187aabfe test: open settings before creating evaluator in batch test 2025-11-11 13:17:10 +05:30
Jannat Patel
f825887181 fix: replaced notifications Link with a tag 2025-11-11 13:01:51 +05:30
Jannat Patel
2bdc35055c Merge pull request #1815 from rehanrehman389/link-fix
fix: ensure options reload after updates
2025-11-11 13:00:26 +05:30
Jannat Patel
b6602f9e4b feat: session refresh 2025-11-11 12:55:51 +05:30
Jannat Patel
ab366837a2 fix: escape HTML in forms 2025-11-11 12:17:26 +05:30
Alexandrina-Kuzeleva
655df62d6c UPD 3.1
- add imports in ProfileAbout
2025-11-10 17:34:30 +03:00
Alexandrina-Kuzeleva
d827a10c84 UPD 3
- checked and fixed problems: timer in lesson, sing-up, page length in courses
- add Points and Courses in ProfileAbout
NEW:
- add icon for RuTube
2025-11-10 17:28:26 +03:00
Alexandrina-Kuzeleva
25c640fabb UPD 2
- add RuTube servise
2025-11-10 17:00:10 +03:00
Jannat Patel
c0a7a9b753 Merge branch 'develop' of https://github.com/frappe/lms into data-import 2025-11-10 15:35:42 +05:30
Jannat Patel
c951732eb4 fix: profile edit issue 2025-11-10 15:11:13 +05:30
Jannat Patel
1b638d118d chore: upgraded frappe-ui to latest data import commit 2025-11-10 15:08:07 +05:30
Alexandrina-Kuzeleva
0cb8d21290 UPD 1
- add all AppSidebar links that used
- add singup form
2025-11-10 12:27:28 +03:00
Jannat Patel
1173ac6504 Merge pull request #1830 from pateljannat/issues-147
fix: misc issues
2025-11-10 13:42:13 +05:30
Jannat Patel
9447903d5b fix: misc issues 2025-11-10 13:19:54 +05:30
Jannat Patel
fff9769791 fix: profile slot and schedule tab visibility 2025-11-10 12:09:51 +05:30
Jannat Patel
d5c5faf0ca Merge pull request #1829 from pateljannat/issues-146
fix: evaluation issues
2025-11-10 11:34:18 +05:30
Jannat Patel
85d793ee64 fix: evaluation issues 2025-11-10 11:24:43 +05:30
Jannat Patel
cf9c9fb5d3 chore: upgraded frappe-ui to latest data import commit 2025-11-10 10:39:43 +05:30
Jannat Patel
b7144727e9 Merge pull request #1828 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-10 09:12:29 +05:30
Jannat Patel
60cd84972c Merge pull request #1827 from frappe/pot_develop_2025-11-07
chore: update POT file
2025-11-10 09:12:16 +05:30
Jannat Patel
5854d2514f fix: reload import list after import completes 2025-11-09 15:16:32 +05:30
Jannat Patel
226893a8b2 chore: upgraded frappe-ui to latest data import commit 2025-11-09 14:57:27 +05:30
Jannat Patel
b182d5ea16 fix: replaced Link component with a tag in notifications 2025-11-09 12:12:35 +05:30
Jannat Patel
6bc28eafbf chore: upgraded frappe-ui to latest data import commit 2025-11-08 19:28:54 +05:30
Jannat Patel
1983190da3 chore: Indonesian translations 2025-11-08 14:05:12 +05:30
frappe-pr-bot
656c0cf012 chore: update POT file 2025-11-07 16:04:28 +00:00
Jannat Patel
60ddc1f8b2 chore: merged conflicts 2025-11-07 15:45:21 +05:30
Jannat Patel
c3604ab74a chore: changed frappe-ui version to data-import commit 2025-11-07 15:26:25 +05:30
Jannat Patel
eac0428dc6 Merge pull request #1822 from frappe/develop
chore: merge 'develop' into 'main'
2025-11-07 12:41:36 +05:30
Frappe PR Bot
53cd427a75 chore(release): Bumped to Version 2.40.0 2025-11-07 06:34:46 +00:00
Jannat Patel
96cf6ddbf9 Merge pull request #1791 from rehanrehman389/job-application
feat: show job applications on frontend
2025-11-07 12:02:02 +05:30
Jannat Patel
66c2ec013c Merge pull request #1814 from rehanrehman389/fix-resume-url
fix: use file_url instead of file_name
2025-11-07 12:00:54 +05:30
Rehan Ansari
e3d5bf0220 refactor: add dropdown menu for job application actions 2025-11-06 23:42:36 +05:30
Jannat Patel
3281358282 fix: added horizontal scroll to settings modal 2025-11-06 16:45:31 +05:30
Jannat Patel
7a47591967 Merge pull request #1819 from rehanrehman389/misc-fix
fix: UI improvements
2025-11-06 16:43:28 +05:30
Jannat Patel
6931ca27c3 Merge pull request #1824 from pateljannat/issues-145
fix: roles, permission and access on profile page
2025-11-06 12:51:45 +05:30
Jannat Patel
d00d2de1cc fix: export livecodeURL from settings store 2025-11-06 12:28:27 +05:30
Jannat Patel
b1be568991 fix: removed uncalled function 2025-11-06 12:23:18 +05:30
Jannat Patel
28be3891d2 fix: roles, permission and access on profile page 2025-11-06 12:21:12 +05:30
Jannat Patel
27d2297e2b Merge pull request #1823 from pateljannat/issues-144
fix: misc improvements
2025-11-05 12:50:21 +05:30
Jannat Patel
7212ddd5c5 fix: evaluators and modetators can now see schedule of other evaluators 2025-11-05 12:34:40 +05:30
Jannat Patel
f4e9ac5bf1 fix: IPhone PWA install prompt 2025-11-05 11:59:45 +05:30
Jannat Patel
18e499e6de fix: coupon code application on billing page 2025-11-04 15:12:17 +05:30
Jannat Patel
8fec484d66 Merge pull request #1818 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-11-04 13:14:49 +05:30
Rehan Ansari
514d52f895 fix: improve job applications UI 2025-11-03 09:49:01 +05:30
Rehan Ansari
8719fa6696 feat: refactor job applications UI with ListView 2025-11-03 01:49:59 +05:30
Jannat Patel
bcf781c37b chore: Serbian (Latin) translations 2025-11-03 00:37:46 +05:30
Jannat Patel
d8a8e689d0 chore: Bosnian translations 2025-11-03 00:37:45 +05:30
Jannat Patel
a844b95de3 chore: Burmese translations 2025-11-03 00:37:44 +05:30
Jannat Patel
ece885f973 chore: Thai translations 2025-11-03 00:37:42 +05:30
Jannat Patel
66dd30604b chore: Tamil translations 2025-11-03 00:37:41 +05:30
Jannat Patel
d0f0f4905c chore: Indonesian translations 2025-11-03 00:37:40 +05:30
Jannat Patel
c9cb6702b6 chore: Portuguese, Brazilian translations 2025-11-03 00:37:39 +05:30
Jannat Patel
1ddb980242 chore: Vietnamese translations 2025-11-03 00:37:37 +05:30
Jannat Patel
94b626a4d2 chore: Chinese Simplified translations 2025-11-03 00:37:36 +05:30
Jannat Patel
d2a011462d chore: Turkish translations 2025-11-03 00:37:35 +05:30
Jannat Patel
4c34926af0 chore: Serbian (Cyrillic) translations 2025-11-03 00:37:34 +05:30
Jannat Patel
ce35cd1009 chore: Russian translations 2025-11-03 00:37:32 +05:30
Jannat Patel
56d072bd06 chore: Portuguese translations 2025-11-03 00:37:31 +05:30
Jannat Patel
5d336ef669 chore: Polish translations 2025-11-03 00:37:30 +05:30
Jannat Patel
b47c59eac1 chore: Dutch translations 2025-11-03 00:37:29 +05:30
Jannat Patel
87285db361 chore: Italian translations 2025-11-03 00:37:27 +05:30
Jannat Patel
84312e498c chore: Hungarian translations 2025-11-03 00:37:26 +05:30
Jannat Patel
bd763d9462 chore: German translations 2025-11-03 00:37:25 +05:30
Jannat Patel
a00e66f786 chore: Czech translations 2025-11-03 00:37:24 +05:30
Jannat Patel
78c7b52088 chore: Arabic translations 2025-11-03 00:37:23 +05:30
Jannat Patel
c3a5bee993 chore: Spanish translations 2025-11-03 00:37:21 +05:30
Jannat Patel
c2b5b7c3e2 chore: French translations 2025-11-03 00:37:20 +05:30
Jannat Patel
3992f00353 chore: Persian translations 2025-11-03 00:37:19 +05:30
Jannat Patel
97d853e0d3 chore: Danish translations 2025-11-03 00:37:18 +05:30
Jannat Patel
f786cec75f chore: Norwegian Bokmal translations 2025-11-03 00:37:16 +05:30
Jannat Patel
07cd08b55e chore: Croatian translations 2025-11-03 00:37:15 +05:30
Jannat Patel
ca42faf14a chore: Swedish translations 2025-11-03 00:37:14 +05:30
Rehan Ansari
87f5b68279 fix: UI improvements 2025-11-02 13:43:25 +05:30
Rehan Ansari
9c38444c4b fix: make resumes private, linked and shared with job owner 2025-11-01 19:08:36 +05:30
Rehan Ansari
a5f9adc875 fix: patch old resumes to be private, link to job application, and share with job owner 2025-11-01 14:48:45 +05:30
Jannat Patel
6b31edb687 chore: Esperanto translations 2025-10-31 23:03:40 +05:30
Jannat Patel
6a64048bb6 Merge pull request #1817 from frappe/pot_develop_2025-10-31
chore: update POT file
2025-10-31 22:03:56 +05:30
frappe-pr-bot
6cf069ee6a chore: update POT file 2025-10-31 16:04:36 +00:00
Rehan Ansari
74de43c3d6 fix: ensure options reload after updates 2025-10-31 00:10:16 +05:30
Jannat Patel
3b74bba6ab Merge pull request #1813 from rehanrehman389/missing-type-fix
fix: add missing type prop
2025-10-30 11:13:31 +05:30
Rehan Ansari
9f81bf695c fix: use file_url instead of file_name 2025-10-30 01:03:01 +05:30
Rehan Ansari
8689788523 fix: add missing type prop 2025-10-29 21:41:48 +05:30
Jannat Patel
3caf743f29 Merge pull request #1811 from frappe/develop
chore: merge 'develop' into 'main'
2025-10-29 21:02:30 +05:30
Frappe PR Bot
1193776d06 chore(release): Bumped to Version 2.39.2 2025-10-29 12:29:14 +00:00
Jannat Patel
ad28218893 refactor: coupon list and form 2025-10-28 18:05:31 +05:30
Jannat Patel
022514a0a7 Merge pull request #1810 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-28 10:56:10 +05:30
Jannat Patel
dc7f8a59ed chore: Esperanto translations 2025-10-27 22:34:42 +05:30
Jannat Patel
4e5a76a6c1 chore: Serbian (Latin) translations 2025-10-27 22:34:41 +05:30
Jannat Patel
64c4a25ee8 chore: Bosnian translations 2025-10-27 22:34:39 +05:30
Jannat Patel
47bbdbaa26 chore: Burmese translations 2025-10-27 22:34:38 +05:30
Jannat Patel
f8e0c0e19a chore: Thai translations 2025-10-27 22:34:37 +05:30
Jannat Patel
94cdd19224 chore: Tamil translations 2025-10-27 22:34:35 +05:30
Jannat Patel
d86d046eb0 chore: Indonesian translations 2025-10-27 22:34:34 +05:30
Jannat Patel
25ec6b5a3f chore: Portuguese, Brazilian translations 2025-10-27 22:34:32 +05:30
Jannat Patel
967453a683 chore: Vietnamese translations 2025-10-27 22:34:30 +05:30
Jannat Patel
4c17305c05 chore: Chinese Simplified translations 2025-10-27 22:34:29 +05:30
Jannat Patel
6092131303 chore: Turkish translations 2025-10-27 22:34:28 +05:30
Jannat Patel
35749834d0 chore: Serbian (Cyrillic) translations 2025-10-27 22:34:26 +05:30
Jannat Patel
fe56c7b887 chore: Russian translations 2025-10-27 22:34:25 +05:30
Jannat Patel
2b58a744d2 chore: Portuguese translations 2025-10-27 22:34:24 +05:30
Jannat Patel
dfb94d05e4 chore: Polish translations 2025-10-27 22:34:22 +05:30
Jannat Patel
987c1790d8 chore: Dutch translations 2025-10-27 22:34:21 +05:30
Jannat Patel
0d416b17ce chore: Italian translations 2025-10-27 22:34:19 +05:30
Jannat Patel
473e165c89 chore: Hungarian translations 2025-10-27 22:34:18 +05:30
Jannat Patel
3d52d15004 chore: German translations 2025-10-27 22:34:17 +05:30
Jannat Patel
27278e128c chore: Czech translations 2025-10-27 22:34:16 +05:30
Jannat Patel
13cee3c9b3 chore: Arabic translations 2025-10-27 22:34:14 +05:30
Jannat Patel
fd95e42e9b chore: Spanish translations 2025-10-27 22:34:13 +05:30
Jannat Patel
65cd2f5d01 chore: French translations 2025-10-27 22:34:11 +05:30
Jannat Patel
70759d1888 chore: Persian translations 2025-10-27 22:34:10 +05:30
Jannat Patel
705d6e2f00 chore: Danish translations 2025-10-27 22:34:09 +05:30
Jannat Patel
088591a335 chore: Norwegian Bokmal translations 2025-10-27 22:34:07 +05:30
Jannat Patel
3f037e0d17 chore: Croatian translations 2025-10-27 22:34:06 +05:30
Jannat Patel
e6884b6c93 chore: Swedish translations 2025-10-27 22:34:04 +05:30
Jannat Patel
760811f172 Merge branch 'develop' of https://github.com/frappe/lms into feature/coupons 2025-10-27 12:39:10 +05:30
Jannat Patel
9943268ca0 Merge pull request #1809 from pateljannat/issues-143
fix: resolved the issue that appeared when adding a chapter
2025-10-27 11:50:25 +05:30
Jannat Patel
620e4d20c2 Merge pull request #1805 from rehanrehman389/dark-mode-fix
fix: multiple fixes for dark mode visibility
2025-10-27 11:39:26 +05:30
Jannat Patel
fd03033ac6 Merge pull request #1804 from frappe/pot_develop_2025-10-24
chore: update POT file
2025-10-27 11:37:35 +05:30
Jannat Patel
939099b8c8 Merge pull request #1808 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-27 11:37:19 +05:30
Jannat Patel
75001b494d fix: escape HTML in job form fields 2025-10-27 11:36:46 +05:30
Jannat Patel
8749e21744 fix: only users with moderator and instructor role should have access to quiz form 2025-10-27 11:06:09 +05:30
Jannat Patel
982ac98e27 fix: resolved the issue that appeared when adding a chapter 2025-10-27 10:32:24 +05:30
Jannat Patel
f31bf17a41 chore: Polish translations 2025-10-26 21:43:16 +05:30
Rehan Ansari
3425d9118d fix: NotPermitted text visibility in dark mode 2025-10-26 00:19:46 +05:30
Rehan Ansari
6be49ecdf3 fix: multiple fixes for dark mode visibility 2025-10-25 15:38:08 +05:30
Rehan Ansari
68e7684da3 fix: avoid backend updates without save action 2025-10-25 14:42:22 +05:30
Rehan Ansari
ca2cc7bbda fix: improve delete button UX and add confirmation dialog 2025-10-25 14:09:31 +05:30
Rehan Ansari
741cc4ccc7 fix: allow adding courses/members to new programs 2025-10-25 13:03:53 +05:30
Rehan Ansari
0c1f1fada4 fix: incorrect error msg 2025-10-25 10:09:31 +05:30
frappe-pr-bot
ffd6f9578b chore: update POT file 2025-10-24 16:04:25 +00:00
Jannat Patel
41293130ad Merge pull request #1803 from rehanrehman389/brand-setting
fix: handle missing file_url
2025-10-24 10:22:33 +05:30
Rehan Ansari
6cccd28b92 fix: handle missing file_url 2025-10-23 22:50:48 +05:30
Jannat Patel
384f10a722 Merge pull request #1801 from rehanrehman389/streak-fix
fix: improve visibility in dark mode
2025-10-23 18:37:36 +05:30
Jannat Patel
a603e299f1 Merge pull request #1800 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-23 18:36:46 +05:30
Rehan Ansari
05822f82da fix: streak number visibility in dark mode 2025-10-22 23:24:02 +05:30
Rehan Ansari
0508e718cb fix: job modal text in dark mode 2025-10-22 22:56:54 +05:30
Rehan Ansari
574913e9e4 fix: improve visibility in billing page 2025-10-22 22:35:01 +05:30
Rehan Ansari
068adb62a7 fix: improve visibility in dark mode 2025-10-22 21:55:03 +05:30
Jannat Patel
73fa1f9cfe chore: Serbian (Latin) translations 2025-10-22 21:17:52 +05:30
Jannat Patel
f518882926 chore: Serbian (Cyrillic) translations 2025-10-22 21:17:42 +05:30
Himanshu Shivhare
5a91c73a91 Merge branch 'develop' into patch-2 2025-10-22 17:38:05 +05:30
Jannat Patel
ed566f9eea Merge pull request #1794 from rehanrehman389/setting-scroll-fix
fix: scroll issue in settings
2025-10-22 10:56:40 +05:30
Jannat Patel
8ca32e439a Merge pull request #1795 from rehanrehman389/notes-dark-mode
fix: update font color in dark mode for My Notes
2025-10-22 10:47:35 +05:30
Jannat Patel
35b3b11a3c Merge pull request #1798 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-22 10:46:45 +05:30
Jannat Patel
57d4a53081 chore: Bosnian translations 2025-10-21 21:17:36 +05:30
Jannat Patel
6da05961f2 chore: Polish translations 2025-10-21 21:17:35 +05:30
Jannat Patel
7db3b8c5b8 chore: Danish translations 2025-10-21 21:17:32 +05:30
Jannat Patel
50bafb6fa6 chore: Croatian translations 2025-10-21 21:17:31 +05:30
Jannat Patel
2b3a9072d1 Merge pull request #1793 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-21 21:11:41 +05:30
Rehan Ansari
1c08e57086 fix: update font color in dark mode for My Notes 2025-10-21 13:57:52 +05:30
Rehan Ansari
4290ed2f04 fix: scroll issue in settings 2025-10-20 21:42:41 +05:30
Jannat Patel
342512f3e1 chore: Esperanto translations 2025-10-20 20:49:25 +05:30
Jannat Patel
942c04cb68 chore: Serbian (Latin) translations 2025-10-20 20:49:23 +05:30
Jannat Patel
64bf4ab3f7 chore: Bosnian translations 2025-10-20 20:49:22 +05:30
Jannat Patel
052e69737e chore: Burmese translations 2025-10-20 20:49:21 +05:30
Jannat Patel
02adc4517c chore: Thai translations 2025-10-20 20:49:19 +05:30
Jannat Patel
e36fdd6823 chore: Tamil translations 2025-10-20 20:49:18 +05:30
Jannat Patel
d10a7ed57f chore: Indonesian translations 2025-10-20 20:49:16 +05:30
Jannat Patel
79adf44dfe chore: Portuguese, Brazilian translations 2025-10-20 20:49:15 +05:30
Jannat Patel
4bc3113f34 chore: Vietnamese translations 2025-10-20 20:49:14 +05:30
Jannat Patel
0826704282 chore: Chinese Simplified translations 2025-10-20 20:49:12 +05:30
Jannat Patel
52aa5e6954 chore: Turkish translations 2025-10-20 20:49:11 +05:30
Jannat Patel
fde85607d9 chore: Serbian (Cyrillic) translations 2025-10-20 20:49:09 +05:30
Jannat Patel
cc087af012 chore: Russian translations 2025-10-20 20:49:08 +05:30
Jannat Patel
2c7da1e32e chore: Portuguese translations 2025-10-20 20:49:06 +05:30
Jannat Patel
49fe8952ae chore: Polish translations 2025-10-20 20:49:05 +05:30
Jannat Patel
b298cd0509 chore: Dutch translations 2025-10-20 20:49:04 +05:30
Jannat Patel
a81fc11e73 chore: Italian translations 2025-10-20 20:49:02 +05:30
Jannat Patel
199fb6229d chore: Hungarian translations 2025-10-20 20:49:01 +05:30
Jannat Patel
ec6ecee455 chore: German translations 2025-10-20 20:48:59 +05:30
Jannat Patel
fa72172b77 chore: Czech translations 2025-10-20 20:48:58 +05:30
Jannat Patel
6789700def chore: Arabic translations 2025-10-20 20:48:56 +05:30
Jannat Patel
752744b3a4 chore: Spanish translations 2025-10-20 20:48:55 +05:30
Jannat Patel
c24fa85bf4 chore: French translations 2025-10-20 20:48:54 +05:30
Jannat Patel
4e0b59f6a9 chore: Persian translations 2025-10-20 20:48:52 +05:30
Jannat Patel
bd20214552 chore: Danish translations 2025-10-20 20:48:51 +05:30
Jannat Patel
4af0ea9e47 chore: Norwegian Bokmal translations 2025-10-20 20:48:49 +05:30
Jannat Patel
8651679634 chore: Croatian translations 2025-10-20 20:48:48 +05:30
Jannat Patel
99dcac6d12 chore: Swedish translations 2025-10-20 20:48:46 +05:30
Rehan Ansari
9da1bfeea1 fix: set job owner as reply-to for job emails 2025-10-20 13:21:52 +05:30
Jannat Patel
d4b603a4dd Merge pull request #1792 from frappe/develop
chore: merge 'develop' into 'main'
2025-10-20 12:08:28 +05:30
Frappe PR Bot
853bf01c9e chore(release): Bumped to Version 2.39.1 2025-10-20 06:28:36 +00:00
Jannat Patel
39c5ad7267 Merge pull request #1788 from frappe/pot_develop_2025-10-17
chore: update POT file
2025-10-20 11:55:24 +05:30
Jannat Patel
8daa2948fa Merge pull request #1787 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-20 11:55:10 +05:30
Rehan Ansari
a1a302f222 feat: show job applications on frontend 2025-10-20 11:17:02 +05:30
Jannat Patel
f2ba25429e chore: Persian translations 2025-10-19 20:29:28 +05:30
Jannat Patel
1fffb4dc67 chore: Danish translations 2025-10-19 20:29:27 +05:30
frappe-pr-bot
45ce2439fd chore: update POT file 2025-10-17 16:04:25 +00:00
Jannat Patel
cb2e77e8f6 chore: Norwegian Bokmal translations 2025-10-17 20:15:15 +05:30
Jannat Patel
800c0b0336 chore: Croatian translations 2025-10-17 20:15:13 +05:30
Jannat Patel
14c23496d5 chore: Swedish translations 2025-10-17 20:15:12 +05:30
Jannat Patel
7756a6d593 fix: increased the rate limit 2025-10-17 15:59:32 +05:30
Jannat Patel
ae7791a204 Merge branch 'develop' of https://github.com/frappe/lms into develop 2025-10-15 14:45:14 +05:30
Jannat Patel
44232c44fc fix: activation level doctype name 2025-10-15 14:44:53 +05:30
Jannat Patel
7d2b98f674 Merge pull request #1782 from frappe/develop
chore: merge 'develop' into 'main'
2025-10-15 12:21:17 +05:30
Jannat Patel
142fc99761 Merge pull request #1783 from pateljannat/issues-140
fix: misc issues
2025-10-15 12:08:39 +05:30
Jannat Patel
5e6dc55c76 fix: added rate limit to all apis that can be accessed by guest 2025-10-15 11:55:58 +05:30
Jannat Patel
bb2447e821 fix: misc issues 2025-10-15 11:52:23 +05:30
Jannat Patel
2f5b0a3bf8 feat: data import 2025-10-14 19:21:35 +05:30
Joedeep Singh
1efd5ebad5 Merge branch 'develop' into feature/coupons 2025-10-13 21:37:44 +05:30
Joedeep Singh
acb5e5e1c9 chore: removed unnecessary comments and log statements 2025-10-13 15:35:56 +00:00
Joedeep Singh
0f24fd6edc feat(coupon-details): prevent duplicate course/batch selections and clear name on type change 2025-10-13 14:54:03 +00:00
Joedeep Singh
99c448e0e5 chore: applied pre-commit hooks 2025-10-13 14:06:37 +00:00
Joedeep Singh
cabb499a43 fix: correctly show coupon code in transactions 2025-10-13 13:47:02 +00:00
Joedeep Singh
6933105261 feat: implement coupon code manage
ment with billing and transaction integration; bug fixes
2025-10-13 13:03:43 +00:00
Joedeep Singh
bf36890bd3 feat: added coupon code functionality 2025-10-12 17:07:42 +00:00
Himanshu Shivhare
05b6b97b2a fix: beautifulsoap4 version upgrade to match with Frappe Framework 2025-10-12 17:00:34 +05:30
Jannat Patel
6a8aca39a0 Merge pull request #1764 from frappe/develop
chore: merge 'develop' into 'main'
2025-10-10 16:45:01 +05:30
Jannat Patel
7434a324fb Merge pull request #1754 from frappe/develop
chore: merge 'develop' into 'main'
2025-10-01 13:08:16 +05:30
Jannat Patel
fd934e1e82 Merge pull request #1735 from frappe/develop
chore: merge 'develop' into 'main'
2025-09-24 13:02:43 +05:30
Jannat Patel
dd94d12e3a Merge pull request #1723 from frappe/develop
chore: merge 'develop' into 'main'
2025-09-19 10:51:28 +05:30
Jannat Patel
fe85dd867d Merge branch 'develop' of https://github.com/frappe/lms 2025-09-03 15:44:43 +05:30
Jannat Patel
12f2047910 Merge pull request #1709 from frappe/develop
chore: merge 'develop' into 'main'
2025-09-03 12:30:33 +05:30
Jannat Patel
cb6931bd88 Merge pull request #1699 from frappe/develop
chore: merge 'develop' into 'main'
2025-08-29 17:42:38 +05:30
Jannat Patel
c76f9141fc Merge pull request #1685 from frappe/develop
chore: merge 'develop' into 'main'
2025-08-13 10:47:33 +05:30
Jannat Patel
ddf70ce3d4 Merge pull request #1662 from frappe/develop
chore: merge 'develop' into 'main'
2025-07-30 11:57:59 +05:30
Jannat Patel
7beb82e804 Merge pull request #1654 from frappe/develop
chore: merge 'develop' into 'main'
2025-07-23 11:34:46 +05:30
Jannat Patel
8d9b8951bf Merge pull request #1652 from frappe/develop
chore: merge 'develop' into 'main'
2025-07-23 11:10:44 +05:30
Jannat Patel
b20d045c8e Merge pull request #1650 from frappe/develop
chore: merge 'develop' into 'main'
2025-07-22 19:00:42 +05:30
Jannat Patel
736ce8eb3f Merge pull request #1636 from frappe/develop
chore: merge 'develop' into 'main'
2025-07-14 12:06:42 +05:30
Jannat Patel
3409049559 Merge pull request #1576 from frappe/develop
chore: merge 'develop' into 'main'
2025-06-16 17:17:02 +05:30
Jannat Patel
5212122946 Merge pull request #1570 from frappe/develop
chore: merge 'develop' into 'main'
2025-06-10 15:51:20 +05:30
457 changed files with 69855 additions and 46491 deletions

View File

@@ -3,10 +3,15 @@ on:
push:
branches:
- main
- develop
- main-hotfix
pull_request: {}
jobs:
tests:
name: Server Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
services:
redis-cache:
image: redis:alpine
@@ -30,13 +35,13 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: setup python
uses: actions/setup-python@v2
uses: actions/setup-python@v6
with:
python-version: '3.10'
python-version: '3.14'
- name: setup node
uses: actions/setup-node@v2
uses: actions/setup-node@v6
with:
node-version: '18'
node-version: '24'
check-latest: true
- name: setup cache for bench
uses: actions/cache@v4
@@ -69,6 +74,9 @@ jobs:
- name: setup requirements
working-directory: /home/runner/frappe-bench
run: bench setup requirements --dev
- name: block endpoints
working-directory: /home/runner/frappe-bench
run: bench --site frappe.local set-config block_endpoints 1
- name: allow tests
working-directory: /home/runner/frappe-bench
run: bench --site frappe.local set-config allow_tests true
@@ -77,4 +85,27 @@ jobs:
run: bench --site frappe.local build
- name: run tests
working-directory: /home/runner/frappe-bench
run: bench --site frappe.local run-tests --app lms
run: bench --site frappe.local run-tests --app lms --coverage
- name: Upload coverage data
uses: actions/upload-artifact@v4
with:
path: /home/runner/frappe-bench/sites/coverage.xml
coverage:
name: Coverage Wrap Up
needs: tests
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v3
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Upload coverage data
uses: codecov/codecov-action@v5
with:
name: Server
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true

View File

@@ -22,9 +22,14 @@ jobs:
ref: ${{ matrix.branch }}
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.12"
python-version: "3.14"
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Run script to update POT file
run: |

View File

@@ -16,9 +16,9 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 200
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
check-latest: true
- name: Check commit titles
@@ -35,9 +35,9 @@ jobs:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: '3.10'
python-version: '3.14'
- name: Cache pip
uses: actions/cache@v4

View File

@@ -18,9 +18,9 @@ jobs:
owner: frappe
repo: lms
title: |-
"chore: merge 'develop' into 'main'"
"chore: merge 'main-hotfix' into 'main'"
body: "Automated weekly release"
base: main
head: develop
head: main-hotfix
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

View File

@@ -15,9 +15,9 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save

View File

@@ -4,7 +4,10 @@ on:
pull_request:
workflow_dispatch:
push:
branches: [ main ]
branches:
- main
- develop
- main-hotfix
permissions:
# Do not change this as GITHUB_TOKEN is being used by roulette
@@ -36,9 +39,9 @@ jobs:
uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: '3.11'
python-version: '3.14'
- name: Check for valid Python & Merge Conflicts
run: |
@@ -48,9 +51,9 @@ jobs:
exit 1
fi
- uses: actions/setup-node@v3
- uses: actions/setup-node@v6
with:
node-version: 18
node-version: 24
check-latest: true
- name: Add to Hosts

4
.gitignore vendored
View File

@@ -12,4 +12,6 @@ node_modules
package-lock.json
lms/public/frontend
lms/www/lms.html
frappe-ui
lms/www/_lms.html
frappe-ui
frappe-semgrep-rules

30
.mergify.yml Normal file
View File

@@ -0,0 +1,30 @@
pull_request_rules:
- name: backport to develop
conditions:
- label="backport develop"
actions:
backport:
branches:
- develop
assignees:
- "{{ author }}"
- name: backport to main-hotfix
conditions:
- label="backport main-hotfix"
actions:
backport:
branches:
- main-hotfix
assignees:
- "{{ author }}"
- name: backport to main
conditions:
- label="backport main"
actions:
backport:
branches:
- main
assignees:
- "{{ author }}"

View File

@@ -1,5 +1,5 @@
{
"branches": ["develop"],
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular"

178
README.md
View File

@@ -1,178 +0,0 @@
<div align="center" markdown="1">
<img src=".github/lms-logo.png" alt="Frappe Learning logo" width="80" height="80"/>
<h1>Frappe Learning</h1>
**Easy to use, open source, Learning Management System**
![Tests](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/vandxn/main&style=flat&logo=cypress)
</div>
<div align="center">
<img src=".github/hero.png?v=5" alt="Hero Image" width="72%" />
</div>
<br />
<div align="center">
<a href="https://frappe.io/learning">Website</a>
-
<a href="https://docs.frappe.io/learning">Documentation</a>
</div>
## Frappe Learning
Frappe Learning is an easy-to-use learning system that helps you bring structure to your content.
### Motivation
In 2021, we were looking for a Learning Management System to launch [Mon.School](https://mon.school) for FOSS United. We checked out Moodle, but it didnt feel right. The forms were unnecessarily lengthy and the UI was confusing. It shouldn't be this hard to create a course right? So I started making a learning system for Mon.School which soon became a product in itself. The aim is to have a simple platform that anyone can use to launch a course of their own and make knowledge sharing easier.
### Key Features
- **Structured Learning**: Design a course with a 3-level hierarchy, where your courses have chapters and you can group your lessons within these chapters. This ensures that the context of the lesson is set by the chapter.
- **Live Classes**: Group learners into batches based on courses and duration. You can then create Zoom live class for these batches right from the app. Learners get to see the list of live classes they have to take as a part of this batch.
- **Quizzes and Assignments**: Create quizzes where questions can have single-choice, multiple-choice options, or can be open ended. Instructors can also add assignments which learners can submit as PDF's or Documents.
- **Getting Certified**: Once a learner has completed the course or batch, you can grant them a certificate. The app provides an inbuilt certificate template. You can use this or else create a template of your own and use that instead.
<details>
<summary>View Screenshots</summary>
![Batch](.github/batch.png)
<div align="center">
<sub>
Create batches to group your learners
</sub>
</div>
<br>
![Quiz](.github/quiz.png)
<div align="center">
<sub>
Evaluate their knowledge by quizzes
</sub>
</div>
<br>
![Cerficicate](.github/certificate.png)
<div align="center">
<sub>
Autenticate their work with certification
</sub>
</div>
</details>
### Under the Hood
- [**Frappe Framework**](https://github.com/frappe/frappe): A full-stack web application framework.
- [**Frappe UI**](https://github.com/frappe/frappe-ui): A Vue-based UI library, to provide a modern user interface.
## Production Setup
### Managed Hosting
You can try [Frappe Cloud](https://frappecloud.com), a simple, user-friendly and sophisticated [open-source](https://github.com/frappe/press) platform to host Frappe applications with peace of mind.
It takes care of installation, setup, upgrades, monitoring, maintenance and support of your Frappe deployments. It is a fully featured developer platform with an ability to manage and control multiple Frappe deployments.
<div>
<a href="https://frappecloud.com/lms/signup" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/try-on-fc-white.png">
<img src="https://frappe.io/files/try-on-fc-black.png" alt="Try on Frappe Cloud" height="28" />
</picture>
</a>
</div>
### Self Hosting
Follow these steps to set up Frappe Learning in production:
**Step 1**: Download the easy install script
```bash
wget https://frappe.io/easy-install.py
```
**Step 2**: Run the deployment command
```bash
python3 ./easy-install.py deploy \
--project=learning_prod_setup \
--email=your_email.example.com \
--image=ghcr.io/frappe/lms \
--version=stable \
--app=lms \
--sitename subdomain.domain.tld
```
Replace the following parameters with your values:
- `your_email.example.com`: Your email address
- `subdomain.domain.tld`: Your domain name where Learning will be hosted
The script will set up a production-ready instance of Frappe Learning with all the necessary configurations in about 5 minutes.
**Note:** To avoid a `404 Page Not Found` error:
- If hosting on a **public server**, make sure your DNS **A record** points to your server's IP.
- If hosting **locally**, map your domain to `127.0.0.1` in your `/etc/hosts` file:
## Development Setup
### Docker
You need Docker, docker-compose and git setup on your machine. Refer [Docker documentation](https://docs.docker.com/). After that, follow below steps:
**Step 1**: Setup folder and download the required files
mkdir frappe-learning
cd frappe-learning
# Download the docker-compose file
wget -O docker-compose.yml https://raw.githubusercontent.com/frappe/lms/develop/docker/docker-compose.yml
# Download the setup script
wget -O init.sh https://raw.githubusercontent.com/frappe/lms/develop/docker/init.sh
**Step 2**: Run the container and daemonize it
docker compose up -d
**Step 3**: The site [http://lms.localhost:8000/lms](http://lms.localhost:8000/lms) should now be available. The default credentials are:
- Username: Administrator
- Password: admin
### Local
To setup the repository locally follow the steps mentioned below:
1. Install bench and setup a `frappe-bench` directory by following the [Installation Steps](https://frappeframework.com/docs/user/en/installation)
1. Start the server by running `bench start`
1. In a separate terminal window, create a new site by running `bench new-site learning.test`
1. Map your site to localhost with the command `bench --site learning.test add-to-hosts`
1. Get the Learning app. Run `bench get-app https://github.com/frappe/lms`
1. Run `bench --site learning.test install-app lms`.
1. Now open the URL `http://learning.test:8000/lms` in your browser, you should see the app running
## Learn and connect
- [Telegram Public Group](https://t.me/frappelms)
- [Discuss Forum](https://discuss.frappe.io/c/lms/70)
- [Documentation](https://docs.frappe.io/learning)
- [YouTube](https://www.youtube.com/channel/UCn3bV5kx77HsVwtnlCeEi_A)
<br>
<br>
<div align="center" style="padding-top: 0.75rem;">
<a href="https://frappe.io" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/Frappe-white.png">
<img src="https://frappe.io/files/Frappe-black.png" alt="Frappe Technologies" height="28"/>
</picture>
</a>
</div>

2
codecov.yml Normal file
View File

@@ -0,0 +1,2 @@
ignore:
- "**/test_helper.py"

View File

@@ -27,6 +27,10 @@ describe("Batch Creation", () => {
cy.get("input[placeholder='Jane']").type(randomName);
cy.get("button").contains("Add").click();
// Open Settings
cy.get("span").contains("Learning").click();
cy.get("span").contains("Settings").click();
// Add evaluator
cy.get("[data-dismissable-layer]")
.find("span")
@@ -48,26 +52,23 @@ describe("Batch Creation", () => {
// Create a batch
cy.get("button").contains("Create").click();
cy.get("span").contains("New Batch").click();
cy.wait(500);
cy.url().should("include", "/batches/new/edit");
cy.get("label").contains("Title").type("Test Batch");
cy.get("label").contains("Start Date").type("2030-10-01");
cy.get("label").contains("End Date").type("2030-10-31");
cy.get("label").contains("Start Time").type("10:00");
cy.get("label").contains("End Time").type("11:00");
cy.get("label").contains("Timezone").type("IST");
cy.get("label").contains("Seat Count").type("10");
cy.get("label").contains("Published").click();
cy.get("label")
.contains("Short Description")
.contains("Description")
.type("Test Batch Short Description to test the UI");
cy.get("div[contenteditable=true").invoke(
"text",
"Test Batch Description. I need a very big description to test the UI. This is a very big description. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now."
);
/* Instructor */
cy.get("label")
.contains("Instructors")
@@ -85,13 +86,14 @@ describe("Batch Creation", () => {
cy.get("[id^=headlessui-combobox-option-").first().click();
});
});
cy.button("Save").click();
cy.get("label").contains("Published").click();
cy.button("Save").click();
cy.wait(1000);
let batchName;
cy.url().then((url) => {
console.log(url);
batchName = url.split("/").pop();
batchName = url.split("/").pop().split("#")[0];
cy.wrap(batchName).as("batchName");
});
cy.wait(500);
@@ -110,7 +112,7 @@ describe("Batch Creation", () => {
.click();
cy.get("@batchName").then((batchName) => {
cy.get(`a[href='/lms/batches/details/${batchName}'`).within(() => {
cy.get(`a[href='/lms/batches/${batchName}'`).within(() => {
cy.get("div").contains("Test Batch").should("be.visible");
cy.get("div")
.contains("Test Batch Short Description to test the UI")
@@ -123,14 +125,11 @@ describe("Batch Creation", () => {
.should("be.visible");
cy.get("span").contains("IST").should("be.visible");
cy.get("a").contains("Evaluator").should("be.visible");
cy.get("div")
.contains("10")
.should("be.visible")
.get("span")
.contains("Seats Left")
.should("be.visible");
cy.contains("div:visible", "10 Seats Left").should(
"be.visible"
);
});
cy.get(`a[href='/lms/batches/details/${batchName}'`).click();
cy.get(`a[href='/lms/batches/${batchName}'`).click();
});
cy.get("div").contains("Test Batch").should("be.visible");
@@ -152,17 +151,22 @@ describe("Batch Creation", () => {
"Test Batch Description. I need a very big description to test the UI. This is a very big description. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now."
)
.should("be.visible");
cy.get("button:visible").contains("Manage Batch").click();
cy.get("button:visible").contains("Dashboard").click();
/* Add student to batch */
cy.get("button").contains("Add").click();
cy.get('div[role="dialog"]').first().find("button").eq(1).click();
cy.get("input[id^='headlessui-combobox-input-v-']").type(randomEmail);
cy.get("button").contains("Enroll").click();
cy.get('div[role="dialog"]')
.first()
.find("div[label='Student']")
.find("div")
.first()
.click();
cy.get("input[placeholder='Search']").type(randomEmail);
cy.get("div").contains(randomEmail).click();
cy.get("button").contains("Submit").click();
// Verify Seat Count
cy.get("span").contains("Details").click();
cy.get("button:visible").contains("Overview").click();
cy.contains("div:visible", "9 Seats Left").should("be.visible");
});
});

View File

@@ -9,8 +9,8 @@ describe("Course Creation", () => {
// Create a course
cy.get("button").contains("Create").click();
cy.get("span").contains("New Course").click();
cy.wait(500);
cy.url().should("include", "/courses/new/edit");
cy.get("label").contains("Title").type("Test Course");
cy.get("label")
@@ -34,27 +34,13 @@ describe("Course Creation", () => {
});
});
cy.get("label")
.contains("Preview Video")
.type("https://www.youtube.com/embed/-LPmw2Znl2c");
cy.get("[id=tags]").type("Learning{enter}Frappe{enter}ERPNext{enter}");
cy.get("label")
.contains("Category")
.parent()
.within(() => {
cy.get("button").click();
});
cy.get("[id^=headlessui-combobox-option-")
.should("be.visible")
.first()
.click();
/* Instructor */
cy.get("label")
.contains("Instructors")
.parent()
.within(() => {
cy.get("input").click().type("frappe");
cy.wait(500);
cy.get("input")
.invoke("attr", "aria-controls")
.as("instructor_list_id");
@@ -67,13 +53,29 @@ describe("Course Creation", () => {
});
});
cy.button("Save").last().click();
// Edit Course Details
cy.wait(500);
cy.get("label")
.contains("Preview Video")
.type("https://www.youtube.com/embed/-LPmw2Znl2c");
cy.get("[id=tags]").type("Learning{enter}Frappe{enter}ERPNext{enter}");
cy.get("label")
.contains("Category")
.parent()
.within(() => {
cy.get("button").click();
});
cy.get("div").contains("Business").click();
cy.get("label").contains("Published").click();
cy.get("label").contains("Published On").type("2021-01-01");
cy.button("Save").click();
// Add Chapter
cy.wait(1000);
cy.button("Add Chapter").click();
cy.button("Add").click();
cy.wait(1000);
cy.get("[data-dismissable-layer]")

View File

@@ -6,5 +6,7 @@
// biome-ignore lint: disable
export {}
declare global {
const LucideGithub: typeof import('~icons/lucide/github').default
const LucideLinkedin: typeof import('~icons/lucide/linkedin').default
const LucideTwitter: typeof import('~icons/lucide/twitter').default
}

View File

@@ -8,14 +8,10 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
Annoucements: typeof import('./src/components/Annoucements.vue')['default']
AnnouncementModal: typeof import('./src/components/Modals/AnnouncementModal.vue')['default']
AppHeader: typeof import('./src/components/AppHeader.vue')['default']
Apps: typeof import('./src/components/Apps.vue')['default']
AppSidebar: typeof import('./src/components/AppSidebar.vue')['default']
Apps: typeof import('./src/components/Sidebar/Apps.vue')['default']
AppSidebar: typeof import('./src/components/Sidebar/AppSidebar.vue')['default']
AssessmentModal: typeof import('./src/components/Modals/AssessmentModal.vue')['default']
AssessmentPlugin: typeof import('./src/components/AssessmentPlugin.vue')['default']
Assessments: typeof import('./src/components/Assessments.vue')['default']
Assignment: typeof import('./src/components/Assignment.vue')['default']
AssignmentForm: typeof import('./src/components/Modals/AssignmentForm.vue')['default']
AudioBlock: typeof import('./src/components/AudioBlock.vue')['default']
@@ -24,16 +20,8 @@ declare module 'vue' {
BadgeAssignments: typeof import('./src/components/Settings/BadgeAssignments.vue')['default']
BadgeForm: typeof import('./src/components/Settings/BadgeForm.vue')['default']
Badges: typeof import('./src/components/Settings/Badges.vue')['default']
BatchCard: typeof import('./src/components/BatchCard.vue')['default']
BatchCourseModal: typeof import('./src/components/Modals/BatchCourseModal.vue')['default']
BatchCourses: typeof import('./src/components/BatchCourses.vue')['default']
BatchDashboard: typeof import('./src/components/BatchDashboard.vue')['default']
BatchFeedback: typeof import('./src/components/BatchFeedback.vue')['default']
BatchOverlay: typeof import('./src/components/BatchOverlay.vue')['default']
BatchStudentProgress: typeof import('./src/components/Modals/BatchStudentProgress.vue')['default']
BatchStudents: typeof import('./src/components/BatchStudents.vue')['default']
BrandSettings: typeof import('./src/components/Settings/BrandSettings.vue')['default']
BulkCertificates: typeof import('./src/components/Modals/BulkCertificates.vue')['default']
Categories: typeof import('./src/components/Settings/Categories.vue')['default']
CertificationLinks: typeof import('./src/components/CertificationLinks.vue')['default']
ChapterModal: typeof import('./src/components/Modals/ChapterModal.vue')['default']
@@ -42,12 +30,18 @@ declare module 'vue' {
CodeEditor: typeof import('./src/components/Controls/CodeEditor.vue')['default']
CollapseSidebar: typeof import('./src/components/Icons/CollapseSidebar.vue')['default']
ColorSwatches: typeof import('./src/components/Controls/ColorSwatches.vue')['default']
CommandPalette: typeof import('./src/components/CommandPalette/CommandPalette.vue')['default']
CommandPaletteGroup: typeof import('./src/components/CommandPalette/CommandPaletteGroup.vue')['default']
Configuration: typeof import('./src/components/Sidebar/Configuration.vue')['default']
ContactUsEmail: typeof import('./src/components/ContactUsEmail.vue')['default']
CouponDetails: typeof import('./src/components/Settings/Coupons/CouponDetails.vue')['default']
CouponItems: typeof import('./src/components/Settings/Coupons/CouponItems.vue')['default']
CouponList: typeof import('./src/components/Settings/Coupons/CouponList.vue')['default']
Coupons: typeof import('./src/components/Settings/Coupons/Coupons.vue')['default']
CourseCard: typeof import('./src/components/CourseCard.vue')['default']
CourseCardOverlay: typeof import('./src/components/CourseCardOverlay.vue')['default']
CourseInstructors: typeof import('./src/components/CourseInstructors.vue')['default']
CourseOutline: typeof import('./src/components/CourseOutline.vue')['default']
CourseProgressSummary: typeof import('./src/components/Modals/CourseProgressSummary.vue')['default']
CourseReviews: typeof import('./src/components/CourseReviews.vue')['default']
CreateOutline: typeof import('./src/components/CreateOutline.vue')['default']
DateRange: typeof import('./src/components/Common/DateRange.vue')['default']
@@ -73,11 +67,9 @@ declare module 'vue' {
InviteIcon: typeof import('./src/components/Icons/InviteIcon.vue')['default']
JobApplicationModal: typeof import('./src/components/Modals/JobApplicationModal.vue')['default']
JobCard: typeof import('./src/components/JobCard.vue')['default']
LayoutHeader: typeof import('./src/components/LayoutHeader.vue')['default']
LessonContent: typeof import('./src/components/LessonContent.vue')['default']
LessonHelp: typeof import('./src/components/LessonHelp.vue')['default']
Link: typeof import('./src/components/Controls/Link.vue')['default']
LiveClass: typeof import('./src/components/LiveClass.vue')['default']
LiveClassAttendance: typeof import('./src/components/Modals/LiveClassAttendance.vue')['default']
LiveClassModal: typeof import('./src/components/Modals/LiveClassModal.vue')['default']
LMSLogo: typeof import('./src/components/Icons/LMSLogo.vue')['default']
@@ -88,6 +80,7 @@ declare module 'vue' {
NoSidebarLayout: typeof import('./src/components/NoSidebarLayout.vue')['default']
Notes: typeof import('./src/components/Notes/Notes.vue')['default']
NotPermitted: typeof import('./src/components/NotPermitted.vue')['default']
NumberChartGraph: typeof import('./src/components/NumberChartGraph.vue')['default']
PageModal: typeof import('./src/components/Modals/PageModal.vue')['default']
PaymentGatewayDetails: typeof import('./src/components/Settings/PaymentGatewayDetails.vue')['default']
PaymentGateways: typeof import('./src/components/Settings/PaymentGateways.vue')['default']
@@ -105,18 +98,19 @@ declare module 'vue' {
SettingDetails: typeof import('./src/components/Settings/SettingDetails.vue')['default']
SettingFields: typeof import('./src/components/Settings/SettingFields.vue')['default']
Settings: typeof import('./src/components/Settings/Settings.vue')['default']
SidebarLink: typeof import('./src/components/SidebarLink.vue')['default']
SidebarLink: typeof import('./src/components/Sidebar/SidebarLink.vue')['default']
StudentHeatmap: typeof import('./src/components/StudentHeatmap.vue')['default']
StudentModal: typeof import('./src/components/Modals/StudentModal.vue')['default']
Tags: typeof import('./src/components/Tags.vue')['default']
TransactionDetails: typeof import('./src/components/Settings/TransactionDetails.vue')['default']
Transactions: typeof import('./src/components/Settings/Transactions.vue')['default']
TransactionDetails: typeof import('./src/components/Settings/Transactions/TransactionDetails.vue')['default']
TransactionList: typeof import('./src/components/Settings/Transactions/TransactionList.vue')['default']
Transactions: typeof import('./src/components/Settings/Transactions/Transactions.vue')['default']
UnsplashImageBrowser: typeof import('./src/components/UnsplashImageBrowser.vue')['default']
UpcomingEvaluations: typeof import('./src/components/UpcomingEvaluations.vue')['default']
Uploader: typeof import('./src/components/Controls/Uploader.vue')['default']
UploadPlugin: typeof import('./src/components/UploadPlugin.vue')['default']
UserAvatar: typeof import('./src/components/UserAvatar.vue')['default']
UserDropdown: typeof import('./src/components/UserDropdown.vue')['default']
UserDropdown: typeof import('./src/components/Sidebar/UserDropdown.vue')['default']
VideoBlock: typeof import('./src/components/VideoBlock.vue')['default']
VideoStatistics: typeof import('./src/components/Modals/VideoStatistics.vue')['default']
ZoomAccountModal: typeof import('./src/components/Modals/ZoomAccountModal.vue')['default']

View File

@@ -6,55 +6,57 @@
"scripts": {
"dev": "vite",
"serve": "vite preview",
"build": "vite build --base=/assets/lms/frontend/ && yarn copy-html-entry",
"copy-html-entry": "cp ../lms/public/frontend/index.html ../lms/www/lms.html"
"build": "vite build --base=/assets/lms/frontend/ && yarn copy-html-entry && yarn copy-colors-json",
"copy-html-entry": "cp ../lms/public/frontend/index.html ../lms/www/_lms.html",
"copy-colors-json": "cp node_modules/frappe-ui/tailwind/colors.json src/utils/frappe-ui-colors.json"
},
"dependencies": {
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-python": "^6.2.1",
"@editorjs/checklist": "^1.6.0",
"@editorjs/code": "^2.9.0",
"@editorjs/editorjs": "^2.29.0",
"@editorjs/embed": "^2.7.0",
"@editorjs/header": "^2.8.1",
"@editorjs/inline-code": "^1.5.0",
"@editorjs/nested-list": "^1.4.2",
"@editorjs/paragraph": "^2.11.3",
"@editorjs/simple-image": "^1.6.0",
"@editorjs/table": "^2.4.2",
"@vueuse/router": "^12.7.0",
"ace-builds": "^1.36.2",
"apexcharts": "^4.3.0",
"chart.js": "^4.4.1",
"codemirror": "^6.0.1",
"dayjs": "^1.11.6",
"dompurify": "^3.2.6",
"feather-icons": "^4.28.0",
"frappe-ui": "^0.1.201",
"highlight.js": "^11.11.1",
"lucide-vue-next": "^0.383.0",
"markdown-it": "^14.0.0",
"pinia": "^2.0.33",
"plyr": "^3.7.8",
"socket.io-client": "^4.7.2",
"tailwindcss": "3.4.15",
"thememirror": "^2.0.1",
"typescript": "^5.7.2",
"vue": "^3.4.23",
"vue-chartjs": "^5.3.0",
"vue-codemirror": "^6.1.1",
"vue-draggable-next": "^2.2.1",
"vue-router": "^4.0.12",
"vue3-apexcharts": "^1.8.0",
"@codemirror/lang-html": "6.4.9",
"@codemirror/lang-javascript": "6.2.4",
"@codemirror/lang-json": "6.0.1",
"@codemirror/lang-python": "6.2.1",
"@editorjs/checklist": "1.6.0",
"@editorjs/code": "2.9.0",
"@editorjs/editorjs": "2.29.0",
"@editorjs/embed": "2.7.0",
"@editorjs/header": "2.8.1",
"@editorjs/inline-code": "1.5.0",
"@editorjs/nested-list": "1.4.2",
"@editorjs/paragraph": "2.11.3",
"@editorjs/simple-image": "1.6.0",
"@editorjs/table": "2.4.2",
"@vueuse/core": "^14.1.0",
"ace-builds": "1.36.2",
"apexcharts": "4.3.0",
"chart.js": "4.4.1",
"codemirror": "6.0.1",
"dayjs": "1.11.10",
"dompurify": "3.2.6",
"feather-icons": "4.28.0",
"frappe-ui": "^0.1.261",
"highlight.js": "11.11.1",
"lucide-vue-next": "0.383.0",
"markdown-it": "14.0.0",
"pinia": "2.0.33",
"plyr": "3.7.8",
"socket.io-client": "4.7.2",
"thememirror": "2.0.1",
"typescript": "5.7.2",
"vue": "^3.5.27",
"vue-chartjs": "5.3.0",
"vue-codemirror": "6.1.1",
"vue-draggable-next": "2.2.1",
"vue-router": "^4.6.4",
"vue3-apexcharts": "1.8.0",
"vuedraggable": "4.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.3",
"autoprefixer": "^10.4.2",
"postcss": "^8.4.5",
"vite": "^5.0.11",
"vite-plugin-pwa": "^1.0.2"
"@vitejs/plugin-vue": "5.0.3",
"autoprefixer": "10.4.2",
"postcss": "8.4.5",
"tailwindcss": "^3.4.15",
"unplugin-auto-import": "^20.3.0",
"vite": "5.0.11",
"vite-plugin-pwa": "^1.2.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512.001 512.001" xml:space="preserve">
<g>
<path style="fill:#A67C52;" d="M84.096,436.178l-49.312,37.686c-7.54,5.762-8.981,16.547-3.219,24.087
c3.383,4.425,8.494,6.751,13.666,6.751c3.638,0,7.306-1.151,10.421-3.532l49.312-37.686c7.54-5.762,8.981-16.547,3.219-24.087
C102.421,431.858,91.637,430.416,84.096,436.178z"/>
<path style="fill:#A67C52;" d="M441.194,473.864l-49.312-37.686c-7.541-5.762-18.325-4.32-24.087,3.219
c-5.762,7.541-4.321,18.325,3.219,24.087l49.312,37.686c3.115,2.38,6.782,3.532,10.421,3.532c5.171,0,10.284-2.326,13.665-6.751
C450.175,490.411,448.734,479.627,441.194,473.864z"/>
</g>
<path style="fill:#DBAD75;" d="M237.989,36.024c-131.227,0-237.989,106.761-237.989,237.989s106.761,237.989,237.989,237.989
S475.978,405.24,475.978,274.012S369.216,36.024,237.989,36.024z"/>
<path style="fill:#EABD81;" d="M237.989,36.024c-131.227,0-237.989,106.761-237.989,237.989s106.761,237.989,237.989,237.989V36.024
z"/>
<path style="fill:#BC2A46;" d="M237.989,80.411c-106.752,0-193.601,86.849-193.601,193.601s86.849,193.601,193.601,193.601
s193.601-86.849,193.601-193.601S344.742,80.411,237.989,80.411z"/>
<path style="fill:#D62D46;" d="M237.989,80.411c-106.752,0-193.601,86.849-193.601,193.601s86.849,193.601,193.601,193.601V80.411z"
/>
<path style="fill:#DBAD75;" d="M237.989,142.771c-72.367,0-131.241,58.874-131.241,131.241s58.874,131.241,131.241,131.241
S369.23,346.379,369.23,274.012S310.355,142.771,237.989,142.771z"/>
<path style="fill:#EABD81;" d="M237.989,142.771c-72.367,0-131.241,58.874-131.241,131.241s58.874,131.241,131.241,131.241V142.771z
"/>
<path style="fill:#BC2A46;" d="M237.989,209.763c-35.427,0-64.248,28.821-64.248,64.248s28.821,64.248,64.248,64.248
s64.248-28.821,64.248-64.248S273.416,209.763,237.989,209.763z"/>
<path style="fill:#D62D46;" d="M237.989,209.763c-35.427,0-64.248,28.821-64.248,64.248s28.821,64.248,64.248,64.248V209.763z"/>
<path style="fill:#CFCDD6;" d="M237.989,291.196c-4.398,0-8.796-1.677-12.15-5.034c-6.711-6.711-6.711-17.59,0-24.301
L448.687,39.014c6.71-6.711,17.59-6.711,24.301,0s6.711,17.59,0,24.301L250.14,286.162
C246.784,289.519,242.386,291.196,237.989,291.196z"/>
<path style="fill:#DEE1E7;" d="M237.989,291.196c-4.398,0-8.796-1.677-12.15-5.034c-6.711-6.711-6.711-17.59,0-24.301
l106.576-106.576l24.301,24.301L250.14,286.162C246.784,289.519,242.386,291.196,237.989,291.196z"/>
<path style="fill:#39B7B6;" d="M457.533,105.266h-33.615c-9.49,0-17.184-7.694-17.184-17.184V54.467
c0-9.49,7.694-17.184,17.184-17.184s17.184,7.694,17.184,17.184v16.432h16.431c9.49,0,17.184,7.694,17.184,17.184
S467.023,105.266,457.533,105.266z"/>
<path style="fill:#FBB03B;" d="M476.175,86.623h-33.614c-9.49,0-17.184-7.694-17.184-17.184V35.825
c0-9.49,7.694-17.184,17.184-17.184s17.184,7.694,17.184,17.184v16.431h16.431c9.49,0,17.184,7.694,17.184,17.184
S485.665,86.623,476.175,86.623z"/>
<path style="fill:#39B7B6;" d="M494.817,67.982h-33.614c-9.49,0-17.184-7.694-17.184-17.184V17.184
c0-9.49,7.694-17.184,17.184-17.184s17.184,7.694,17.184,17.184v16.431h16.431c9.49,0,17.184,7.694,17.184,17.184
S504.308,67.982,494.817,67.982z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M68.05 7.23l13.46 30.7a7.047 7.047 0 0 0 5.82 4.19l32.79 2.94c3.71.54 5.19 5.09 2.5 7.71l-24.7 20.75c-2 1.68-2.91 4.32-2.36 6.87l7.18 33.61c.63 3.69-3.24 6.51-6.56 4.76L67.56 102a7.033 7.033 0 0 0-7.12 0l-28.62 16.75c-3.31 1.74-7.19-1.07-6.56-4.76l7.18-33.61c.54-2.55-.36-5.19-2.36-6.87L5.37 52.78c-2.68-2.61-1.2-7.17 2.5-7.71l32.79-2.94a7.047 7.047 0 0 0 5.82-4.19l13.46-30.7c1.67-3.36 6.45-3.36 8.11-.01z" fill="#fdd835"></path><path d="M67.07 39.77l-2.28-22.62c-.09-1.26-.35-3.42 1.67-3.42c1.6 0 2.47 3.33 2.47 3.33l6.84 18.16c2.58 6.91 1.52 9.28-.97 10.68c-2.86 1.6-7.08.35-7.73-6.13z" fill="#ffff8d"></path><path d="M95.28 71.51L114.9 56.2c.97-.81 2.72-2.1 1.32-3.57c-1.11-1.16-4.11.51-4.11.51l-17.17 6.71c-5.12 1.77-8.52 4.39-8.82 7.69c-.39 4.4 3.56 7.79 9.16 3.97z" fill="#f4b400"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -3,18 +3,17 @@
<Layout class="isolate text-base">
<router-view />
</Layout>
<InstallPrompt v-if="isMobile" />
<InstallPrompt v-if="isMobile && !settings.data?.disable_pwa" />
<Dialogs />
</FrappeUIProvider>
</template>
<script setup>
import { FrappeUIProvider } from 'frappe-ui'
import { Dialogs } from '@/utils/dialogs'
import { computed, onUnmounted, ref, watch } from 'vue'
import { computed, onUnmounted, ref } from 'vue'
import { useScreenSize } from './utils/composables'
import { usersStore } from '@/stores/user'
import { useSettings } from '@/stores/settings'
import { useRouter } from 'vue-router'
import { posthogSettings } from '@/telemetry'
import DesktopLayout from './components/DesktopLayout.vue'
import MobileLayout from './components/MobileLayout.vue'
import NoSidebarLayout from './components/NoSidebarLayout.vue'
@@ -23,7 +22,7 @@ import InstallPrompt from './components/InstallPrompt.vue'
const { isMobile } = useScreenSize()
const router = useRouter()
const noSidebar = ref(false)
const { userResource } = usersStore()
const { settings } = useSettings()
router.beforeEach((to, from, next) => {
if (to.query.fromLesson || to.path === '/persona') {
@@ -47,10 +46,4 @@ const Layout = computed(() => {
onUnmounted(() => {
noSidebar.value = false
})
watch(userResource, () => {
if (userResource.data) {
posthogSettings.reload()
}
})
</script>

View File

@@ -1,152 +0,0 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2?v=3.12") format("woff2"),
url("Inter-Thin.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2?v=3.12") format("woff2"),
url("Inter-ThinItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2?v=3.12") format("woff2"),
url("Inter-ExtraLight.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2?v=3.12") format("woff2"),
url("Inter-ExtraLightItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2?v=3.12") format("woff2"),
url("Inter-Light.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2?v=3.12") format("woff2"),
url("Inter-LightItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2?v=3.12") format("woff2"),
url("Inter-Regular.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2?v=3.12") format("woff2"),
url("Inter-Italic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2?v=3.12") format("woff2"),
url("Inter-Medium.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2?v=3.12") format("woff2"),
url("Inter-MediumItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2?v=3.12") format("woff2"),
url("Inter-SemiBold.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2?v=3.12") format("woff2"),
url("Inter-SemiBoldItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2?v=3.12") format("woff2"),
url("Inter-Bold.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2?v=3.12") format("woff2"),
url("Inter-BoldItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2?v=3.12") format("woff2"),
url("Inter-ExtraBold.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2?v=3.12") format("woff2"),
url("Inter-ExtraBoldItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2?v=3.12") format("woff2"),
url("Inter-Black.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2?v=3.12") format("woff2"),
url("Inter-BlackItalic.woff?v=3.12") format("woff");
}

View File

@@ -1,53 +0,0 @@
<template>
<div v-if="communications.data?.length">
<div v-for="comm in communications.data">
<div class="mb-8">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<Avatar :label="comm.sender_full_name" size="lg" />
<div class="ml-2 text-ink-gray-7">
{{ comm.sender_full_name }}
</div>
</div>
<div class="text-sm">
{{ timeAgo(comm.communication_date) }}
</div>
</div>
<div
class="prose prose-sm bg-surface-menu-bar !min-w-full px-4 py-2 rounded-md"
v-html="comm.content"
></div>
</div>
</div>
</div>
<div v-else class="text-sm italic text-ink-gray-5">
{{ __('No announcements') }}
</div>
</template>
<script setup>
import { createResource, Avatar } from 'frappe-ui'
import { timeAgo } from '@/utils'
const props = defineProps({
batch: {
type: String,
required: true,
},
})
const communications = createResource({
url: 'lms.lms.api.get_announcements',
makeParams(value) {
return {
batch: props.batch,
}
},
auto: true,
cache: ['announcement', props.batch],
})
</script>
<style>
.prose-sm p {
margin: 0 0 0.5rem;
}
</style>

View File

@@ -26,28 +26,52 @@
v-model="quiz"
doctype="LMS Quiz"
:label="__('Select a quiz')"
placeholder=" "
:onCreate="(value, close) => redirectToForm()"
/>
<Link
v-else
v-model="assignment"
doctype="LMS Assignment"
:label="__('Select an assignment')"
:onCreate="(value, close) => redirectToForm()"
/>
<div v-else class="space-y-4">
<Link
v-if="filterAssignmentsByCourse"
v-model="assignment"
doctype="LMS Assignment"
:filters="{
course: route.params.courseName,
}"
placeholder=" "
:label="__('Select an Assignment')"
:onCreate="(value, close) => redirectToForm()"
/>
<Link
v-else
v-model="assignment"
doctype="LMS Assignment"
placeholder=" "
:label="__('Select an Assignment')"
:onCreate="(value, close) => redirectToForm()"
/>
<FormControl
type="checkbox"
:label="__('Filter assignments by course')"
v-model="filterAssignmentsByCourse"
/>
</div>
</div>
</div>
</template>
</Dialog>
</template>
<script setup>
import { Dialog } from 'frappe-ui'
import { onMounted, ref, nextTick } from 'vue'
import { Dialog, FormControl } from 'frappe-ui'
import { nextTick, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { getLmsRoute } from '@/utils/basePath'
import Link from '@/components/Controls/Link.vue'
const show = ref(false)
const quiz = ref(null)
const assignment = ref(null)
const filterAssignmentsByCourse = ref(false)
const route = useRoute()
const props = defineProps({
type: {
@@ -71,7 +95,10 @@ const addAssessment = () => {
}
const redirectToForm = () => {
if (props.type == 'quiz') window.open('/lms/quizzes/new', '_blank')
else window.open('/lms/assignments/new', '_blank')
if (props.type == 'quiz') {
window.open(getLmsRoute('quizzes?new=true'), '_blank')
} else {
window.open(getLmsRoute('assignments?new=true'), '_blank')
}
}
</script>

View File

@@ -16,8 +16,8 @@
{{ __('Submission by') }} {{ submissionResource.doc?.member_name }}
</div>
</div>
<div class="text-sm text-ink-gray-7 font-medium mb-2">
{{ __('Question') }}:
<div class="text-ink-gray-9 font-semibold mb-5">
{{ __('Assignment Question') }}
</div>
<div
v-html="assignment.data.question"
@@ -25,9 +25,9 @@
></div>
</div>
<div class="flex flex-col">
<div class="p-5">
<div class="flex items-center justify-between mb-4">
<div class="flex flex-col overflow-y-auto">
<div class="p-5 space-y-5">
<div class="flex items-center justify-between">
<div class="font-semibold text-ink-gray-9">
{{ __('Submission') }}
</div>
@@ -42,7 +42,11 @@
>
{{ submissionResource.doc?.status }}
</Badge>
<Button variant="solid" @click="submitAssignment()">
<Button
v-if="canModifyAssignment"
variant="solid"
@click="submitAssignment()"
>
{{ __('Save') }}
</Button>
</div>
@@ -53,7 +57,7 @@
!['Pass', 'Fail'].includes(submissionResource.doc?.status) &&
submissionResource.doc?.owner == user.data?.name
"
class="bg-surface-blue-2 text-ink-blue-2 p-3 rounded-md leading-5 text-sm mb-4"
class="bg-surface-blue-2 text-ink-blue-2 p-3 rounded-md leading-5 text-sm"
>
{{ __("You've successfully submitted the assignment.") }}
{{
@@ -63,17 +67,24 @@
}}
{{ __('Feel free to make edits to your submission if needed.') }}
</div>
<div v-if="showUploader()">
<div class="text-xs text-ink-gray-5 mt-1 mb-2">
{{ __('Add your assignment as {0}').format(assignment.data.type) }}
<div v-if="showUploader()" class="border rounded-lg p-3">
<div class="font-semibold mb-2">
{{ __('Upload Assignment') }}
</div>
<div class="text-ink-gray-5 text-sm mt-1 mb-4">
{{
__('You can only upload {0} files').format(assignment.data.type)
}}
</div>
<FileUploader
v-if="!submissionFile"
v-if="!attachment"
:fileTypes="getType()"
:uploadArgs="{
private: true,
}"
:validateFile="validateFile"
:validateFile="
(file) => validateFile(file, assignment.data.type.toLowerCase())
"
@success="(file) => saveSubmission(file)"
>
<template #default="{ uploading, progress, openFileSelector }">
@@ -87,21 +98,20 @@
</template>
</FileUploader>
<div v-else>
<div class="flex text-ink-gray-7">
<div class="border self-start rounded-md p-2 mr-2">
<FileText class="h-5 w-5 stroke-1.5" />
</div>
<div class="flex items-center text-ink-gray-7">
<a
:href="submissionFile.file_url"
:href="attachment"
target="_blank"
class="flex flex-col cursor-pointer !no-underline"
class="cursor-pointer !no-underline text-sm leading-5"
>
<span class="text-sm leading-5">
{{ submissionFile.file_name }}
</span>
<span class="text-sm text-ink-gray-5 mt-1">
{{ getFileSize(submissionFile.file_size) }}
</span>
<div class="flex items-center">
<div class="border rounded-md p-2 mr-2">
<FileText class="h-5 w-5 stroke-1.5" />
</div>
<span>
{{ attachment.split('/').pop() }}
</span>
</div>
</a>
<X
v-if="canModifyAssignment"
@@ -130,10 +140,11 @@
@change="(val) => (answer = val)"
:editable="true"
:fixedMenu="true"
:readonly="!canModifyAssignment"
: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]"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem]"
/>
</div>
@@ -142,13 +153,13 @@
user.data?.name == submissionResource.doc?.owner &&
submissionResource.doc?.comments
"
class="mt-8 p-3 bg-surface-blue-2 rounded-md"
class="mt-8 p-3 border rounded-lg bg-surface-gray-2"
>
<div class="text-sm text-ink-gray-5 font-medium mb-2">
{{ __('Comments by Evaluator') }}:
<div class="text-ink-gray-5 mb-4">
{{ __('Comments by Evaluator') }}
</div>
<div
class="leading-5 text-ink-gray-9"
class="leading-6 text-ink-gray-9"
v-html="submissionResource.doc.comments"
></div>
</div>
@@ -179,7 +190,10 @@
"
:editable="true"
:fixedMenu="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]"
:uploadArgs="{
private: true,
}"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem]"
/>
</div>
</div>
@@ -201,11 +215,11 @@ import {
} from 'frappe-ui'
import { computed, inject, onMounted, onBeforeUnmount, ref, watch } from 'vue'
import { FileText, X } from 'lucide-vue-next'
import { getFileSize } from '@/utils'
import { useRouter } from 'vue-router'
import { validateFile } from '@/utils'
const submissionFile = ref(null)
const answer = ref(null)
const attachment = ref(null)
const comments = ref(null)
const router = useRouter()
const user = inject('$user')
@@ -255,129 +269,98 @@ const assignment = createResource({
},
})
const newSubmission = createResource({
url: 'frappe.client.insert',
makeParams(values) {
let doc = {
doctype: 'LMS Assignment Submission',
assignment: props.assignmentID,
member: user.data?.name,
}
if (showUploader()) {
doc.assignment_attachment = submissionFile.value.file_url
} else {
doc.answer = answer.value
}
return {
doc: doc,
}
},
})
const imageResource = createResource({
url: 'lms.lms.api.get_file_info',
makeParams(values) {
return {
file_url: values.image,
}
},
auto: false,
onSuccess(data) {
submissionFile.value = data
},
})
const submissionResource = createDocumentResource({
doctype: 'LMS Assignment Submission',
name: props.submissionName,
auto: false,
onError(err) {
toast.error(err.messages?.[0] || err)
},
auto: false,
cache: [user.data?.name, props.assignmentID],
})
watch(submissionResource, () => {
if (submissionResource.doc) {
if (submissionResource.doc.assignment_attachment) {
imageResource.reload({
image: submissionResource.doc.assignment_attachment,
})
}
if (submissionResource.doc.answer) {
answer.value = submissionResource.doc.answer
}
if (submissionResource.doc.comments) {
comments.value = submissionResource.doc.comments
}
if (submissionResource.isDirty) {
isDirty.value = true
} else if (showUploader() && !submissionFile.value) {
isDirty.value = true
} else if (!showUploader() && !answer.value) {
isDirty.value = true
} else {
isDirty.value = false
}
if (!submissionResource.doc) return
console.log(submissionResource.doc)
if (submissionResource.doc.answer) {
answer.value = submissionResource.doc.answer
}
})
watch(submissionFile, () => {
if (props.submissionName == 'new' && submissionFile.value) {
isDirty.value = true
if (submissionResource.doc.assignment_attachment) {
attachment.value = submissionResource.doc.assignment_attachment
}
if (submissionResource.doc.comments) {
comments.value = submissionResource.doc.comments
}
})
const submitAssignment = () => {
if (props.submissionName != 'new') {
let evaluator =
submissionResource.doc && submissionResource.doc.owner != user.data?.name
? user.data?.name
: null
submissionResource.setValue.submit(
{
...submissionResource.doc,
assignment_attachment: submissionFile.value?.file_url,
evaluator: evaluator,
comments: comments.value,
answer: answer.value,
},
{
onSuccess(data) {
toast.success(__('Changes saved successfully'))
},
}
)
updateSubmission()
} else {
addNewSubmission()
}
}
const addNewSubmission = () => {
newSubmission.submit(
{},
let doc = {
doctype: 'LMS Assignment Submission',
assignment: props.assignmentID,
member: user.data?.name,
}
if (!showUploader()) {
doc.answer = answer.value
} else {
doc.assignment_attachment = attachment.value
}
call('frappe.client.insert', {
doc: doc,
})
.then((data) => {
toast.success(__('Assignment submitted successfully'))
if (router.currentRoute.value.name == 'AssignmentSubmission') {
router.push({
name: 'AssignmentSubmission',
params: {
assignmentID: props.assignmentID,
submissionName: data.name,
},
query: { fromLesson: router.currentRoute.value.query.fromLesson },
})
} else {
markLessonProgress()
router.go()
}
isDirty.value = false
submissionResource.name = data.name
submissionResource.reload()
})
.catch((err) => {
toast.error(err.messages?.[0] || err)
console.error(err)
})
}
const updateSubmission = () => {
let evaluator =
submissionResource.doc && submissionResource.doc.owner != user.data?.name
? user.data?.name
: null
submissionResource.setValue.submit(
{
...submissionResource.doc,
evaluator: evaluator,
comments: comments.value,
answer: answer.value,
assignment_attachment: attachment.value,
},
{
onSuccess(data) {
toast.success(__('Assignment submitted successfully'))
if (router.currentRoute.value.name == 'AssignmentSubmission') {
router.push({
name: 'AssignmentSubmission',
params: {
assignmentID: props.assignmentID,
submissionName: data.name,
},
query: { fromLesson: router.currentRoute.value.query.fromLesson },
})
} else {
markLessonProgress()
router.go()
}
submissionResource.name = data.name
submissionResource.reload()
isDirty.value = false
toast.success(__('Changes saved successfully'))
},
onError(err) {
toast.error(err.messages?.[0] || err)
console.error(err)
},
}
)
@@ -385,7 +368,7 @@ const addNewSubmission = () => {
const saveSubmission = (file) => {
isDirty.value = true
submissionFile.value = file
attachment.value = file.file_url
}
const markLessonProgress = () => {
@@ -419,24 +402,9 @@ const getType = () => {
}
}
const validateFile = (file) => {
let type = assignment.data?.type
let extension = file.name.split('.').pop().toLowerCase()
if (type == 'Image' && !['jpg', 'jpeg', 'png'].includes(extension)) {
return 'Only image file is allowed.'
} else if (
type == 'Document' &&
!['doc', 'docx', 'xml'].includes(extension)
) {
return 'Only document file is allowed.'
} else if (type == 'PDF' && !['pdf'].includes(extension)) {
return 'Only PDF file is allowed.'
}
}
const removeSubmission = () => {
isDirty.value = true
submissionFile.value = null
submissionResource.doc.assignment_attachment = ''
}
const canGradeSubmission = computed(() => {

View File

@@ -1,26 +0,0 @@
<template>
<div class="space-y-10">
<UpcomingEvaluations
:batch="batch.data.name"
:endDate="batch.data.evaluation_end_date"
:courses="batch.data.courses"
/>
<Assessments :batch="batch.data.name" />
<!-- <StudentHeatmap /> -->
</div>
</template>
<script setup>
import UpcomingEvaluations from '@/components/UpcomingEvaluations.vue'
import Assessments from '@/components/Assessments.vue'
const props = defineProps({
batch: {
type: Object,
default: null,
},
isStudent: {
type: Boolean,
default: false,
},
})
</script>

View File

@@ -1,354 +0,0 @@
<template>
<div v-if="batch.data" class="">
<div class="w-full flex items-center justify-between pb-4">
<div class="font-medium text-ink-gray-7">
{{ __('Statistics') }}
</div>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-5 mb-8">
<NumberChart
class="border rounded-md"
:config="{ title: __('Students'), value: students.data?.length || 0 }"
/>
<NumberChart
class="border rounded-md"
:config="{
title: __('Certified'),
value: certificationCount.data || 0,
}"
/>
<NumberChart
class="border rounded-md"
:config="{
title: __('Courses'),
value: batch.data.courses?.length || 0,
}"
/>
<NumberChart
class="border rounded-md"
:config="{ title: __('Assessments'), value: assessmentCount || 0 }"
/>
</div>
<AxisChart
v-if="showProgressChart"
:config="{
data: chartData,
title: __('Batch Summary'),
subtitle: __('Progress of students in courses and assessments'),
xAxis: {
key: 'task',
title: 'Tasks',
type: 'category',
},
yAxis: {
title: __('Number of Students'),
echartOptions: {
minInterval: 1,
},
},
swapXY: true,
series: [
{
name: 'value',
type: 'bar',
},
],
}"
/>
</div>
<div>
<div class="flex items-center justify-between mb-4">
<div class="text-ink-gray-7 font-medium">
{{ __('Students') }}
</div>
<Button v-if="!readOnlyMode" @click="openStudentModal()">
<template #prefix>
<Plus class="h-4 w-4" />
</template>
{{ __('Add') }}
</Button>
</div>
<div v-if="students.data?.length">
<ListView
:columns="getStudentColumns()"
:rows="students.data"
row-key="name"
:options="{
showTooltip: false,
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
>
<ListHeaderItem
:item="item"
v-for="item in getStudentColumns()"
:title="item.label"
>
<template #prefix="{ item }">
<FeatherIcon
v-if="item.icon"
:name="item.icon"
class="h-4 w-4 stroke-1.5"
/>
</template>
</ListHeaderItem>
</ListHeader>
<ListRows>
<ListRow
:row="row"
v-for="row in students.data"
class="group cursor-pointer"
@click="openStudentProgressModal(row)"
>
<template #default="{ column, item }">
<ListRowItem
:item="row[column.key]"
:align="column.align"
class="text-sm"
>
<template #prefix>
<div v-if="column.key == 'full_name'">
<Avatar
class="flex items-center"
:image="row['user_image']"
:label="item"
size="sm"
/>
</div>
</template>
<div
v-if="column.key == 'progress'"
class="flex items-center space-x-4 w-full"
>
<ProgressBar :progress="row[column.key]" size="sm" />
<div class="text-xs">{{ row[column.key] }}%</div>
</div>
<div v-else>
{{ row[column.key] }}
</div>
</ListRowItem>
</template>
</ListRow>
</ListRows>
<ListSelectBanner>
<template #actions="{ unselectAll, selections }">
<div class="flex gap-2">
<Button
variant="ghost"
@click="removeStudents(selections, unselectAll)"
>
<Trash2 class="h-4 w-4 stroke-1.5" />
</Button>
</div>
</template>
</ListSelectBanner>
</ListView>
</div>
<div v-else class="text-sm italic text-ink-gray-5">
{{ __('There are no students in this batch.') }}
</div>
</div>
<StudentModal
:batch="props.batch.data.name"
v-model="showStudentModal"
v-model:reloadStudents="students"
v-model:batchModal="props.batch"
/>
<BatchStudentProgress
:student="selectedStudent"
v-model="showStudentProgressModal"
/>
</template>
<script setup>
import {
Avatar,
AxisChart,
Button,
createResource,
FeatherIcon,
ListHeader,
ListHeaderItem,
ListSelectBanner,
ListRow,
ListRows,
ListView,
ListRowItem,
NumberChart,
toast,
} from 'frappe-ui'
import {
BookOpen,
GraduationCap,
Plus,
ShieldCheck,
Trash2,
User,
} from 'lucide-vue-next'
import { ref, watch } from 'vue'
import StudentModal from '@/components/Modals/StudentModal.vue'
import ProgressBar from '@/components/ProgressBar.vue'
import BatchStudentProgress from '@/components/Modals/BatchStudentProgress.vue'
import ApexChart from 'vue3-apexcharts'
import { theme } from '@/utils/theme'
const showStudentModal = ref(false)
const showStudentProgressModal = ref(false)
const selectedStudent = ref(null)
const chartData = ref(null)
const showProgressChart = ref(false)
const assessmentCount = ref(0)
const readOnlyMode = window.read_only_mode
const props = defineProps({
batch: {
type: Object,
default: null,
},
})
const students = createResource({
url: 'lms.lms.utils.get_batch_students',
params: {
batch: props.batch?.data?.name,
},
auto: true,
onSuccess(data) {
chartData.value = getChartData()
showProgressChart.value =
data.length &&
(props.batch?.data?.courses?.length || assessmentCount.value)
},
})
const getStudentColumns = () => {
let columns = [
{
label: 'Full Name',
key: 'full_name',
width: '20rem',
icon: 'user',
},
{
label: 'Progress',
key: 'progress',
width: '15rem',
icon: 'activity',
},
{
label: 'Last Active',
key: 'last_active',
width: '10rem',
align: 'center',
icon: 'clock',
},
]
return columns
}
const openStudentModal = () => {
showStudentModal.value = true
}
const openStudentProgressModal = (row) => {
showStudentProgressModal.value = true
selectedStudent.value = row
}
const deleteStudents = createResource({
url: 'lms.lms.api.delete_documents',
makeParams(values) {
return {
doctype: 'LMS Batch Enrollment',
documents: values.students,
}
},
})
const removeStudents = (selections, unselectAll) => {
deleteStudents.submit(
{
students: Array.from(selections),
},
{
onSuccess(data) {
students.reload()
props.batch.reload()
toast.success(__('Students deleted successfully'))
unselectAll()
},
}
)
}
const getChartData = () => {
let tasks = []
let data = []
students.data.forEach((row) => {
tasks = countAssessments(row, tasks)
tasks = countCourses(row, tasks)
})
tasks.forEach((task) => {
data.push({
task: task.label,
value: task.value,
})
})
return data
}
const countAssessments = (row, tasks) => {
Object.keys(row.assessments).forEach((assessment) => {
if (row.assessments[assessment].result === 'Pass') {
tasks.filter((task) => task.label === assessment).length
? tasks.filter((task) => task.label === assessment)[0].value++
: tasks.push({
value: 1,
label: assessment,
})
}
})
return tasks
}
const countCourses = (row, tasks) => {
Object.keys(row.courses).forEach((course) => {
if (row.courses[course] === 100) {
tasks.filter((task) => task.label === course).length
? tasks.filter((task) => task.label === course)[0].value++
: tasks.push({
value: 1,
label: course,
})
}
})
return tasks
}
watch(students, () => {
if (students.data?.length) {
assessmentCount.value = Object.keys(students.data?.[0].assessments).length
}
})
const certificationCount = createResource({
url: 'frappe.client.get_count',
params: {
doctype: 'LMS Certificate',
filters: {
batch_name: props.batch?.data?.name,
},
},
auto: true,
})
</script>

View File

@@ -68,11 +68,12 @@ const props = defineProps({
const certification = createResource({
url: 'lms.lms.api.get_certification_details',
params: {
course: props.courseName,
makeParams(values) {
return {
course: props.courseName,
}
},
auto: user.data ? true : false,
cache: ['certificationData', user.data?.name],
})
const downloadCertificate = () => {

View File

@@ -0,0 +1,272 @@
<template>
<Dialog v-model="show" :options="{ size: '2xl' }">
<template #body>
<div class="text-base">
<div class="flex items-center space-x-2 pl-4.5 border-b">
<Search class="size-4 text-ink-gray-4" />
<input
ref="inputRef"
type="text"
placeholder="Search"
class="w-full border-none bg-transparent py-3 !pl-2 pr-4.5 text-base text-ink-gray-7 placeholder-ink-gray-4 focus:ring-0"
@input="onInput"
v-model="query"
autocomplete="off"
/>
</div>
<div class="max-h-96 overflow-auto mb-2">
<div v-if="query.length" class="mt-5 space-y-5">
<CommandPaletteGroup
:list="searchResults"
@navigateTo="navigateTo"
/>
</div>
<div v-else class="mt-5 space-y-5">
<CommandPaletteGroup
:list="jumpToOptions"
@navigateTo="navigateTo"
/>
</div>
</div>
<div
class="flex items-center space-x-5 w-full border-t py-2 text-sm text-ink-gray-7 px-4.5"
>
<div class="flex items-center space-x-2">
<MoveUp
class="size-5 stroke-1.5 bg-surface-gray-2 p-1 rounded-sm"
/>
<MoveDown
class="size-5 stroke-1.5 bg-surface-gray-2 p-1 rounded-sm"
/>
<span>
{{ __('to navigate') }}
</span>
</div>
<div class="flex items-center space-x-2">
<CornerDownLeft
class="size-5 stroke-1.5 bg-surface-gray-2 p-1 rounded-sm"
/>
<span>
{{ __('to select') }}
</span>
</div>
<div class="flex items-center space-x-2">
<span class="bg-surface-gray-2 p-1 rounded-sm"> esc </span>
<span>
{{ __('to close') }}
</span>
</div>
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { createResource, debounce, Dialog } from 'frappe-ui'
import { nextTick, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import {
BookOpen,
Briefcase,
CornerDownLeft,
FileSearch,
MoveUp,
MoveDown,
Search,
Users,
} from 'lucide-vue-next'
import CommandPaletteGroup from './CommandPaletteGroup.vue'
const show = defineModel<boolean>({ required: true, default: false })
const router = useRouter()
const query = ref<string>('')
const searchResults = ref<Array<any>>([])
const search = createResource({
url: 'lms.command_palette.search_sqlite',
makeParams: () => ({
query: query.value,
}),
onSuccess() {
generateSearchResults()
},
})
const debouncedSearch = debounce(() => {
if (query.value.length > 2) {
search.reload()
}
}, 500)
const onInput = () => {
debouncedSearch()
}
const generateSearchResults = () => {
search.data?.forEach((type: any) => {
let result: { title: string; items: any[] } = { title: '', items: [] }
result.title = type.title
type.items.forEach((item: any) => {
let paramName = item.doctype === 'LMS Course' ? 'courseName' : 'batchName'
item.route = {
name: item.doctype === 'LMS Course' ? 'CourseDetail' : 'BatchDetail',
params: {
[paramName]: item.name,
},
}
item.isActive = false
})
result.items = type.items
searchResults.value.push(result)
})
}
const appendSearchPage = () => {
let searchPage: { title: string; items: Array<any> } = {
title: '',
items: [],
}
searchPage.title = __('Jump to')
searchPage.items = [
{
title: __('Search for ') + `"${query.value}"`,
route: {
name: 'Search',
query: {
q: query.value,
},
},
icon: FileSearch,
isActive: true,
},
]
searchResults.value = [searchPage]
}
watch(
query,
() => {
appendSearchPage()
},
{ immediate: true }
)
watch(show, () => {
if (!show.value) {
query.value = ''
searchResults.value = []
}
})
onMounted(() => {
addKeyboardShortcuts()
})
const addKeyboardShortcuts = () => {
window.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'ArrowUp' && show.value) {
e.preventDefault()
shortcutForArrowKey(-1)
} else if (e.key === 'ArrowDown' && show.value) {
shortcutForArrowKey(1)
} else if (e.key === 'Enter' && show.value) {
shortcutForEnter()
} else if (e.key === 'Escape' && show.value) {
show.value = false
}
})
}
const shortcutForArrowKey = (direction: number) => {
let currentList = query.value.length
? searchResults.value
: jumpToOptions.value
let allItems = currentList.flatMap((result: any) => result.items)
let indexOfActive = allItems.findIndex((option: any) => option.isActive)
let newIndex = indexOfActive + direction
if (newIndex < 0) newIndex = allItems.length - 1
if (newIndex >= allItems.length) newIndex = 0
allItems[indexOfActive].isActive = false
allItems[newIndex].isActive = true
nextTick(scrollActiveItemIntoView)
}
const scrollActiveItemIntoView = () => {
const activeItem = document.querySelector(
'.hover\\:bg-surface-gray-2.bg-surface-gray-2'
) as HTMLElement
if (activeItem) {
activeItem.scrollIntoView({ block: 'nearest' })
}
}
const shortcutForEnter = () => {
let currentList = query.value.length
? searchResults.value
: jumpToOptions.value
let allItems = currentList.flatMap((result: any) => result.items)
let activeOption = allItems.find((option) => option.isActive)
if (activeOption) {
navigateTo(activeOption.route)
}
}
const navigateTo = (route: {
name: string
params?: Record<string, any>
query?: Record<string, any>
}) => {
show.value = false
query.value = ''
router.replace({ name: route.name, params: route.params, query: route.query })
}
const jumpToOptions = ref([
{
title: __('Jump to'),
items: [
{
title: 'Advanced Search',
icon: Search,
route: {
name: 'Search',
},
isActive: true,
},
{
title: 'Courses',
icon: BookOpen,
route: {
name: 'Courses',
},
isActive: false,
},
{
title: 'Batches',
icon: Users,
route: {
name: 'Batches',
},
isActive: false,
},
{
title: 'Jobs',
icon: Briefcase,
route: {
name: 'Jobs',
},
isActive: false,
},
],
},
])
</script>
<style>
mark {
background-color: theme('colors.amber.100');
font-weight: 500;
}
</style>

View File

@@ -0,0 +1,45 @@
<template>
<div v-for="result in list" class="px-2.5 space-y-2">
<div class="text-ink-gray-5 px-2">
{{ result.title }}
</div>
<div class="">
<div
v-for="item in result.items"
class="flex items-center justify-between p-2 rounded hover:bg-surface-gray-2 cursor-pointer"
:class="{ 'bg-surface-gray-2': item.isActive }"
@click="emit('navigateTo', item.route)"
>
<div class="flex items-center space-x-3">
<component
v-if="item.icon"
:is="item.icon"
class="size-4 stroke-1.5 text-ink-gray-6"
/>
<div v-html="item.title"></div>
</div>
<div v-if="item.modified" class="text-ink-gray-5">
{{ dayjs.unix(item.modified).fromNow(true) }}
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { inject } from 'vue'
const dayjs = inject<any>('$dayjs')
const emit = defineEmits(['navigateTo'])
const props = defineProps<{
list: Array<{
title: string
items: Array<{
title: string
icon?: any
isActive?: boolean
modified?: string
}>
}>
}>()
</script>

View File

@@ -48,7 +48,7 @@ const settingsStore = useSettings()
const sendMail = (close: Function) => {
call('frappe.core.doctype.communication.email.make', {
recipients: settingsStore.contactUsEmail?.data,
recipients: settingsStore.settings?.data?.contact_us_email,
subject: subject.value,
content: message.value,
send_email: true,

View File

@@ -16,13 +16,18 @@
<button
class="flex w-full items-center justify-between focus:outline-none"
:class="inputClasses"
@click="() => togglePopover()"
@click="
() => {
showOptions = !showOptions
togglePopover()
}
"
:disabled="attrs.readonly"
>
<div class="flex items-center">
<div class="flex items-center w-[90%]">
<slot name="prefix" />
<span
class="overflow-hidden text-ellipsis whitespace-nowrap text-base leading-5"
class="block truncate text-base leading-5"
v-if="selectedValue"
>
{{ displayValue(selectedValue) }}
@@ -99,18 +104,17 @@
name="item-label"
v-bind="{ active, selected, option }"
>
<div class="flex flex-col space-y-1 text-ink-gray-8">
<div>
{{ option.label }}
<div class="flex flex-col gap-1 p-1">
<div class="text-base font-medium text-ink-gray-8">
{{
option.value == option.label && option.description
? option.description
: option.label
}}
</div>
<div class="text-sm text-ink-gray-5">
{{ option.value }}
</div>
<div
v-if="
option.description &&
option.description != option.label
"
class="text-xs text-ink-gray-7"
v-html="option.description"
></div>
</div>
</slot>
</li>
@@ -120,7 +124,7 @@
v-if="groups.length == 0"
class="mt-1.5 rounded-md px-2.5 py-1.5 text-base text-ink-gray-5"
>
No results found
{{ __('No results found') }}
</li>
</ComboboxOptions>
<div v-if="slots.footer" class="border-t p-1.5 pb-0.5">
@@ -284,7 +288,7 @@ const inputClasses = computed(() => {
let variant = props.disabled ? 'disabled' : props.variant
let variantClasses = {
subtle:
'border border-gray-100 bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3',
'border border-outline-gray-modals bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3',
outline:
'border border-outline-gray-2 bg-surface-white placeholder-ink-gray-4 hover:border-outline-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3',
disabled: [

View File

@@ -3,59 +3,67 @@
<div class="text-xs text-ink-gray-5 mb-2">
{{ label }}
</div>
<div class="overflow-x-auto border rounded-md">
<div
class="grid items-center space-x-4 p-2 border-b"
:style="{ gridTemplateColumns: getGridTemplateColumns() }"
>
<div class="overflow-visible border border-outline-gray-modals rounded-md">
<div class="overflow-x-auto">
<div
v-for="(column, index) in columns"
:key="index"
class="text-sm text-ink-gray-5"
class="grid items-center space-x-4 p-2 border-b border-outline-gray-modals"
:style="{ gridTemplateColumns: getGridTemplateColumns() }"
>
{{ column }}
</div>
<div></div>
</div>
<div
v-for="(row, rowIndex) in rows"
:key="rowIndex"
class="grid items-center space-x-4 p-2"
:style="{ gridTemplateColumns: getGridTemplateColumns() }"
>
<template v-for="key in Object.keys(row)" :key="key">
<input
v-if="showKey(key)"
v-model="row[key]"
class="py-1.5 px-2 border-none focus:ring-0 focus:border focus:border-gray-300 focus:bg-surface-gray-2 rounded-sm text-sm focus:outline-none"
/>
</template>
<div class="relative" ref="menuRef">
<Button
variant="ghost"
@click="(event: MouseEvent) => toggleMenu(rowIndex, event)"
>
<template #icon>
<Ellipsis
class="size-4 text-ink-gray-7 stroke-1.5 cursor-pointer"
/>
</template>
</Button>
<div
v-if="menuOpenIndex === rowIndex"
class="absolute right-[30px] top-5 mt-1 w-32 bg-surface-white border border-outline-gray-1 rounded-md shadow-sm"
v-for="(column, index) in columns"
:key="index"
class="text-sm text-ink-gray-5"
>
<button
@click="deleteRow(rowIndex)"
class="flex items-center space-x-2 w-full text-left px-3 py-2 text-sm text-ink-red-3"
{{ column }}
</div>
<div></div>
</div>
<div
v-for="(row, rowIndex) in rows"
:key="rowIndex"
class="grid items-center space-x-4 p-2"
:style="{ gridTemplateColumns: getGridTemplateColumns() }"
>
<template v-for="key in Object.keys(row)" :key="key">
<input
v-if="showKey(key)"
v-model="row[key]"
class="py-1.5 px-2 w-full border-none bg-transparent text-ink-gray-8 focus:ring-0 focus:border focus:border-outline-gray-3 focus:bg-surface-gray-2 rounded-md text-sm focus:outline-none"
/>
</template>
<div class="relative">
<Button
variant="ghost"
@click="(event: MouseEvent) => toggleMenu(rowIndex, event)"
>
<Trash2 class="size-4 stroke-1.5" />
<span>
{{ __('Delete') }}
</span>
</button>
<template #icon>
<Ellipsis
class="size-4 text-ink-gray-7 stroke-1.5 cursor-pointer"
/>
</template>
</Button>
<div
v-if="menuOpenIndex === rowIndex"
ref="menuRef"
class="absolute right-0 w-32 z-50 bg-surface-modal border border-outline-gray-modals rounded-md shadow-sm"
:class="
rowIndex == (rows?.length ?? 0) - 1
? 'bottom-full mb-1'
: 'top-full mt-1'
"
>
<button
@click="deleteRow(rowIndex)"
class="flex items-center space-x-2 w-full text-left px-3 py-2 text-sm text-ink-red-3"
>
<Trash2 class="size-4 stroke-1.5" />
<span>
{{ __('Delete') }}
</span>
</button>
</div>
</div>
</div>
</div>
@@ -73,17 +81,19 @@
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { nextTick, ref, watch } from 'vue'
import { Button } from 'frappe-ui'
import { Ellipsis, Plus, Trash2 } from 'lucide-vue-next'
import { onClickOutside } from '@vueuse/core'
const rows = defineModel<Cell[][]>()
const rows = defineModel<Record<string, string>[]>()
const menuRef = ref(null)
const menuOpenIndex = ref<number | null>(null)
const menuTopPosition = ref<string>('')
const menuLeftPosition = ref('0px')
const emit = defineEmits<{
(e: 'update:modelValue', value: Cell[][]): void
(e: 'update:modelValue', value: Record<string, string>[]): void
}>()
type Cell = {
@@ -93,19 +103,19 @@ type Cell = {
const props = withDefaults(
defineProps<{
modelValue?: Cell[][]
modelValue?: Record<string, string>[]
columns?: string[]
label?: string
}>(),
{
columns: [],
columns: () => [] as string[],
}
)
const columns = ref(props.columns)
watch(rows, () => {
if (rows.value?.length < 1) {
if (rows.value && rows.value.length < 1) {
addRow()
}
})
@@ -119,12 +129,25 @@ const addRow = () => {
newRow[column.toLowerCase().split(' ').join('_')] = ''
})
rows.value.push(newRow)
focusNewRowInput()
emit('update:modelValue', rows.value)
}
const focusNewRowInput = () => {
nextTick(() => {
const rowElements = document.querySelectorAll('.overflow-x-auto .grid')[
rows.value!.length
]
const firstInput = rowElements.querySelector('input')
if (firstInput) {
;(firstInput as HTMLInputElement).focus()
}
})
}
const deleteRow = (index: number) => {
rows.value.splice(index, 1)
emit('update:modelValue', rows.value)
rows.value?.splice(index, 1)
emit('update:modelValue', rows.value ?? [])
}
const getGridTemplateColumns = () => {
@@ -133,7 +156,6 @@ const getGridTemplateColumns = () => {
const toggleMenu = (index: number, event: MouseEvent) => {
menuOpenIndex.value = menuOpenIndex.value === index ? null : index
menuTopPosition.value = `${event.clientY + 10}px`
}
onClickOutside(menuRef, () => {

View File

@@ -107,7 +107,7 @@ async function setLanguageExtension() {
if (!languageImport) return
const module = await languageImport()
languageExtension.value = (module as any)[props.language]()
languageExtension.value = (module as any)[props.language]?.()
if (props.completions) {
const languageData = (module as any)[`${props.language}Language`]

View File

@@ -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'])

View File

@@ -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">

View File

@@ -11,7 +11,6 @@
:size="attrs.size || 'sm'"
:variant="attrs.variant"
:placeholder="attrs.placeholder"
:filterable="false"
:readonly="attrs.readonly"
>
<template #target="{ open, togglePopover }">
@@ -67,6 +66,7 @@ import { watchDebounced } from '@vueuse/core'
import { createResource, Button } from 'frappe-ui'
import { Plus, X } from 'lucide-vue-next'
import { useAttrs, computed, ref } from 'vue'
import { useSettings } from '@/stores/settings'
const props = defineProps({
doctype: {
@@ -96,13 +96,14 @@ const value = computed({
set: (val) => {
return (
val?.value &&
emit(valuePropPassed.value ? 'change' : 'update:modelValue', val?.value)
emit(valuePropPassed.value ? 'change' : 'update:modelValue', val.value)
)
},
})
const autocomplete = ref(null)
const text = ref('')
const settingsStore = useSettings()
watchDebounced(
() => autocomplete.value?.query,
@@ -121,6 +122,16 @@ watchDebounced(
{ debounce: 300, immediate: true }
)
watchDebounced(
() => settingsStore.isSettingsOpen,
(isOpen, wasOpen) => {
if (wasOpen && !isOpen) {
reload('')
}
},
{ debounce: 200 }
)
const options = createResource({
url: 'frappe.desk.search.search_link',
cache: [props.doctype, text.value],

View File

@@ -1,160 +1,145 @@
<template>
<div>
<label class="block mb-1" :class="labelClasses" v-if="label">
<label v-if="label" class="block mb-1" :class="labelClasses">
{{ label }}
<span class="text-ink-red-3" v-if="required">*</span>
<span v-if="required" class="text-ink-red-3">*</span>
</label>
<div class="w-full">
<Combobox v-model="selectedValue" nullable>
<Popover class="w-full" v-model:show="showOptions">
<template #target="{ togglePopover }">
<ComboboxInput
ref="search"
class="search-input form-input w-full focus-visible:!ring-0"
type="text"
:value="query"
@change="
(e) => {
query = e.target.value
showOptions = true
}
"
autocomplete="off"
@focus="() => togglePopover()"
@keydown.delete.capture.stop="removeLastValue"
/>
</template>
<template #body="{ isOpen, close }">
<div v-show="isOpen">
<div
class="mt-1 rounded-lg bg-surface-white py-1 text-base border-2"
<Combobox v-model="selectedValue" nullable v-slot="{ open }">
<div class="relative w-full">
<ComboboxInput
ref="search"
class="form-input w-full focus-visible:!ring-0"
type="text"
@change="
(e) => {
query = e.target.value
}
"
autocomplete="off"
@focus="onFocus"
/>
<ComboboxButton ref="trigger" class="hidden" />
<ComboboxOptions
v-show="open"
static
class="absolute z-20 mt-1 w-full rounded-lg bg-surface-modal border-2 border-outline-gray-modals max-h-[13rem] flex flex-col"
>
<div
class="flex-1 my-1 overflow-y-auto px-1.5"
:class="options.length ? 'min-h-[6rem]' : 'min-h-[3.8rem]'"
>
<template v-if="options.length">
<ComboboxOption
v-for="option in options"
:key="option.value"
:value="option"
v-slot="{ active }"
>
<ComboboxOptions
class="my-1 min-h-[6rem] max-h-[12rem] overflow-y-auto px-1.5"
static
<li
:class="[
'flex cursor-pointer items-center rounded px-2 py-1 text-base',
{ 'bg-surface-gray-2': active },
]"
>
<ComboboxOption
v-for="option in options"
:key="option.value"
:value="option"
v-slot="{ active }"
>
<li
:class="[
'flex cursor-pointer items-center rounded px-2 py-1 text-base',
{ 'bg-surface-gray-2': active },
]"
>
<div class="flex flex-col gap-1 p-1">
<div class="text-base font-medium text-ink-gray-8">
{{ option.description }}
</div>
<div class="text-sm text-ink-gray-5">
{{ option.value }}
</div>
</div>
</li>
</ComboboxOption>
<div class="h-10"></div>
<div
v-if="attrs.onCreate"
class="absolute bottom-2 left-1 w-[99%] pt-2 bg-white border-t"
>
<Button
variant="ghost"
class="w-full !justify-start"
:label="__('Create New')"
@click="attrs.onCreate(close)"
>
<template #prefix>
<Plus class="h-4 w-4 stroke-1.5" />
</template>
</Button>
<div class="flex flex-col gap-1 p-1">
<div class="text-base font-medium text-ink-gray-8">
{{
option.value === option.label
? option.description
: option.label
}}
</div>
<div class="text-sm text-ink-gray-5">
{{ option.value }}
</div>
</div>
</ComboboxOptions>
</div>
</li>
</ComboboxOption>
</template>
<div v-else class="text-ink-gray-7 px-4 py-2">
{{ __('No results found') }}
</div>
</template>
</Popover>
</Combobox>
</div>
<div v-if="values.length" class="grid grid-cols-2 gap-2 mt-1">
</div>
<div
v-if="attrs.onCreate"
class="p-1 bg-surface-white border-t rounded-b-lg"
>
<Button
variant="ghost"
class="w-full !justify-start"
:label="__('Create New')"
@click="attrs.onCreate()"
>
<template #prefix>
<Plus class="h-4 w-4 stroke-1.5" />
</template>
</Button>
</div>
</ComboboxOptions>
</div>
</Combobox>
<!-- Selected values -->
<div v-if="values?.length" class="grid grid-cols-2 gap-2 mt-1">
<div
v-for="value in values"
class="flex items-center justify-between break-all bg-surface-gray-2 text-ink-gray-7 word-wrap p-2 rounded-md mr-2"
:key="value"
class="flex items-center justify-between break-all bg-surface-gray-2 text-ink-gray-7 p-2 rounded-md"
>
<span class="break-all">
{{ value }}
</span>
<span>{{ value }}</span>
<X
class="size-4 stroke-1.5 cursor-pointer"
@click="removeValue(value)"
/>
</div>
</div>
<!-- <ErrorMessage class="mt-2 pl-2" v-if="error" :message="error" /> -->
</div>
</template>
<script setup>
import {
Combobox,
ComboboxButton,
ComboboxInput,
ComboboxOptions,
ComboboxOption,
} from '@headlessui/vue'
import { createResource, Popover, Button } from 'frappe-ui'
import { ref, computed, nextTick, useAttrs } from 'vue'
import { createResource, Button } from 'frappe-ui'
import { ref, computed, useAttrs, watch } from 'vue'
import { watchDebounced } from '@vueuse/core'
import { X, Plus } from 'lucide-vue-next'
const props = defineProps({
label: {
type: String,
},
size: {
type: String,
default: 'sm',
},
doctype: {
type: String,
required: true,
},
filters: {
type: Object,
default: () => ({}),
},
validate: {
type: Function,
default: null,
},
label: String,
size: { type: String, default: 'sm' },
doctype: { type: String, required: true },
filters: { type: Object, default: () => ({}) },
validate: Function,
errorMessage: {
type: Function,
default: (value) => `${value} is an Invalid value`,
},
required: {
type: Boolean,
},
required: Boolean,
})
const values = defineModel()
const attrs = useAttrs()
const emails = ref([])
const search = ref(null)
const error = ref(null)
const trigger = ref(null)
const query = ref('')
const text = ref('')
const showOptions = ref(false)
const selectedValue = ref(null)
const error = ref(null)
const selectedValue = computed({
get: () => query.value || '',
set: (val) => {
query.value = ''
if (val) {
showOptions.value = false
}
val?.value && addValue(val.value)
},
const emit = defineEmits(['update:modelValue'])
watch(selectedValue, (val) => {
if (!val?.value) return
query.value = ''
addValue(val.value)
selectedValue.value = null
emit('update:modelValue', values.value)
})
watchDebounced(
@@ -171,7 +156,6 @@ watchDebounced(
const filterOptions = createResource({
url: 'frappe.desk.search.search_link',
method: 'POST',
cache: [text.value, props.doctype],
auto: true,
params: {
txt: text.value,
@@ -180,7 +164,8 @@ const filterOptions = createResource({
})
const options = computed(() => {
return filterOptions.data || []
const allOptions = filterOptions.data || []
return allOptions.filter((option) => !values.value?.includes(option.value))
})
function reload(val) {
@@ -193,70 +178,46 @@ function reload(val) {
filterOptions.reload()
}
const addValue = (value) => {
function onFocus() {
if (!filterOptions.data?.length) {
reload('')
}
trigger.value?.$el.click()
}
function addValue(value) {
error.value = null
if (value) {
const splitValues = value.split(',')
splitValues.forEach((value) => {
value = value.trim()
if (value) {
// check if value is not already in the values array
if (!values.value?.includes(value)) {
// check if value is valid
if (value && props.validate && !props.validate(value)) {
error.value = props.errorMessage(value)
return
}
// add value to values array
if (!values.value) {
values.value = [value]
} else {
values.value.push(value)
}
value = value.replace(value, '')
}
}
})
!error.value && (value = '')
if (!value) return
const splitValues = value.split(',')
splitValues.forEach((val) => {
val = val.trim()
if (!val) return
if (values.value?.includes(val)) return
if (props.validate && !props.validate(val)) {
error.value = props.errorMessage(val)
return
}
if (!values.value) values.value = [val]
else values.value.push(val)
})
}
function removeValue(value) {
let indexToRemove = values.value.indexOf(value)
if (indexToRemove > -1) {
values.value.splice(indexToRemove, 1)
}
emit('update:modelValue', values.value)
}
const removeValue = (value) => {
values.value = values.value.filter((v) => v !== value)
}
const removeLastValue = () => {
if (query.value) return
let emailRef = emails.value[emails.value.length - 1]?.$el
if (document.activeElement === emailRef) {
values.value.pop()
nextTick(() => {
if (values.value.length) {
emailRef = emails.value[emails.value.length - 1].$el
emailRef?.focus()
} else {
setFocus()
}
})
} else {
emailRef?.focus()
}
}
function setFocus() {
search.value.$el.focus()
}
defineExpose({ setFocus })
const labelClasses = computed(() => {
return [
{
sm: 'text-xs',
md: 'text-base',
}[props.size || 'sm'],
'text-ink-gray-5',
]
})
const labelClasses = computed(() => [
{ sm: 'text-xs', md: 'text-base' }[props.size || 'sm'],
'text-ink-gray-5',
])
</script>

View File

@@ -2,18 +2,21 @@
<div class="mb-4">
<div v-if="label" class="text-xs text-ink-gray-5 mb-2">
{{ __(label) }}
<span class="text-ink-red-3">*</span>
<span v-if="required" class="text-ink-red-3">*</span>
</div>
<FileUploader
v-if="!modelValue"
:fileTypes="['image/*']"
:validateFile="validateFile"
@success="(file: File) => saveImage(file)"
:fileTypes="[fileType]"
:validateFile="(file: File) => validateFile(file, true, type)"
@success="(file: File) => saveFile(file)"
>
<template v-slot="{ file, progress, uploading, openFileSelector }">
<div class="flex items-center">
<div class="border rounded-md w-fit py-7 px-20">
<Image class="size-5 stroke-1 text-ink-gray-7" />
<component
:is="props.type === 'image' ? Image : Video"
class="size-5 stroke-1 text-ink-gray-7"
/>
</div>
<div class="ml-4">
<Button @click="openFileSelector">
@@ -28,7 +31,20 @@
</FileUploader>
<div v-else class="mb-4">
<div class="flex items-center">
<img :src="modelValue" class="border rounded-md w-44 h-auto" />
<img
v-if="type == 'image'"
:src="modelValue"
:class="[
'border object-cover',
shape === 'circle'
? 'w-20 h-20 rounded-full'
: 'w-44 h-auto min-h-20 max-h-32 rounded-md',
]"
/>
<video v-else controls class="border rounded-md w-44 h-auto">
<source :src="modelValue" />
{{ __('Your browser does not support the video tag.') }}
</video>
<div class="ml-4">
<Button @click="removeImage()">
{{ __('Remove') }}
@@ -47,7 +63,8 @@
<script setup lang="ts">
import { validateFile } from '@/utils'
import { Button, FileUploader } from 'frappe-ui'
import { Image } from 'lucide-vue-next'
import { Image, Video } from 'lucide-vue-next'
import { computed } from 'vue'
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
@@ -55,18 +72,28 @@ const emit = defineEmits<{
const props = withDefaults(
defineProps<{
modelValue: string
modelValue: string | null
label?: string
description?: string
type?: 'image' | 'video'
required?: boolean
shape?: 'square' | 'circle'
}>(),
{
modelValue: '',
label: '',
description: '',
type: 'image',
required: true,
shape: 'square',
}
)
const saveImage = (file: any) => {
const fileType = computed(() => {
return props.type === 'image' ? 'image/*' : 'video/*'
})
const saveFile = (file: any) => {
emit('update:modelValue', file.file_url)
}

View File

@@ -62,7 +62,7 @@
<Tooltip :text="__('Enrolled Students')">
<span class="flex items-center">
<Users class="h-4 w-4 stroke-1.5 mr-1" />
{{ course.enrollments }}
{{ formatAmount(course.enrollments) }}
</span>
</Tooltip>
</div>
@@ -116,28 +116,31 @@
<CourseInstructors :instructors="course.instructors" />
</div>
<div v-if="course.paid_course" class="font-semibold">
{{ course.price }}
</div>
<div class="flex items-center space-x-2">
<div v-if="course.paid_course" class="font-semibold">
{{ course.price }}
</div>
<Tooltip
v-if="course.paid_certificate || course.enable_certification"
:text="__('Get Certified')"
>
<GraduationCap class="size-5 stroke-1.5 text-ink-gray-7" />
</Tooltip>
<Tooltip
v-if="course.paid_certificate || course.enable_certification"
:text="__('Get Certified')"
>
<GraduationCap class="size-5 stroke-1.5 text-ink-gray-7" />
</Tooltip>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { Award, BookOpen, GraduationCap, Star, Users } from 'lucide-vue-next'
import UserAvatar from '@/components/UserAvatar.vue'
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()
@@ -149,19 +152,10 @@ const props = defineProps({
})
const getGradientColor = () => {
let theme = localStorage.getItem('theme') == 'dark' ? 'darkMode' : 'lightMode'
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]})` */
/* return `radial-gradient(ellipse at 80% 20%, black 20%, ${colorMap[500]} 100%)` */
/* return `radial-gradient(ellipse at 30% 70%, black 50%, ${colorMap[500]} 100%)` */
/* return `radial-gradient(ellipse at 80% 20%, ${colorMap[100]} 0%, ${colorMap[300]} 50%, ${colorMap[500]} 100%)` */
/* return `conic-gradient(from 180deg at 50% 50%, ${colorMap[100]} 0%, ${colorMap[200]} 50%, ${colorMap[400]} 100%)` */
/* return `linear-gradient(135deg, ${colorMap[100]}, ${colorMap[300]}), linear-gradient(120deg, rgba(255,255,255,0.4) 0%, transparent 60%) ` */
/* return `radial-gradient(circle at 20% 30%, ${colorMap[100]} 0%, transparent 40%),
radial-gradient(circle at 80% 40%, ${colorMap[200]} 0%, transparent 50%),
linear-gradient(135deg, ${colorMap[300]} 0%, ${colorMap[400]} 100%);` */
}
</script>
<style>

View File

@@ -37,7 +37,7 @@
<CertificationLinks :courseName="course.data.name" class="w-full" />
</div>
<router-link
v-else-if="course.data.paid_course"
v-else-if="course.data.paid_course && !isAdmin"
:to="{
name: 'Billing',
params: {
@@ -56,14 +56,15 @@
</Button>
</router-link>
<Badge
v-else-if="course.data.disable_self_learning"
v-else-if="course.data.disable_self_learning && !isAdmin"
theme="blue"
size="lg"
class="mb-4"
>
{{ __('Contact the Administrator to enroll for this course.') }}
{{ __('Contact the Administrator to enroll for this course') }}
</Badge>
<Button
v-else-if="!user.data?.is_moderator && !is_instructor()"
v-else-if="!isAdmin"
@click="enrollStudent()"
variant="solid"
class="w-full"
@@ -88,35 +89,6 @@
</template>
{{ __('Get Certificate') }}
</Button>
<Button
v-if="user.data?.is_moderator || is_instructor()"
class="w-full mt-2"
size="md"
@click="showProgressSummary"
>
<template #prefix>
<TrendingUp class="size-4 stroke-1.5" />
{{ __('Progress Summary') }}
</template>
</Button>
<router-link
v-if="user?.data?.is_moderator || is_instructor()"
:to="{
name: 'CourseForm',
params: {
courseName: course.data.name,
},
}"
>
<Button variant="subtle" class="w-full mt-2" size="md">
<template #prefix>
<Pencil class="size-4 stroke-1.5" />
</template>
<span>
{{ __('Edit') }}
</span>
</Button>
</router-link>
</div>
<div class="space-y-4">
<div
@@ -168,12 +140,6 @@
</div>
</div>
</div>
<CourseProgressSummary
v-if="user.data?.is_moderator || is_instructor()"
v-model="showProgressModal"
:courseName="course.data.name"
:enrollments="course.data.enrollments"
/>
</template>
<script setup>
import {
@@ -189,15 +155,14 @@ import {
import { computed, inject, ref } from 'vue'
import { Badge, Button, call, createResource, toast } from 'frappe-ui'
import { formatAmount } from '@/utils/'
import { capture } from '@/telemetry'
import { useRouter } from 'vue-router'
import CertificationLinks from '@/components/CertificationLinks.vue'
import CourseProgressSummary from '@/components/Modals/CourseProgressSummary.vue'
import { useTelemetry } from 'frappe-ui/frappe'
const router = useRouter()
const user = inject('$user')
const showProgressModal = ref(false)
const readOnlyMode = window.read_only_mode
const { capture } = useTelemetry()
const props = defineProps({
course: {
@@ -215,13 +180,17 @@ const video_link = computed(() => {
function enrollStudent() {
if (!user.data) {
toast.success(__('You need to login first to enroll for this course'))
toast.warning(__('You need to login first to enroll for this course'))
setTimeout(() => {
window.location.href = `/login?redirect-to=${window.location.pathname}`
}, 500)
} else {
call('lms.lms.doctype.lms_enrollment.lms_enrollment.create_membership', {
course: props.course.data.name,
call('frappe.client.insert', {
doc: {
doctype: 'LMS Enrollment',
course: props.course.data.name,
member: user.data.name,
},
})
.then(() => {
capture('enrolled_in_course', {
@@ -290,7 +259,7 @@ const fetchCertificate = () => {
})
}
const showProgressSummary = () => {
showProgressModal.value = true
}
const isAdmin = computed(() => {
return user.data?.is_moderator || is_instructor()
})
</script>

View File

@@ -1,11 +1,12 @@
<template>
<div class="">
<div class="text-ink-gray-7">
<span v-if="instructors?.length == 1">
<router-link
:to="{
name: 'Profile',
params: { username: instructors[0].username },
}"
class="text-ink-gray-7 hover:text-ink-gray-9"
>
{{ instructors[0].full_name }}
</router-link>
@@ -16,6 +17,7 @@
name: 'Profile',
params: { username: instructors[0].username },
}"
class="text-ink-gray-7 hover:text-ink-gray-9"
>
{{ instructors[0].first_name }}
</router-link>
@@ -25,6 +27,7 @@
name: 'Profile',
params: { username: instructors[1].username },
}"
class="text-ink-gray-7 hover:text-ink-gray-9"
>
{{ instructors[1].first_name }}
</router-link>
@@ -35,6 +38,7 @@
name: 'Profile',
params: { username: instructors[0].username },
}"
class="text-ink-gray-7 hover:text-ink-gray-9"
>
{{ instructors[0].first_name }}
</router-link>

View File

@@ -15,7 +15,10 @@
{{ __(title) }}
</div>
<Button size="sm" v-if="allowEdit" @click="openChapterModal()">
{{ __('Add Chapter') }}
<template #prefix>
<Plus class="size-4 stroke-1.5" />
</template>
{{ __('Add') }}
</Button>
</div>
<div
@@ -95,8 +98,8 @@
name: allowEdit ? 'LessonForm' : 'Lesson',
params: {
courseName: courseName,
chapterNumber: lesson.number.split('.')[0],
lessonNumber: lesson.number.split('.')[1],
chapterNumber: lesson.number.split('-')[0],
lessonNumber: lesson.number.split('-')[1],
},
}"
>
@@ -109,6 +112,14 @@
v-else-if="lesson.icon === 'icon-quiz'"
class="h-4 w-4 stroke-1 mr-2"
/>
<NotebookPen
v-else-if="lesson.icon === 'icon-assignment'"
class="h-4 w-4 stroke-1 mr-2"
/>
<SquareCode
v-else-if="lesson.icon === 'icon-code'"
class="h-4 w-4 stroke-1 mr-2"
/>
<FileText
v-else-if="lesson.icon === 'icon-list'"
class="h-4 w-4 text-ink-gray-9 stroke-1 mr-2"
@@ -174,7 +185,11 @@ import {
FilePenLine,
HelpCircle,
MonitorPlay,
NotebookPen,
Plus,
SquareCode,
Trash2,
Notebook,
} from 'lucide-vue-next'
import { useRoute, useRouter } from 'vue-router'
import ChapterModal from '@/components/Modals/ChapterModal.vue'
@@ -389,8 +404,8 @@ const redirectToChapter = (chapter) => {
const isActiveLesson = (lessonNumber) => {
return (
route.params.chapterNumber == lessonNumber.split('.')[0] &&
route.params.lessonNumber == lessonNumber.split('.')[1]
route.params.chapterNumber == lessonNumber.split('-')[0] &&
route.params.lessonNumber == lessonNumber.split('-')[1]
)
}
</script>

View File

@@ -12,7 +12,7 @@
</div>
<div class="grid gap-8 mt-10">
<div v-for="(review, index) in reviews.data">
<div class="flex items-center">
<div class="flex">
<router-link
:to="{
name: 'Profile',
@@ -46,11 +46,11 @@
"
/>
</div>
<div v-if="review.review" class="mt-4 leading-5 text-ink-gray-7">
{{ review.review }}
</div>
</div>
</div>
<div v-if="review.review" class="mt-4 leading-5 text-ink-gray-7">
{{ review.review }}
</div>
</div>
</div>
</div>
@@ -80,7 +80,7 @@ const props = defineProps({
required: true,
},
membership: {
type: Object,
type: Object || null,
required: false,
},
})

View File

@@ -9,5 +9,5 @@
</div>
</template>
<script setup>
import AppSidebar from './AppSidebar.vue'
import AppSidebar from '@/components/Sidebar/AppSidebar.vue'
</script>

View File

@@ -93,11 +93,19 @@
</div>
</template>
<script setup>
import { createResource, TextEditor, Button, Dropdown, toast } from 'frappe-ui'
import {
call,
createResource,
TextEditor,
Button,
Dropdown,
toast,
} from 'frappe-ui'
import { timeAgo } from '@/utils'
import UserAvatar from '@/components/UserAvatar.vue'
import { ChevronLeft, MoreHorizontal } from 'lucide-vue-next'
import { ref, inject, onMounted, onUnmounted } from 'vue'
import { useTelemetry } from 'frappe-ui/frappe'
const showTopics = defineModel('showTopics')
const newReply = ref('')
@@ -107,6 +115,7 @@ const allUsers = inject('$allUsers')
const mentionUsers = ref([])
const renderEditor = ref(false)
const readOnlyMode = window.read_only_mode
const { capture } = useTelemetry()
const props = defineProps({
topic: {
@@ -143,19 +152,6 @@ const replies = createResource({
auto: true,
})
const newReplyResource = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'Discussion Reply',
reply: newReply.value,
topic: props.topic.name,
},
}
},
})
const fetchMentionUsers = () => {
if (user.data?.is_student) {
renderEditor.value = true
@@ -178,78 +174,61 @@ const fetchMentionUsers = () => {
}
const postReply = () => {
newReplyResource.submit(
{},
{
validate() {
if (!newReply.value) {
return 'Reply cannot be empty'
}
},
onSuccess() {
newReply.value = ''
replies.reload()
},
onError(err) {
toast.error(err.messages?.[0] || err)
},
}
)
}
const editReplyResource = createResource({
url: 'frappe.client.set_value',
makeParams(values) {
return {
if (!newReply.value) {
toast.error(__('Reply cannot be empty.'))
return
}
call('frappe.client.insert', {
doc: {
doctype: 'Discussion Reply',
name: values.name,
fieldname: 'reply',
value: values.reply,
}
},
})
reply: newReply.value,
topic: props.topic.name,
},
})
.then((data) => {
newReply.value = ''
replies.reload()
capture('discussion_reply_created')
})
.catch((err) => {
toast.error(err.messages?.[0] || err)
console.error(err)
})
}
const postEdited = (reply) => {
editReplyResource.submit(
{
name: reply.name,
reply: reply.reply,
},
{
validate() {
if (!reply.reply) {
return 'Reply cannot be empty'
}
},
onSuccess() {
reply.editable = false
replies.reload()
},
}
)
if (!reply.reply) {
toast.error(__('Reply cannot be empty.'))
return
}
call('frappe.client.set_value', {
doctype: 'Discussion Reply',
name: reply.name,
fieldname: 'reply',
value: reply.reply,
})
.then(() => {
reply.editable = false
replies.reload()
})
.catch((err) => {
toast.error(err.messages?.[0] || err)
console.error(err)
})
}
const deleteReplyResource = createResource({
url: 'frappe.client.delete',
makeParams(values) {
return {
doctype: 'Discussion Reply',
name: values.name,
}
},
})
const deleteReply = (reply) => {
deleteReplyResource.submit(
{
name: reply.name,
},
{
onSuccess() {
replies.reload()
},
}
)
call('frappe.client.delete', {
doctype: 'Discussion Reply',
name: reply.name,
})
.then(() => {
replies.reload()
})
.catch((err) => {
toast.error(err.messages?.[0] || err)
console.error(err)
})
}
onUnmounted(() => {

View File

@@ -20,10 +20,10 @@
</template>
</Dialog>
<Popover :show="iosInstallMessage" placement="top">
<Popover :show="iosInstallMessage" placement="top-start">
<template #body>
<div
class="fixed bottom-[4rem] left-1/2 -translate-x-1/2 z-20 w-[90%] flex flex-col gap-3 rounded bg-blue-100 py-5 drop-shadow-xl"
class="fixed top-[20rem] translate-x-1/3 z-20 flex flex-col gap-3 rounded bg-surface-white py-5 drop-shadow-xl"
>
<div
class="mb-1 flex flex-row items-center justify-between px-3 text-center"
@@ -41,7 +41,7 @@
</div>
<div class="px-3 text-xs text-gray-800">
<span class="flex flex-col gap-2">
<span>
<span class="leading-5">
{{
__(
'Get the app on your iPhone for easy access & a better experience'
@@ -76,7 +76,14 @@ const isIos = () => {
const isInStandaloneMode = () =>
'standalone' in window.navigator && window.navigator.standalone
if (isIos() && !isInStandaloneMode()) iosInstallMessage.value = true
if (
isIos() &&
!isInStandaloneMode() &&
localStorage.getItem('learningIosInstallPromptShown') !== 'true'
) {
iosInstallMessage.value = true
localStorage.setItem('learningIosInstallPromptShown', 'true')
}
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()

View File

@@ -3,7 +3,7 @@
class="flex flex-col border rounded-md p-3 h-full hover:border-outline-gray-3"
>
<div class="flex space-x-4 mb-4">
<div class="flex flex-col space-y-2 flex-1">
<div class="flex flex-col space-y-2 flex-1 break-all">
<div class="text-lg font-semibold text-ink-gray-9">
{{ job.company_name }}
</div>

View File

@@ -9,6 +9,16 @@
allowfullscreen
></iframe>
</div>
<div v-if="rutube">
<iframe
class="rutube-video"
:src="getRutubeVideoSource(rutube.split('/').pop())"
width="100%"
:height="screenSize.width < 640 ? 200 : 400"
frameborder="0"
allowfullscreen
></iframe>
</div>
<div v-for="block in content?.split('\n\n')">
<div v-if="block.includes('{{ YouTubeVideo')">
<iframe
@@ -20,6 +30,16 @@
allowfullscreen
></iframe>
</div>
<div v-else-if="block.includes('{{ RutubeVideo')">
<iframe
class="rutube-video"
:src="getRutubeVideoSource(block)"
width="100%"
:height="screenSize.width < 640 ? 200 : 400"
frameborder="0"
allowfullscreen
></iframe>
</div>
<div v-else-if="block.includes('{{ Quiz')">
<Quiz :quiz="getId(block)" />
</div>
@@ -97,6 +117,13 @@ const getYouTubeVideoSource = (block) => {
return `https://www.youtube.com/embed/${block}`
}
const getRutubeVideoSource = (block) => {
if (block.includes('{{')) {
block = getId(block)
}
return `https://rutube.ru/play/embed/${block}`
}
const getPDFSource = (block) => {
return `${getId(block)}#toolbar=0`
}
@@ -105,3 +132,11 @@ const getId = (block) => {
return block.match(/\(["']([^"']+?)["']\)/)[1]
}
</script>
<style scoped>
.youtube-video,
.rutube-video {
display: block;
margin: 0 auto;
}
</style>

View File

@@ -52,9 +52,9 @@ const contentMap = {
'To upload Image, Video, Audio or PDF from your system, click on the add icon and select upload from the menu. Then choose the file you want to add to the lesson and it gets added to your lesson.',
},
youtube: {
title: 'How to add a YouTube Video?',
title: 'How to add a YouTube Video/RuTube?',
description:
'Copy the URL of the video from YouTube and paste it in the editor.',
'Copy the URL of the video from YouTube/RuTube and paste it in the editor.',
},
remove: {
title: 'How to remove an embed?',

View File

@@ -1,217 +0,0 @@
<template>
<div
v-if="hasPermission() && !props.zoomAccount"
class="flex items-center space-x-2 mb-5 bg-surface-amber-1 py-1 px-2 rounded-md text-ink-amber-3"
>
<AlertCircle class="size-4 stroke-1.5" />
<span>
{{ __('Please add a zoom account to the batch to create live classes.') }}
</span>
</div>
<div class="flex items-center justify-between">
<div class="text-lg font-semibold text-ink-gray-9">
{{ __('Live Class') }}
</div>
<Button v-if="canCreateClass()" @click="openLiveClassModal">
<template #prefix>
<Plus class="h-4 w-4" />
</template>
<span>
{{ __('Add') }}
</span>
</Button>
</div>
<div
v-if="liveClasses.data?.length"
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 mt-5"
>
<div
v-for="cls in liveClasses.data"
class="flex flex-col border rounded-md h-full text-ink-gray-7 hover:border-outline-gray-3 p-3"
:class="{
'cursor-pointer': hasPermission() && cls.attendees > 0,
}"
@click="
() => {
openAttendanceModal(cls)
}
"
>
<div class="font-semibold text-ink-gray-9 text-lg mb-1">
{{ cls.title }}
</div>
<div class="short-introduction">
{{ cls.description }}
</div>
<div class="mt-auto space-y-3">
<div class="flex items-center space-x-2">
<Calendar class="w-4 h-4 stroke-1.5" />
<span>
{{ dayjs(cls.date).format('DD MMMM YYYY') }}
</span>
</div>
<div class="flex items-center space-x-2">
<Clock class="w-4 h-4 stroke-1.5" />
<span>
{{ dayjs(getClassStart(cls)).format('hh:mm A') }} -
{{ dayjs(getClassEnd(cls)).format('hh:mm A') }}
</span>
</div>
<div
v-if="canAccessClass(cls)"
class="flex items-center space-x-2 text-ink-gray-9 mt-auto"
>
<a
v-if="user.data?.is_moderator || user.data?.is_evaluator"
:href="cls.start_url"
target="_blank"
class="cursor-pointer inline-flex items-center justify-center gap-2 transition-colors focus:outline-none text-ink-gray-8 bg-surface-gray-2 hover:bg-surface-gray-3 active:bg-surface-gray-4 focus-visible:ring focus-visible:ring-outline-gray-3 h-7 text-base px-2 rounded"
:class="cls.join_url ? 'w-full' : 'w-1/2'"
>
<Monitor class="h-4 w-4 stroke-1.5" />
{{ __('Start') }}
</a>
<a
:href="cls.join_url"
target="_blank"
class="w-full cursor-pointer inline-flex items-center justify-center gap-2 transition-colors focus:outline-none text-ink-gray-8 bg-surface-gray-2 hover:bg-surface-gray-3 active:bg-surface-gray-4 focus-visible:ring focus-visible:ring-outline-gray-3 h-7 text-base px-2 rounded"
>
<Video class="h-4 w-4 stroke-1.5" />
{{ __('Join') }}
</a>
</div>
<Tooltip
v-else-if="hasClassEnded(cls)"
:text="__('This class has ended')"
placement="right"
>
<div class="flex items-center space-x-2 text-ink-amber-3 w-fit">
<Info class="w-4 h-4 stroke-1.5" />
<span>
{{ __('Ended') }}
</span>
</div>
</Tooltip>
</div>
</div>
</div>
<div v-else class="text-sm italic text-ink-gray-5 mt-2">
{{ __('No live classes scheduled') }}
</div>
<LiveClassModal
:batch="props.batch"
:zoomAccount="props.zoomAccount"
v-model="showLiveClassModal"
v-model:reloadLiveClasses="liveClasses"
/>
<LiveClassAttendance v-model="showAttendance" :live_class="attendanceFor" />
</template>
<script setup>
import { createListResource, Button, Tooltip } from 'frappe-ui'
import {
Plus,
Clock,
Calendar,
Video,
Monitor,
Info,
AlertCircle,
} from 'lucide-vue-next'
import { inject, ref } from 'vue'
import { formatTime } from '@/utils/'
import LiveClassModal from '@/components/Modals/LiveClassModal.vue'
import LiveClassAttendance from '@/components/Modals/LiveClassAttendance.vue'
const user = inject('$user')
const showLiveClassModal = ref(false)
const dayjs = inject('$dayjs')
const readOnlyMode = window.read_only_mode
const showAttendance = ref(false)
const attendanceFor = ref(null)
const props = defineProps({
batch: {
type: String,
required: true,
},
zoomAccount: String,
})
const liveClasses = createListResource({
doctype: 'LMS Live Class',
filters: {
batch_name: props.batch,
},
fields: [
'title',
'description',
'time',
'date',
'duration',
'attendees',
'start_url',
'join_url',
'owner',
],
orderBy: 'date',
auto: true,
})
const openLiveClassModal = () => {
showLiveClassModal.value = true
}
const canCreateClass = () => {
if (readOnlyMode) return false
if (!props.zoomAccount) return false
return hasPermission()
}
const hasPermission = () => {
return user.data?.is_moderator || user.data?.is_evaluator
}
const canAccessClass = (cls) => {
if (cls.date < dayjs().format('YYYY-MM-DD')) return false
if (cls.date > dayjs().format('YYYY-MM-DD')) return false
if (hasClassEnded(cls)) return false
return true
}
const getClassStart = (cls) => {
return new Date(`${cls.date}T${cls.time}`)
}
const getClassEnd = (cls) => {
const classStart = getClassStart(cls)
return new Date(classStart.getTime() + cls.duration * 60000)
}
const hasClassEnded = (cls) => {
const classEnd = getClassEnd(cls)
const now = new Date()
return now > classEnd
}
const openAttendanceModal = (cls) => {
if (!hasPermission()) return
if (cls.attendees <= 0) return
showAttendance.value = true
attendanceFor.value = cls
}
</script>
<style>
.short-introduction {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
width: 100%;
overflow: hidden;
margin: 0.25rem 0 1.5rem;
line-height: 1.5;
}
</style>

View File

@@ -80,6 +80,11 @@ onMounted(() => {
{},
{
onSuccess(data) {
if (userResource.data) {
addSideBar()
} else {
destructureSidebarLinks()
}
filterLinksToShow(data)
addOtherLinks()
},
@@ -103,6 +108,16 @@ watch(showMenu, (val) => {
}
})
const destructureSidebarLinks = () => {
let links = []
sidebarLinks.value.forEach((link) => {
link.items?.forEach((item) => {
links.push(item)
})
})
sidebarLinks.value = links
}
const filterLinksToShow = (data) => {
Object.keys(data).forEach((key) => {
if (!parseInt(data[key])) {
@@ -113,24 +128,184 @@ const filterLinksToShow = (data) => {
})
}
const addSideBar = () => {
sidebarLinks.value = [] // Очищаем, чтобы избежать дублирования
// Проверяем роли пользователя
const roles = userResource.data?.roles || []
sidebarLinks.value.push({
label: __('Courses'),
icon: 'BookOpen',
to: 'Courses',
activeFor: [
'Courses',
'CourseDetail',
'Lesson',
'CourseForm',
'LessonForm',
],
})
sidebarLinks.value.push({
label: __('Leader Board'),
icon: 'Trophy',
to: 'LeaderBoard',
activeFor: [],
})
if (roles.includes('LMS Student') || roles.includes('LMS Schoolchild')) {
otherLinks.value.push({
label: __('My points'),
icon: 'Award',
to: 'MyPoints',
activeFor: [],
})
}
if (roles.includes('Parent')) {
otherLinks.value.push({
label: __('My Child'),
icon: 'User',
to: 'my-child',
external: true,
activeFor: [],
})
}
let chatGPTURL = ''
let chatGPTLabel = ''
if (roles.includes('LMS Schoolchild')) {
chatGPTURL = 'chatgpt-schoolchild'
chatGPTLabel = __('ChatGPT for Schoolers')
} else if (roles.includes('LMS Student')) {
chatGPTURL = 'chatgpt-schoolchild'
chatGPTLabel = __('ChatGPT for Students')
} else if (roles.includes('Course Creator')) {
chatGPTURL = 'ai-teachers'
chatGPTLabel = __('ChatGPT for Teachers')
}
if (chatGPTURL) {
sidebarLinks.value.push({
label: chatGPTLabel,
icon: 'Cpu',
to: chatGPTURL,
external: true,
activeFor: [],
})
}
}
const addOtherLinks = () => {
otherLinks.value = []
if (user) {
const roles = userResource.data?.roles || []
if (!userResource.data?.is_instructor && !userResource.data?.is_moderator) {
otherLinks.value.push({
label: __('Programs'),
icon: 'Route',
to: 'Programs',
activeFor: ['Programs', 'ProgramForm', 'CourseDetail', 'Lesson'],
})
} else if (userResource.data?.is_instructor || userResource.data?.is_moderator) {
otherLinks.value.push({
label: __('Programs'),
icon: 'Route',
to: 'Programs',
activeFor: ['Programs', 'ProgramForm'],
})
}
if (userResource.data?.is_moderator || userResource.data?.is_instructor) {
otherLinks.value.push({
label: __('Quizzes'),
icon: 'CircleHelp',
to: 'Quizzes',
activeFor: [
'Quizzes',
'QuizForm',
'QuizSubmissionList',
'QuizSubmission',
],
})
otherLinks.value.push({
label: __('Assignments'),
icon: 'Pencil',
to: 'Assignments',
activeFor: [
'Assignments',
'AssignmentForm',
'AssignmentSubmissionList',
'AssignmentSubmission',
],
}),
otherLinks.value.push({
label: 'Programming Exercises',
icon: 'Code',
to: 'ProgrammingExercises',
})
}
if (roles.includes('LMS Student') || roles.includes('LMS Schoolchild')) {
otherLinks.value.push({
label: __('My points'),
icon: 'Award',
to: 'my_points',
external: true,
activeFor: [],
})
}
let chatGPTURL = ''
let chatGPTLabel = ''
if (roles.includes('LMS Schoolchild')) {
chatGPTURL = 'chatgpt-schoolchild'
chatGPTLabel = __('ChatGPT for Schoolers')
} else if (roles.includes('LMS Student')) {
chatGPTURL = 'chatgpt-schoolchild'
chatGPTLabel = __('ChatGPT for Students')
} else if (roles.includes('Course Creator')) {
chatGPTURL = 'ai-teachers'
chatGPTLabel = __('ChatGPT for Teachers')
}
if (chatGPTURL) {
otherLinks.value.push({
label: chatGPTLabel,
icon: 'Cpu',
to: chatGPTURL,
external: true,
activeFor: [],
})
}
otherLinks.value.push({
label: 'Notifications',
icon: 'Bell',
to: 'Notifications',
label: __('Leader Board'),
icon: 'Trophy',
to: 'leaderboardsample',
external: true,
activeFor: [],
})
otherLinks.value.push({
label: 'Profile',
label: __('Profile'),
icon: 'UserRound',
to: 'Profile',
params: { username: userResource.data?.username },
})
otherLinks.value.push({
label: 'Log out',
label: __('Log out'),
icon: 'LogOut',
})
} else {
otherLinks.value.push({
label: 'Log in',
label: __('Log in'),
icon: 'LogIn',
})
}
@@ -138,55 +313,11 @@ const addOtherLinks = () => {
watch(userResource, () => {
if (userResource.data) {
isModerator.value = userResource.data.is_moderator
isInstructor.value = userResource.data.is_instructor
addPrograms()
if (isModerator.value || isInstructor.value) {
addProgrammingExercises()
addQuizzes()
addAssignments()
}
addSideBar() // Обновляем sidebarLinks при изменении userResource
addOtherLinks() // Обновляем otherLinks
}
})
const addQuizzes = () => {
otherLinks.value.push({
label: 'Quizzes',
icon: 'CircleHelp',
to: 'Quizzes',
})
}
const addAssignments = () => {
otherLinks.value.push({
label: 'Assignments',
icon: 'Pencil',
to: 'Assignments',
})
}
const addPrograms = async () => {
let canAddProgram = await checkIfCanAddProgram()
if (!canAddProgram) return
let activeFor = ['Programs', 'ProgramDetail']
let index = 1
sidebarLinks.value.splice(index, 0, {
label: 'Programs',
icon: 'Route',
to: 'Programs',
activeFor: activeFor,
})
}
const checkIfCanAddProgram = async () => {
if (isModerator.value || isInstructor.value) {
return true
}
const programs = await call('lms.lms.utils.get_programs')
return programs.enrolled.length > 0 || programs.published.length > 0
}
let isActive = (tab) => {
return tab.activeFor?.includes(router.currentRoute.value.name)
}
@@ -204,6 +335,7 @@ const handleClick = (tab) => {
username: userResource.data?.username,
},
})
else if (tab.external) window.location.href = `/${tab.to}`
else router.push({ name: tab.to })
}

View File

@@ -20,11 +20,15 @@
:options="assessmentTypes"
v-model="assessmentType"
:label="__('Type')"
placeholder=" "
@update:modelValue="() => (assessment = null)"
/>
<Link
v-if="assessmentType"
v-model="assessment"
:doctype="assessmentType"
:label="__('Assessment')"
placeholder=" "
:onCreate="
(value, close) => {
close()
@@ -49,7 +53,7 @@
</template>
<script setup>
import { Dialog, FormControl, createResource, toast } from 'frappe-ui'
import Link from '@/components/Controls/Link.vue'
import { Link } from 'frappe-ui/frappe'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'

View File

@@ -27,6 +27,12 @@
:label="__('Submission Type')"
:required="true"
/>
<Link
v-model="assignment.course"
:label="__('Course')"
doctype="LMS Course"
placeholder=" "
/>
<div>
<div class="text-xs text-ink-gray-5 mb-2">
{{ __('Question') }}
@@ -37,7 +43,7 @@
@change="(val) => (assignment.question = val)"
:editable="true"
:fixedMenu="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] max-h-[18rem] overflow-y-auto"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[18rem] overflow-y-auto"
/>
</div>
</div>
@@ -66,6 +72,8 @@
<script setup lang="ts">
import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { computed, reactive, watch } from 'vue'
import { escapeHTML, sanitizeHTML } from '@/utils'
import { Link } from 'frappe-ui/frappe'
const show = defineModel()
const assignments = defineModel<Assignments>('assignments')
@@ -74,6 +82,7 @@ interface Assignment {
title: string
type: string
question: string
course?: string
}
interface Assignments {
@@ -88,6 +97,7 @@ const assignment = reactive({
title: '',
type: '',
question: '',
course: '',
})
const props = defineProps({
@@ -106,6 +116,7 @@ watch(
assignment.title = row.title
assignment.type = row.type
assignment.question = row.question
assignment.course = row.course || ''
}
})
}
@@ -113,33 +124,55 @@ watch(
{ flush: 'post' }
)
const saveAssignment = () => {
if (props.assignmentID == 'new') {
assignments.value.insert.submit(
{
...assignment,
},
{
onSuccess() {
show.value = false
toast.success(__('Assignment created successfully'))
},
}
)
} else {
assignments.value.setValue.submit(
{
...assignment,
name: props.assignmentID,
},
{
onSuccess() {
show.value = false
toast.success(__('Assignment updated successfully'))
},
}
)
watch(show, (newVal) => {
if (newVal && props.assignmentID === 'new') {
assignment.title = ''
assignment.type = ''
assignment.question = ''
}
})
const validateFields = () => {
assignment.title = escapeHTML(assignment.title.trim())
assignment.question = sanitizeHTML(assignment.question)
}
const saveAssignment = () => {
validateFields()
if (props.assignmentID == 'new') {
createAssignment()
} else {
updateAssignment()
}
}
const createAssignment = () => {
assignments.value.insert.submit(
{
...assignment,
},
{
onSuccess() {
show.value = false
toast.success(__('Assignment created successfully'))
},
}
)
}
const updateAssignment = () => {
assignments.value.setValue.submit(
{
...assignment,
name: props.assignmentID,
},
{
onSuccess() {
show.value = false
toast.success(__('Assignment updated successfully'))
},
}
)
}
const assignmentOptions = computed(() => {

View File

@@ -2,8 +2,8 @@
<Dialog
v-model="show"
:options="{
title: __('Add a course'),
size: 'sm',
title: __('Add a course to the batch'),
size: 'lg',
actions: [
{
label: __('Submit'),
@@ -19,14 +19,13 @@
v-model="course"
:label="__('Course')"
:required="true"
:filters="{ published: 1 }"
:onCreate="
(value, close) => {
close()
router.push({
name: 'CourseForm',
params: {
courseName: 'new',
},
name: 'Courses',
query: { newCourse: '1' },
})
}
"
@@ -42,7 +41,7 @@
</Dialog>
</template>
<script setup>
import { Dialog, createResource, toast } from 'frappe-ui'
import { Dialog, toast } from 'frappe-ui'
import { ref, inject } from 'vue'
import Link from '@/components/Controls/Link.vue'
import { useOnboarding } from 'frappe-ui/frappe'
@@ -64,37 +63,28 @@ const props = defineProps({
},
})
const createBatchCourse = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'Batch Course',
parent: props.batch,
parenttype: 'LMS Batch',
parentfield: 'courses',
course: course.value,
evaluator: evaluator.value,
},
}
},
})
const addCourse = (close) => {
createBatchCourse.submit(
{},
courses.value.insert.submit(
{
course: course.value,
evaluator: evaluator.value,
parent: props.batch,
parenttype: 'LMS Batch',
parentfield: 'courses',
},
{
onSuccess() {
if (user.data?.is_system_manager)
updateOnboardingStep('add_batch_course')
close()
courses.value.reload()
course.value = null
evaluator.value = null
toast.success(__('Course added to batch successfully'))
},
onError(err) {
toast.error(err.messages?.[0] || err)
console.log(err)
},
}
)

View File

@@ -1,142 +0,0 @@
<template>
<Dialog
v-model="show"
:options="{
size: 'xl',
}"
>
<template #body>
<div class="p-5 space-y-10 text-base">
<div class="flex items-center space-x-2">
<Avatar :image="student.user_image" size="3xl" />
<div class="space-y-1">
<div class="flex items-center space-x-2">
<div class="text-xl font-semibold">
{{ student.full_name }}
</div>
<Badge
v-if="
Object.keys(student.assessments).length ||
Object.keys(student.courses).length
"
:theme="student.progress === 100 ? 'green' : 'red'"
>
{{ student.progress }}% {{ __('Complete') }}
</Badge>
</div>
<div class="text-sm text-ink-gray-7">
{{ student.email }}
</div>
</div>
</div>
<div class="space-y-8">
<!-- Assessments -->
<div
v-if="Object.keys(student.assessments).length"
class="space-y-2 text-sm"
>
<div class="flex items-center border-b pb-1 font-medium">
<span class="flex-1">
{{ __('Assessment') }}
</span>
<span>
{{ __('Percentage/Status') }}
</span>
</div>
<router-link
v-for="assessment in Object.keys(student.assessments)"
class="flex items-center text-ink-gray-7 font-medium"
:to="{
name:
student.assessments[assessment].type == 'LMS Assignment'
? 'AssignmentSubmission'
: '',
params:
student.assessments[assessment].type == 'LMS Assignment'
? {
assignmentID:
student.assessments[assessment].assessment,
submissionName:
student.assessments[assessment].submission,
}
: {},
}"
>
<span class="flex-1">
{{ assessment }}
</span>
<span v-if="isAssignment(student.assessments[assessment].status)">
<Badge
:theme="
getStatusTheme(student.assessments[assessment].status)
"
>
{{ student.assessments[assessment].status }}
</Badge>
</span>
<span v-else>
{{ student.assessments[assessment].status }}
</span>
</router-link>
</div>
<!-- Courses -->
<div
v-if="Object.keys(student.courses).length"
class="space-y-2 text-sm"
>
<div class="flex items-center border-b pb-1 font-medium">
<span class="flex-1">
{{ __('Courses') }}
</span>
<span>
{{ __('Progress') }}
</span>
</div>
<div
v-for="course in Object.keys(student.courses)"
class="flex items-center text-ink-gray-7 font-medium"
>
<span class="flex-1">
{{ course }}
</span>
<span>
{{ Math.floor(student.courses[course]) }}
</span>
</div>
</div>
</div>
<!-- Heatmap -->
<StudentHeatmap :member="student.email" :days="120" />
</div>
</template>
</Dialog>
</template>
<script setup>
import { Avatar, Badge, Dialog } from 'frappe-ui'
import StudentHeatmap from '@/components/StudentHeatmap.vue'
const show = defineModel()
const props = defineProps({
student: {
type: Object,
default: null,
},
})
const isAssignment = (value) => {
return isNaN(value)
}
const getStatusTheme = (status) => {
if (status === 'Pass') {
return 'green'
} else if (status == 'Not Graded') {
return 'orange'
} else {
return 'red'
}
}
</script>

View File

@@ -50,7 +50,7 @@
<FileText class="h-5 w-5 stroke-1.5 text-ink-gray-7" />
</div>
<div class="flex flex-col">
<span>
<span class="text-ink-gray-9">
{{ chapter.scorm_package.file_name }}
</span>
<span class="text-sm text-ink-gray-4 mt-1">
@@ -80,13 +80,13 @@ import {
} from 'frappe-ui'
import { reactive, watch, inject } from 'vue'
import { getFileSize } from '@/utils/'
import { capture } from '@/telemetry'
import { FileText, X } from 'lucide-vue-next'
import { useOnboarding } from 'frappe-ui/frappe'
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
const show = defineModel()
const outline = defineModel('outline')
const user = inject('$user')
const { capture } = useTelemetry()
const { updateOnboardingStep } = useOnboarding('learning')
const props = defineProps({

View File

@@ -1,231 +0,0 @@
<template>
<Dialog
v-model="show"
:options="{
title: __('Course Progress Summary'),
size: '5xl',
}"
>
<template #body-content>
<div
class="flex flex-col-reverse md:flex-row justify-between md:space-x-10 text-base mt-10"
>
<div class="w-full">
<div class="flex items-center justify-between space-x-5 mb-4">
<FormControl
v-model="searchFilter"
:placeholder="__('Search by Member')"
type="text"
class="w-full"
/>
</div>
<div class="max-h-[70vh] overflow-y-auto">
<ListView
v-if="progressList.loading || progressList.data?.length"
:columns="progressColumns"
:rows="progressList.data"
rowKey="name"
:options="{
selectable: false,
showTooltip: false,
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
>
<ListHeaderItem
:item="item"
v-for="item in progressColumns"
:key="item.key"
>
<template #prefix="{ item }">
<FeatherIcon
:name="item.icon?.toString()"
class="h-4 w-4"
/>
</template>
</ListHeaderItem>
</ListHeader>
<ListRows v-for="row in progressList.data">
<router-link
:to="{
name: 'Profile',
params: { username: row.member_username },
}"
>
<ListRow :row="row">
<template #default="{ column, item }">
<ListRowItem
:item="row[column.key]"
:align="column.align"
>
<template #prefix>
<div v-if="column.key == 'member_name'">
<Avatar
class="flex items-center"
:image="row['member_image']"
:label="item"
size="sm"
/>
</div>
</template>
<div>
{{ row[column.key].toString() }}
</div>
</ListRowItem>
</template>
</ListRow>
</router-link>
</ListRows>
</ListView>
<div
v-if="progressList.data && progressList.hasNextPage"
class="flex justify-center my-5"
>
<Button @click="progressList.next()">
{{ __('Load More') }}
</Button>
</div>
</div>
</div>
<div class="mb-4 self-start w-full space-y-5">
<div
class="flex flex-col md:flex-row items-center space-y-2 md:space-y-0 md:space-x-4"
>
<NumberChart
class="border rounded-md w-full"
:config="{
title: __('Enrollments'),
value: memberCount || 0,
}"
/>
<NumberChart
class="border rounded-md w-full"
:config="{
title: __('Average Progress %'),
value: chartDetails.data?.average_progress || 0,
}"
/>
</div>
<DonutChart
:config="{
data: chartDetails.data?.progress_distribution || [],
title: __('Progress Distribution'),
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'],
],
}"
/>
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import {
Avatar,
Button,
createListResource,
createResource,
Dialog,
DonutChart,
FeatherIcon,
FormControl,
ListView,
ListHeader,
ListHeaderItem,
ListRows,
ListRow,
ListRowItem,
NumberChart,
} from 'frappe-ui'
import { computed, ref, watch } from 'vue'
import { theme } from '@/utils/theme'
const show = defineModel<boolean>({ default: false })
const searchFilter = ref<string | null>(null)
type Filters = {
course: string | undefined
member_name?: string[]
}
const props = defineProps<{
courseName?: string
enrollments?: number
}>()
const memberCount = ref<number>(props.enrollments || 0)
const chartDetails = createResource({
url: 'lms.lms.api.get_course_progress_distribution',
params: {
course: props.courseName,
},
auto: true,
})
const progressList = createListResource({
doctype: 'LMS Enrollment',
filters: {
course: props.courseName,
},
fields: [
'name',
'member',
'member_name',
'member_image',
'member_username',
'progress',
],
pageLength: 50,
auto: true,
})
watch([searchFilter], () => {
let filterApplied = false
let filters: Filters = {
course: props.courseName,
}
if (searchFilter.value) {
filters.member_name = ['like', `%${searchFilter.value}%`]
filterApplied = true
}
progressList.update({
filters: filters,
})
progressList.reload(
{},
{
onSuccess(data: any[]) {
memberCount.value = filterApplied ? data.length : props.enrollments || 0
},
}
)
})
const progressColumns = computed(() => {
return [
{
label: __('Member'),
key: 'member_name',
width: '60%',
icon: 'user',
},
{
label: __('Progress'),
key: 'progress',
align: 'right',
icon: 'trending-up',
},
]
})
</script>

View File

@@ -26,7 +26,7 @@
@change="(val) => (topic.reply = val)"
:editable="true"
:fixedMenu="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]"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem]"
/>
</div>
</div>
@@ -34,17 +34,13 @@
</Dialog>
</template>
<script setup>
import {
Dialog,
FormControl,
TextEditor,
createResource,
toast,
} from 'frappe-ui'
import { call, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { reactive } from 'vue'
import { singularize } from '@/utils'
import { useTelemetry } from 'frappe-ui/frappe'
const topics = defineModel('reloadTopics')
const { capture } = useTelemetry()
const props = defineProps({
title: {
@@ -66,64 +62,50 @@ const topic = reactive({
reply: '',
})
const topicResource = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'Discussion Topic',
reference_doctype: props.doctype,
reference_docname: props.docname,
title: topic.title,
},
}
},
})
const replyResource = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'Discussion Reply',
topic: values.topic,
reply: topic.reply,
},
}
},
})
const submitTopic = (close) => {
topicResource.submit(
{},
{
validate() {
if (!topic.title) {
return 'Title cannot be empty.'
}
if (!topic.reply) {
return 'Reply cannot be empty.'
}
},
onSuccess(data) {
replyResource.submit(
{
topic: data.name,
},
{
onSuccess() {
topic.title = ''
topic.reply = ''
topics.value.reload()
close()
},
}
)
},
onError(err) {
toast.error(err.messages?.[0] || err)
},
}
)
if (!topic.title) {
toast.error(__('Title cannot be empty.'))
return
}
if (!topic.reply) {
toast.error(__('Details cannot be empty.'))
return
}
call('frappe.client.insert', {
doc: {
doctype: 'Discussion Topic',
reference_doctype: props.doctype,
reference_docname: props.docname,
title: topic.title,
},
})
.then((data) => {
createReply(data.name, close)
})
.catch((err) => {
toast.error(err.messages?.[0] || err)
console.error(err)
})
}
const createReply = (topicName, close) => {
call('frappe.client.insert', {
doc: {
doctype: 'Discussion Reply',
topic: topicName,
reply: topic.reply,
},
})
.then((data) => {
topic.title = ''
topic.reply = ''
topics.value.reload()
capture('discussion_topic_created')
close()
})
.catch((err) => {
toast.error(err.messages?.[0] || err)
console.error(err)
})
}
</script>

Some files were not shown because too many files have changed in this diff Show More