904 lines
34 KiB
TypeScript
904 lines
34 KiB
TypeScript
import * as icon from '@mdi/js';
|
|
import Head from 'next/head';
|
|
import React from 'react';
|
|
import axios from 'axios';
|
|
import type { ReactElement } from 'react';
|
|
import LayoutAuthenticated from '../layouts/Authenticated';
|
|
import SectionMain from '../components/SectionMain';
|
|
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
|
|
import BaseIcon from '../components/BaseIcon';
|
|
import { getPageTitle } from '../config';
|
|
import Link from 'next/link';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
|
import { hasPermission } from '../helpers/userPermissions';
|
|
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
|
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
|
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
|
|
|
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
|
const Dashboard = () => {
|
|
const { t } = useTranslation('common');
|
|
const dispatch = useAppDispatch();
|
|
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
|
const corners = useAppSelector((state) => state.style.corners);
|
|
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
|
|
|
const loadingMessage = t('pages.dashboard.loading', {
|
|
defaultValue: 'Loading...',
|
|
});
|
|
|
|
const [roles, setRoles] = React.useState(loadingMessage);
|
|
const [permissions, setPermissions] = React.useState(loadingMessage);
|
|
const [courses, setCourses] = React.useState(loadingMessage);
|
|
const [course_sections, setCourse_sections] = React.useState(loadingMessage);
|
|
const [lessons, setLessons] = React.useState(loadingMessage);
|
|
const [enrollments, setEnrollments] = React.useState(loadingMessage);
|
|
const [lesson_progress, setLesson_progress] = React.useState(loadingMessage);
|
|
const [course_reviews, setCourse_reviews] = React.useState(loadingMessage);
|
|
const [course_announcements, setCourse_announcements] =
|
|
React.useState(loadingMessage);
|
|
const [quizzes, setQuizzes] = React.useState(loadingMessage);
|
|
const [quiz_questions, setQuiz_questions] = React.useState(loadingMessage);
|
|
const [quiz_options, setQuiz_options] = React.useState(loadingMessage);
|
|
const [quiz_attempts, setQuiz_attempts] = React.useState(loadingMessage);
|
|
const [assignment_submissions, setAssignment_submissions] =
|
|
React.useState(loadingMessage);
|
|
const [marketing_pages, setMarketing_pages] = React.useState(loadingMessage);
|
|
const [contact_messages, setContact_messages] =
|
|
React.useState(loadingMessage);
|
|
const [pricing_plans, setPricing_plans] = React.useState(loadingMessage);
|
|
const [plan_features, setPlan_features] = React.useState(loadingMessage);
|
|
const [users, setUsers] = React.useState(loadingMessage);
|
|
|
|
const [widgetsRole, setWidgetsRole] = React.useState({
|
|
role: { value: '', label: '' },
|
|
});
|
|
const { currentUser } = useAppSelector((state) => state.auth);
|
|
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
|
|
|
|
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
|
|
|
|
async function loadData() {
|
|
const entities = [
|
|
'roles',
|
|
'permissions',
|
|
'courses',
|
|
'course_sections',
|
|
'lessons',
|
|
'enrollments',
|
|
'lesson_progress',
|
|
'course_reviews',
|
|
'course_announcements',
|
|
'quizzes',
|
|
'quiz_questions',
|
|
'quiz_options',
|
|
'quiz_attempts',
|
|
'assignment_submissions',
|
|
'marketing_pages',
|
|
'contact_messages',
|
|
'pricing_plans',
|
|
'plan_features',
|
|
'users',
|
|
];
|
|
const fns = [
|
|
setRoles,
|
|
setPermissions,
|
|
setCourses,
|
|
setCourse_sections,
|
|
setLessons,
|
|
setEnrollments,
|
|
setLesson_progress,
|
|
setCourse_reviews,
|
|
setCourse_announcements,
|
|
setQuizzes,
|
|
setQuiz_questions,
|
|
setQuiz_options,
|
|
setQuiz_attempts,
|
|
setAssignment_submissions,
|
|
setMarketing_pages,
|
|
setContact_messages,
|
|
setPricing_plans,
|
|
setPlan_features,
|
|
setUsers,
|
|
];
|
|
|
|
const requests = entities.map((entity, index) => {
|
|
if (hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
|
|
return axios.get(`/${entity.toLowerCase()}/count`);
|
|
} else {
|
|
fns[index](null);
|
|
return Promise.resolve({ data: { count: null } });
|
|
}
|
|
});
|
|
|
|
Promise.allSettled(requests).then((results) => {
|
|
results.forEach((result, i) => {
|
|
if (result.status === 'fulfilled') {
|
|
fns[i](result.value.data.count);
|
|
} else {
|
|
fns[i](result.reason.message);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
async function getWidgets(roleId) {
|
|
await dispatch(fetchWidgets(roleId));
|
|
}
|
|
React.useEffect(() => {
|
|
if (!currentUser) return;
|
|
loadData().then();
|
|
setWidgetsRole({
|
|
role: {
|
|
value: currentUser?.app_role?.id,
|
|
label: currentUser?.app_role?.name,
|
|
},
|
|
});
|
|
}, [currentUser]);
|
|
|
|
React.useEffect(() => {
|
|
if (!currentUser || !widgetsRole?.role?.value) return;
|
|
getWidgets(widgetsRole?.role?.value || '').then();
|
|
}, [widgetsRole?.role?.value]);
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<title>
|
|
{getPageTitle(
|
|
t('pages.dashboard.pageTitle', { defaultValue: 'Overview' }),
|
|
)}
|
|
</title>
|
|
</Head>
|
|
<SectionMain>
|
|
<SectionTitleLineWithButton
|
|
icon={icon.mdiChartTimelineVariant}
|
|
title={t('pages.dashboard.overview', { defaultValue: 'Overview' })}
|
|
main
|
|
>
|
|
{''}
|
|
</SectionTitleLineWithButton>
|
|
|
|
{hasPermission(currentUser, 'CREATE_ROLES') && (
|
|
<WidgetCreator
|
|
currentUser={currentUser}
|
|
isFetchingQuery={isFetchingQuery}
|
|
setWidgetsRole={setWidgetsRole}
|
|
widgetsRole={widgetsRole}
|
|
/>
|
|
)}
|
|
{!!rolesWidgets.length &&
|
|
hasPermission(currentUser, 'CREATE_ROLES') && (
|
|
<p className=' text-gray-500 dark:text-gray-400 mb-4'>
|
|
{`${widgetsRole?.role?.label || 'Users'}'s widgets`}
|
|
</p>
|
|
)}
|
|
|
|
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6 grid-flow-dense'>
|
|
{(isFetchingQuery || loading) && (
|
|
<div
|
|
className={` ${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 text-lg leading-tight text-gray-500 flex items-center ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<BaseIcon
|
|
className={`${iconsColor} animate-spin mr-5`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
path={icon.mdiLoading}
|
|
/>{' '}
|
|
{t('pages.dashboard.loadingWidgets', {
|
|
defaultValue: 'Loading widgets...',
|
|
})}
|
|
</div>
|
|
)}
|
|
|
|
{rolesWidgets &&
|
|
rolesWidgets.map((widget) => (
|
|
<SmartWidget
|
|
key={widget.id}
|
|
userId={currentUser?.id}
|
|
widget={widget}
|
|
roleId={widgetsRole?.role?.value || ''}
|
|
admin={hasPermission(currentUser, 'CREATE_ROLES')}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{!!rolesWidgets.length && <hr className='my-6 ' />}
|
|
|
|
<div
|
|
id='dashboard'
|
|
className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'
|
|
>
|
|
{hasPermission(currentUser, 'READ_ROLES') && (
|
|
<Link href={'/roles/roles-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Roles
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{roles}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
icon.mdiShieldAccountVariantOutline || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_PERMISSIONS') && (
|
|
<Link href={'/permissions/permissions-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Permissions
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{permissions}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={icon.mdiShieldAccountOutline || icon.mdiTable}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_COURSES') && (
|
|
<Link href={'/courses/courses-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Courses
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{courses}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiSchool' in icon
|
|
? icon['mdiSchool' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_COURSE_SECTIONS') && (
|
|
<Link href={'/course_sections/course_sections-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Course sections
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{course_sections}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiViewList' in icon
|
|
? icon['mdiViewList' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_LESSONS') && (
|
|
<Link href={'/lessons/lessons-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Lessons
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{lessons}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiPlayCircleOutline' in icon
|
|
? icon['mdiPlayCircleOutline' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_ENROLLMENTS') && (
|
|
<Link href={'/enrollments/enrollments-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Enrollments
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{enrollments}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiAccountSchool' in icon
|
|
? icon['mdiAccountSchool' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_LESSON_PROGRESS') && (
|
|
<Link href={'/lesson_progress/lesson_progress-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Lesson progress
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{lesson_progress}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiProgressCheck' in icon
|
|
? icon['mdiProgressCheck' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_COURSE_REVIEWS') && (
|
|
<Link href={'/course_reviews/course_reviews-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Course reviews
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{course_reviews}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiStarCircle' in icon
|
|
? icon['mdiStarCircle' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_COURSE_ANNOUNCEMENTS') && (
|
|
<Link href={'/course_announcements/course_announcements-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Course announcements
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{course_announcements}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiBullhorn' in icon
|
|
? icon['mdiBullhorn' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_QUIZZES') && (
|
|
<Link href={'/quizzes/quizzes-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Quizzes
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{quizzes}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiHelpCircleOutline' in icon
|
|
? icon['mdiHelpCircleOutline' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_QUIZ_QUESTIONS') && (
|
|
<Link href={'/quiz_questions/quiz_questions-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Quiz questions
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{quiz_questions}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiOrderBoolAscendingVariant' in icon
|
|
? icon[
|
|
'mdiOrderBoolAscendingVariant' as keyof typeof icon
|
|
]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_QUIZ_OPTIONS') && (
|
|
<Link href={'/quiz_options/quiz_options-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Quiz options
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{quiz_options}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiCheckboxMarkedCircleOutline' in icon
|
|
? icon[
|
|
'mdiCheckboxMarkedCircleOutline' as keyof typeof icon
|
|
]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_QUIZ_ATTEMPTS') && (
|
|
<Link href={'/quiz_attempts/quiz_attempts-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Quiz attempts
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{quiz_attempts}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiClipboardTextOutline' in icon
|
|
? icon['mdiClipboardTextOutline' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_ASSIGNMENT_SUBMISSIONS') && (
|
|
<Link href={'/assignment_submissions/assignment_submissions-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Assignment submissions
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{assignment_submissions}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiFileUploadOutline' in icon
|
|
? icon['mdiFileUploadOutline' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_MARKETING_PAGES') && (
|
|
<Link href={'/marketing_pages/marketing_pages-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Marketing pages
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{marketing_pages}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiWeb' in icon
|
|
? icon['mdiWeb' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_CONTACT_MESSAGES') && (
|
|
<Link href={'/contact_messages/contact_messages-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Contact messages
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{contact_messages}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiEmailOutline' in icon
|
|
? icon['mdiEmailOutline' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_PRICING_PLANS') && (
|
|
<Link href={'/pricing_plans/pricing_plans-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Pricing plans
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{pricing_plans}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiCashMultiple' in icon
|
|
? icon['mdiCashMultiple' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_PLAN_FEATURES') && (
|
|
<Link href={'/plan_features/plan_features-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Plan features
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{plan_features}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={
|
|
'mdiChecklist' in icon
|
|
? icon['mdiChecklist' as keyof typeof icon]
|
|
: icon.mdiTable || icon.mdiTable
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
|
|
{hasPermission(currentUser, 'READ_USERS') && (
|
|
<Link href={'/users/users-list'}>
|
|
<div
|
|
className={`${
|
|
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
|
} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
>
|
|
<div className='flex justify-between align-center'>
|
|
<div>
|
|
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
|
Users
|
|
</div>
|
|
<div className='text-3xl leading-tight font-semibold'>
|
|
{users}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<BaseIcon
|
|
className={`${iconsColor}`}
|
|
w='w-16'
|
|
h='h-16'
|
|
size={48}
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
path={icon.mdiAccountGroup || icon.mdiTable}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
)}
|
|
</div>
|
|
</SectionMain>
|
|
</>
|
|
);
|
|
};
|
|
|
|
Dashboard.getLayout = function getLayout(page: ReactElement) {
|
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
|
|
};
|
|
|
|
export default Dashboard;
|