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 AsideMenuList from './AsideMenuList'
|
||||
import { MenuAsideItem } from '../interfaces'
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
||||
import { useAppSelector } from '../stores/hooks'
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useAppDispatch } from '../stores/hooks';
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
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 { useState } from 'react'
|
||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||
import BaseDivider from './BaseDivider'
|
||||
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 { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
||||
import menuAside from '../menuAside'
|
||||
|
||||
@ -8,27 +8,6 @@ const menuAside: MenuAsideItem[] = [
|
||||
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',
|
||||
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 SectionFullScreen from '../components/SectionFullScreen';
|
||||
import LayoutGuest from '../layouts/Guest';
|
||||
import BaseDivider from '../components/BaseDivider';
|
||||
import BaseButtons from '../components/BaseButtons';
|
||||
import { getPageTitle } from '../config';
|
||||
import { useAppSelector } from '../stores/hooks';
|
||||
@ -127,45 +128,22 @@ export default function Starter() {
|
||||
: null}
|
||||
<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'>
|
||||
<CardBoxComponentTitle title="Bienvenido a NWEA Exámenes"/>
|
||||
<CardBoxComponentTitle title="Welcome to your NWEA Examenes app!"/>
|
||||
|
||||
<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'>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'>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'>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>
|
||||
|
||||
<BaseButtons type='justify-center' className='mt-6'>
|
||||
<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'
|
||||
/>
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
href='/login'
|
||||
label='Login'
|
||||
color='whiteDark'
|
||||
className='w-full sm:w-auto'
|
||||
color='info'
|
||||
className='w-full'
|
||||
/>
|
||||
|
||||
</BaseButtons>
|
||||
</CardBox>
|
||||
</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 Head from 'next/head';
|
||||
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 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