Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e21f0e3
refactor(ui): settings
pateljannat Jan 2, 2026
b2b1d2b
feat: email notifications for published courses
pateljannat Jan 5, 2026
ea94813
feat: system notification for new courses published
pateljannat Jan 6, 2026
45e98b9
fix: notification on quiz update and mention
pateljannat Jan 7, 2026
6eeb688
chore: Hungarian translations
pateljannat Jan 7, 2026
e1d3435
Merge pull request #1972 from frappe/l10n_develop2
pateljannat Jan 9, 2026
b5fcfb6
fix: certification filters
pateljannat Jan 13, 2026
af5bc2b
chore: upgraded node version for linter and release github actions
pateljannat Jan 13, 2026
ad650c7
fix: changed order by query for certified participants listing
pateljannat Jan 13, 2026
c9c6aef
fix: certified participants query
pateljannat Jan 13, 2026
566711d
Merge pull request #1983 from pateljannat/issues-167
pateljannat Jan 13, 2026
0f1b6f3
chore: upgraded node version for pot file runner
pateljannat Jan 13, 2026
64bdd85
chore: update POT file
frappe-pr-bot Jan 13, 2026
1d83402
Merge pull request #1984 from frappe/pot_develop_2026-01-13
pateljannat Jan 13, 2026
78494b9
chore: added frappe dependency range to pyproject
pateljannat Jan 13, 2026
65b1864
Merge branch 'develop' of https://github.com/frappe/lms into develop
pateljannat Jan 13, 2026
8ad5288
chore: resolved merge conflicts
pateljannat Jan 13, 2026
a09599e
feat: notification sent checkbox to prevent resending the notification
pateljannat Jan 13, 2026
60fcf04
fix: mark notification as read when link is visited
pateljannat Jan 13, 2026
775d5ec
chore: formatting of BrandSettings
pateljannat Jan 13, 2026
57c7a8d
chore: fixed translatable string
pateljannat Jan 13, 2026
4c81eef
Merge pull request #1971 from pateljannat/notifications-feed
pateljannat Jan 13, 2026
0265c9d
chore: Dutch translations
pateljannat Jan 13, 2026
d4e3676
chore: Russian translations
pateljannat Jan 13, 2026
d0ee3fe
chore: Persian translations
pateljannat Jan 13, 2026
d32a80e
chore: Hungarian translations
pateljannat Jan 13, 2026
949ea33
chore: Italian translations
pateljannat Jan 13, 2026
27cbc26
chore: French translations
pateljannat Jan 13, 2026
f7e8707
chore: Spanish translations
pateljannat Jan 13, 2026
c7a991e
chore: Arabic translations
pateljannat Jan 13, 2026
d387c7b
chore: Czech translations
pateljannat Jan 13, 2026
bd8baa6
chore: Danish translations
pateljannat Jan 13, 2026
dd7eb9a
chore: German translations
pateljannat Jan 13, 2026
6ce655c
chore: Polish translations
pateljannat Jan 13, 2026
e793afd
chore: Portuguese translations
pateljannat Jan 13, 2026
0cf9984
chore: Slovenian translations
pateljannat Jan 13, 2026
cf6292c
chore: Serbian (Cyrillic) translations
pateljannat Jan 13, 2026
0004006
chore: Swedish translations
pateljannat Jan 13, 2026
02c1f4c
chore: Turkish translations
pateljannat Jan 13, 2026
eb37bd1
chore: Chinese Simplified translations
pateljannat Jan 13, 2026
a7f494f
chore: Vietnamese translations
pateljannat Jan 13, 2026
0f7e9d2
chore: Portuguese, Brazilian translations
pateljannat Jan 13, 2026
9736300
chore: Indonesian translations
pateljannat Jan 13, 2026
ce8f1a5
chore: Tamil translations
pateljannat Jan 13, 2026
ece6427
chore: Thai translations
pateljannat Jan 13, 2026
fa63a1d
chore: Croatian translations
pateljannat Jan 13, 2026
971fe8f
chore: Burmese translations
pateljannat Jan 13, 2026
ad092a7
chore: Bosnian translations
pateljannat Jan 13, 2026
675caa3
chore: Norwegian Bokmal translations
pateljannat Jan 13, 2026
bf024af
chore: Serbian (Latin) translations
pateljannat Jan 13, 2026
40c917f
chore: Esperanto translations
pateljannat Jan 13, 2026
4ace8b2
Merge pull request #1986 from frappe/l10n_develop2
pateljannat Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
with:
python-version: '3.14'
- name: setup node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: '24'
check-latest: true
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/generate-pot-file.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ jobs:
with:
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: |
bash ${GITHUB_WORKSPACE}/.github/helper/update_pot_file.sh
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/on_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
exit 1
fi

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 24
check-latest: true
Expand Down
4 changes: 3 additions & 1 deletion frontend/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
7 changes: 2 additions & 5 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"serve": "vite preview",
"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/src/tailwind/colors.json src/utils/frappe-ui-colors.json"
"copy-colors-json": "cp node_modules/frappe-ui/tailwind/colors.json src/utils/frappe-ui-colors.json"
},
"dependencies": {
"@codemirror/lang-html": "6.4.9",
Expand All @@ -34,7 +34,7 @@
"dayjs": "1.11.10",
"dompurify": "3.2.6",
"feather-icons": "4.28.0",
"frappe-ui": "0.1.227",
"frappe-ui": "^0.1.254",
"highlight.js": "11.11.1",
"lucide-vue-next": "0.383.0",
"markdown-it": "14.0.0",
Expand All @@ -59,8 +59,5 @@
"unplugin-auto-import": "^20.3.0",
"vite": "5.0.11",
"vite-plugin-pwa": "0.15.0"
},
"resolutions": {
"@iconify/utils": "2.1.7"
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/Assignment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
></div>
</div>

<div class="flex flex-col">
<div class="flex flex-col overflow-y-auto">
<div class="p-5">
<div class="flex items-center justify-between mb-4">
<div class="font-semibold text-ink-gray-9">
Expand Down
36 changes: 28 additions & 8 deletions frontend/src/components/Controls/Uploader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand All @@ -28,7 +31,15 @@
</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 rounded-md w-44 h-auto"
/>
<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') }}
Expand All @@ -47,7 +58,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
Expand All @@ -58,15 +70,23 @@ const props = withDefaults(
modelValue: string
label?: string
description?: string
type: 'image' | 'video'
required?: boolean
}>(),
{
modelValue: '',
label: '',
description: '',
type: 'image',
required: true,
}
)

