fix: ui issues
This commit is contained in:
@@ -16,13 +16,13 @@
|
|||||||
}"
|
}"
|
||||||
class="h-5 w-5 text-gray-900 stroke-1 mr-2"
|
class="h-5 w-5 text-gray-900 stroke-1 mr-2"
|
||||||
/>
|
/>
|
||||||
<div class="text-base">
|
<div class="text-base font-medium">
|
||||||
{{ chapter.title }}
|
{{ chapter.title }}
|
||||||
</div>
|
</div>
|
||||||
</DisclosureButton>
|
</DisclosureButton>
|
||||||
<DisclosurePanel class="pb-2">
|
<DisclosurePanel class="pb-2">
|
||||||
<div v-for="lesson in chapter.lessons" :key="lesson.name">
|
<div v-for="lesson in chapter.lessons" :key="lesson.name">
|
||||||
<div class="outline-lesson mb-2 pl-9">
|
<div class="outline-lesson my-2 pl-9">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
name: 'Lesson',
|
name: 'Lesson',
|
||||||
@@ -91,6 +91,6 @@ const openChapter = (index) => {
|
|||||||
<style>
|
<style>
|
||||||
.outline-lesson:has(.router-link-active) {
|
.outline-lesson:has(.router-link-active) {
|
||||||
background-color: theme('colors.gray.100');
|
background-color: theme('colors.gray.100');
|
||||||
padding: 0.5rem 0 0.5rem 2rem;
|
padding: 0.5rem 0 0.5rem 2.25rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
45
frontend/src/components/JobCard.vue
Normal file
45
frontend/src/components/JobCard.vue
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex border rounded-md p-2 mb-4 h-full">
|
||||||
|
<div class="mr-4">
|
||||||
|
<img
|
||||||
|
:src="job.company_logo"
|
||||||
|
class="w-11 h-11 rounded-lg object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-lg font-semibold mb-2">
|
||||||
|
{{ job.job_title }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mb-2 text-gray-700">
|
||||||
|
<div class="mr-5">
|
||||||
|
{{ job.company_name }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<MapPin class="h-4 w-4 mr-1 stroke-1.5" />
|
||||||
|
<span class="text-gray-700">
|
||||||
|
{{ job.location }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Badge :label="job.type" theme="green" />
|
||||||
|
<div class="ml-5">
|
||||||
|
{{ dayjs(job.creation).format('DD MMM YYYY') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { MapPin } from 'lucide-vue-next'
|
||||||
|
import { Badge } from 'frappe-ui'
|
||||||
|
import { inject } from 'vue'
|
||||||
|
|
||||||
|
const dayjs = inject('$dayjs')
|
||||||
|
const props = defineProps({
|
||||||
|
job: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||||
<Button @click="openAnnouncementModal()">
|
<Button v-if="user.data?.is_moderator" @click="openAnnouncementModal()">
|
||||||
<span>
|
<span>
|
||||||
{{ __('Make an Announcement') }}
|
{{ __('Make an Announcement') }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,93 +1,112 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-screen text-base">
|
<div class="h-screen text-base">
|
||||||
<header
|
<header
|
||||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs class="h-7" :items="[{ label: __('All Batches'), route: { name: 'Batches' } }]"/>
|
<Breadcrumbs
|
||||||
<div class="flex">
|
class="h-7"
|
||||||
<Button variant="solid">
|
:items="[{ label: __('All Batches'), route: { name: 'Batches' } }]"
|
||||||
<template #prefix>
|
/>
|
||||||
<Plus class="h-4 w-4" />
|
<div class="flex">
|
||||||
</template>
|
<Button variant="solid">
|
||||||
{{ __("New Batch") }}
|
<template #prefix>
|
||||||
</Button>
|
<Plus class="h-4 w-4" />
|
||||||
</div>
|
</template>
|
||||||
</header>
|
{{ __('New Batch') }}
|
||||||
<div class="mx-5 py-5">
|
</Button>
|
||||||
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
</div>
|
||||||
<template #tab="{ tab, selected }">
|
</header>
|
||||||
<div>
|
<div class="pb-5">
|
||||||
<button
|
<Tabs v-model="tabIndex" :tabs="tabs" tablistClass="overflow-x-visible">
|
||||||
class="group -mb-px flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
|
<template #tab="{ tab, selected }">
|
||||||
:class="{ 'text-gray-900': selected }">
|
<div>
|
||||||
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
<button
|
||||||
{{ __(tab.label) }}
|
class="group -mb-px flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
|
||||||
<Badge :class="{ 'text-gray-900 border border-gray-900': selected }" variant="subtle" theme="gray"
|
:class="{ 'text-gray-900': selected }"
|
||||||
size="sm">
|
>
|
||||||
{{ tab.count }}
|
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
||||||
</Badge>
|
{{ __(tab.label) }}
|
||||||
</button>
|
<Badge
|
||||||
</div>
|
:class="
|
||||||
</template>
|
selected
|
||||||
<template #default="{ tab }">
|
? 'text-gray-800 border border-gray-800'
|
||||||
<div v-if="tab.batches && tab.batches.value.length" class="grid grid-cols-1 sm:grid-cols-3 md:grid-cols-4 gap-8 mt-5">
|
: 'border border-gray-500'
|
||||||
<router-link v-for="batch in tab.batches.value"
|
"
|
||||||
:to="{ name: 'BatchDetail', params: { batchName: batch.name } }">
|
variant="subtle"
|
||||||
<BatchCard :batch="batch" />
|
theme="gray"
|
||||||
</router-link>
|
size="sm"
|
||||||
</div>
|
>
|
||||||
<div v-else class="grid flex-1 place-items-center text-xl font-medium text-gray-500">
|
{{ tab.count }}
|
||||||
<div class="flex flex-col items-center justify-center mt-4">
|
</Badge>
|
||||||
<div>
|
</button>
|
||||||
{{ __("No {0} batches found").format(tab.label.toLowerCase()) }}
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
<template #default="{ tab }">
|
||||||
</div>
|
<div
|
||||||
</template>
|
v-if="tab.batches && tab.batches.value.length"
|
||||||
</Tabs>
|
class="grid grid-cols-1 sm:grid-cols-3 md:grid-cols-4 gap-5 mt-5 mx-5"
|
||||||
</div>
|
>
|
||||||
</div>
|
<router-link
|
||||||
|
v-for="batch in tab.batches.value"
|
||||||
|
:to="{ name: 'BatchDetail', params: { batchName: batch.name } }"
|
||||||
|
>
|
||||||
|
<BatchCard :batch="batch" />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="grid flex-1 place-items-center text-xl font-medium text-gray-500"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center justify-center mt-4">
|
||||||
|
<div>
|
||||||
|
{{ __('No {0} batches found').format(tab.label.toLowerCase()) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { createResource, Breadcrumbs, Button, Tabs, Badge } from "frappe-ui";
|
import { createResource, Breadcrumbs, Button, Tabs, Badge } from 'frappe-ui'
|
||||||
import { Plus } from "lucide-vue-next"
|
import { Plus } from 'lucide-vue-next'
|
||||||
import BatchCard from '@/components/BatchCard.vue';
|
import BatchCard from '@/components/BatchCard.vue'
|
||||||
import { inject, ref, computed } from "vue";
|
import { inject, ref, computed } from 'vue'
|
||||||
|
|
||||||
const user = inject("$user")
|
const user = inject('$user')
|
||||||
const batches = createResource({
|
const batches = createResource({
|
||||||
url: "lms.lms.utils.get_batches",
|
url: 'lms.lms.utils.get_batches',
|
||||||
cache: ["batches", user?.data?.email],
|
cache: ['batches', user?.data?.email],
|
||||||
auto: true,
|
auto: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
const tabIndex = ref(0)
|
const tabIndex = ref(0)
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
label: "Upcoming",
|
label: 'Upcoming',
|
||||||
batches: computed(() => batches.data?.upcoming || []),
|
batches: computed(() => batches.data?.upcoming || []),
|
||||||
count: computed(() => batches.data?.upcoming?.length),
|
count: computed(() => batches.data?.upcoming?.length),
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
if (user.data?.is_moderator) {
|
if (user.data?.is_moderator) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
label: "Archived",
|
label: 'Archived',
|
||||||
batches: computed(() => batches.data?.archived),
|
batches: computed(() => batches.data?.archived),
|
||||||
count: computed(() => batches.data?.archived?.length),
|
count: computed(() => batches.data?.archived?.length),
|
||||||
});
|
})
|
||||||
tabs.push({
|
tabs.push({
|
||||||
label: "Private",
|
label: 'Private',
|
||||||
batches: computed(() => batches.data?.private),
|
batches: computed(() => batches.data?.private),
|
||||||
count: computed(() => batches.data?.private?.length),
|
count: computed(() => batches.data?.private?.length),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (user.data) {
|
if (user.data) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
label: "Enrolled",
|
label: 'Enrolled',
|
||||||
batches: computed(() => batches.data?.enrolled),
|
batches: computed(() => batches.data?.enrolled),
|
||||||
count: computed(() => batches.data?.enrolled?.length)
|
count: computed(() => batches.data?.enrolled?.length),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
:items="[{ label: __('All Courses'), route: { name: 'Courses' } }]"
|
:items="[{ label: __('All Courses'), route: { name: 'Courses' } }]"
|
||||||
/>
|
/>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<Button variant="solid">
|
<Button v-if="user.data.is_moderator" variant="solid">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Plus class="h-4 w-4" />
|
<Plus class="h-4 w-4" />
|
||||||
</template>
|
</template>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="mx-5 py-5">
|
<div class="pb-5">
|
||||||
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
<Tabs v-model="tabIndex" :tabs="tabs" tablistClass="overflow-x-visible">
|
||||||
<template #tab="{ tab, selected }">
|
<template #tab="{ tab, selected }">
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
@@ -28,7 +28,11 @@
|
|||||||
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
||||||
{{ __(tab.label) }}
|
{{ __(tab.label) }}
|
||||||
<Badge
|
<Badge
|
||||||
:class="{ 'text-gray-900 border border-gray-900': selected }"
|
:class="
|
||||||
|
selected
|
||||||
|
? 'text-gray-800 border border-gray-800'
|
||||||
|
: 'border border-gray-500'
|
||||||
|
"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
theme="gray"
|
theme="gray"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -41,7 +45,7 @@
|
|||||||
<template #default="{ tab }">
|
<template #default="{ tab }">
|
||||||
<div
|
<div
|
||||||
v-if="tab.courses && tab.courses.value.length"
|
v-if="tab.courses && tab.courses.value.length"
|
||||||
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8 mt-5"
|
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8 mt-5 mx-5"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-for="course in tab.courses.value"
|
v-for="course in tab.courses.value"
|
||||||
|
|||||||
27
frontend/src/pages/JobDetail.vue
Normal file
27
frontend/src/pages/JobDetail.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<header
|
||||||
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||||
|
>
|
||||||
|
<Breadcrumbs
|
||||||
|
class="h-7"
|
||||||
|
:items="[{ label: __('Jobs'), route: { name: 'Jobs' } }]"
|
||||||
|
/>
|
||||||
|
<div class="flex">
|
||||||
|
<Button v-if="user.data?.name" variant="solid">
|
||||||
|
<template #prefix>
|
||||||
|
<Plus class="h-4 w-4" />
|
||||||
|
</template>
|
||||||
|
{{ __('New Job') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
job: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
48
frontend/src/pages/Jobs.vue
Normal file
48
frontend/src/pages/Jobs.vue
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-screen text-base">
|
||||||
|
<header
|
||||||
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||||
|
>
|
||||||
|
<Breadcrumbs
|
||||||
|
class="h-7"
|
||||||
|
:items="[{ label: __('Jobs'), route: { name: 'Jobs' } }]"
|
||||||
|
/>
|
||||||
|
<div class="flex">
|
||||||
|
<Button v-if="user.data?.name" variant="solid">
|
||||||
|
<template #prefix>
|
||||||
|
<Plus class="h-4 w-4" />
|
||||||
|
</template>
|
||||||
|
{{ __('New Job') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div v-if="jobs.data">
|
||||||
|
<div class="grid grid-cols-2 gap-5 p-5">
|
||||||
|
<div v-if="jobs.data.length" v-for="job in jobs.data">
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
|
name: 'JobDetail',
|
||||||
|
params: { job: job.name },
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<JobCard :job="job" />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Button, Breadcrumbs, createResource } from 'frappe-ui'
|
||||||
|
import { Plus } from 'lucide-vue-next'
|
||||||
|
import { inject } from 'vue'
|
||||||
|
import JobCard from '@/components/JobCard.vue'
|
||||||
|
|
||||||
|
const user = inject('$user')
|
||||||
|
|
||||||
|
const jobs = createResource({
|
||||||
|
url: 'lms.lms.utils.get_job_opportunities',
|
||||||
|
cache: ['jobs'],
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -54,6 +54,17 @@ const routes = [
|
|||||||
name: 'Statistics',
|
name: 'Statistics',
|
||||||
component: () => import('@/pages/Statistics.vue'),
|
component: () => import('@/pages/Statistics.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/job-openings',
|
||||||
|
name: 'Jobs',
|
||||||
|
component: () => import('@/pages/Jobs.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/job-openings/:job',
|
||||||
|
name: 'JobDetail',
|
||||||
|
component: () => import('@/pages/JobDetail.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
let router = createRouter({
|
let router = createRouter({
|
||||||
|
|||||||
@@ -1726,3 +1726,14 @@ def get_order_summary(doctype, docname, country=None):
|
|||||||
|
|
||||||
details.total_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
details.total_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
||||||
return details
|
return details
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist(allow_guest=True)
|
||||||
|
def get_job_opportunities():
|
||||||
|
jobs = frappe.get_all(
|
||||||
|
"Job Opportunity",
|
||||||
|
{"status": "Open", "disabled": False},
|
||||||
|
["job_title", "location", "type", "company_name", "company_logo", "name", "creation"],
|
||||||
|
order_by="creation desc",
|
||||||
|
)
|
||||||
|
return jobs
|
||||||
|
|||||||
Reference in New Issue
Block a user