parent
acb6d36638
commit
0ace78c403
@ -0,0 +1,7 @@ |
||||
.p-skeleton { |
||||
@apply bg-gray-30 rounded-lg w-full; |
||||
|
||||
&:after { |
||||
@apply bg-gradient-to-r from-transparent via-gray-15 to-transparent; |
||||
} |
||||
} |
@ -0,0 +1,21 @@ |
||||
@layer components { |
||||
.empty-state { |
||||
@apply border border-solid border-support-1 bg-gray-10 flex flex-auto justify-center items-center p-10 rounded-lg; |
||||
|
||||
&__container { |
||||
@apply flex flex-col items-center text-center w-60; |
||||
} |
||||
|
||||
&__icon { |
||||
@apply mb-4 text-9xl text-transparent bg-clip-text bg-gradient-to-br from-primary to-primary-gradient w-32 h-32; |
||||
} |
||||
|
||||
&__summary { |
||||
@apply text-body-2-bold text-gray-90; |
||||
} |
||||
|
||||
&__detail { |
||||
@apply text-body-2 text-gray-90; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
<template> |
||||
<div |
||||
class="empty-state" |
||||
> |
||||
<div class="empty-state__container"> |
||||
<span |
||||
aria-hidden="true" |
||||
class="empty-state__icon" |
||||
:class="icon" |
||||
/> |
||||
<p |
||||
class="empty-state__summary" |
||||
v-text="summary" |
||||
/> |
||||
<p |
||||
class="empty-state__detail" |
||||
v-text="detail" |
||||
/> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup> |
||||
// eslint-disable-next-line no-undef |
||||
defineProps({ |
||||
summary: { |
||||
type: String, |
||||
default: '', |
||||
required: true, |
||||
}, |
||||
detail: { |
||||
type: String, |
||||
default: '', |
||||
required: false, |
||||
}, |
||||
icon: { |
||||
type: String, |
||||
default: '', |
||||
required: false, |
||||
}, |
||||
}); |
||||
</script> |
@ -1,13 +1,30 @@ |
||||
<template> |
||||
<q-layout> |
||||
<!-- <q-tabs align="left" dense inline-label no-caps>--> |
||||
<!-- <q-route-tab to="/courses" label="My courses" />--> |
||||
<!-- <q-route-tab to="/sessions" label="My sessions" />--> |
||||
<!-- </q-tabs>--> |
||||
|
||||
<!-- this is where the Pages are injected --> |
||||
<q-page-container> |
||||
<router-view></router-view> |
||||
</q-page-container> |
||||
</q-layout> |
||||
<div class="flex justify-between items-center"> |
||||
<h2 v-t="'My Courses'" /> |
||||
<Button |
||||
v-if="isTeacher" |
||||
class="p-button-secondary md:hidden" |
||||
icon="pi pi-plus" |
||||
/> |
||||
<Button |
||||
v-if="isTeacher" |
||||
:label="t('Course')" |
||||
class="p-button-secondary hidden md:inline-flex" |
||||
icon="pi pi-plus" |
||||
/> |
||||
</div> |
||||
<hr> |
||||
<router-view /> |
||||
</template> |
||||
|
||||
<script setup> |
||||
import Button from 'primevue/button'; |
||||
import { computed } from 'vue'; |
||||
import { useStore } from 'vuex'; |
||||
import { useI18n } from 'vue-i18n'; |
||||
|
||||
const store = useStore(); |
||||
const { t } = useI18n(); |
||||
|
||||
const isTeacher = computed(() => store.getters['security/hasRole']('ROLE_TEACHER')); |
||||
</script> |
||||
|
@ -1,71 +1,75 @@ |
||||
<template> |
||||
<StickyCourses /> |
||||
<!-- {{ loading }}--> |
||||
|
||||
<div |
||||
v-if="courses.length" |
||||
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 mt-2"> |
||||
<CourseCardList |
||||
:courses="courses" |
||||
v-if="isLoading" |
||||
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" |
||||
> |
||||
<Skeleton |
||||
height="16rem" |
||||
/> |
||||
</div> |
||||
<div v-else> |
||||
<div class="bg-gradient-to-r from-gray-100 to-gray-50 flex flex-col rounded-md text-center p-2"> |
||||
<div class="p-10 text-center"> |
||||
<div> |
||||
<v-icon |
||||
icon="mdi-book-open-page-variant" |
||||
size="72px" |
||||
class="font-extrabold text-transparent bg-clip-text bg-gradient-to-br from-primary to-primary-gradient" |
||||
<Skeleton |
||||
class="hidden md:block" |
||||
height="16rem" |
||||
/> |
||||
<Skeleton |
||||
class="hidden lg:block" |
||||
height="16rem" |
||||
/> |
||||
<Skeleton |
||||
class="hidden xl:block" |
||||
height="16rem" |
||||
/> |
||||
</div> |
||||
|
||||
<div class="mt-2 font-bold"> |
||||
{{ $t("You don't have any course yet.") }} |
||||
</div> |
||||
<div> |
||||
{{ $t('Go to "Explore" to find a topic of interest, or wait for someone to subscribe you.') }} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div |
||||
v-if="!isLoading && courses.length > 0" |
||||
class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" |
||||
> |
||||
<CourseCardList |
||||
:courses="courses" |
||||
/> |
||||
</div> |
||||
<EmptyState |
||||
v-else-if="!isLoading && 0 === courses.length" |
||||
:detail="t('Go to Explore to find a topic of interest, or wait for someone to subscribe you')" |
||||
:summary="t('You don\'t have any course yet')" |
||||
icon="mdi mdi-book-open-page-variant" |
||||
/> |
||||
</template> |
||||
|
||||
<script> |
||||
import CourseCardList from '../../../components/course/CourseCardList.vue'; |
||||
import {computed} from "vue"; |
||||
<script setup> |
||||
import { computed, ref } from 'vue'; |
||||
import { useStore } from 'vuex'; |
||||
import {useQuery, useResult} from '@vue/apollo-composable' |
||||
import {GET_COURSE_REL_USER} from "../../../graphql/queries/CourseRelUser.js"; |
||||
import { useQuery } from '@vue/apollo-composable'; |
||||
import { useI18n } from 'vue-i18n'; |
||||
import { GET_COURSE_REL_USER } from '../../../graphql/queries/CourseRelUser.js'; |
||||
import Skeleton from 'primevue/skeleton'; |
||||
import StickyCourses from '../../../views/user/courses/StickyCourses.vue'; |
||||
import CourseCardList from '../../../components/course/CourseCardList.vue'; |
||||
import EmptyState from '../../../components/EmptyState'; |
||||
|
||||
export default { |
||||
name: 'CourseList', |
||||
components: { |
||||
StickyCourses, |
||||
CourseCardList, |
||||
}, |
||||
setup() { |
||||
const store = useStore(); |
||||
const { t } = useI18n(); |
||||
|
||||
let user = computed(() => store.getters['security/getUser']); |
||||
let courses = ref([]); |
||||
let isLoading = ref(true); |
||||
|
||||
if (user.value) { |
||||
let userId = user.value.id; |
||||
|
||||
const {result, loading, error} = useQuery(GET_COURSE_REL_USER, { |
||||
user: "/api/users/" + userId |
||||
}); |
||||
const { result, loading } = useQuery( |
||||
GET_COURSE_REL_USER, |
||||
{ |
||||
user: user.value['@id'], |
||||
} |
||||
); |
||||
|
||||
const courses = useResult(result, [], (data) => { |
||||
return data.courseRelUsers.edges.map(function (edge) { |
||||
return edge.node.course; |
||||
}); |
||||
}); |
||||
isLoading = computed( |
||||
() => loading.value |
||||
); |
||||
|
||||
return { |
||||
courses, |
||||
loading |
||||
} |
||||
} |
||||
courses = computed( |
||||
() => result.value?.courseRelUsers.edges.map(({ node }) => node.course) ?? [] |
||||
); |
||||
} |
||||
}; |
||||
</script> |
||||
|
Loading…
Reference in new issue