const saveImage = (file: any) => {
const fileType = computed(() => {
return props.type === 'image' ? 'image/*' : 'video/*'
})

const saveFile = (file: any) => {
emit('update:modelValue', file.file_url)
}

Expand Down
80 changes: 51 additions & 29 deletions frontend/src/components/Settings/BrandSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</div>
</div>
<div class="overflow-y-auto">
<SettingFields :fields="fields" :data="branding.data" />
<SettingFields :sections="sections" :data="branding.data" />
</div>
<div class="flex flex-row-reverse mt-auto">
<Button variant="solid" :loading="saveSettings.loading" @click="update">
Expand All @@ -34,7 +34,7 @@ import { watch, ref } from 'vue'
const isDirty = ref(false)

const props = defineProps({
fields: {
sections: {
type: Array,
required: true,
},
Expand Down Expand Up @@ -65,23 +65,9 @@ const saveSettings = createResource({
})

const update = () => {
let fieldsToSave = {}
let imageFields = ['favicon', 'banner_image']
props.fields.forEach((f) => {
if (imageFields.includes(f.name)) {
fieldsToSave[f.name] =
branding.data[f.name] && branding.data[f.name].file_url
? branding.data[f.name].file_url
: null
} else {
fieldsToSave[f.name] = branding.data[f.name]
}
})

fieldsToSave['app_logo'] = fieldsToSave['banner_image']
saveSettings.submit(
{
fields: fieldsToSave,
fields: getFieldsToSave(),
},
{
onSuccess(data) {
Expand All @@ -91,18 +77,36 @@ const update = () => {
)
}

watch(branding, (updatedDoc) => {
let textFields = []
let imageFields = []
const getFieldsToSave = () => {
let imageFields = ['favicon', 'banner_image']
let fieldsToSave = {}

props.fields.forEach((f) => {
if (f.type === 'Upload') {
imageFields.push(f.name)
} else {
textFields.push(f.name)
}
props.sections.forEach((section) => {
section.columns.forEach((column) => {
column.fields.forEach((field) => {
if (imageFields.includes(field.name)) {
fieldsToSave[field.name] =
branding.data[field.name] && branding.data[field.name].file_url
? branding.data[field.name].file_url
: null
} else {
fieldsToSave[field.name] = branding.data[field.name]
}
})
})
})

fieldsToSave['app_logo'] = fieldsToSave['banner_image']
return fieldsToSave
}

watch(branding, (updatedDoc) => {
updateDirtyState(updatedDoc)
})

const updateDirtyState = (updatedDoc) => {
const { textFields, imageFields } = segregateFields()

textFields.forEach((field) => {
if (updatedDoc.data[field] != updatedDoc.previousData[field]) {
isDirty.value = true
Expand All @@ -111,11 +115,29 @@ watch(branding, (updatedDoc) => {

imageFields.forEach((field) => {
if (
updatedDoc.data[field] &&
updatedDoc.data[field].file_url != updatedDoc.previousData[field].file_url
updatedDoc.data[field]?.file_url !=
updatedDoc.previousData[field]?.file_url
) {
isDirty.value = true
}
})
})
}

const segregateFields = () => {
let textFields = []
let imageFields = []

props.sections.forEach((section) => {
section.columns.forEach((column) => {
column.fields.forEach((field) => {
if (field.type === 'Upload') {
imageFields.push(field.name)
} else {
textFields.push(field.name)
}
})
})
})
return { textFields, imageFields }
}
</script>
43 changes: 18 additions & 25 deletions frontend/src/components/Settings/SettingDetails.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
<template>
<div class="flex flex-col justify-between h-full text-base">
<div>
<div class="flex items-center justify-between">
<div class="text-xl font-semibold leading-none mb-2 text-ink-gray-9">
{{ __(label) }}
<div class="flex flex-col h-full text-base overflow-y-hidden">
<div class="">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center space-x-2">
<div class="text-xl font-semibold leading-none text-ink-gray-9">
{{ __(label) }}
</div>
<Badge
v-if="data.isDirty"
:label="__('Not Saved')"
variant="subtle"
theme="orange"
/>
</div>
<Badge
v-if="data.isDirty"
:label="__('Not Saved')"
variant="subtle"
theme="orange"
/>
<Button variant="solid" :loading="data.save.loading" @click="update">
{{ __('Update') }}
</Button>
</div>
<div class="text-ink-gray-6 leading-5">
{{ __(description) }}
</div>
</div>

<SettingFields :fields="fields" :data="data.doc" />
<div class="flex flex-row-reverse mt-auto">
<Button variant="solid" :loading="data.save.loading" @click="update">
{{ __('Update') }}
</Button>
</div>
<SettingFields :sections="sections" :data="data.doc" />
</div>
</template>

Expand All @@ -31,7 +31,7 @@ import { Button, Badge, toast } from 'frappe-ui'
import SettingFields from '@/components/Settings/SettingFields.vue'

const props = defineProps({
fields: {
sections: {
type: Array,
required: true,
},
Expand All @@ -49,13 +49,6 @@ const props = defineProps({
})

const update = () => {
props.fields.forEach((f) => {
if (f.type == 'Upload') {
props.data.doc[f.name] = f.value ? f.value.file_url : null
} else if (f.type != 'Column Break') {
props.data.doc[f.name] = f.value
}
})
props.data.save.submit(
{},
{
Expand Down
Loading
Loading