Initial commit

This commit is contained in:
2025-08-04 16:33:07 +03:30
commit f798e8e35c
9595 changed files with 1208683 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
<script setup>
const assignmentData = [
{
title: 'User Experience Design',
tasks: 120,
progress: 72,
color: 'primary',
},
{
title: 'Basic fundamentals',
tasks: 32,
progress: 48,
color: 'success',
},
{
title: 'React Native components',
tasks: 182,
progress: 15,
color: 'error',
},
{
title: 'Basic of music theory',
tasks: 56,
progress: 24,
color: 'info',
},
]
</script>
<template>
<VCard>
<VCardItem title="Assignment progress">
<template #append>
<MoreBtn />
</template>
</VCardItem>
<VCardText>
<VList class="card-list">
<VListItem
v-for="assignment in assignmentData"
:key="assignment.title"
>
<template #prepend>
<VProgressCircular
v-model="assignment.progress"
:size="54"
class="me-4"
:color="assignment.color"
>
<span class="text-body-1 text-high-emphasis font-weight-medium">
{{ assignment.progress }}%
</span>
</VProgressCircular>
</template>
<VListItemTitle class="font-weight-medium mb-2 me-2">
{{ assignment.title }}
</VListItemTitle>
<VListItemSubtitle class="me-2">
{{ assignment.tasks }} Tasks
</VListItemSubtitle>
<template #append>
<VBtn
icon
variant="tonal"
color="secondary"
rounded
size="30"
>
<VIcon
icon="tabler-chevron-right"
size="20"
class="flip-in-rtl"
/>
</VBtn>
</template>
</VListItem>
</VList>
</VCardText>
</VCard>
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: 2rem;
}
</style>

View File

@@ -0,0 +1,64 @@
<script setup>
import avatar1 from '@images/avatars/avatar-1.png'
import avatar2 from '@images/avatars/avatar-2.png'
import avatar3 from '@images/avatars/avatar-3.png'
import avatar4 from '@images/avatars/avatar-4.png'
</script>
<template>
<VCard>
<VCardItem title="Popular Instructors">
<template #append>
<MoreBtn />
</template>
</VCardItem>
<VDivider />
<div class="d-flex justify-space-between py-4 px-6">
<div class="text-body-1 text-uppercase">
instructors
</div>
<div class="text-body-1 text-uppercase">
Courses
</div>
</div>
<VDivider />
<VCardText>
<VList class="card-list">
<VListItem
v-for="instructor in [
{ name: 'Jordan Stevenson', profession: 'Business Intelligence', totalCourses: 33, avatar: avatar1 },
{ name: 'Bentlee Emblin', profession: 'Digital Marketing', totalCourses: 52, avatar: avatar2 },
{ name: 'Benedetto Rossiter', profession: 'UI/UX Design', totalCourses: 12, avatar: avatar3 },
{ name: 'Beverlie Krabbe', profession: 'Vue', totalCourses: 8, avatar: avatar4 },
]"
:key="instructor.name"
>
<template #prepend>
<VAvatar
size="34"
:image="instructor.avatar"
/>
</template>
<VListItemTitle class="font-weight-medium">
{{ instructor.name }}
</VListItemTitle>
<VListItemSubtitle>
{{ instructor.profession }}
</VListItemSubtitle>
<template #append>
<h6 class="text-h6">
{{ instructor.totalCourses }}
</h6>
</template>
</VListItem>
</VList>
</VCardText>
</VCard>
</template>
<style lang="scss">
.card-list {
--v-card-list-gap: 1rem;
}
</style>

View File

@@ -0,0 +1,84 @@
<script setup>
const coursesData = [
{
title: 'Videography Basic Design Course',
views: '1.2k',
icon: 'tabler-brand-zoom',
color: 'primary',
},
{
title: 'Basic Front-end Development Course',
views: '834',
icon: 'tabler-code',
color: 'info',
},
{
title: 'Basic Fundamentals of Photography',
views: '3.7k',
icon: 'tabler-camera',
color: 'success',
},
{
title: 'Advance Dribble Base Visual Design',
views: '2.5k',
icon: 'tabler-brand-dribbble',
color: 'warning',
},
{
title: 'Your First Singing Lesson',
views: '948',
icon: 'tabler-microphone-2',
color: 'error',
},
]
</script>
<template>
<VCard>
<VCardItem title="Top Courses">
<template #append>
<MoreBtn />
</template>
</VCardItem>
<VCardText>
<VList class="card-list">
<VListItem
v-for="(course, index) in coursesData"
:key="index"
>
<template #prepend>
<VAvatar
rounded
variant="tonal"
:color="course.color"
>
<VIcon
:icon="course.icon"
size="24"
/>
</VAvatar>
</template>
<VListItemTitle class="me-4">
<div class="d-flex flex-column">
<h6 class="text-h6 text-truncate">
{{ course.title }}
</h6>
<div>
<VChip
variant="tonal"
color="secondary"
label
size="small"
>
{{ course.views }} Views
</VChip>
</div>
</div>
</VListItemTitle>
</VListItem>
</VList>
</VCardText>
</VCard>
</template>

