Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -3,9 +3,10 @@ import { mdiLogout, mdiClose } from '@mdi/js'
|
|||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
import AsideMenuList from './AsideMenuList'
|
import AsideMenuList from './AsideMenuList'
|
||||||
import { MenuAsideItem } from '../interfaces'
|
import { MenuAsideItem } from '../interfaces'
|
||||||
import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
import { useAppSelector } from '../stores/hooks'
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { useAppDispatch } from '../stores/hooks';
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { useState } from 'react'
|
||||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||||
import BaseDivider from './BaseDivider'
|
import BaseDivider from './BaseDivider'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import type { ColorButtonKey } from '../interfaces'
|
|
||||||
import BaseButton from './BaseButton'
|
|
||||||
import BaseIcon from './BaseIcon'
|
|
||||||
import CardBox from './CardBox'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
icon: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
href: string
|
|
||||||
cta: string
|
|
||||||
color?: ColorButtonKey
|
|
||||||
note?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PortalActionCard({
|
|
||||||
icon,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
href,
|
|
||||||
cta,
|
|
||||||
color = 'info',
|
|
||||||
note,
|
|
||||||
}: Props) {
|
|
||||||
return (
|
|
||||||
<CardBox className="h-full" isHoverable>
|
|
||||||
<div className="flex h-full flex-col">
|
|
||||||
<div className="mb-4 flex items-start justify-between gap-4">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">{title}</h2>
|
|
||||||
<p className="mt-2 text-sm leading-relaxed text-gray-500 dark:text-gray-400">
|
|
||||||
{description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<BaseIcon
|
|
||||||
path={icon}
|
|
||||||
size={30}
|
|
||||||
className="rounded-full bg-blue-50 p-3 text-blue-600 dark:bg-dark-800 dark:text-pavitra-blue"
|
|
||||||
w="w-14"
|
|
||||||
h="h-14"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{note && <p className="mb-4 text-xs font-medium text-gray-400 dark:text-gray-500">{note}</p>}
|
|
||||||
|
|
||||||
<div className="mt-auto">
|
|
||||||
<BaseButton href={href} label={cta} color={color} className="w-full" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { ReactNode, useEffect, useState } from 'react'
|
import React, { ReactNode, useEffect } from 'react'
|
||||||
|
import { useState } from 'react'
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
||||||
import menuAside from '../menuAside'
|
import menuAside from '../menuAside'
|
||||||
|
|||||||
@ -8,27 +8,6 @@ const menuAside: MenuAsideItem[] = [
|
|||||||
label: 'Dashboard',
|
label: 'Dashboard',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
href: '/admin-portal',
|
|
||||||
icon: icon.mdiShieldAccount,
|
|
||||||
label: 'Portal Admin',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/teacher-portal',
|
|
||||||
icon: icon.mdiHumanMaleBoard,
|
|
||||||
label: 'Portal Maestros',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/student-portal',
|
|
||||||
icon: icon.mdiAccountSchool,
|
|
||||||
label: 'Portal Estudiantes',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/parent-portal',
|
|
||||||
icon: icon.mdiAccountGroup,
|
|
||||||
label: 'Portal Padres',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
href: '/users/users-list',
|
href: '/users/users-list',
|
||||||
label: 'Users',
|
label: 'Users',
|
||||||
|
|||||||
@ -1,101 +0,0 @@
|
|||||||
import {
|
|
||||||
mdiCalendarClock,
|
|
||||||
mdiChartBox,
|
|
||||||
mdiClipboardText,
|
|
||||||
mdiShieldAccount,
|
|
||||||
} from '@mdi/js'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import React, { ReactElement } from 'react'
|
|
||||||
import BaseButton from '../components/BaseButton'
|
|
||||||
import CardBox from '../components/CardBox'
|
|
||||||
import PortalActionCard from '../components/PortalActionCard'
|
|
||||||
import SectionMain from '../components/SectionMain'
|
|
||||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
|
|
||||||
import { getPageTitle } from '../config'
|
|
||||||
import LayoutAuthenticated from '../layouts/Authenticated'
|
|
||||||
|
|
||||||
const adminActions = [
|
|
||||||
{
|
|
||||||
title: 'Crear examen',
|
|
||||||
description: 'Empieza un examen nuevo con título, descripción, tiempo límite, puntaje máximo y estado.',
|
|
||||||
href: '/exams/exams-new',
|
|
||||||
cta: 'Crear examen',
|
|
||||||
icon: mdiClipboardText,
|
|
||||||
color: 'info' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Crear sesión',
|
|
||||||
description: 'Abre una sesión para que estudiantes entren con el código indicado por el maestro o proctor.',
|
|
||||||
href: '/exam_sessions/exam_sessions-new',
|
|
||||||
cta: 'Crear sesión',
|
|
||||||
icon: mdiCalendarClock,
|
|
||||||
color: 'success' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Resultados',
|
|
||||||
description: 'Revisa reportes, calificaciones y resultados generados para estudiantes y familias.',
|
|
||||||
href: '/report_cards/report_cards-list',
|
|
||||||
cta: 'Ver resultados',
|
|
||||||
icon: mdiChartBox,
|
|
||||||
color: 'warning' as const,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const AdminPortal = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>{getPageTitle('Portal Admin')}</title>
|
|
||||||
</Head>
|
|
||||||
<SectionMain>
|
|
||||||
<SectionTitleLineWithButton icon={mdiShieldAccount} title="Portal Admin" main>
|
|
||||||
{''}
|
|
||||||
</SectionTitleLineWithButton>
|
|
||||||
|
|
||||||
<CardBox className="mb-6">
|
|
||||||
<div className="grid gap-6 lg:grid-cols-[1.5fr_1fr] lg:items-center">
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-sm font-semibold uppercase tracking-wide text-blue-600 dark:text-pavitra-blue">
|
|
||||||
Administración rápida
|
|
||||||
</p>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
|
||||||
Crear examen, crear sesión y revisar resultados.
|
|
||||||
</h1>
|
|
||||||
<p className="mt-3 max-w-3xl text-gray-500 dark:text-gray-400">
|
|
||||||
Este portal deja lo esencial en un solo lugar para que el Admin no tenga que buscar en
|
|
||||||
todas las tablas del sistema.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3 sm:flex-row lg:flex-col">
|
|
||||||
<BaseButton href="/exams/exams-new" label="Crear examen" color="info" className="w-full" />
|
|
||||||
<BaseButton
|
|
||||||
href="/exam_sessions/exam_sessions-new"
|
|
||||||
label="Crear sesión"
|
|
||||||
color="success"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
href="/report_cards/report_cards-list"
|
|
||||||
label="Resultados"
|
|
||||||
color="warning"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
||||||
{adminActions.map((action) => (
|
|
||||||
<PortalActionCard key={action.href} {...action} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</SectionMain>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AdminPortal.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AdminPortal
|
|
||||||
@ -7,6 +7,7 @@ import BaseButton from '../components/BaseButton';
|
|||||||
import CardBox from '../components/CardBox';
|
import CardBox from '../components/CardBox';
|
||||||
import SectionFullScreen from '../components/SectionFullScreen';
|
import SectionFullScreen from '../components/SectionFullScreen';
|
||||||
import LayoutGuest from '../layouts/Guest';
|
import LayoutGuest from '../layouts/Guest';
|
||||||
|
import BaseDivider from '../components/BaseDivider';
|
||||||
import BaseButtons from '../components/BaseButtons';
|
import BaseButtons from '../components/BaseButtons';
|
||||||
import { getPageTitle } from '../config';
|
import { getPageTitle } from '../config';
|
||||||
import { useAppSelector } from '../stores/hooks';
|
import { useAppSelector } from '../stores/hooks';
|
||||||
@ -127,45 +128,22 @@ export default function Starter() {
|
|||||||
: null}
|
: null}
|
||||||
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
||||||
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
||||||
<CardBoxComponentTitle title="Bienvenido a NWEA Exámenes"/>
|
<CardBoxComponentTitle title="Welcome to your NWEA Examenes app!"/>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<p className='text-center text-gray-500'>Elige tu portal para entrar más rápido al flujo correcto.</p>
|
<p className='text-center text-gray-500'>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
|
||||||
<p className='text-center text-gray-500'>Admin y maestros pueden crear exámenes y sesiones. Estudiantes entran con código. Padres revisan resultados.</p>
|
<p className='text-center text-gray-500'>For guides and documentation please check
|
||||||
|
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BaseButtons type='justify-center' className='mt-6'>
|
<BaseButtons>
|
||||||
<BaseButton
|
|
||||||
href='/admin-portal'
|
|
||||||
label='Portal Admin'
|
|
||||||
color='info'
|
|
||||||
className='w-full sm:w-auto'
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
href='/teacher-portal'
|
|
||||||
label='Portal Maestros'
|
|
||||||
color='success'
|
|
||||||
className='w-full sm:w-auto'
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
href='/student-portal'
|
|
||||||
label='Portal Estudiantes'
|
|
||||||
color='warning'
|
|
||||||
className='w-full sm:w-auto'
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
href='/parent-portal'
|
|
||||||
label='Portal Padres'
|
|
||||||
color='info'
|
|
||||||
outline
|
|
||||||
className='w-full sm:w-auto'
|
|
||||||
/>
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
href='/login'
|
href='/login'
|
||||||
label='Login'
|
label='Login'
|
||||||
color='whiteDark'
|
color='info'
|
||||||
className='w-full sm:w-auto'
|
className='w-full'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
import { mdiAccountGroup, mdiChartBox, mdiFileChart, mdiLogin, mdiAccountCircle } from '@mdi/js'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import React, { ReactElement } from 'react'
|
|
||||||
import BaseButton from '../components/BaseButton'
|
|
||||||
import CardBox from '../components/CardBox'
|
|
||||||
import PortalActionCard from '../components/PortalActionCard'
|
|
||||||
import SectionMain from '../components/SectionMain'
|
|
||||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
|
|
||||||
import { getPageTitle } from '../config'
|
|
||||||
import LayoutAuthenticated from '../layouts/Authenticated'
|
|
||||||
|
|
||||||
const parentActions = [
|
|
||||||
{
|
|
||||||
title: 'Resultados del estudiante',
|
|
||||||
description: 'Consulta reportes y calificaciones disponibles para tu estudiante.',
|
|
||||||
href: '/report_cards/report_cards-list',
|
|
||||||
cta: 'Ver resultados',
|
|
||||||
icon: mdiChartBox,
|
|
||||||
color: 'info' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Reporte detallado',
|
|
||||||
description: 'Entra a las tarjetas de reporte para revisar progreso y evidencia del examen.',
|
|
||||||
href: '/report_cards/report_cards-list',
|
|
||||||
cta: 'Abrir reportes',
|
|
||||||
icon: mdiFileChart,
|
|
||||||
color: 'success' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Mi perfil',
|
|
||||||
description: 'Revisa tu cuenta de padre/madre o tutor y mantén tus datos actualizados.',
|
|
||||||
href: '/profile',
|
|
||||||
cta: 'Ver perfil',
|
|
||||||
icon: mdiAccountCircle,
|
|
||||||
color: 'warning' as const,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const ParentPortal = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>{getPageTitle('Portal Padres')}</title>
|
|
||||||
</Head>
|
|
||||||
<SectionMain>
|
|
||||||
<SectionTitleLineWithButton icon={mdiAccountGroup} title="Portal Padres" main>
|
|
||||||
{''}
|
|
||||||
</SectionTitleLineWithButton>
|
|
||||||
|
|
||||||
<CardBox className="mb-6">
|
|
||||||
<div className="grid gap-6 lg:grid-cols-[1.5fr_1fr] lg:items-center">
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-sm font-semibold uppercase tracking-wide text-blue-600 dark:text-pavitra-blue">
|
|
||||||
Familias y tutores
|
|
||||||
</p>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
|
||||||
Resultados claros para padres.
|
|
||||||
</h1>
|
|
||||||
<p className="mt-3 max-w-3xl text-gray-500 dark:text-gray-400">
|
|
||||||
Este portal está pensado para que las familias entren, revisen resultados y entiendan el
|
|
||||||
progreso del estudiante sin navegar por todo el sistema.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<BaseButton href="/login" label="Entrar con mi cuenta" icon={mdiLogin} color="info" className="w-full" />
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
||||||
{parentActions.map((action) => (
|
|
||||||
<PortalActionCard key={`${action.href}-${action.title}`} {...action} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</SectionMain>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ParentPortal.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ParentPortal
|
|
||||||
@ -1,7 +1,9 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react';
|
import React, { ReactElement, useEffect, useState } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
import { useAppDispatch } from '../stores/hooks';
|
||||||
|
|
||||||
|
import { useAppSelector } from '../stores/hooks';
|
||||||
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import LayoutAuthenticated from '../layouts/Authenticated';
|
import LayoutAuthenticated from '../layouts/Authenticated';
|
||||||
|
|||||||
@ -1,215 +0,0 @@
|
|||||||
import { mdiAccountSchool, mdiCheckCircle, mdiClipboardText, mdiHome, mdiLogin } from '@mdi/js'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import React, { ReactElement } from 'react'
|
|
||||||
import BaseButton from '../components/BaseButton'
|
|
||||||
import CardBox from '../components/CardBox'
|
|
||||||
import BaseIcon from '../components/BaseIcon'
|
|
||||||
import SectionMain from '../components/SectionMain'
|
|
||||||
import { getPageTitle } from '../config'
|
|
||||||
import LayoutGuest from '../layouts/Guest'
|
|
||||||
|
|
||||||
type StudentPortalStage = 'entry' | 'confirm' | 'waiting'
|
|
||||||
|
|
||||||
const StudentPortal = () => {
|
|
||||||
const [stage, setStage] = React.useState<StudentPortalStage>('entry')
|
|
||||||
const [form, setForm] = React.useState({
|
|
||||||
sessionCode: '',
|
|
||||||
studentName: '',
|
|
||||||
studentId: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
const canContinue = Boolean(
|
|
||||||
form.sessionCode.length === 3 && form.studentName.trim() && form.studentId.trim(),
|
|
||||||
)
|
|
||||||
|
|
||||||
const updateField = (field: keyof typeof form, value: string) => {
|
|
||||||
setForm((current) => ({ ...current, [field]: value }))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCodeChange = (value: string) => {
|
|
||||||
updateField('sessionCode', value.replace(/\D/g, '').slice(0, 3))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault()
|
|
||||||
if (!canContinue) return
|
|
||||||
setStage('confirm')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>{getPageTitle('Portal Estudiantes')}</title>
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-emerald-50 dark:from-dark-900 dark:via-dark-800 dark:to-dark-900">
|
|
||||||
<header className="border-b border-gray-200 bg-white/80 backdrop-blur dark:border-dark-700 dark:bg-dark-900/80">
|
|
||||||
<div className="mx-auto flex max-w-6xl items-center justify-between px-6 py-4">
|
|
||||||
<Link href="/" className="flex items-center gap-2 font-bold text-gray-900 dark:text-white">
|
|
||||||
<BaseIcon path={mdiAccountSchool} size={24} className="text-blue-600 dark:text-pavitra-blue" />
|
|
||||||
NWEA Estudiantes
|
|
||||||
</Link>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<BaseButton href="/" label="Inicio" icon={mdiHome} color="whiteDark" small />
|
|
||||||
<BaseButton href="/login" label="Login" icon={mdiLogin} color="info" small />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<SectionMain>
|
|
||||||
<div className="mx-auto grid max-w-6xl gap-6 lg:grid-cols-[1fr_1.15fr] lg:items-center">
|
|
||||||
<div>
|
|
||||||
<p className="mb-3 inline-flex rounded-full bg-blue-100 px-3 py-1 text-sm font-semibold text-blue-700 dark:bg-dark-800 dark:text-pavitra-blue">
|
|
||||||
Entrada rápida con código de sesión
|
|
||||||
</p>
|
|
||||||
<h1 className="text-4xl font-black leading-tight text-gray-900 dark:text-white md:text-5xl">
|
|
||||||
Portal para estudiantes
|
|
||||||
</h1>
|
|
||||||
<p className="mt-4 text-lg leading-relaxed text-gray-600 dark:text-gray-300">
|
|
||||||
Escribe el código de 3 dígitos que te dio tu maestro, tu nombre y tu ID de estudiante.
|
|
||||||
Después confirma que eres tú y espera a que empiece el examen.
|
|
||||||
</p>
|
|
||||||
<div className="mt-6 grid gap-3 text-sm text-gray-500 dark:text-gray-400 sm:grid-cols-3">
|
|
||||||
<div className="rounded-lg bg-white/80 p-4 dark:bg-dark-900/80">
|
|
||||||
<strong className="block text-gray-900 dark:text-white">1. Código</strong>
|
|
||||||
Usa 3 dígitos.
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg bg-white/80 p-4 dark:bg-dark-900/80">
|
|
||||||
<strong className="block text-gray-900 dark:text-white">2. Confirmar</strong>
|
|
||||||
Verifica tu identidad.
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg bg-white/80 p-4 dark:bg-dark-900/80">
|
|
||||||
<strong className="block text-gray-900 dark:text-white">3. Esperar</strong>
|
|
||||||
El proctor inicia la prueba.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CardBox>
|
|
||||||
{stage === 'entry' && (
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
|
||||||
<div className="text-center">
|
|
||||||
<BaseIcon
|
|
||||||
path={mdiClipboardText}
|
|
||||||
size={36}
|
|
||||||
className="mx-auto mb-3 rounded-full bg-blue-50 p-4 text-blue-600 dark:bg-dark-800 dark:text-pavitra-blue"
|
|
||||||
w="w-20"
|
|
||||||
h="h-20"
|
|
||||||
/>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">Entrar al examen</h2>
|
|
||||||
<p className="mt-2 text-gray-500 dark:text-gray-400">
|
|
||||||
Completa estos datos exactamente como te los dio tu escuela.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="mb-1 block text-sm font-semibold text-gray-700 dark:text-gray-200">
|
|
||||||
Código de sesión
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
value={form.sessionCode}
|
|
||||||
onChange={(event) => handleCodeChange(event.target.value)}
|
|
||||||
inputMode="numeric"
|
|
||||||
placeholder="123"
|
|
||||||
className="h-14 w-full rounded border border-gray-300 px-4 text-center text-3xl font-black tracking-[0.5em] focus:border-blue-600 focus:outline-none focus:ring focus:ring-blue-200 dark:border-dark-700 dark:bg-dark-800 dark:text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="mb-1 block text-sm font-semibold text-gray-700 dark:text-gray-200">
|
|
||||||
Nombre completo
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
value={form.studentName}
|
|
||||||
onChange={(event) => updateField('studentName', event.target.value)}
|
|
||||||
placeholder="Ejemplo: Ana García"
|
|
||||||
className="h-12 w-full rounded border border-gray-300 px-4 focus:border-blue-600 focus:outline-none focus:ring focus:ring-blue-200 dark:border-dark-700 dark:bg-dark-800 dark:text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="mb-1 block text-sm font-semibold text-gray-700 dark:text-gray-200">
|
|
||||||
ID de estudiante
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
value={form.studentId}
|
|
||||||
onChange={(event) => updateField('studentId', event.target.value)}
|
|
||||||
placeholder="ID escolar"
|
|
||||||
className="h-12 w-full rounded border border-gray-300 px-4 focus:border-blue-600 focus:outline-none focus:ring focus:ring-blue-200 dark:border-dark-700 dark:bg-dark-800 dark:text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<BaseButton
|
|
||||||
type="submit"
|
|
||||||
label="Continuar"
|
|
||||||
color="info"
|
|
||||||
className="w-full"
|
|
||||||
disabled={!canContinue}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{stage === 'confirm' && (
|
|
||||||
<div className="space-y-5 text-center">
|
|
||||||
<BaseIcon
|
|
||||||
path={mdiCheckCircle}
|
|
||||||
size={42}
|
|
||||||
className="mx-auto rounded-full bg-emerald-50 p-4 text-emerald-600 dark:bg-dark-800"
|
|
||||||
w="w-20"
|
|
||||||
h="h-20"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">¿Eres tú?</h2>
|
|
||||||
<p className="mt-2 text-gray-500 dark:text-gray-400">
|
|
||||||
Confirma que la información está correcta antes de entrar a la sala de espera.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg bg-gray-50 p-4 text-left dark:bg-dark-800">
|
|
||||||
<p>
|
|
||||||
<strong>Código:</strong> {form.sessionCode}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Nombre:</strong> {form.studentName}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>ID:</strong> {form.studentId}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
|
||||||
<BaseButton label="Cambiar datos" color="whiteDark" onClick={() => setStage('entry')} />
|
|
||||||
<BaseButton label="Sí, soy yo" color="success" onClick={() => setStage('waiting')} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{stage === 'waiting' && (
|
|
||||||
<div className="space-y-5 text-center">
|
|
||||||
<div className="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-blue-50 text-blue-600 dark:bg-dark-800 dark:text-pavitra-blue">
|
|
||||||
<span className="h-10 w-10 animate-pulse rounded-full bg-current opacity-40" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">Sala de espera</h2>
|
|
||||||
<p className="mt-2 text-gray-500 dark:text-gray-400">
|
|
||||||
Listo, {form.studentName}. Espera a que tu maestro o proctor inicie el examen.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg bg-blue-50 p-4 text-sm text-blue-700 dark:bg-dark-800 dark:text-pavitra-blue">
|
|
||||||
Código de sesión: <strong>{form.sessionCode}</strong>
|
|
||||||
</div>
|
|
||||||
<BaseButton label="Volver al inicio" color="whiteDark" onClick={() => setStage('entry')} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
</SectionMain>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
StudentPortal.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default StudentPortal
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
import {
|
|
||||||
mdiAccountSchool,
|
|
||||||
mdiCalendarClock,
|
|
||||||
mdiChartBox,
|
|
||||||
mdiClipboardText,
|
|
||||||
mdiFileDocumentEdit,
|
|
||||||
mdiHumanMaleBoard,
|
|
||||||
} from '@mdi/js'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import React, { ReactElement } from 'react'
|
|
||||||
import CardBox from '../components/CardBox'
|
|
||||||
import PortalActionCard from '../components/PortalActionCard'
|
|
||||||
import SectionMain from '../components/SectionMain'
|
|
||||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
|
|
||||||
import { getPageTitle } from '../config'
|
|
||||||
import LayoutAuthenticated from '../layouts/Authenticated'
|
|
||||||
|
|
||||||
const teacherActions = [
|
|
||||||
{
|
|
||||||
title: 'Mis exámenes',
|
|
||||||
description: 'Crea, revisa o edita exámenes antes de publicarlos para tus estudiantes.',
|
|
||||||
href: '/exams/exams-list',
|
|
||||||
cta: 'Ver exámenes',
|
|
||||||
icon: mdiClipboardText,
|
|
||||||
color: 'info' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Sesiones de examen',
|
|
||||||
description: 'Crea sesiones, comparte códigos y acompaña a los estudiantes durante el examen.',
|
|
||||||
href: '/exam_sessions/exam_sessions-list',
|
|
||||||
cta: 'Ver sesiones',
|
|
||||||
icon: mdiCalendarClock,
|
|
||||||
color: 'success' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Estudiantes',
|
|
||||||
description: 'Administra estudiantes, IDs y datos necesarios para entrar a una sesión.',
|
|
||||||
href: '/students/students-list',
|
|
||||||
cta: 'Ver estudiantes',
|
|
||||||
icon: mdiAccountSchool,
|
|
||||||
color: 'warning' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Calificar respuestas',
|
|
||||||
description: 'Revisa respuestas abiertas y deja listas las calificaciones finales.',
|
|
||||||
href: '/attempt_answers/attempt_answers-list',
|
|
||||||
cta: 'Calificar',
|
|
||||||
icon: mdiFileDocumentEdit,
|
|
||||||
color: 'info' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Resultados',
|
|
||||||
description: 'Consulta reportes por estudiante para compartir avances con la escuela o familias.',
|
|
||||||
href: '/report_cards/report_cards-list',
|
|
||||||
cta: 'Ver resultados',
|
|
||||||
icon: mdiChartBox,
|
|
||||||
color: 'success' as const,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const TeacherPortal = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>{getPageTitle('Portal Maestros')}</title>
|
|
||||||
</Head>
|
|
||||||
<SectionMain>
|
|
||||||
<SectionTitleLineWithButton icon={mdiHumanMaleBoard} title="Portal Maestros" main>
|
|
||||||
{''}
|
|
||||||
</SectionTitleLineWithButton>
|
|
||||||
|
|
||||||
<CardBox className="mb-6">
|
|
||||||
<p className="mb-2 text-sm font-semibold uppercase tracking-wide text-blue-600 dark:text-pavitra-blue">
|
|
||||||
Flujo para maestros
|
|
||||||
</p>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
|
||||||
Exámenes, sesiones, estudiantes y resultados en un solo lugar.
|
|
||||||
</h1>
|
|
||||||
<p className="mt-3 max-w-4xl text-gray-500 dark:text-gray-400">
|
|
||||||
Usa este portal como panel rápido para preparar pruebas, abrir sesiones y revisar el progreso
|
|
||||||
de tus estudiantes.
|
|
||||||
</p>
|
|
||||||
</CardBox>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3">
|
|
||||||
{teacherActions.map((action) => (
|
|
||||||
<PortalActionCard key={action.href} {...action} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</SectionMain>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
TeacherPortal.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TeacherPortal
|
|
||||||
Loading…
x
Reference in New Issue
Block a user