fix: logout issue

This commit is contained in:
Jannat Patel
2023-12-14 14:32:50 +05:30
parent 4053984ca2
commit e7b6001e5f
15 changed files with 271 additions and 156 deletions

View File

@@ -22,7 +22,6 @@
<script setup>
import UserDropdown from '@/components/UserDropdown.vue'
import LMSLogo from '@/components/Icons/LMSLogo.vue'
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
import SidebarLink from '@/components/SidebarLink.vue'
import { useStorage } from '@vueuse/core'

View File

@@ -1,27 +1,44 @@
<template>
<div class="shadow rounded-md">
<div>
<div class="shadow rounded-md p-4 h-full" style="min-height: 150px;">
<div class="text-xl font-semibold mb-1">
{{ batch.title }}
</div>
<div>
<div class="short-introduction">
{{ batch.description }}
</div>
<div>
<Calendar class="h-4 w-4 stroke-1" />
{{ batch.start_date }} - {{ batch.end_date }}
</div>
<div>
<Clock class="h-4 w-4 stroke-1" />
{{ batch.start_time }} - {{ batch.end_time }}
<div class="mt-auto">
<div class="flex items-center mb-1">
<Calendar class="h-4 w-4 stroke-1 mr-2" />
{{ dayjs(batch.start_date).format("DD MMM YYYY") }} - {{ dayjs(batch.end_date).format("DD MMM YYYY") }}
</div>
<div class="flex items-center">
<Clock class="h-4 w-4 stroke-1 mr-2" />
{{ batch.start_time }} - {{ batch.end_time }}
</div>
</div>
</div>
</template>
<script setup>
import { Calendar, Clock } from "lucide-vue-next"
import { inject } from "vue"
const dayjs = inject("$dayjs")
const props = defineProps({
batch: {
type: Object,
default: null,
},
});
</script>
</script>
<style>
.short-introduction {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
width: 100%;
overflow: hidden;
margin: 0.25rem 0 1.25rem;
line-height: 1.5;
}
</style>

View File

