Compare commits

..

1325 Commits

Author SHA1 Message Date
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
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 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 af5bce9e34)

# Conflicts:
#	lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
2026-02-25 07:21:47 +00:00
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 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 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 61d13aeb12 Merge pull request #2094 from frappe/develop
chore: merge `develop` into `main-hotfix`
2026-02-18 12:11:10 +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
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 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 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 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
MochaMind 80a9f2abe2 chore: Spanish translations 2026-02-12 19:28:48 +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 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
MochaMind 560ac8d5c4 chore: Burmese translations 2026-02-11 18:52:08 +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
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
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
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
Jannat Patel 466b248c30 fix: validate course details before enrolling 2025-12-04 17:48:39 +05:30
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
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
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
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
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
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
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
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
Jannat Patel aacd5ab7a1 chore: upgraded frappe-ui to latest data import commit 2025-11-21 17:40:18 +05:30
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
Jannat Patel b0b79f1d19 test: click on dropdown option to open course or batch form 2025-11-20 18:30:52 +05:30
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
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
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
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
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
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
Frappe PR Bot a88d9cd78e chore(release): Bumped to Version 2.39.0 2025-10-15 05:13:17 +00:00
Jannat Patel dab82db693 Merge pull request #1779 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-15 10:38:52 +05:30
Jannat Patel a1183df72c Merge pull request #1780 from pateljannat/issues-139
fix: moderators should be able to access unpublished courses
2025-10-14 20:46:14 +05:30
Jannat Patel 5cfa4f173a fix: moderators should be able to access unpublished courses 2025-10-14 19:27:45 +05:30
Jannat Patel 2f5b0a3bf8 feat: data import 2025-10-14 19:21:35 +05:30
Jannat Patel 451ef49d98 chore: Serbian (Latin) translations 2025-10-14 19:20:20 +05:30
Jannat Patel 36a8ebdc1b chore: Serbian (Cyrillic) translations 2025-10-14 19:20:07 +05:30
Jannat Patel 27577edb16 Merge pull request #1776 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-14 10:35:24 +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
Jannat Patel bd69ab314b chore: Burmese translations 2025-10-13 19:07:28 +05:30
Jannat Patel 6abe4ac04a chore: Tamil translations 2025-10-13 19:07:26 +05:30
Jannat Patel 1eeb190653 chore: Esperanto translations 2025-10-13 19:07:24 +05:30
Jannat Patel 23f22d9d9a chore: Serbian (Latin) translations 2025-10-13 19:07:22 +05:30
Jannat Patel b913f33b3f chore: Norwegian Bokmal translations 2025-10-13 19:07:21 +05:30
Jannat Patel 52da1feb91 chore: Bosnian translations 2025-10-13 19:07:18 +05:30
Jannat Patel 0af9f1bfbf chore: Croatian translations 2025-10-13 19:07:16 +05:30
Jannat Patel 3b0e1c3ce7 chore: Thai translations 2025-10-13 19:07:14 +05:30
Jannat Patel 37ea270c56 chore: Persian translations 2025-10-13 19:07:12 +05:30
Jannat Patel 304550dd94 chore: Indonesian translations 2025-10-13 19:07:11 +05:30
Jannat Patel 69cee24ffe chore: Portuguese, Brazilian translations 2025-10-13 19:07:09 +05:30
Jannat Patel 8334c06a9b chore: Vietnamese translations 2025-10-13 19:07:07 +05:30
Jannat Patel 3e739f2877 chore: Chinese Simplified translations 2025-10-13 19:07:05 +05:30
Jannat Patel 5e33ff4a34 chore: Turkish translations 2025-10-13 19:07:03 +05:30
Jannat Patel 2b234e5d64 chore: Swedish translations 2025-10-13 19:07:01 +05:30
Jannat Patel c8b328a1c9 chore: Serbian (Cyrillic) translations 2025-10-13 19:07:00 +05:30
Jannat Patel 5e0ac05f90 chore: Russian translations 2025-10-13 19:06:58 +05:30
Jannat Patel e440097272 chore: Portuguese translations 2025-10-13 19:06:57 +05:30
Jannat Patel 263c858a66 chore: Polish translations 2025-10-13 19:06:55 +05:30
Jannat Patel 82371fb2a8 chore: Dutch translations 2025-10-13 19:06:53 +05:30
Jannat Patel b6336a4096 chore: Italian translations 2025-10-13 19:06:51 +05:30
Jannat Patel 7acfbbaae7 chore: Hungarian translations 2025-10-13 19:06:50 +05:30
Jannat Patel 5a76b4eb2d chore: German translations 2025-10-13 19:06:48 +05:30
Jannat Patel 8aac88b696 chore: Danish translations 2025-10-13 19:06:47 +05:30
Jannat Patel d6e71068be chore: Czech translations 2025-10-13 19:06:45 +05:30
Jannat Patel 6b44951ef0 chore: Arabic translations 2025-10-13 19:06:43 +05:30
Jannat Patel 5289ebb923 chore: Spanish translations 2025-10-13 19:06:42 +05:30
Jannat Patel 263fcda053 chore: French translations 2025-10-13 19:06:40 +05:30
Joedeep Singh 6933105261 feat: implement coupon code manage
ment with billing and transaction integration; bug fixes
2025-10-13 13:03:43 +00:00
Jannat Patel 2e0d26575e Merge pull request #1775 from pateljannat/issues-138
fix: empty state for profile certificates section
2025-10-13 15:24:22 +05:30
Jannat Patel b9d6670bee fix: timezone for direct evaluation courses 2025-10-13 15:08:29 +05:30
Jannat Patel f20d39a3e7 fix: empty state for profile certificates section 2025-10-13 14:56:08 +05:30
Jannat Patel 09d948b3a0 Merge pull request #1774 from pateljannat/contact-us-mail-to
feat: contact us email modal
2025-10-13 13:22:26 +05:30
Jannat Patel 96941c83f3 fix: improved sidebar for settings 2025-10-13 13:14:13 +05:30
Jannat Patel b8ca0e381a feat: contact us email modal 2025-10-13 11:34:46 +05:30
Jannat Patel 4a2c5d77aa Merge pull request #1770 from frappe/pot_develop_2025-10-10
chore: update POT file
2025-10-13 11:11:39 +05:30
Jannat Patel cf2d29d82e Merge pull request #1771 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-13 11:11:27 +05:30
Joedeep Singh bf36890bd3 feat: added coupon code functionality 2025-10-12 17:07:42 +00:00
Jannat Patel fb2ab63550 chore: Polish translations 2025-10-12 18:34:31 +05:30
Himanshu Shivhare 05b6b97b2a fix: beautifulsoap4 version upgrade to match with Frappe Framework 2025-10-12 17:00:34 +05:30
Jannat Patel 90efc152a8 chore: Burmese translations 2025-10-11 17:41:57 +05:30
frappe-pr-bot de6ba49409 chore: update POT file 2025-10-10 16:04:57 +00:00
Jannat Patel 6a8aca39a0 Merge pull request #1764 from frappe/develop
chore: merge 'develop' into 'main'
2025-10-10 16:45:01 +05:30
Frappe PR Bot 9d4196f15a chore(release): Bumped to Version 2.38.0 2025-10-10 10:59:06 +00:00
Jannat Patel eed7fb970d Merge pull request #1769 from pateljannat/issues-137
fix: private file uploads in assignment text editor
2025-10-10 16:27:00 +05:30
Jannat Patel fe67f1ab61 Merge pull request #1767 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-10 15:55:51 +05:30
Jannat Patel 78640561f5 fix: private file uploads in assignment text editor 2025-10-10 15:52:35 +05:30
Jannat Patel f72631a262 fix: reverted the alternative host change 2025-10-10 12:31:49 +05:30
Jannat Patel 670e5d0202 Merge pull request #1768 from pateljannat/issues-136
fix: misc zoom issues
2025-10-10 11:48:47 +05:30
Jannat Patel ea59d1158a fix: misc zoom issues 2025-10-10 11:40:19 +05:30
Jannat Patel ba23cf9789 fix: misc zoom issues 2025-10-10 11:39:40 +05:30
Jannat Patel de585b90ea chore: Persian translations 2025-10-09 16:56:19 +05:30
Jannat Patel cf40f4e525 Merge pull request #1766 from pateljannat/issues-135
fix: live class issues
2025-10-09 15:50:01 +05:30
Jannat Patel b273e34ac8 fix: live class issues 2025-10-09 15:35:00 +05:30
Jannat Patel 1a00d708e1 Merge pull request #1762 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-08 17:10:46 +05:30
Jannat Patel 583584d8c5 Merge pull request #1763 from pateljannat/issues-134
fix: misc issues
2025-10-07 19:56:47 +05:30
Jannat Patel 09c087dee7 fix: misc issues 2025-10-07 19:24:47 +05:30
Jannat Patel f5cff50674 chore: Burmese translations 2025-10-07 17:03:51 +05:30
Jannat Patel 81c48d5182 chore: Persian translations 2025-10-07 17:03:43 +05:30
Jannat Patel c44414cadb chore: Portuguese, Brazilian translations 2025-10-07 17:03:41 +05:30
Jannat Patel 85db4be514 chore: Arabic translations 2025-10-07 17:03:24 +05:30
Jannat Patel 6526eefaf5 chore: French translations 2025-10-07 17:03:21 +05:30
Jannat Patel bd1fc5d705 Merge pull request #1760 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-07 10:46:27 +05:30
Jannat Patel ca547023b0 chore: Tamil translations 2025-10-06 16:21:31 +05:30
Jannat Patel caf57d355b chore: Esperanto translations 2025-10-06 16:21:30 +05:30
Jannat Patel 7e6da62480 chore: Serbian (Latin) translations 2025-10-06 16:21:28 +05:30
Jannat Patel e14e560415 chore: Norwegian Bokmal translations 2025-10-06 16:21:26 +05:30
Jannat Patel 0a8ac87cee chore: Bosnian translations 2025-10-06 16:21:25 +05:30
Jannat Patel 42441aafd6 chore: Croatian translations 2025-10-06 16:21:23 +05:30
Jannat Patel 9c3ff958e3 chore: Thai translations 2025-10-06 16:21:21 +05:30
Jannat Patel efac7af750 chore: Persian translations 2025-10-06 16:21:20 +05:30
Jannat Patel 314935f68e chore: Indonesian translations 2025-10-06 16:21:18 +05:30
Jannat Patel 1efa857d95 chore: Portuguese, Brazilian translations 2025-10-06 16:21:16 +05:30
Jannat Patel a7409b498e chore: Vietnamese translations 2025-10-06 16:21:13 +05:30
Jannat Patel a9cb0a8c26 chore: Chinese Simplified translations 2025-10-06 16:21:12 +05:30
Jannat Patel 9333affaf1 chore: Turkish translations 2025-10-06 16:21:10 +05:30
Jannat Patel 38a32be503 chore: Swedish translations 2025-10-06 16:21:08 +05:30
Jannat Patel fe5f7daf78 chore: Serbian (Cyrillic) translations 2025-10-06 16:20:58 +05:30
Jannat Patel 3c07c3e1cf chore: Russian translations 2025-10-06 16:20:57 +05:30
Jannat Patel cb87c75ac0 chore: Portuguese translations 2025-10-06 16:20:55 +05:30
Jannat Patel 62ead16817 chore: Polish translations 2025-10-06 16:20:53 +05:30
Jannat Patel 6352e4deb1 chore: Dutch translations 2025-10-06 16:20:52 +05:30
Jannat Patel 0c4b569be6 chore: Italian translations 2025-10-06 16:20:51 +05:30
Jannat Patel fe4d7cfb75 chore: Hungarian translations 2025-10-06 16:20:49 +05:30
Jannat Patel d3e791b017 chore: German translations 2025-10-06 16:20:48 +05:30
Jannat Patel 0849183d26 chore: Danish translations 2025-10-06 16:20:46 +05:30
Jannat Patel 3410af8899 chore: Czech translations 2025-10-06 16:20:45 +05:30
Jannat Patel 81a1e3a4c3 chore: Arabic translations 2025-10-06 16:20:44 +05:30
Jannat Patel c8e18dc445 chore: Spanish translations 2025-10-06 16:20:42 +05:30
Jannat Patel ad21bd6f53 chore: French translations 2025-10-06 16:20:41 +05:30
Jannat Patel 781457fce3 Merge pull request #1759 from pateljannat/contact-us
feat: contact us
2025-10-06 11:11:19 +05:30
Jannat Patel 6662b713f1 Merge pull request #1757 from frappe/pot_develop_2025-10-03
chore: update POT file
2025-10-06 10:57:28 +05:30
Jannat Patel 34c0d16411 refactor: changed Certified Members to Certifications 2025-10-06 10:33:50 +05:30
Jannat Patel f7003ecbbe feat: contact us 2025-10-06 10:15:53 +05:30
frappe-pr-bot 134090df5d chore: update POT file 2025-10-03 16:04:25 +00:00
Jannat Patel efb4feab2e Merge pull request #1755 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-03 10:44:01 +05:30
Jannat Patel 900e959b0b chore: Tamil translations 2025-10-01 14:02:00 +05:30
Jannat Patel 946ffeb3ca chore: Portuguese, Brazilian translations 2025-10-01 14:01:52 +05:30
Jannat Patel 56d32b0674 chore: Vietnamese translations 2025-10-01 14:01:50 +05:30
Jannat Patel 03fc5c084a chore: Chinese Simplified translations 2025-10-01 14:01:48 +05:30
Jannat Patel 2a32355dd8 chore: Polish translations 2025-10-01 14:01:42 +05:30
Jannat Patel d80d0e9d9b chore: Hungarian translations 2025-10-01 14:01:38 +05:30
Jannat Patel be0388ee6e chore: Arabic translations 2025-10-01 14:01:33 +05:30
Jannat Patel 414c41162a chore: French translations 2025-10-01 14:01:31 +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
Frappe PR Bot 39106de96c chore(release): Bumped to Version 2.37.0 2025-10-01 07:23:35 +00:00
Jannat Patel 8adb76abfb Merge pull request #1753 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-10-01 12:52:01 +05:30
Jannat Patel 4889b04283 chore: Persian translations 2025-09-30 14:05:54 +05:30
Jannat Patel e9973a242b Merge pull request #1748 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-09-30 10:29:49 +05:30
Jannat Patel d324ad0ac5 Merge pull request #1752 from pateljannat/issues-132
fix: misc issues
2025-09-30 10:29:33 +05:30
Jannat Patel ab6cc2698a fix: assignment should be uploaded as private file 2025-09-29 18:27:50 +05:30
Jannat Patel da076f71a1 fix: when course is unpublished and not coming either, user should not be able to access the course details 2025-09-29 18:09:03 +05:30
Jannat Patel 63c1fe8e75 fix: border when course card has an image 2025-09-29 15:26:14 +05:30
Jannat Patel 31f0833629 chore: removed unused files 2025-09-29 15:13:39 +05:30
Jannat Patel d4dc094049 chore: Serbian (Latin) translations 2025-09-29 13:56:06 +05:30
Jannat Patel 21a84e8032 chore: Serbian (Cyrillic) translations 2025-09-29 13:55:53 +05:30
Jannat Patel 819a1baae0 Merge pull request #1745 from UmakanthKaspa/fix/dark-theme-text-visibility
fix: make text visible in dark theme
2025-09-29 12:17:56 +05:30
Jannat Patel b0ee67faff Merge pull request #1743 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-09-29 12:17:14 +05:30
Jannat Patel 0684bd105a chore: Norwegian Bokmal translations 2025-09-28 13:59:13 +05:30
UmakanthKaspa 5c948862a0 fix: remove extra space in CSS class 2025-09-27 14:15:39 +00:00
UmakanthKaspa 23291b38de fix: add dark theme text colors to Create Program dialog 2025-09-27 13:59:07 +00:00
UmakanthKaspa 9357bd55a6 fix: make text visible in dark theme
- Add text-ink-gray-9 to headings
- Text now shows in both light and dark themes
2025-09-27 13:04:07 +00:00
Jannat Patel e51d668418 chore: Esperanto translations 2025-09-27 13:46:00 +05:30
Jannat Patel a48df70631 chore: Serbian (Latin) translations 2025-09-27 13:45:59 +05:30
Jannat Patel 9853e8311b chore: Norwegian Bokmal translations 2025-09-27 13:45:57 +05:30
Jannat Patel cd8041b048 chore: Bosnian translations 2025-09-27 13:45:56 +05:30
Jannat Patel 00bf5b7ad6 chore: Croatian translations 2025-09-27 13:45:54 +05:30
Jannat Patel 40bcc983c7 chore: Thai translations 2025-09-27 13:45:53 +05:30
Jannat Patel dfb26f31db chore: Persian translations 2025-09-27 13:45:51 +05:30
Jannat Patel a57a0bebef chore: Indonesian translations 2025-09-27 13:45:50 +05:30
Jannat Patel 4853621b1b chore: Portuguese, Brazilian translations 2025-09-27 13:45:49 +05:30
Jannat Patel e3209c6fb3 chore: Vietnamese translations 2025-09-27 13:45:48 +05:30
Jannat Patel 13d25cce1f chore: Chinese Simplified translations 2025-09-27 13:45:47 +05:30
Jannat Patel cb16b0ca64 chore: Turkish translations 2025-09-27 13:45:45 +05:30
Jannat Patel b3ce3159e7 chore: Swedish translations 2025-09-27 13:45:44 +05:30
Jannat Patel 76ffc70892 chore: Serbian (Cyrillic) translations 2025-09-27 13:45:42 +05:30
Jannat Patel 2734587981 chore: Russian translations 2025-09-27 13:45:41 +05:30
Jannat Patel 223e93d654 chore: Portuguese translations 2025-09-27 13:45:39 +05:30
Jannat Patel 26351726a8 chore: Polish translations 2025-09-27 13:45:38 +05:30
Jannat Patel efc84db580 chore: Dutch translations 2025-09-27 13:45:37 +05:30
Jannat Patel 3bf58bb6f0 chore: Italian translations 2025-09-27 13:45:35 +05:30
Jannat Patel a7962d9404 chore: Hungarian translations 2025-09-27 13:45:34 +05:30
Jannat Patel 5ce9bb306d chore: German translations 2025-09-27 13:45:33 +05:30
Jannat Patel 5b5bb38f4f chore: Danish translations 2025-09-27 13:45:32 +05:30
Jannat Patel 419ad311a0 chore: Czech translations 2025-09-27 13:45:30 +05:30
Jannat Patel 0c3af09566 chore: Arabic translations 2025-09-27 13:45:29 +05:30
Jannat Patel c9063625ec chore: Spanish translations 2025-09-27 13:45:28 +05:30
Jannat Patel 205858a41d chore: French translations 2025-09-27 13:45:26 +05:30
Jannat Patel 87edad17c3 Merge pull request #1742 from frappe/pot_develop_2025-09-26
chore: update POT file
2025-09-26 22:45:31 +05:30
frappe-pr-bot 1c54e80951 chore: update POT file 2025-09-26 16:04:27 +00:00
Jannat Patel 6c19cdc729 Merge pull request #1741 from pateljannat/fui-upgrade
chore: upgraded frappe ui and made relevant changes
2025-09-25 18:30:15 +05:30
Jannat Patel 84a703bb50 test: updated tests as per latest frappe ui version 2025-09-25 18:19:03 +05:30
Jannat Patel 27ed95b044 Merge pull request #1739 from KerollesFathy/work-mode-on-job-portal
feat(jobs): Add Work Mode on Job Portal
2025-09-25 17:42:57 +05:30
Jannat Patel 0358dfe790 fix: upgraded evaluator schedule as per latest frappe ui 2025-09-25 17:42:12 +05:30
KerollesFathy 68cee65f22 refactor: make all filters on the same line 2025-09-25 11:59:57 +00:00
Jannat Patel 24b2125b97 chore: upgraded frappe ui and made relevant changes 2025-09-25 16:46:29 +05:30
Jannat Patel eceed12992 Merge pull request #1740 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-09-25 13:02:02 +05:30
Jannat Patel f9028433a0 chore: Serbian (Latin) translations 2025-09-25 12:44:36 +05:30
Jannat Patel 1936e7b212 chore: Bosnian translations 2025-09-25 12:44:33 +05:30
Jannat Patel c04a972be3 chore: Croatian translations 2025-09-25 12:44:32 +05:30
Jannat Patel 9c26b011e4 chore: Swedish translations 2025-09-25 12:44:23 +05:30
Jannat Patel 821f125789 chore: Serbian (Cyrillic) translations 2025-09-25 12:44:22 +05:30
Jannat Patel e0f376880a chore: Dutch translations 2025-09-25 12:44:18 +05:30
Jannat Patel 6da77bb3c7 chore: Hungarian translations 2025-09-25 12:44:15 +05:30
Jannat Patel 2fd660a93f chore: German translations 2025-09-25 12:44:14 +05:30
Jannat Patel f3eefc748a chore: Danish translations 2025-09-25 12:44:12 +05:30
Jannat Patel 4852698d74 chore: Czech translations 2025-09-25 12:44:11 +05:30
Jannat Patel 36b24dc826 chore: Arabic translations 2025-09-25 12:44:09 +05:30
Jannat Patel d60bc1d4b6 chore: Spanish translations 2025-09-25 12:44:08 +05:30
Jannat Patel 06a02c0877 chore: French translations 2025-09-25 12:44:06 +05:30
KerollesFathy 1a53a9f30b feat: Add work mode selection to job form 2025-09-24 14:24:08 +00:00
KerollesFathy 7ee81d4693 feat: Add work mode badge to job detail page 2025-09-24 14:23:45 +00:00
KerollesFathy 0a32d03fda feat: Display work mode badge on job card 2025-09-24 14:22:59 +00:00
KerollesFathy 6c43dfea18 feat: Add work mode filter and selection to job opportunities 2025-09-24 14:22:07 +00:00
KerollesFathy 7d1e226743 feat: Add work mode on job opportunity 2025-09-24 14:21:03 +00:00
Jannat Patel fd934e1e82 Merge pull request #1735 from frappe/develop
chore: merge 'develop' into 'main'
2025-09-24 13:02:43 +05:30
Frappe PR Bot 8ea903b81a chore(release): Bumped to Version 2.36.0 2025-09-24 07:17:24 +00:00
Jannat Patel 24b08599b3 Merge pull request #1737 from frappe/l10n_develop2
chore: sync translations from crowdin
2025-09-24 12:46:37 +05:30
Jannat Patel a0f1c1f227 chore: Esperanto translations 2025-09-24 12:46:22 +05:30
Jannat Patel efbb014588 chore: Serbian (Latin) translations 2025-09-24 12:46:20 +05:30
Jannat Patel f881a0e1d5 chore: Norwegian Bokmal translations 2025-09-24 12:46:19 +05:30
Jannat Patel acdb81e8a3 chore: Bosnian translations 2025-09-24 12:46:17 +05:30
Jannat Patel 6559a87323 chore: Croatian translations 2025-09-24 12:46:15 +05:30
Jannat Patel 8301bab768 chore: Thai translations 2025-09-24 12:46:14 +05:30
Jannat Patel eb0b2010f9 chore: Persian translations 2025-09-24 12:46:12 +05:30
Jannat Patel 8158ea164d chore: Indonesian translations 2025-09-24 12:46:10 +05:30
Jannat Patel beb3134af9 chore: Portuguese, Brazilian translations 2025-09-24 12:46:08 +05:30
Jannat Patel 5fdc6a21a5 chore: Vietnamese translations 2025-09-24 12:46:07 +05:30
Jannat Patel e7516c57bc chore: Chinese Simplified translations 2025-09-24 12:46:05 +05:30
Jannat Patel 2169d81b73 chore: Turkish translations 2025-09-24 12:46:04 +05:30
Jannat Patel 1d6bb9f9f6 chore: Swedish translations 2025-09-24 12:46:02 +05:30
Jannat Patel 812bd07d03 chore: Serbian (Cyrillic) translations 2025-09-24 12:46:01 +05:30
Jannat Patel 19bb02f905 chore: Russian translations 2025-09-24 12:45:59 +05:30
Jannat Patel 58d750f726 chore: Portuguese translations 2025-09-24 12:45:58 +05:30
Jannat Patel 7f3bb58ec1 chore: Polish translations 2025-09-24 12:45:56 +05:30
Jannat Patel 08ceaf204f chore: Dutch translations 2025-09-24 12:45:54 +05:30
Jannat Patel eced1221a8 chore: Italian translations 2025-09-24 12:45:52 +05:30
Jannat Patel 3a5bbde0cc chore: Hungarian translations 2025-09-24 12:45:51 +05:30
Jannat Patel eb1a790485 chore: German translations 2025-09-24 12:45:49 +05:30
Jannat Patel 21e9c85bf7 chore: Danish translations 2025-09-24 12:45:47 +05:30
Jannat Patel 632f783d57 chore: Czech translations 2025-09-24 12:45:46 +05:30
Jannat Patel 3ba3908108 chore: Arabic translations 2025-09-24 12:45:44 +05:30
Jannat Patel 903a4e91b0 chore: Spanish translations 2025-09-24 12:45:43 +05:30
Jannat Patel 6496b129ce chore: French translations 2025-09-24 12:45:41 +05:30
Jannat Patel 9fff7a2ea8 Merge pull request #1736 from pateljannat/pwa-customizations
feat: PWA Customizations
2025-09-24 12:26:54 +05:30
Jannat Patel 6d94617e59 feat: PWA Customizations 2025-09-24 12:11:12 +05:30
Jannat Patel b95d07babb Merge pull request #1732 from pateljannat/activation
chore: site data for analytics
2025-09-23 14:54:47 +05:30
Jannat Patel 9748d075fa chore: site data for analytics 2025-09-23 13:00:58 +05:30
Jannat Patel aaeeb84ed3 Merge pull request #1726 from pateljannat/payment-settings-refactor
refactor: payment settings
2025-09-23 11:37:03 +05:30
Jannat Patel c3702ee6d5 feat: transaction list 2025-09-23 11:26:46 +05:30
Jannat Patel f239987043 feat: new payment gateway creation from settings 2025-09-22 14:19:27 +05:30
Jannat Patel 6aa67c3fae Merge pull request #1727 from frappe/pot_develop_2025-09-19
chore: update POT file
2025-09-22 11:16:45 +05:30
frappe-pr-bot 1fc5d75cc0 chore: update POT file 2025-09-19 16:04:26 +00:00
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 bcfd3bb636 refactor: payment settings 2025-09-18 18:29:08 +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
414 changed files with 87468 additions and 44274 deletions
+35 -5
View File
@@ -3,10 +3,14 @@ on:
push:
branches:
- main
- develop
- main-hotfix
pull_request: {}
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
services:
redis-cache:
image: redis:alpine
@@ -30,13 +34,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 +73,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 +84,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
+7 -2
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: |
+4 -4
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
+2 -2
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 }}
+2 -2
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
+8 -5
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
+3 -1
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
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 }}"
+1 -1
View File
@@ -1,5 +1,5 @@
{
"branches": ["develop"],
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular"
+2
View File
@@ -0,0 +1,2 @@
ignore:
- "**/test_helper.py"
+17 -14
View File
@@ -10,11 +10,11 @@ describe("Batch Creation", () => {
cy.get("span").contains("Settings").click();
// Add a new member
cy.get('[id^="headlessui-dialog-panel-v-"]')
cy.get("[data-dismissable-layer]")
.find("span")
.contains(/^Members$/)
.click();
cy.get('[id^="headlessui-dialog-panel-v-"]')
cy.get("[data-dismissable-layer]")
.find("button")
.contains("New")
.click();
@@ -27,13 +27,17 @@ 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('[id^="headlessui-dialog-panel-v-"]')
cy.get("[data-dismissable-layer]")
.find("span")
.contains(/^Evaluators$/)
.click();
cy.get('[id^="headlessui-dialog-panel-v-"]')
cy.get("[data-dismissable-layer]")
.find("button")
.contains("New")
.click();
@@ -48,6 +52,7 @@ 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");
@@ -123,12 +128,9 @@ 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();
});
@@ -155,13 +157,14 @@ describe("Batch Creation", () => {
cy.get("button:visible").contains("Manage Batch").click();
/* Add student to batch */
cy.get("button").contains("Students").click();
cy.get("button").contains("Add").click();
cy.get('div[id^="headlessui-dialog-panel-v-"]')
cy.get('div[role="dialog"]')
.first()
.find("input[id^='headlessui-combobox-input-v-']")
.first()
.find("button")
.eq(1)
.click();
cy.get("input[id^='headlessui-combobox-input-v-']").type(randomEmail);
cy.get("input[placeholder='Search']").type(randomEmail);
cy.get("div").contains(randomEmail).click();
cy.get("button").contains("Submit").click();
+24 -19
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,16 +53,35 @@ describe("Course Creation", () => {
});
});
cy.button("Create").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("input").click();
});
cy.get("[id^=headlessui-combobox-option-")
.should("be.visible")
.first()
.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("[id^=headlessui-dialog-panel-")
cy.get("[data-dismissable-layer]")
.should("be.visible")
.within(() => {
cy.get("label").contains("Title").type("Test Chapter");
@@ -143,7 +148,7 @@ describe("Course Creation", () => {
cy.get("span").contains("Community").click();
cy.button("New Question").click();
cy.wait(500);
cy.get("[id^=headlessui-dialog-panel-").within(() => {
cy.get("[data-dismissable-layer]").within(() => {
cy.get("label").contains("Title").type("Test Discussion");
cy.get("div[contenteditable=true]").invoke(
"text",
+3 -1
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
}
+19 -6
View File
@@ -8,10 +8,11 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AdminBatchDashboard: typeof import('./src/components/AdminBatchDashboard.vue')['default']
Annoucements: typeof import('./src/components/Annoucements.vue')['default']
AnnouncementModal: typeof import('./src/components/Modals/AnnouncementModal.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']
@@ -41,11 +42,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']
@@ -85,8 +93,10 @@ 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']
PaymentSettings: typeof import('./src/components/Settings/PaymentSettings.vue')['default']
PaymentGatewayDetails: typeof import('./src/components/Settings/PaymentGatewayDetails.vue')['default']
PaymentGateways: typeof import('./src/components/Settings/PaymentGateways.vue')['default']
Play: typeof import('./src/components/Icons/Play.vue')['default']
ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']
Question: typeof import('./src/components/Modals/Question.vue')['default']
@@ -101,16 +111,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/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']
+2 -1
View File
@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="{{ favicon }}" />
<link rel="manifest" href="/api/method/lms.lms.api.get_pwa_manifest" />
<link rel="apple-touch-icon" href="public/manifest/apple-icon-180.png" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" />
@@ -212,7 +213,7 @@
<meta name="twitter:image" content="{{ meta.image }}" />
<meta name="twitter:description" content="{{ meta.description }}" />
</head>
<body>
<body class="sm:overscroll-y-none no-scrollbar">
<div id="app">
<div id="seo-content">
<h1>{{ meta.title }}</h1>
+47 -44
View File
@@ -6,54 +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.173",
"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"
"@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": "0.15.0"
}
}
+6 -15
View File
@@ -1,22 +1,19 @@
<template>
<FrappeUIProvider>
<Layout>
<div class="text-base">
<router-view />
</div>
<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'
@@ -25,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') {
@@ -49,10 +46,4 @@ const Layout = computed(() => {
onUnmounted(() => {
noSidebar.value = false
})
watch(userResource, () => {
if (userResource.data) {
posthogSettings.reload()
}
})
</script>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-152
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");
}
@@ -0,0 +1,118 @@
<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: studentCount.data || 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.data || 0 }"
/>
</div>
<AxisChart
v-if="showProgressChart"
class="border rounded-lg p-3 min-h-[300px]"
:config="{
data: filteredChartData,
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>
</template>
<script setup lang="ts">
import { AxisChart, createResource, NumberChart } from 'frappe-ui'
import { computed } from 'vue'
const props = defineProps<{
batch: { [key: string]: any } | null
}>()
const studentCount = createResource({
url: 'frappe.client.get_count',
cache: ['batch_student_count', props.batch?.data?.name],
params: {
doctype: 'LMS Batch Enrollment',
filters: { batch: props.batch?.data?.name },
},
auto: true,
})
const assessmentCount = createResource({
url: 'lms.lms.utils.get_batch_assessment_count',
cache: ['batch_assessment_count', props.batch?.data?.name],
params: {
batch: props.batch?.data?.name,
},
auto: true,
})
const chartData = createResource({
url: 'lms.lms.utils.get_batch_chart_data',
cache: ['batch_chart_data', props.batch?.data?.name],
params: { batch: props.batch?.data?.name },
auto: true,
})
const certificationCount = createResource({
url: 'frappe.client.get_count',
cache: ['batch_certificate_count', props.batch?.data?.name],
params: {
doctype: 'LMS Certificate',
filters: { batch_name: props.batch?.data?.name },
},
auto: true,
})
const filteredChartData = computed(() =>
(chartData.data || []).filter((item: { value: number }) => item.value > 0)
)
const showProgressChart = computed(
() =>
studentCount.data &&
(props.batch?.data?.courses?.length || assessmentCount.data)
)
</script>
+39 -12
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 Link from '@/components/Controls/Link.vue'
import { Dialog, FormControl } from 'frappe-ui'
import { nextTick, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { Link } from 'frappe-ui/frappe'
import { getLmsRoute } from '@/utils/basePath'
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>
+3 -3
View File
@@ -208,12 +208,12 @@ const canAddAssessments = () => {
const getAssessmentColumns = () => {
let columns = [
{
label: 'Assessment',
label: __('Assessment'),
key: 'title',
width: '25rem',
},
{
label: 'Type',
label: __('Type'),
key: 'assessment_type',
width: '15rem',
},
@@ -221,7 +221,7 @@ const getAssessmentColumns = () => {
if (!user.data?.is_moderator) {
columns.push({
label: 'Status/Percentage',
label: __('Status/Percentage'),
key: 'status',
align: 'left',
width: '10rem',
+61 -57
View File
@@ -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>
@@ -53,7 +53,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,13 +63,21 @@
}}
{{ __('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="!submissionResource.doc?.assignment_attachment"
:fileTypes="getType()"
:uploadArgs="{
private: true,
}"
:validateFile="validateFile"
@success="(file) => saveSubmission(file)"
>
@@ -84,21 +92,24 @@
</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="submissionResource.doc.assignment_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>
{{
submissionResource.doc.assignment_attachment
.split('/')
.pop()
}}
</span>
</div>
</a>
<X
v-if="canModifyAssignment"
@@ -127,7 +138,10 @@
@change="(val) => (answer = 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]"
: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>
@@ -136,13 +150,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"
>
<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>
@@ -173,7 +187,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>
@@ -195,10 +212,8 @@ 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'
const submissionFile = ref(null)
const answer = ref(null)
const comments = ref(null)
const router = useRouter()
@@ -257,9 +272,7 @@ const newSubmission = createResource({
assignment: props.assignmentID,
member: user.data?.name,
}
if (showUploader()) {
doc.assignment_attachment = submissionFile.value.file_url
} else {
if (!showUploader()) {
doc.answer = answer.value
}
return {
@@ -268,19 +281,6 @@ const newSubmission = createResource({
},
})
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,
@@ -293,11 +293,6 @@ const submissionResource = createDocumentResource({
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
}
@@ -306,7 +301,10 @@ watch(submissionResource, () => {
}
if (submissionResource.isDirty) {
isDirty.value = true
} else if (showUploader() && !submissionFile.value) {
} else if (
showUploader() &&
!submissionResource.doc.assignment_attachment
) {
isDirty.value = true
} else if (!showUploader() && !answer.value) {
isDirty.value = true
@@ -316,11 +314,17 @@ watch(submissionResource, () => {
}
})
watch(submissionFile, () => {
if (props.submissionName == 'new' && submissionFile.value) {
isDirty.value = true
watch(
() => submissionResource.doc,
() => {
if (
props.submissionName == 'new' &&
submissionResource.doc?.assignment_attachment
) {
isDirty.value = true
}
}
})
)
const submitAssignment = () => {
if (props.submissionName != 'new') {
@@ -332,13 +336,13 @@ const submitAssignment = () => {
submissionResource.setValue.submit(
{
...submissionResource.doc,
assignment_attachment: submissionFile.value?.file_url,
evaluator: evaluator,
comments: comments.value,
answer: answer.value,
},
{
onSuccess(data) {
isDirty.value = false
toast.success(__('Changes saved successfully'))
},
}
@@ -379,7 +383,7 @@ const addNewSubmission = () => {
const saveSubmission = (file) => {
isDirty.value = true
submissionFile.value = file
submissionResource.doc.assignment_attachment = file.file_url
}
const markLessonProgress = () => {
@@ -430,7 +434,7 @@ const validateFile = (file) => {
const removeSubmission = () => {
isDirty.value = true
submissionFile.value = null
submissionResource.doc.assignment_attachment = ''
}
const canGradeSubmission = computed(() => {
+19 -16
View File
@@ -6,24 +6,26 @@
<div class="text-lg leading-5 font-semibold mb-2 text-ink-gray-9">
{{ batch.title }}
</div>
<div
<Badge
v-if="batch.seat_count && batch.seats_left > 0"
class="text-xs bg-green-100 text-green-700 self-start px-2 py-0.5 rounded-md"
>
{{ batch.seats_left }}
<span v-if="batch.seats_left > 1">
{{ __('Seats Left') }}
</span>
<span v-else-if="batch.seats_left == 1">
{{ __('Seat Left') }}
</span>
</div>
<div
variant="subtle"
theme="green"
size="md"
class="self-start"
:label="
batch.seats_left +
' ' +
(batch.seats_left > 1 ? __('Seats Left') : __('Seat Left'))
"
/>
<Badge
v-else-if="batch.seat_count && batch.seats_left <= 0"
class="text-xs bg-red-100 text-red-700 self-start px-2 py-0.5 rounded-md"
>
{{ __('Sold Out') }}
</div>
variant="subtle"
theme="red"
size="md"
class="self-start"
:label="__('Sold Out')"
/>
<div class="short-introduction text-sm text-ink-gray-7">
{{ batch.description }}
</div>
@@ -70,6 +72,7 @@
</div>
</template>
<script setup>
import { Badge } from 'frappe-ui'
import { formatTime } from '@/utils'
import { Clock, Globe } from 'lucide-vue-next'
import DateRange from '@/components/Common/DateRange.vue'
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div>
<div class="flex items-center justify-between mb-4">
<div class="text-lg font-semibold text-ink-gray-9">
<div class="font-medium text-ink-gray-9">
{{ __('Courses') }}
</div>
<Button v-if="canSeeAddButton()" @click="openCourseModal()">
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div v-if="user.data?.is_student">
<div>
<div class="leading-5 mb-4">
<div class="leading-5 mb-4 text-ink-gray-7">
<div v-if="readOnly">
{{ __('Thank you for providing your feedback.') }}
<span
+40 -28
View File
@@ -1,28 +1,29 @@
<template>
<div v-if="batch.data" class="border-2 rounded-md p-5 lg:w-72">
<div
v-if="batch.data.seat_count && seats_left > 0"
class="text-sm bg-green-100 text-green-700 px-2 py-1 rounded-md"
<Badge
v-if="batch.data.seat_count && batch.data.seats_left > 0"
variant="subtle"
theme="green"
size="md"
:class="
batch.data.amount || batch.data.courses.length
? 'float-right'
: 'w-fit mb-4'
"
>
{{ seats_left }}
<span v-if="seats_left > 1">
{{ __('Seats Left') }}
</span>
<span v-else-if="seats_left == 1">
{{ __('Seat Left') }}
</span>
</div>
<div
v-else-if="batch.data.seat_count && seats_left <= 0"
class="text-xs bg-red-100 text-red-700 float-right px-2 py-0.5 rounded-md"
>
{{ __('Sold Out') }}
</div>
:label="
batch.data.seats_left +
' ' +
(batch.data.seats_left > 1 ? __('Seats Left') : __('Seat Left'))
"
/>
<Badge
v-else-if="batch.data.seat_count && batch.data.seats_left <= 0"
variant="subtle"
theme="red"
size="md"
class="float-right"
:label="__('Sold Out')"
/>
<div
v-if="batch.data.amount"
class="text-lg font-semibold mb-3 text-ink-gray-9"
@@ -54,6 +55,7 @@
{{ batch.data.timezone }}
</span>
</div>
<div v-if="!readOnlyMode">
<router-link
v-if="canAccessBatch"
@@ -113,7 +115,7 @@
{{ __('Enroll Now') }}
</Button>
<router-link
v-if="isModerator"
v-if="canEditBatch"
:to="{
name: 'BatchForm',
params: {
@@ -135,7 +137,7 @@
</template>
<script setup>
import { inject, computed } from 'vue'
import { Button, createResource, toast } from 'frappe-ui'
import { Badge, Button, createResource, toast } from 'frappe-ui'
import {
BookOpen,
Clock,
@@ -190,15 +192,10 @@ const enrollInBatch = () => {
)
}
const seats_left = computed(() => {
if (props.batch.data?.seat_count) {
return props.batch.data?.seat_count - props.batch.data?.students?.length
}
return null
})
const isStudent = computed(() => {
return props.batch.data?.students?.includes(user.data?.name)
return user.data
? props.batch.data?.students?.includes(user.data?.name)
: false
})
const isModerator = computed(() => {
@@ -209,7 +206,22 @@ const isEvaluator = computed(() => {
return user.data?.is_evaluator
})
const isInstructor = computed(() => {
return (
props.batch.data?.instructors?.filter(
(instructor) => instructor.name === user.data?.name
).length > 0
)
})
const canAccessBatch = computed(() => {
if (!user.data) {
return false
}
return isModerator.value || isStudent.value || isEvaluator.value
})
const canEditBatch = computed(() => {
return isModerator.value || isInstructor.value
})
</script>
+52 -180
View File
@@ -1,70 +1,8 @@
<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 class="text-ink-gray-9 font-medium">
{{ studentCount.data ?? 0 }} {{ __('Students') }}
</div>
<Button v-if="!readOnlyMode" @click="openStudentModal()">
<template #prefix>
@@ -76,7 +14,8 @@
<div v-if="students.data?.length">
<ListView
:columns="getStudentColumns()"
class="max-h-[75vh]"
:columns="studentColumns"
:rows="students.data"
row-key="name"
:options="{
@@ -88,7 +27,7 @@
>
<ListHeaderItem
:item="item"
v-for="item in getStudentColumns()"
v-for="item in studentColumns"
:title="item.label"
>
<template #prefix="{ item }">
@@ -104,7 +43,7 @@
<ListRow
:row="row"
v-for="row in students.data"
class="group cursor-pointer"
class="group cursor-pointer hover:bg-surface-gray-2 rounded"
@click="openStudentProgressModal(row)"
>
<template #default="{ column, item }">
@@ -149,9 +88,14 @@
</div>
</template>
</ListSelectBanner>
<div class="mt-4 flex justify-center" v-if="students.hasNextPage">
<Button @click="students.next()">
{{ __('Load More') }}
</Button>
</div>
</ListView>
</div>
<div v-else class="text-sm italic text-ink-gray-5">
<div v-else-if="!students.loading" class="text-sm italic text-ink-gray-5">
{{ __('There are no students in this batch.') }}
</div>
</div>
@@ -170,8 +114,8 @@
<script setup>
import {
Avatar,
AxisChart,
Button,
createListResource,
createResource,
FeatherIcon,
ListHeader,
@@ -181,30 +125,17 @@ import {
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 { Plus, Trash2 } from 'lucide-vue-next'
import { ref } 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({
@@ -214,45 +145,48 @@ const props = defineProps({
},
})
const students = createResource({
url: 'lms.lms.utils.get_batch_students',
const studentCount = createResource({
url: 'frappe.client.get_count',
cache: ['batch_student_count', props.batch?.data?.name],
params: {
doctype: 'LMS Batch Enrollment',
filters: { batch: props.batch?.data?.name },
},
auto: true,
})
const students = createListResource({
doctype: 'LMS Batch Enrollment',
url: 'lms.lms.utils.get_batch_students',
cache: ['batch_students', props.batch?.data?.name],
pageLength: 50,
filters: {
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 studentColumns = [
{
label: 'Full Name',
key: 'full_name',
width: '25rem',
icon: 'user',
},
{
label: 'Progress',
key: 'progress',
width: '15rem',
icon: 'activity',
},
{
label: 'Last Active',
key: 'last_active',
width: '10rem',
align: 'center',
icon: 'clock',
},
]
const openStudentModal = () => {
showStudentModal.value = true
@@ -281,6 +215,7 @@ const removeStudents = (selections, unselectAll) => {
{
onSuccess(data) {
students.reload()
studentCount.reload()
props.batch.reload()
toast.success(__('Students deleted successfully'))
unselectAll()
@@ -288,67 +223,4 @@ const removeStudents = (selections, 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>
@@ -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 = () => {
@@ -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>
@@ -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>
@@ -0,0 +1,67 @@
<template>
<Dialog
v-model="show"
:options="{
title: __('Contact Us'),
size: 'md',
}"
>
<template #body-content>
<div class="flex flex-col gap-4">
<FormControl
v-model="subject"
:label="__('Subject')"
type="text"
:required="true"
/>
<div>
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Message') }}
<span class="text-ink-red-3">*</span>
</div>
<TextEditor
:fixedMenu="true"
@change="(val) => (message = val)"
editorClass="prose-sm py-2 px-2 min-h-[200px] border-outline-gray-2 hover:border-outline-gray-3 rounded-b-md bg-surface-gray-3"
/>
</div>
</div>
</template>
<template #actions="{ close }">
<div class="pb-5 float-right">
<Button variant="solid" @click="sendMail(close)">
{{ __('Send') }}
</Button>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { Button, call, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { ref } from 'vue'
import { useSettings } from '@/stores/settings'
const show = defineModel<boolean>({ required: true, default: false })
const subject = ref('')
const message = ref('')
const settingsStore = useSettings()
const sendMail = (close: Function) => {
call('frappe.core.doctype.communication.email.make', {
recipients: settingsStore.settings?.data?.contact_us_email,
subject: subject.value,
content: message.value,
send_email: true,
})
.then(() => {
toast.success(__('Email sent successfully'))
close()
subject.value = ''
message.value = ''
})
.catch(() => {
toast.error(__('Failed to send email'))
close()
})
}
</script>
+133 -192
View File
@@ -1,138 +1,95 @@
<template>
<div>
<!-- Label -->
<div v-if="label" class="text-xs text-ink-gray-5 mb-1">
{{ __(label) }}
<span class="text-ink-red-3" v-if="attrs.required">*</span>
</div>
<Combobox
v-model="selectedValue"
nullable
v-slot="{ open: isComboboxOpen }"
>
<Popover class="w-full" v-model:show="showOptions">
<template #target="{ open: openPopover, togglePopover }">
<slot name="target" v-bind="{ open: openPopover, togglePopover }">
<div class="w-full">
<button
class="flex w-full items-center justify-between focus:outline-none"
:class="inputClasses"
@click="() => togglePopover()"
:disabled="attrs.readonly"
<Combobox v-model="selectedValue" nullable v-slot="{ open }">
<div class="relative w-full">
<ComboboxInput
class="form-input w-full"
:class="inputClasses"
type="text"
:value="selectedValue"
autocomplete="off"
@click="onFocus"
/>
<ComboboxButton ref="trigger" class="hidden" />
<!-- Dropdown -->
<ComboboxOptions
class="absolute z-20 mt-1 w-full rounded-lg bg-surface-modal py-1 text-base border-2 border-outline-gray-modals shadow-lg"
>
<input
ref="search"
v-model="query"
class="form-input w-[98%] rounded-tl-lg rounded-tr-lg mb-1 mx-1"
type="text"
placeholder="Search"
autocomplete="off"
/>
<!-- Options -->
<div class="my-1 max-h-[12rem] overflow-y-auto px-1.5">
<template v-for="group in groups" :key="group.key">
<div
v-if="group.group && !group.hideLabel"
class="px-2.5 py-1.5 text-sm font-medium text-ink-gray-4"
>
<div class="flex items-center">
<slot name="prefix" />
<span
class="overflow-hidden text-ellipsis whitespace-nowrap text-base leading-5"
v-if="selectedValue"
>
{{ displayValue(selectedValue) }}
</span>
<span class="text-base leading-5 text-ink-gray-4" v-else>
{{ placeholder || '' }}
</span>
</div>
<ChevronDown class="h-4 w-4 stroke-1.5" />
</button>
</div>
</slot>
</template>
<template #body="{ isOpen }">
<div v-show="isOpen">
<div
class="mt-1 rounded-lg bg-surface-white py-1 text-base border-2"
>
<div class="relative px-1.5 pt-0.5">
<ComboboxInput
ref="search"
class="form-input w-full"
type="text"
@change="
(e) => {
query = e.target.value
}
"
:value="query"
autocomplete="off"
placeholder="Search"
/>
<button
class="absolute right-1.5 inline-flex h-7 w-7 items-center justify-center"
@click="selectedValue = null"
>
<X class="h-4 w-4 stroke-1.5 text-ink-gray-7" />
</button>
{{ group.group }}
</div>
<ComboboxOptions
class="my-1 max-h-[12rem] overflow-y-auto px-1.5"
static
<ComboboxOption
v-for="option in group.items"
:key="option.value"
:value="option.value"
v-slot="{ active }"
>
<div
class="mt-1.5"
v-for="group in groups"
:key="group.key"
v-show="group.items.length > 0"
>
<div
v-if="group.group && !group.hideLabel"
class="px-2.5 py-1.5 text-sm font-medium text-ink-gray-4"
>
{{ group.group }}
</div>
<ComboboxOption
as="template"
v-for="option in group.items"
:key="option.value"
:value="option"
v-slot="{ active, selected }"
>
<li
:class="[
'flex items-center rounded px-2.5 py-2 text-base',
{ 'bg-surface-gray-2': active },
]"
>
<slot
name="item-prefix"
v-bind="{ active, selected, option }"
/>
<slot
name="item-label"
v-bind="{ active, selected, option }"
>
<div class="flex flex-col space-y-1 text-ink-gray-8">
<div>
{{ option.label }}
</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>
</ComboboxOption>
</div>
<li
v-if="groups.length == 0"
class="mt-1.5 rounded-md px-2.5 py-1.5 text-base text-ink-gray-5"
:class="[
'flex items-center rounded px-2.5 py-2 text-base cursor-pointer',
{ 'bg-surface-gray-2': active },
]"
>
No results found
<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>
</li>
</ComboboxOptions>
<div v-if="slots.footer" class="border-t p-1.5 pb-0.5">
<slot
name="footer"
v-bind="{ value: search?.el._value, close }"
></slot>
</div>
</ComboboxOption>
</template>
<div
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') }}
</div>
</div>
</template>
</Popover>
<!-- Footer -->
<div
v-if="slots.footer"
class="border-t border-outline-gray-modals p-1.5 pb-0.5"
>
<slot
name="footer"
v-bind="{
value: selectedValue,
close,
}"
/>
</div>
</ComboboxOptions>
</div>
</Combobox>
</div>
</template>
@@ -143,15 +100,15 @@ import {
ComboboxInput,
ComboboxOptions,
ComboboxOption,
ComboboxButton,
} from '@headlessui/vue'
import { Popover } from 'frappe-ui'
import { ChevronDown, X } from 'lucide-vue-next'
import { ref, computed, useAttrs, useSlots, watch, nextTick } from 'vue'
import { watchDebounced } from '@vueuse/core'
const props = defineProps({
modelValue: {
type: String,
default: '',
type: [String, Object],
default: null,
},
options: {
type: Array,
@@ -182,109 +139,95 @@ const props = defineProps({
default: true,
},
})
const emit = defineEmits(['update:modelValue', 'update:query', 'change'])
const query = ref('')
const showOptions = ref(false)
const trigger = ref(null)
const search = ref(null)
const attrs = useAttrs()
const slots = useSlots()
const selectedValue = ref(props.modelValue)
const query = ref('')
const valuePropPassed = computed(() => 'value' in attrs)
const selectedValue = computed({
get() {
return valuePropPassed.value ? attrs.value : props.modelValue
},
set(val) {
query.value = ''
if (val) {
showOptions.value = false
}
emit(valuePropPassed.value ? 'change' : 'update:modelValue', val)
},
watch(selectedValue, (val) => {
query.value = ''
emit(valuePropPassed.value ? 'change' : 'update:modelValue', val)
})
function close() {
showOptions.value = false
function clearValue() {
emit('update:modelValue', null)
}
const groups = computed(() => {
if (!props.options || props.options.length == 0) return []
if (!props.options?.length) return []
let groups = props.options[0]?.group
const normalized = props.options[0]?.group
? props.options
: [{ group: '', items: props.options }]
return groups
.map((group, i) => {
return {
key: i,
group: group.group,
hideLabel: group.hideLabel || false,
items: props.filterable ? filterOptions(group.items) : group.items,
}
})
return normalized
.map((group, i) => ({
key: i,
group: group.group,
hideLabel: group.hideLabel || false,
items: props.filterable ? filterOptions(group.items) : group.items,
}))
.filter((group) => group.items.length > 0)
})
function filterOptions(options) {
if (!query.value) {
return options
}
return options.filter((option) => {
let searchTexts = [option.label, option.value]
return searchTexts.some((text) =>
(text || '').toString().toLowerCase().includes(query.value.toLowerCase())
)
if (!query.value) return options
const q = query.value.toLowerCase()
return options.filter((option) =>
[option.label, option.value]
.filter(Boolean)
.some((text) => text.toString().toLowerCase().includes(q))
)
}
watchDebounced(
query,
(val) => {
emit('update:query', val)
},
{ debounce: 300 }
)
const onFocus = () => {
trigger.value?.$el.click()
nextTick(() => {
search.value?.focus()
})
}
function displayValue(option) {
if (typeof option === 'string') {
let allOptions = groups.value.flatMap((group) => group.items)
let selectedOption = allOptions.find((o) => o.value === option)
return selectedOption?.label || option
}
return option?.label
const close = () => {
selectedValue.value = null
trigger.value?.$el.click()
}
watch(query, (q) => {
emit('update:query', q)
})
watch(showOptions, (val) => {
if (val) {
nextTick(() => {
search.value.el.focus()
})
}
})
const textColor = computed(() => {
return props.disabled ? 'text-ink-gray-5' : 'text-ink-gray-8'
})
const textColor = computed(() =>
props.disabled ? 'text-ink-gray-5' : 'text-ink-gray-8'
)
const inputClasses = computed(() => {
let sizeClasses = {
const sizeClasses = {
sm: 'text-base rounded h-7',
md: 'text-base rounded h-8',
lg: 'text-lg rounded-md h-10',
xl: 'text-xl rounded-md h-10',
}[props.size]
let paddingClasses = {
const paddingClasses = {
sm: 'py-1.5 px-2',
md: 'py-1.5 px-2.5',
lg: 'py-1.5 px-3',
xl: 'py-1.5 px-3',
}[props.size]
let variant = props.disabled ? 'disabled' : props.variant
let variantClasses = {
const variant = props.disabled ? 'disabled' : props.variant
const 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: [
@@ -303,6 +246,4 @@ const inputClasses = computed(() => {
'transition-colors w-full',
]
})
defineExpose({ query })
</script>
+79 -57
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, () => {
+1 -1
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`]
@@ -21,8 +21,10 @@
:style="
modelValue
? {
backgroundColor:
theme.backgroundColor[modelValue.toLowerCase()][400],
backgroundColor: getColor(
modelValue.toLowerCase(),
400
),
}
: {}
"
@@ -55,8 +57,7 @@
:key="color"
class="size-5 rounded-full cursor-pointer"
:style="{
backgroundColor:
theme.backgroundColor[color.toLowerCase()][400],
backgroundColor: getColor(color.toLowerCase(), 400),
}"
@click="
(e) => {
@@ -79,7 +80,7 @@
import { Button, FormControl, Popover } from 'frappe-ui'
import { computed } from 'vue'
import { Palette, X } from 'lucide-vue-next'
import { theme } from '@/utils/theme'
import { getColor } from '@/utils'
const emit = defineEmits(['update:modelValue', 'change'])
@@ -20,7 +20,7 @@
class="w-4 h-4 text-ink-gray-7 stroke-1.5"
:is="icons.Folder"
/>
<span v-if="selectedIcon">
<span v-if="selectedIcon" class="text-ink-gray-7">
{{ selectedIcon }}
</span>
<span v-else class="text-ink-gray-5">
+13 -3
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: {
@@ -95,14 +95,14 @@ const value = computed({
get: () => (valuePropPassed.value ? attrs.value : props.modelValue),
set: (val) => {
return (
val?.value &&
emit(valuePropPassed.value ? 'change' : 'update:modelValue', val?.value)
val && emit(valuePropPassed.value ? 'change' : 'update:modelValue', val)
)
},
})
const autocomplete = ref(null)
const text = ref('')
const settingsStore = useSettings()
watchDebounced(
() => autocomplete.value?.query,
@@ -121,6 +121,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],
+140 -179
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>
+36 -9
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 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)
}
+18 -24
View File
@@ -5,7 +5,7 @@
style="min-height: 350px"
>
<div
class="w-[100%] h-[168px] bg-cover bg-center bg-no-repeat"
class="w-[100%] h-[168px] bg-cover bg-center bg-no-repeat border-t border-x rounded-t-md"
:style="
course.image
? { backgroundImage: `url('${encodeURI(course.image)}')` }
@@ -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>
+17 -47
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,11 +140,6 @@
</div>
</div>
</div>
<CourseProgressSummary
v-model="showProgressModal"
:courseName="course.data.name"
:enrollments="course.data.enrollments"
/>
</template>
<script setup>
import {
@@ -188,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: {
@@ -214,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', {
@@ -289,7 +259,7 @@ const fetchCertificate = () => {
})
}
const showProgressSummary = () => {
showProgressModal.value = true
}
const isAdmin = computed(() => {
return user.data?.is_moderator || is_instructor()
})
</script>
@@ -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>
+20 -5
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>
+1 -1
View File
@@ -80,7 +80,7 @@ const props = defineProps({
required: true,
},
membership: {
type: Object,
type: Object || null,
required: false,
},
})
+7 -13
View File
@@ -1,19 +1,13 @@
<template>
<div class="relative flex h-full flex-col">
<div class="h-full flex-1">
<div class="flex h-screen text-base bg-surface-white">
<div
class="relative block min-h-0 flex-shrink-0 overflow-hidden hover:overflow-auto"
>
<AppSidebar />
</div>
<div class="w-full overflow-auto" id="scrollContainer">
<slot />
</div>
</div>
<div class="flex h-screen w-screen">
<div class="h-full border-r bg-surface-menu-bar">
<AppSidebar />
</div>
<div class="flex-1 flex flex-col h-full overflow-auto bg-surface-white">
<slot />
</div>
</div>
</template>
<script setup>
import AppSidebar from './AppSidebar.vue'
import AppSidebar from '@/components/Sidebar/AppSidebar.vue'
</script>
+59 -80
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(() => {
+11 -4
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()
+4 -1
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>
@@ -33,6 +33,9 @@
<Badge>
{{ job.type }}
</Badge>
<Badge v-if="job.work_mode">
{{ job.work_mode }}
</Badge>
<Badge>
{{ dayjs(job.creation).fromNow() }}
</Badge>
+13 -5
View File
@@ -1,7 +1,7 @@
<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"
class="flex items-center space-x-2 mb-5 bg-surface-amber-1 py-1 px-2 rounded-md text-ink-amber-3 text-xs"
>
<AlertCircle class="size-4 stroke-1.5" />
<span>
@@ -54,8 +54,8 @@
<div class="flex items-center space-x-2">
<Clock class="w-4 h-4 stroke-1.5" />
<span>
{{ formatTime(cls.time) }} -
{{ dayjs(getClassEnd(cls)).format('HH:mm A') }}
{{ dayjs(getClassStart(cls)).format('hh:mm A') }} -
{{ dayjs(getClassEnd(cls)).format('hh:mm A') }}
</span>
</div>
<div
@@ -107,7 +107,11 @@
v-model:reloadLiveClasses="liveClasses"
/>
<LiveClassAttendance v-model="showAttendance" :live_class="attendanceFor" />
<LiveClassAttendance
v-if="showAttendance"
v-model="showAttendance"
:live_class="attendanceFor"
/>
</template>
<script setup>
import { createListResource, Button, Tooltip } from 'frappe-ui'
@@ -181,8 +185,12 @@ const canAccessClass = (cls) => {
return true
}
const getClassStart = (cls) => {
return new Date(`${cls.date}T${cls.time}`)
}
const getClassEnd = (cls) => {
const classStart = new Date(`${cls.date}T${cls.time}`)
const classStart = getClassStart(cls)
return new Date(classStart.getTime() + cls.duration * 60000)
}
+19
View File
@@ -80,6 +80,7 @@ onMounted(() => {
{},
{
onSuccess(data) {
destructureSidebarLinks()
filterLinksToShow(data)
addOtherLinks()
},
@@ -103,6 +104,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])) {
@@ -165,6 +176,14 @@ const addAssignments = () => {
})
}
const addProgrammingExercises = () => {
otherLinks.value.push({
label: 'Programming Exercises',
icon: 'Code',
to: 'ProgrammingExercises',
})
}
const addPrograms = async () => {
let canAddProgram = await checkIfCanAddProgram()
if (!canAddProgram) return
@@ -15,20 +15,18 @@
>
<template #body-content>
<div class="flex flex-col gap-4">
<div class="">
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Subject') }}
<span class="text-ink-red-3">*</span>
</div>
<Input type="text" v-model="announcement.subject" />
</div>
<div class="">
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Reply To') }}
<span class="text-ink-red-3">*</span>
</div>
<Input type="text" v-model="announcement.replyTo" />
</div>
<FormControl
:label="__('Subject')"
type="text"
v-model="announcement.subject"
:required="true"
/>
<FormControl
:label="__('Reply To')"
type="text"
v-model="announcement.replyTo"
:required="true"
/>
<div class="mb-4">
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Announcement') }}
@@ -45,7 +43,13 @@
</Dialog>
</template>
<script setup>
import { Dialog, Input, TextEditor, createResource, toast } from 'frappe-ui'
import {
Dialog,
FormControl,
TextEditor,
createResource,
toast,
} from 'frappe-ui'
import { reactive } from 'vue'
const show = defineModel()
@@ -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(() => {
@@ -2,7 +2,7 @@
<Dialog
v-model="show"
:options="{
title: __('Add a course'),
title: __('Add Course'),
size: 'sm',
actions: [
{
@@ -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' },
})
}
"
@@ -11,7 +11,7 @@
<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">
<div class="text-xl font-semibold text-ink-gray-9">
{{ student.full_name }}
</div>
<Badge
@@ -36,7 +36,9 @@
v-if="Object.keys(student.assessments).length"
class="space-y-2 text-sm"
>
<div class="flex items-center border-b pb-1 font-medium">
<div
class="flex items-center border-b pb-1 font-medium text-ink-gray-9"
>
<span class="flex-1">
{{ __('Assessment') }}
</span>
@@ -86,7 +88,9 @@
v-if="Object.keys(student.courses).length"
class="space-y-2 text-sm"
>
<div class="flex items-center border-b pb-1 font-medium">
<div
class="flex items-center border-b pb-1 font-medium text-ink-gray-9"
>
<span class="flex-1">
{{ __('Courses') }}
</span>
@@ -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({
@@ -1,230 +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>
@@ -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>
+113 -132
View File
@@ -1,91 +1,85 @@
<template>
<Dialog
v-model="show"
:options="{
title: 'Edit your profile',
size: '3xl',
actions: [
{
label: 'Save',
variant: 'solid',
onClick: (close) => saveProfile(close),
},
],
}"
>
<template #body-header>
<div class="flex items-center justify-between mb-5">
<div class="text-2xl font-semibold leading-6 text-ink-gray-9">
{{ __('Edit Profile') }}
</div>
<div class="space-x-2">
<Badge v-if="isDirty" theme="orange">
{{ __('Not Saved') }}
</Badge>
<div class="pb-5 float-right">
<Button variant="solid" @click="saveProfile()">
{{ __('Save') }}
</Button>
</div>
</div>
</div>
</template>
<template #body-content>
<div class="grid grid-cols-2 gap-5">
<div class="space-y-4">
<!-- <Uploader
v-model="profile.image.file_url"
label="Profile Image"
description="Your profile image to help others recognize you."
/> -->
<div>
<div class="text-xs text-ink-gray-5 mb-1">
{{ __('Profile Image') }}
</div>
<FileUploader
v-if="!profile.image"
:fileTypes="['image/*']"
:validateFile="validateFile"
@success="(file) => saveImage(file)"
>
<template
v-slot="{ file, progress, uploading, openFileSelector }"
>
<div class="mb-4">
<Button @click="openFileSelector" :loading="uploading">
{{
uploading
? `Uploading ${progress}%`
: 'Upload a profile image'
}}
</Button>
</div>
</template>
</FileUploader>
<div v-else class="mb-4">
<div class="flex items-center">
<img
:src="profile.image.file_url"
class="object-cover h-[50px] w-[50px] rounded-full border-4 border-white object-cover"
/>
<div class="text-base">
<div class="grid grid-cols-2 gap-10">
<div class="space-y-4">
<div class="space-y-4">
<Uploader
v-model="profile.image"
:label="__('Profile Image')"
:required="true"
shape="circle"
/>
<div class="text-base flex flex-col ml-2">
<span>
{{ profile.image.file_name }}
</span>
<span class="text-sm text-ink-gray-4 mt-1">
{{ getFileSize(profile.image.file_size) }}
</span>
</div>
<X
@click="removeImage()"
class="bg-surface-gray-3 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
/>
</div>
<FormControl
v-model="profile.first_name"
:label="__('First Name')"
/>
<FormControl
v-model="profile.last_name"
:label="__('Last Name')"
/>
<FormControl v-model="profile.headline" :label="__('Headline')" />
<FormControl
v-model="profile.linkedin"
:label="__('LinkedIn ID')"
/>
<FormControl v-model="profile.github" :label="__('GitHub ID')" />
<FormControl
v-model="profile.twitter"
:label="__('Twitter ID')"
/>
</div>
</div>
<FormControl v-model="profile.first_name" :label="__('First Name')" />
<FormControl v-model="profile.last_name" :label="__('Last Name')" />
<FormControl v-model="profile.headline" :label="__('Headline')" />
<Link
:label="__('Language')"
v-model="profile.language"
doctype="Language"
/>
</div>
<div>
<div class="mb-4">
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Bio') }}
</div>
<TextEditor
:fixedMenu="true"
@change="(val) => (profile.bio = val)"
:content="profile.bio"
editorClass="prose-sm py-2 px-2 min-h-[200px] border-outline-gray-2 hover:border-outline-gray-3 rounded-b-md bg-surface-gray-3"
<div class="space-y-4">
<FormControl
v-model="profile.open_to"
type="select"
:options="[' ', 'Work', 'Hiring']"
:label="__('Open to')"
:placeholder="__('Looking for new work or hiring talent?')"
/>
<Link
:label="__('Language')"
v-model="profile.language"
doctype="Language"
/>
<div>
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Bio') }}
</div>
<TextEditor
:fixedMenu="true"
@change="(val) => (profile.bio = val)"
:content="profile.bio"
:rows="15"
editorClass="prose-sm py-2 px-2 min-h-[280px] border-outline-gray-2 hover:border-outline-gray-3 rounded-b-md bg-surface-gray-3"
/>
</div>
</div>
</div>
</div>
@@ -94,22 +88,22 @@
</template>
<script setup>
import {
Dialog,
FormControl,
FileUploader,
Badge,
Button,
createResource,
Dialog,
FormControl,
TextEditor,
toast,
} from 'frappe-ui'
import { ref, reactive, watch } from 'vue'
import { X } from 'lucide-vue-next'
import { getFileSize, decodeEntities } from '@/utils'
import { sanitizeHTML } from '@/utils'
import Link from '@/components/Controls/Link.vue'
import DOMPurify from 'dompurify'
const show = defineModel()
const reloadProfile = defineModel('reloadProfile')
const hasLanguageChanged = ref(false)
const isDirty = ref(false)
const props = defineProps({
profile: {
@@ -124,19 +118,10 @@ const profile = reactive({
headline: '',
bio: '',
image: '',
})
const imageResource = createResource({
url: 'lms.lms.api.get_file_info',
makeParams(values) {
return {
file_url: values.image,
}
},
auto: false,
onSuccess(data) {
profile.image = data
},
open_to: '',
linkedin: '',
github: '',
twitter: '',
})
const updateProfile = createResource({
@@ -146,7 +131,7 @@ const updateProfile = createResource({
doctype: 'User',
name: props.profile.data.name,
fieldname: {
user_image: profile.image.file_url,
user_image: profile.image || null,
...profile,
},
}
@@ -156,28 +141,13 @@ const updateProfile = createResource({
},
})
const saveProfile = (close) => {
profile.bio = DOMPurify.sanitize(decodeEntities(profile.bio), {
ALLOWED_TAGS: [
'b',
'i',
'em',
'strong',
'a',
'p',
'br',
'ul',
'ol',
'li',
'img',
],
ALLOWED_ATTR: ['href', 'target', 'src'],
})
const saveProfile = () => {
profile.bio = sanitizeHTML(profile.bio)
updateProfile.submit(
{},
{
onSuccess() {
close()
show.value = false
reloadProfile.value.reload()
if (hasLanguageChanged.value) {
hasLanguageChanged.value = false
@@ -191,20 +161,26 @@ const saveProfile = (close) => {
)
}
const validateFile = (file) => {
let extension = file.name.split('.').pop().toLowerCase()
if (!['jpg', 'jpeg', 'png'].includes(extension)) {
return 'Only image file is allowed.'
}
}
const saveImage = (file) => {
profile.image = file
}
const removeImage = () => {
profile.image = null
}
watch(
() => profile,
(newVal) => {
if (!props.profile.data) return
let keys = Object.keys(newVal)
keys.splice(keys.indexOf('image'), 1)
for (let key of keys) {
if (newVal[key] !== props.profile.data[key]) {
isDirty.value = true
return
}
}
if (profile.image !== props.profile.data.user_image) {
isDirty.value = true
return
}
isDirty.value = false
},
{ deep: true }
)
watch(
() => props.profile.data,
@@ -215,15 +191,20 @@ watch(
profile.headline = newVal.headline
profile.language = newVal.language
profile.bio = newVal.bio
if (newVal.user_image) imageResource.submit({ image: newVal.user_image })
profile.open_to = newVal.open_to
profile.linkedin = newVal.linkedin
profile.github = newVal.github
profile.twitter = newVal.twitter
profile.image = newVal.user_image
isDirty.value = false
}
}
)
watch(
() => profile.language,
(newVal, oldVal) => {
if (newVal !== oldVal) {
() => {
if (profile.language !== props.profile.data.language) {
hasLanguageChanged.value = true
}
}
@@ -67,7 +67,7 @@
'Dear {{ member_name }},\n\nYou have been enrolled in our upcoming batch {{ batch_name }}.\n\nThanks,\nFrappe Learning'
)
"
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>
@@ -2,7 +2,7 @@
<Dialog
v-model="show"
:options="{
title: __('Schedule Evaluation'),
title: __('Schedule your evaluation'),
size: 'xl',
actions: [
{
@@ -14,64 +14,68 @@
}"
>
<template #body-content>
<div class="flex flex-col gap-4">
<div>
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Course') }}
<div class="flex flex-col gap-4 text-base max-h-[60vh]">
<FormControl
v-model="evaluation.course"
type="select"
:label="__('Course')"
:options="getCourses()"
/>
<div v-if="slots.data?.length" class="space-y-4 overflow-y-auto mt-4">
<div class="text-ink-gray-9 font-medium">
{{ __('Available Slots') }}
</div>
<Select v-model="evaluation.course" :options="getCourses()" />
</div>
<div>
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Date') }}
</div>
<FormControl
type="date"
v-model="evaluation.date"
:min="
dayjs()
.add(dayjs.duration({ days: 1 }))
.format('YYYY-MM-DD')
"
/>
</div>
<div v-if="slots.data?.length">
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Select a slot') }}
</div>
<div class="grid grid-cols-2 gap-2">
<div v-for="slot in slots.data">
<div
class="text-base text-center border rounded-md text-ink-gray-8 bg-surface-gray-3 p-2 cursor-pointer"
@click="saveSlot(slot)"
:class="{
'border-outline-gray-4':
evaluation.start_time == slot.start_time,
}"
>
{{ formatTime(slot.start_time) }} -
{{ formatTime(slot.end_time) }}
<div class="space-y-5">
<div v-for="row in slots.data" class="space-y-2">
<div class="flex items-center text-ink-gray-7 space-x-2">
<Calendar class="size-3" />
<div class="text-ink-gray-9">
{{ dayjs(row.date).format('DD MMMM YYYY') }}
</div>
<div>&middot;</div>
<div class="text-ink-gray-5">
{{ row.day }}
</div>
</div>
<div class="grid grid-cols-3 gap-2">
<div
v-for="slot in row.slots"
class="text-base text-center border rounded-md text-ink-gray-8 p-2 cursor-pointer text-ink-gray-7 hover:bg-surface-gray-2 hover:border-outline-gray-3"
@click="saveSlot(slot, row)"
:class="{
'border-outline-gray-4 text-ink-gray-9':
evaluation.date == row.date &&
evaluation.start_time == slot.start_time,
}"
>
{{ formatTime(slot.start_time) }} -
{{ formatTime(slot.end_time) }}
</div>
</div>
</div>
</div>
</div>
<div
v-else-if="evaluation.course && evaluation.date"
class="text-sm italic text-ink-red-4"
>
{{ __('No slots available for this date.') }}
<div v-else class="text-ink-red-3">
{{ __('No slots available for the selected course.') }}
</div>
</div>
</template>
</Dialog>
</template>
<script setup>
import { Dialog, createResource, Select, FormControl, toast } from 'frappe-ui'
import { reactive, watch, inject } from 'vue'
import {
call,
createResource,
dayjs,
Dialog,
FormControl,
toast,
} from 'frappe-ui'
import { ref, watch, inject } from 'vue'
import { Calendar } from 'lucide-vue-next'
import { formatTime } from '@/utils/'
const user = inject('$user')
const dayjs = inject('$dayjs')
const show = defineModel()
const evaluations = defineModel('reloadEvals')
@@ -90,7 +94,7 @@ const props = defineProps({
},
})
const evaluation = reactive({
const evaluation = ref({
course: '',
date: '',
start_time: '',
@@ -100,48 +104,28 @@ const evaluation = reactive({
member: user.data.name,
})
const createEvaluation = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'LMS Certificate Request',
batch_name: values.batch,
...values,
},
}
},
})
function submitEvaluation(close) {
createEvaluation.submit(evaluation, {
validate() {
if (!evaluation.course) {
return 'Please select a course.'
}
if (!evaluation.date) {
return 'Please select a date.'
}
if (!evaluation.start_time) {
return 'Please select a slot.'
}
if (dayjs(evaluation.date).isBefore(dayjs(), 'day')) {
return 'Please select a future date.'
}
if (dayjs(evaluation.date).isAfter(dayjs(props.endDate), 'day')) {
return `Please select a date before the end date ${dayjs(
props.endDate
).format('DD MMMM YYYY')}.`
}
},
onSuccess() {
evaluations.value.reload()
close()
},
onError(err) {
toast.warning(__(err.messages?.[0] || err))
if (!evaluation.value.date || !evaluation.value.start_time) {
toast.warning(__('Please select a slot for your evaluation.'), {
duration: 10,
})
return
}
call('frappe.client.insert', {
doc: {
doctype: 'LMS Certificate Request',
batch_name: evaluation.value.batch,
...evaluation.value,
},
})
.then(() => {
evaluations.value.reload()
close()
})
.catch((err) => {
console.log(err.messages?.[0] || err)
toast.warning(__(err.messages?.[0] || err), { duration: 20 })
})
}
const getCourses = () => {
@@ -156,7 +140,7 @@ const getCourses = () => {
}
if (courses.length === 1) {
evaluation.course = courses[0].value
evaluation.value.course = courses[0].value
}
return courses
@@ -167,34 +151,22 @@ const slots = createResource({
makeParams(values) {
return {
course: values.course,
date: values.date,
batch: props.batch,
}
},
})
watch(
() => evaluation.date,
(date) => {
evaluation.start_time = ''
if (date && evaluation.course) {
slots.submit(evaluation)
}
}
)
watch(
() => evaluation.course,
() => evaluation.value.course,
(course) => {
evaluation.date = ''
evaluation.start_time = ''
slots.reset()
slots.reload(evaluation.value)
}
)
const saveSlot = (slot) => {
evaluation.start_time = slot.start_time
evaluation.end_time = slot.end_time
evaluation.day = slot.day
const saveSlot = (slot, row) => {
evaluation.value.start_time = slot.start_time
evaluation.value.end_time = slot.end_time
evaluation.value.date = row.date
evaluation.value.day = row.day
}
</script>

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