View File

@@ -0,0 +1,192 @@
<script setup>
const searchQuery = ref('')
// Data table options
const itemsPerPage = ref(5)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const headers = [
{
title: 'Course Name',
key: 'courseName',
},
{
title: 'Time',
key: 'time',
sortable: false,
},
{
title: 'Progress',
key: 'progress',
},
{
title: 'Status',
key: 'status',
sortable: false,
},
]
const { data: courseData } = await useApi(createUrl('/apps/academy/courses', {
query: {
q: searchQuery,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const courses = computed(() => courseData.value.courses)
const totalCourse = computed(() => courseData.value.total)
</script>
<template>
<VCard>
<VCardText>
<div class="d-flex flex-wrap justify-space-between align-center gap-4">
<h5 class="text-h5 font-weight-medium">
Courses you are taking
</h5>
<div>
<AppTextField
v-model="searchQuery"
placeholder="Search Course"
style="max-inline-size: 300px;min-inline-size: 300px;"
/>
</div>
</div>
</VCardText>
<VDivider />
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:items-per-page-options="[
{ value: 5, title: '5' },
{ value: 10, title: '10' },
{ value: 20, title: '20' },
{ value: -1, title: '$vuetify.dataFooter.itemsPerPageAll' },
]"
:headers="headers"
:items="courses"
:items-length="totalCourse"
show-select
class="text-no-wrap"
@update:options="updateOptions"
>
<!-- Course Name -->
<template #item.courseName="{ item }">
<div class="d-flex align-center gap-x-4 py-2">
<VAvatar
variant="tonal"
size="40"
rounded
:color="item.color"
>
<VIcon
:icon="item.logo"
size="28"
/>
</VAvatar>
<div>
<div class="text-base font-weight-medium mb-1">
<RouterLink
:to="{ name: 'apps-academy-course-details' }"
class="text-link d-inline-block"
>
{{ item.courseTitle }}
</RouterLink>
</div>
<div class="d-flex align-center">
<VAvatar
size="22"
:image="item.image"
/>
<div class="text-body-2 text-high-emphasis ms-2">
{{ item.user }}
</div>
</div>
</div>
</div>
</template>
<template #item.time="{ item }">
<h6 class="text-h6">
{{ item.time }}
</h6>
</template>
<!-- Progress -->
<template #item.progress="{ item }">
<div
class="d-flex align-center gap-x-4"
style="inline-size: 15.625rem;"
>
<div class="text-no-wrap font-weight-medium text-high-emphasis">
{{ Math.floor((item.completedTasks / item.totalTasks) * 100) }}%
</div>
<div class="w-100">
<VProgressLinear
color="primary"
height="8"
:model-value="Math.floor((item.completedTasks / item.totalTasks) * 100)"
rounded
/>
</div>
<div class="text-body-2">
{{ item.completedTasks }}/{{ item.totalTasks }}
</div>
</div>
</template>
<!-- Status -->
<template #item.status="{ item }">
<div class="d-flex gap-x-5">
<div class="d-flex gap-x-2 align-center">
<VIcon
icon="tabler-users"
color="primary"
size="24"
/>
<div class="text-body-1">
{{ item.userCount }}
</div>
</div>
<div class="d-flex gap-x-2 align-center">
<VIcon
icon="tabler-book"
color="info"
size="24"
/>
<span class="text-body-1">{{ item.note }}</span>
</div>
<div class="d-flex gap-x-2 align-center">
<VIcon
icon="tabler-brand-zoom"
color="error"
size="24"
/>
<span class="text-body-1">{{ item.view }}</span>
</div>
</div>
</template>
<!-- Pagination -->
<template #bottom>
<TablePagination
v-model:page="page"
:items-per-page="itemsPerPage"
:total-items="totalCourse"
/>
</template>
</VDataTableServer>
</VCard>
</template>

View File

@@ -0,0 +1,239 @@
<script setup>
const props = defineProps({
searchQuery: {
type: String,
required: true,
},
})
const itemsPerPage = ref(6)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const hideCompleted = ref(true)
const label = ref('All Courses')
const { data: coursesData } = await useApi(createUrl('/apps/academy/courses', {
query: {
q: () => props.searchQuery,
hideCompleted,
label,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const courses = computed(() => coursesData.value.courses)
const totalCourse = computed(() => coursesData.value.total)
watch([
hideCompleted,
label,
() => props.searchQuery,
], () => {
page.value = 1
})
const resolveChipColor = tags => {
if (tags === 'Web')
return 'primary'
if (tags === 'Art')
return 'success'
if (tags === 'UI/UX')
return 'error'
if (tags === 'Psychology')
return 'warning'
if (tags === 'Design')
return 'info'
}
</script>
<template>
<VCard class="mb-6">
<VCardText>
<!-- 👉 Header -->
<div class="d-flex justify-space-between align-center flex-wrap gap-4 mb-6">
<div>
<h5 class="text-h5">
My Courses
</h5>
<div class="text-body-1">
Total 6 course you have purchased
</div>
</div>
<div class="d-flex flex-wrap gap-x-6 gap-y-4 align-center">
<AppSelect
v-model="label"
:items="[
{ title: 'Web', value: 'web' },
{ title: 'Art', value: 'art' },
{ title: 'UI/UX', value: 'ui/ux' },
{ title: 'Psychology', value: 'psychology' },
{ title: 'Design', value: 'design' },
{ title: 'All Courses', value: 'All Courses' },
]"
style="min-inline-size: 260px;"
/>
<VSwitch
v-model="hideCompleted"
label="Hide Completed"
/>
</div>
</div>
<!-- 👉 Course List -->
<div
v-if="courses.length"
class="mb-6"
>
<VRow>
<template
v-for="course in courses"
:key="course.id"
>
<VCol
cols="12"
md="4"
sm="6"
>
<VCard
flat
border
>
<div class="px-2 pt-2">
<VImg
:src="course.tutorImg"
class="cursor-pointer"
@click="() => $router.push({ name: 'apps-academy-course-details' })"
/>
</div>
<VCardText>
<div class="d-flex justify-space-between align-center mb-4">
<VChip
variant="tonal"
:color="resolveChipColor(course.tags)"
size="small"
>
{{ course.tags }}
</VChip>
<div class="d-flex">
<h6 class="text-h6 text-medium-emphasis align-center me-1">
{{ course.rating }}
</h6>
<VIcon
icon="tabler-star-filled"
color="warning"
size="24"
class="me-2"
/>
<div class="text-body-1">
({{ course.ratingCount }})
</div>
</div>
</div>
<h5 class="text-h5 mb-1">
<RouterLink
:to="{ name: 'apps-academy-course-details' }"
class="course-title"
>
{{ course.courseTitle }}
</RouterLink>
</h5>
<p>
{{ course.desc }}
</p>
<div
v-if="course.completedTasks !== course.totalTasks"
class="d-flex align-center mb-1"
>
<VIcon
icon="tabler-clock"
size="20"
class="me-1"
/>
<span class="text-body-1 my-auto"> {{ course.time }}</span>
</div>
<div
v-else
class="mb-1"
>
<VIcon
icon="tabler-check"
size="20"
color="success"
class="me-1"
/>
<span class="text-success text-body-1">Completed</span>
</div>
<VProgressLinear
:model-value="(course.completedTasks / course.totalTasks) * 100"
rounded
color="primary"
height="8"
class="mb-4"
/>
<div class="d-flex flex-wrap gap-4">
<VBtn
variant="tonal"
color="secondary"
class="flex-grow-1"
:to="{ name: 'apps-academy-course-details' }"
>
<template #prepend>
<VIcon
icon="tabler-rotate-clockwise-2"
class="flip-in-rtl"
/>
</template>
Start Over
</VBtn>
<VBtn
v-if="course.completedTasks !== course.totalTasks"
variant="tonal"
class="flex-grow-1"
:to="{ name: 'apps-academy-course-details' }"
>
<template #append>
<VIcon
icon="tabler-chevron-right"
class="flip-in-rtl"
/>
</template>
Continue
</VBtn>
</div>
</VCardText>
</VCard>
</VCol>
</template>
</VRow>
</div>
<div v-else>
<h4 class="text-h4 text-center mb-6">
No Course Found
</h4>
</div>
<VPagination
v-model="page"
active-color="primary"
first-icon="tabler-chevrons-left"
last-icon="tabler-chevrons-right"
show-first-last-page
:length="Math.ceil(totalCourse / itemsPerPage)"
/>
</VCardText>
</VCard>
</template>
<style lang="scss" scoped>
.course-title {
&:not(:hover) {
color: rgba(var(--v-theme-on-surface), var(--v-text-high-emphasis));
}
}
</style>

View File

@@ -0,0 +1,203 @@
<script setup>
const borderColor = 'rgba(var(--v-border-color), var(--v-border-opacity))'
// Topics Charts config
const topicsChartConfig = {
chart: {
height: 270,
type: 'bar',
toolbar: { show: false },
},
plotOptions: {
bar: {
horizontal: true,
barHeight: '70%',
distributed: true,
borderRadius: 7,
borderRadiusApplication: 'end',
},
},
colors: [
'rgba(var(--v-theme-primary),1)',
'rgba(var(--v-theme-info),1)',
'rgba(var(--v-theme-success),1)',
'rgba(var(--v-theme-secondary),1)',
'rgba(var(--v-theme-error),1)',
'rgba(var(--v-theme-warning),1)',
],
grid: {
borderColor,
strokeDashArray: 10,
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
padding: {
top: -35,
bottom: -12,
},
},
dataLabels: {
enabled: true,
style: {
colors: ['#fff'],
fontWeight: 200,
fontSize: '13px',
},
offsetX: 0,
dropShadow: { enabled: false },
formatter(val, opt) {
return topicsChartConfig.labels[opt.dataPointIndex]
},
},
labels: [
'UI Design',
'UX Design',
'Music',
'Animation',
'Vue',
'SEO',
],
xaxis: {
categories: [
'6',
'5',
'4',
'3',
'2',
'1',
],
axisBorder: { show: false },
axisTicks: { show: false },
labels: {
style: {
colors: 'rgba(var(--v-theme-on-background), var(--v-disabled-opacity))',
fontSize: '13px',
},
formatter(val) {
return `${ val }%`
},
},
},
yaxis: {
max: 35,
labels: {
style: {
colors: 'rgba(var(--v-theme-on-background), var(--v-disabled-opacity))',
fontSize: '13px',
},
},
},
tooltip: {
enabled: true,
style: { fontSize: '12px' },
onDatasetHover: { highlightDataSeries: false },
},
legend: { show: false },
}
const topicsChartSeries = [{
data: [
35,
20,
14,
12,
10,
9,
],
}]
const topicsData = [
{
title: 'UI Design',
value: 35,
color: 'primary',
},
{
title: 'UX Design',
value: 20,
color: 'info',
},
{
title: 'Music',
value: 14,
color: 'success',
},
{
title: 'Animation',
value: 12,
color: 'secondary',
},
{
title: 'Vue',
value: 10,
color: 'error',
},
{
title: 'SEO',
value: 9,
color: 'warning',
},
]
</script>
<template>
<VCard>
<VCardItem title="Topic you are interested in">
<template #append>
<MoreBtn />
</template>
</VCardItem>
<VCardText>
<VRow>
<VCol
cols="12"
md="6"
xl="8"
lg="7"
>
<div>
<VueApexCharts
type="bar"
height="260"
:options="topicsChartConfig"
:series="topicsChartSeries"
/>
</div>
</VCol>
<VCol
cols="12"
md="6"
lg="5"
xl="4"
>
<div class="topic-progress d-flex flex-wrap gap-x-6 gap-y-10 ms-auto">
<div
v-for="topic in topicsData"
:key="topic.title"
class="d-flex gap-x-2"
>
<VBadge
dot
inline
class="mt-1 custom-badge"
:color="topic.color"
/>
<div>
<div
class="text-body-1"
style="min-inline-size: 90px;"
>
{{ topic.title }}
</div>
<h5 class="text-h5">
{{ topic.value }}%
</h5>
</div>
</div>
</div>
</VCol>
</VRow>
</VCardText>
</VCard>
</template>

View File

@@ -0,0 +1,54 @@
<script setup>
import girlWithLaptop from '@images/illustrations/laptop-girl.png'
</script>
<template>
<VCard>
<VCardText>
<div class="d-flex justify-center align-start pb-0 px-3 pt-3 mb-4 bg-light-primary rounded">
<VImg
:src="girlWithLaptop"
width="145"
height="140"
/>
</div>
<div>
<h5 class="text-h5 mb-2">
Upcoming Webinar
</h5>
<div class="text-body-2">
Next Generation Frontend Architecture Using Layout Engine And Vue.
</div>
<div class="d-flex justify-space-between my-4 flex-wrap gap-4">
<div
v-for="{ icon, title, value } in [{ icon: 'tabler-calendar', title: '17 Nov 23', value: 'Date' }, { icon: 'tabler-clock', title: '32 Minutes', value: 'Duration' }]"
:key="title"
class="d-flex gap-x-3 align-center"
>
<VAvatar
color="primary"
variant="tonal"
rounded
>
<VIcon
:icon="icon"
size="28"
/>
</VAvatar>
<div>
<h6 class="text-h6">
{{ title }}
</h6>
<div class="text-sm">
{{ value }}
</div>
</div>
</div>
</div>
<VBtn block>
Join the event
</VBtn>
</div>
</VCardText>
</VCard>
</template>