@@ -69,8 +69,7 @@ import { computed } from 'vue'
import UserAvatar from '@/components/UserAvatar.vue'
import { sessionStore } from '@/stores/session'
const { isLoggedIn, getUser } = sessionStore()
const user = computed(() => isLoggedIn && getUser())
const { isLoggedIn, user } = sessionStore()
const props = defineProps({
course: {

View File

@@ -6,7 +6,7 @@
<div class="mt-4">
<Disclosure v-slot="{ open }" v-for="chapter in outline.data" :key="chapter.name">
<DisclosureButton
class="flex w-full px-2 py-4"
class="flex w-full px-2 pt-2 pb-2"
>
<ChevronUp
:class="open ? 'rotate-180 transform' : ''"
@@ -16,9 +16,9 @@
{{ chapter.title }}
</div>
</DisclosureButton>
<DisclosurePanel class="px-10 pb-4">
<DisclosurePanel class="px-10 pb-2">
<div v-for="lesson in chapter.lessons" :key="lesson.name">
<div class="flex items-center text-lg mb-2">
<div class="flex items-center text-lg mb-4">
<MonitorPlay v-if="lesson.icon === 'icon-youtube'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
<HelpCircle v-else-if="lesson.icon === 'icon-quiz'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
<FileText v-else-if="lesson.icon === 'icon-list'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>

View File

@@ -16,7 +16,7 @@
LMS
</div>
<div v-if="user" class="mt-1 text-sm text-gray-700 leading-none">
{{ user.full_name }}
{{ convertToTitleCase(user.split('@')[0]) }}
</div>
</div>
<div class="duration-300 ease-in-out" :class="isCollapsed
@@ -35,7 +35,6 @@ import LMSLogo from '@/components/Icons/LMSLogo.vue'
import { sessionStore } from '@/stores/session'
import { Dropdown } from 'frappe-ui'
import { ChevronDown } from 'lucide-vue-next'
import { computed } from 'vue'
const props = defineProps({
isCollapsed: {
@@ -44,9 +43,9 @@ const props = defineProps({
},
})
const { getUser, logout } = sessionStore()
const { logout, user } = sessionStore()
let { isLoggedIn } = sessionStore();
const user = computed(() => isLoggedIn && getUser())
const userDropdownOptions = [
{
icon: 'log-out',
@@ -70,5 +69,15 @@ const userDropdownOptions = [
return !isLoggedIn
}
}
]
];
function convertToTitleCase(str) {
if (!str) {
return ""
}
return str.toLowerCase().split(' ').map(function (word) {
return word.charAt(0).toUpperCase().concat(word.substr(1));
}).join(' ');
}
</script>

View File

@@ -4,21 +4,15 @@ import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
import { createPinia } from 'pinia'
import dayjs from '@/utils/dayjs'
import translationPlugin from './translation'
import { usersStore } from './stores/user'
import { sessionStore } from './stores/session'
import {
FrappeUI,
Button,
setConfig,
frappeRequest,
resourcesPlugin,
} from 'frappe-ui'
import { FrappeUI, setConfig, frappeRequest, resourcesPlugin } from 'frappe-ui'
// create a pinia instance
let pinia = createPinia()
let app = createApp(App)
setConfig('resourceFetcher', frappeRequest)
app.use(FrappeUI)
@@ -26,6 +20,16 @@ app.use(pinia)
app.use(router)
app.use(resourcesPlugin)
app.use(translationPlugin)
app.provide('$dayjs', dayjs)
app.component('Button', Button)
app.mount('#app')
const { userResource } = usersStore()
let { isLoggedIn } = sessionStore()
if (isLoggedIn) {
await userResource.reload()
}
app.provide('$user', userResource)
app.config.globalProperties.$user = userResource

View File

@@ -1,5 +1,5 @@
<template>
<div class="h-screen">
<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"
>
@@ -14,7 +14,7 @@
</div>
</header>
<div class="mx-5 my-10">
<div class="grid grid-cols-3 gap-8 mt-5">
<div class="grid grid-cols-4 gap-8 mt-5">
<BatchCard v-for="batch in batches.data" :batch="batch" />
</div>
</div>

View File

@@ -1,41 +1,28 @@
<template>
<div class="h-screen">
<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: __('All Courses'), route: { name: 'Courses' } }]"
/>
<div class="flex">
<Select class="mr-2"
:options="orderOptions"
v-model="orderBy"
/>
<Button variant="solid">
<template #prefix>
<Plus class="h-4 w-4" />
</template>
{{ __("New Course") }}
</Button>
</div>
</header>
<div class="mx-5 my-10">
<div v-if="courses.data" class="h-screen">
<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: __('All Courses'), route: { name: 'Courses' } }]" />
<div class="flex">
<Select class="mr-2" :options="orderOptions" v-model="orderBy" />
<Button variant="solid">
<template #prefix>
<Plus class="h-4 w-4" />
</template>
{{ __("New Course") }}
</Button>
</div>
</header>
<div class="mx-5 py-5">
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
<template #tab="{ tab, selected }">
<div>
<button
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"
:class="{ 'text-gray-900': selected }"
>
:class="{ 'text-gray-900': selected }">
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
{{ __(tab.label) }}
<Badge
:class="{ 'text-gray-900 border border-gray-900': selected }"
variant="subtle"
theme="gray"
size="sm"
>
<Badge :class="{ 'text-gray-900 border border-gray-900': selected }" variant="subtle" theme="gray"
size="sm">
{{ tab.count }}
</Badge>
</button>
@@ -43,7 +30,8 @@
</template>
<template #default="{ tab }">
<div v-if="tab.courses && tab.courses.value.length" class="grid grid-cols-3 gap-8 mt-5">
<router-link v-for="course in tab.courses.value" :to="{ name: 'CourseDetail', params: { courseName: course.name } }">
<router-link v-for="course in tab.courses.value"
:to="{ name: 'CourseDetail', params: { courseName: course.name } }">
<CourseCard :course="course" />
</router-link>
</div>
@@ -58,39 +46,34 @@
</Tabs>
</div>
</div>
</template>
<script setup>
import { sessionStore } from '@/stores/session'
import { createListResource, Breadcrumbs, Tabs, Badge, Select } from 'frappe-ui';
import { createListResource, Breadcrumbs, Tabs, Badge, Select, Button } from 'frappe-ui';
import CourseCard from '@/components/CourseCard.vue';
import { Plus } from 'lucide-vue-next'
import { ref, computed } from 'vue'
import { ref, computed, inject } from 'vue'
const user = inject("$user")
const { isLoggedIn, getUser } = sessionStore()
const user = computed(() => isLoggedIn && getUser())
console.log(user)
const courses = createListResource({
type: 'list',
cache: ["courses", user.email],
doctype: 'LMS Course',
cache: ["courses", user?.data?.email],
url: "lms.lms.utils.get_courses",
auto: true,
});
const is_moderator = computed(() => {
if (user && user.value?.roles?.includes('Moderator')) {
if (user.data?.roles?.includes('Moderator')) {
return true;
}
return false;
});
const is_instructor = computed(() => {
if (user && user.value?.roles?.includes('Course Creator')) {
return true;
}
return false;
return user.data.roles.includes("Course Creator") ? true : false;
});
const tabIndex = ref(0)
@@ -106,8 +89,8 @@ const tabs = [
count: computed(() => courses.data?.upcoming?.length),
}
];
if (user.value) {
console.log(user.data)
if (user.data) {
tabs.push({
label: 'Enrolled',
courses: computed(() => courses.data?.enrolled),
@@ -151,19 +134,4 @@ const orderOptions = [
},
];
const orderBy = 'enrollment';
function sort_courses(order) {
const categories = ['live', 'upcoming', 'enrolled', 'created', 'under_review'];
categories.forEach(category => {
courses.data[category] = courses.data[category].sort((a, b) => {
if (order === 'enrollment') {
return b.enrollment_count - a.enrollment_count;
} else if (order === 'rating') {
return b.avg_rating - a.avg_rating;
} else if (order === 'newest') {
return new Date(b.creation).getTime() - new Date(a.creation).getTime();
}
});
});
}
</script>

View File

@@ -3,7 +3,9 @@
</template>
<script setup>
import { createResource, Button } from "frappe-ui";
import { useRoute } from "vue-router";
const route = useRoute();
console.log(route)
const props = defineProps({
courseName: {
type: String,
@@ -14,7 +16,7 @@ const props = defineProps({
required: true,
},
});
/*
const lesson = createResource({
url: "lms.lms.utils.get_lesson",
cache: ["lesson", props.courseName, props.lessonNumber],
@@ -23,5 +25,5 @@ const lesson = createResource({
lesson: props.lessonNumber,
},
auto: true,
});
}); */
</script>

View File

@@ -1,4 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import { usersStore } from './stores/user'
import { sessionStore } from './stores/session'
const routes = [
{
@@ -19,10 +21,10 @@ const routes = [
},
{
// Create a route for path /courses/inventory-management/learn/1.1
path: '/courses/:courseName/learn/:chapterId',
path: '/courses/:courseName/learn/:lessonNumber',
name: 'Lesson',
component: () => import('@/pages/Lesson.vue'),
props: true,
props: {},
},
{
path: '/batches',
@@ -36,4 +38,18 @@ let router = createRouter({
routes,
})
router.beforeEach(async (to, from, next) => {
const { userResource } = usersStore()
let { isLoggedIn } = sessionStore()
try {
if (isLoggedIn) {
await userResource.reload()
}
} catch (error) {
isLoggedIn = false
}
return next()
})
export default router

View File

@@ -5,9 +5,9 @@ import router from '@/router'
import { ref, computed } from 'vue'
export const sessionStore = defineStore('lms-session', () => {
const { user, usersByName } = usersStore()
let { userResource } = usersStore()
function currentUser() {
function sessionUser() {
let cookies = new URLSearchParams(document.cookie.split('; ').join('&'))
let _sessionUser = cookies.get('user_id')
if (_sessionUser === 'Guest') {
@@ -16,18 +16,8 @@ export const sessionStore = defineStore('lms-session', () => {
return _sessionUser
}
let sessionUser = ref(currentUser())
const isLoggedIn = ref(!!sessionUser.value)
function getUser() {
if (!sessionUser.value) {
return null
}
if (usersByName[sessionUser.value]) {
return usersByName[sessionUser.value]
}
return user.value
}
let user = ref(sessionUser())
const isLoggedIn = computed(() => !!user.value)
const login = createResource({
url: 'login',
@@ -35,8 +25,8 @@ export const sessionStore = defineStore('lms-session', () => {
throw new Error('Invalid email or password')
},
onSuccess() {
user.reload()
sessionUser.value = currentUser()
userResource.reload()
user.value = sessionUser()
login.reset()
router.replace({ path: '/' })
},
@@ -45,16 +35,16 @@ export const sessionStore = defineStore('lms-session', () => {
const logout = createResource({
url: 'logout',
onSuccess() {
user.reset()
sessionUser.value = null
userResource.reset()
user.value = null
window.location.reload()
},
})
return {
sessionUser,
user,
isLoggedIn,
login,
logout,
getUser,
}
})

View File

@@ -1,20 +1,9 @@
import { defineStore } from 'pinia'
import { createResource } from 'frappe-ui'
import { reactive } from 'vue'
export const usersStore = defineStore('lms-users', () => {
let usersByName = reactive({})
const user = createResource({
let userResource = createResource({
url: 'lms.lms.api.get_user_info',
cache: 'Users',
initialData: [],
auto: true,
transform: (data) => {
if (data?.name && !usersByName[data.name]) {
usersByName[data.name] = data
}
},
onError(error) {
if (error && error.exc_type === 'AuthenticationError') {
router.push('/login')
@@ -23,7 +12,6 @@ export const usersStore = defineStore('lms-users', () => {
})
return {
user,
usersByName,
userResource,
}
})

View File

@@ -0,0 +1,12 @@
import dayjs from 'dayjs/esm'
import relativeTime from 'dayjs/esm/plugin/relativeTime'
import localizedFormat from 'dayjs/esm/plugin/localizedFormat'
import updateLocale from 'dayjs/esm/plugin/updateLocale'
import isToday from 'dayjs/esm/plugin/isToday'
dayjs.extend(updateLocale)
dayjs.extend(relativeTime)
dayjs.extend(localizedFormat)
dayjs.extend(isToday)
export default dayjs