feat: livecode settings
This commit is contained in:
2
.github/helper/install_dependencies.sh
vendored
2
.github/helper/install_dependencies.sh
vendored
@@ -5,7 +5,7 @@ echo "Setting Up System Dependencies..."
|
|||||||
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt remove mysql-server mysql-client
|
sudo apt remove mysql-server mysql-client
|
||||||
sudo apt-get install libcups2-dev redis-server mariadb-client
|
sudo apt-get install libcups2-dev redis-server mariadb-client libmariadb-dev
|
||||||
|
|
||||||
install_wkhtmltopdf() {
|
install_wkhtmltopdf() {
|
||||||
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ const tabsStructure = computed(() => {
|
|||||||
doctype: 'Email Template',
|
doctype: 'Email Template',
|
||||||
type: 'Link',
|
type: 'Link',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Livecode URL',
|
||||||
|
name: 'livecode_url',
|
||||||
|
doctype: 'Livecode URL',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Unsplash Access Key',
|
label: 'Unsplash Access Key',
|
||||||
name: 'unsplash_access_key',
|
name: 'unsplash_access_key',
|
||||||
|
|||||||
@@ -5,6 +5,20 @@
|
|||||||
>
|
>
|
||||||
<Breadcrumbs :items="breadcrumbs" />
|
<Breadcrumbs :items="breadcrumbs" />
|
||||||
</header>
|
</header>
|
||||||
|
<div
|
||||||
|
v-if="falconError"
|
||||||
|
class="flex items-center justify-between p-3 text-sm bg-surface-amber-1 text-ink-amber-3"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ falconError }}
|
||||||
|
</span>
|
||||||
|
<Button v-if="user.data?.is_moderator" @click="openSettings('General')">
|
||||||
|
<template #prefix>
|
||||||
|
<Settings class="size-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
|
{{ __('Settings') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 h-[calc(100vh_-_3rem)]">
|
<div class="grid grid-cols-2 h-[calc(100vh_-_3rem)]">
|
||||||
<div class="border-r py-5 px-8 h-full">
|
<div class="border-r py-5 px-8 h-full">
|
||||||
<div class="font-semibold mb-2">
|
<div class="font-semibold mb-2">
|
||||||
@@ -29,7 +43,9 @@
|
|||||||
</Badge>
|
</Badge>
|
||||||
<Button
|
<Button
|
||||||
v-if="
|
v-if="
|
||||||
submissionID == 'new' || user.data?.name == submission.doc?.owner
|
!falconError &&
|
||||||
|
(submissionID == 'new' ||
|
||||||
|
user.data?.name == submission.doc?.owner)
|
||||||
"
|
"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@click="submitCode"
|
@click="submitCode"
|
||||||
@@ -131,9 +147,10 @@ import {
|
|||||||
usePageMeta,
|
usePageMeta,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { computed, inject, onMounted, ref, watch } from 'vue'
|
import { computed, inject, onMounted, ref, watch } from 'vue'
|
||||||
import { Play, X, Check } from 'lucide-vue-next'
|
import { Play, X, Check, Settings } from 'lucide-vue-next'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { openSettings } from '@/utils'
|
||||||
|
|
||||||
const user = inject<any>('$user')
|
const user = inject<any>('$user')
|
||||||
const code = ref<string | null>('')
|
const code = ref<string | null>('')
|
||||||
@@ -141,19 +158,13 @@ const output = ref<string | null>(null)
|
|||||||
const error = ref<boolean | null>(null)
|
const error = ref<boolean | null>(null)
|
||||||
const errorMessage = ref<string | null>(null)
|
const errorMessage = ref<string | null>(null)
|
||||||
const testCaseSection = ref<HTMLElement | null>(null)
|
const testCaseSection = ref<HTMLElement | null>(null)
|
||||||
const testCases = ref<
|
const testCases = ref<TestCase[]>([])
|
||||||
Array<{
|
|
||||||
input: string
|
|
||||||
output: string
|
|
||||||
expected_output: string
|
|
||||||
status: string
|
|
||||||
}>
|
|
||||||
>([])
|
|
||||||
const boilerplate = ref<string>('')
|
const boilerplate = ref<string>('')
|
||||||
const { brand } = sessionStore()
|
const { brand, livecodeURL } = sessionStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const fromLesson = ref(false)
|
const fromLesson = ref(false)
|
||||||
const falconURL = 'https://falcon.frappe.io/'
|
const falconURL = ref<string>('https://falcon.frappe.io/')
|
||||||
|
const falconError = ref<string | null>(null)
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -273,9 +284,23 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const loadFalcon = () => {
|
const loadFalcon = () => {
|
||||||
|
if (livecodeURL.data.includes('falcon.frappe.io') && !user.data?.is_fc_site) {
|
||||||
|
falconError.value = __(
|
||||||
|
'Only Frappe Cloud sites can use Falcon Live Code. Please migrate your site to Frappe Cloud or setup Falcon Live Code on your own server.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
} else if (livecodeURL.data) {
|
||||||
|
falconURL.value = livecodeURL.data
|
||||||
|
} else if (!livecodeURL.data && !user.data?.is_fc_site) {
|
||||||
|
falconError.value = __(
|
||||||
|
'Live Code URL is not set. Please set it from the Settings.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
script.src = `${falconURL}static/livecode.js`
|
script.src = `${falconURL.value}static/livecode.js`
|
||||||
script.onload = resolve
|
script.onload = resolve
|
||||||
script.onerror = reject
|
script.onerror = reject
|
||||||
document.head.appendChild(script)
|
document.head.appendChild(script)
|
||||||
@@ -351,7 +376,7 @@ const execute = (stdin = ''): Promise<string> => {
|
|||||||
let hasError = false
|
let hasError = false
|
||||||
|
|
||||||
let session = new LiveCodeSession({
|
let session = new LiveCodeSession({
|
||||||
base_url: 'https://falcon.frappe.io',
|
base_url: falconURL.value,
|
||||||
runtime: exercise.doc?.language.toLowerCase() || 'python',
|
runtime: exercise.doc?.language.toLowerCase() || 'python',
|
||||||
code: code.value,
|
code: code.value,
|
||||||
files: [{ filename: 'stdin', contents: stdin }],
|
files: [{ filename: 'stdin', contents: stdin }],
|
||||||
|
|||||||
@@ -3,21 +3,35 @@
|
|||||||
class="sticky flex items-center justify-between top-0 z-10 border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
class="sticky flex items-center justify-between top-0 z-10 border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs :items="breadcrumbs" />
|
<Breadcrumbs :items="breadcrumbs" />
|
||||||
<Button
|
<div class="space-x-2">
|
||||||
v-if="!readOnlyMode"
|
<router-link
|
||||||
variant="solid"
|
:to="{
|
||||||
@click="
|
name: 'ProgrammingExerciseSubmissions',
|
||||||
() => {
|
}"
|
||||||
exerciseID = 'new'
|
>
|
||||||
showForm = true
|
<Button>
|
||||||
}
|
<template #prefix>
|
||||||
"
|
<ClipboardList class="size-4 stroke-1.5" />
|
||||||
>
|
</template>
|
||||||
<template #prefix>
|
{{ __('Check All Submissions') }}
|
||||||
<Plus class="h-4 w-4 stroke-1.5" />
|
</Button>
|
||||||
</template>
|
</router-link>
|
||||||
{{ __('New') }}
|
<Button
|
||||||
</Button>
|
v-if="!readOnlyMode"
|
||||||
|
variant="solid"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
exerciseID = 'new'
|
||||||
|
showForm = true
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<Plus class="h-4 w-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
|
{{ __('New') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="md:w-4/5 md:mx-auto p-5">
|
<div class="md:w-4/5 md:mx-auto p-5">
|
||||||
<div class="flex items-center justify-between mb-5">
|
<div class="flex items-center justify-between mb-5">
|
||||||
@@ -89,7 +103,7 @@ import {
|
|||||||
createListResource,
|
createListResource,
|
||||||
usePageMeta,
|
usePageMeta,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { Plus } from 'lucide-vue-next'
|
import { ClipboardList, Plus } from 'lucide-vue-next'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import ProgrammingExerciseForm from '@/pages/ProgrammingExercises/ProgrammingExerciseForm.vue'
|
import ProgrammingExerciseForm from '@/pages/ProgrammingExercises/ProgrammingExerciseForm.vue'
|
||||||
|
|||||||
@@ -60,6 +60,16 @@ export const sessionStore = defineStore('lms-session', () => {
|
|||||||
auto: false,
|
auto: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const livecodeURL = createResource({
|
||||||
|
url: 'frappe.client.get_single_value',
|
||||||
|
params: {
|
||||||
|
doctype: 'LMS Settings',
|
||||||
|
field: 'livecode_url',
|
||||||
|
},
|
||||||
|
cache: 'livecodeURL',
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
@@ -68,5 +78,6 @@ export const sessionStore = defineStore('lms-session', () => {
|
|||||||
brand,
|
brand,
|
||||||
branding,
|
branding,
|
||||||
sidebarSettings,
|
sidebarSettings,
|
||||||
|
livecodeURL,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -578,16 +578,17 @@ export const enablePlyr = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openSettings = (category, close) => {
|
export const openSettings = (category, close = null) => {
|
||||||
const settingsStore = useSettings()
|
const settingsStore = useSettings()
|
||||||
close()
|
if (close) {
|
||||||
|
close()
|
||||||
|
}
|
||||||
settingsStore.activeTab = category
|
settingsStore.activeTab = category
|
||||||
settingsStore.isSettingsOpen = true
|
settingsStore.isSettingsOpen = true
|
||||||
|
console.log(settingsStore.activeTab, settingsStore.isSettingsOpen)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cleanError = (message) => {
|
export const cleanError = (message) => {
|
||||||
// Remove HTML tags but keep the text within the tags
|
|
||||||
|
|
||||||
const cleanMessage = message.replace(/<[^>]+>/g, (match) => {
|
const cleanMessage = message.replace(/<[^>]+>/g, (match) => {
|
||||||
return match.replace(/<\/?[^>]+(>|$)/g, '')
|
return match.replace(/<\/?[^>]+(>|$)/g, '')
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user