feat: add modal to display user contador and update attendance confirmation flow
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -11,6 +11,10 @@ import { useParams } from 'next/navigation';
|
||||
import React, { useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useEvento } from '@/context/evento';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Button from '@/components/button';
|
||||
|
||||
const Modal = dynamic(() => import('@/components/modal'), { ssr: false });
|
||||
|
||||
type Params = {
|
||||
id_evento: string;
|
||||
@@ -27,6 +31,7 @@ export default function Page() {
|
||||
const [loadingAsistencia, setLoadingAsistencia] = React.useState<
|
||||
Record<number, boolean>
|
||||
>({});
|
||||
const [contadorModal, setContadorModal] = React.useState<number | null>(null);
|
||||
|
||||
const { data: participantes, setData } = useGetApi<ParticipacionEvento[]>(
|
||||
`/participante-evento/evento/${params.id_cuestionario}`
|
||||
@@ -45,12 +50,15 @@ export default function Page() {
|
||||
) => {
|
||||
setLoadingAsistencia((prev) => ({ ...prev, [id_participante]: true }));
|
||||
try {
|
||||
await axiosInstance.post(
|
||||
const response = await axiosInstance.post(
|
||||
`/participante-evento/asistencia/${id_participante}/${id_evento}`
|
||||
);
|
||||
|
||||
if (response.data?.contador != null) {
|
||||
setContadorModal(response.data.contador);
|
||||
}
|
||||
|
||||
toast.success('Asistencia confirmada');
|
||||
// Actualiza lista
|
||||
const { data } = await axiosInstance.get<ParticipacionEvento[]>(
|
||||
`/participante-evento/evento/${id_evento}`
|
||||
);
|
||||
@@ -188,6 +196,33 @@ export default function Page() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Modal
|
||||
isVisible={contadorModal !== null}
|
||||
size="sm"
|
||||
onClose={() => setContadorModal(null)}
|
||||
closeButton
|
||||
className={{ content: 'border-0 rounded-3', body: 'text-center' }}
|
||||
>
|
||||
<div className="modal-header bg-primary text-white border-0 rounded-top-3">
|
||||
<h5 className="modal-title mb-0 fw-bold">
|
||||
<i className="bi bi-hash me-2"></i>
|
||||
Contador
|
||||
</h5>
|
||||
</div>
|
||||
<div className="modal-body p-4">
|
||||
<p className="display-4 fw-bold text-primary mb-1">{contadorModal}</p>
|
||||
<p className="text-muted mb-4">
|
||||
Este es el numero de estampillas a entregar al usuario
|
||||
</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => setContadorModal(null)}
|
||||
className="px-4 py-2 rounded-pill"
|
||||
>
|
||||
Cerrar
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export default function Page() {
|
||||
const [statusMessage, setStatusMessage] = useState('');
|
||||
|
||||
const [participante, setParticipante] = useState('');
|
||||
const [contador, setContador] = useState(0);
|
||||
const [message, setMessage] = useState('');
|
||||
const [token, setToken] = useState('');
|
||||
const [tokenValid, setTokenValid] = useState(false);
|
||||
@@ -29,7 +30,10 @@ export default function Page() {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axiosInstance.post('/participante-evento/decode-token', { token: rawValue });
|
||||
const response = await axiosInstance.post(
|
||||
'/participante-evento/decode-token',
|
||||
{ token: rawValue }
|
||||
);
|
||||
|
||||
if (response.data) {
|
||||
// Token válido
|
||||
@@ -37,6 +41,7 @@ export default function Page() {
|
||||
setTokenValid(true);
|
||||
setShowModal(true);
|
||||
setEnableScan(false);
|
||||
setContador(response.data.contador);
|
||||
} else {
|
||||
// Token caducado o cualquier otro error
|
||||
setStatusMessage(`❌ Error el código qr ya caduco`);
|
||||
@@ -53,22 +58,22 @@ export default function Page() {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (!tokenValid) return;
|
||||
|
||||
try {
|
||||
const response = await axiosInstance.post(
|
||||
`/participante-evento/asistencia`, {
|
||||
token: token
|
||||
`/participante-evento/asistencia`,
|
||||
{
|
||||
token: token,
|
||||
}
|
||||
);
|
||||
|
||||
//Validacion
|
||||
if(response.data.success === false) {
|
||||
console.error("El usuario ya se registro");
|
||||
if (response.data.success === false) {
|
||||
console.error('El usuario ya se registro');
|
||||
setMessage(response.data.message);
|
||||
toast.error("❌ Error el usaurio ya se registro previamente.")
|
||||
toast.error('❌ Error el usaurio ya se registro previamente.');
|
||||
} else {
|
||||
setParticipante(response.data.data.participante);
|
||||
setMessage(response.data.message);
|
||||
@@ -137,7 +142,9 @@ export default function Page() {
|
||||
<div className="mb-4">
|
||||
<div className="alert alert-success border-0 rounded-3 d-flex align-items-center">
|
||||
<i className="bi bi-person-check-fill me-2"></i>
|
||||
<span>Ultimo participante registrado: {participante}</span>
|
||||
<span>
|
||||
Ultimo participante registrado: {participante}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -191,8 +198,8 @@ export default function Page() {
|
||||
statusMessage.includes('❌')
|
||||
? 'alert-danger'
|
||||
: statusMessage.includes('⚠️')
|
||||
? 'alert-warning'
|
||||
: 'alert-success'
|
||||
? 'alert-warning'
|
||||
: 'alert-success'
|
||||
}`}
|
||||
>
|
||||
<div className="me-2">
|
||||
@@ -240,34 +247,42 @@ export default function Page() {
|
||||
<div className="modal-body p-4">
|
||||
{tokenValid ? (
|
||||
// Modal de confirmación de asistencia
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-center">
|
||||
<Button
|
||||
variant="success"
|
||||
onClick={handleConfirm}
|
||||
className="px-4 py-2 rounded-pill fw-semibold"
|
||||
>
|
||||
<i className="bi bi-check2 me-2"></i>
|
||||
Confirmar Asistencia
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
onClick={() => {
|
||||
setShowModal(false);
|
||||
setEnableScan(true);
|
||||
setTokenValid(false);
|
||||
setToken('');
|
||||
}}
|
||||
className="px-4 py-2 rounded-pill"
|
||||
>
|
||||
<i className="bi bi-x-lg me-2"></i>
|
||||
Cancelar
|
||||
</Button>
|
||||
</div>
|
||||
<>
|
||||
<p className="text-muted mb-3">
|
||||
Contador del usuario: <strong>{contador}</strong>
|
||||
</p>
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-center">
|
||||
<Button
|
||||
variant="success"
|
||||
onClick={handleConfirm}
|
||||
className="px-4 py-2 rounded-pill fw-semibold"
|
||||
>
|
||||
<i className="bi bi-check2 me-2"></i>
|
||||
Confirmar Asistencia
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
onClick={() => {
|
||||
setShowModal(false);
|
||||
setEnableScan(true);
|
||||
setTokenValid(false);
|
||||
setToken('');
|
||||
}}
|
||||
className="px-4 py-2 rounded-pill"
|
||||
>
|
||||
<i className="bi bi-x-lg me-2"></i>
|
||||
Cancelar
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
// Modal de error
|
||||
<div className="py-4">
|
||||
<div className="mb-3">
|
||||
<i className="bi bi-exclamation-triangle-fill text-warning" style={{ fontSize: '2.5rem' }}></i>
|
||||
<i
|
||||
className="bi bi-exclamation-triangle-fill text-warning"
|
||||
style={{ fontSize: '2.5rem' }}
|
||||
></i>
|
||||
</div>
|
||||
<h6 className="text-danger mb-0">{statusMessage}</h6>
|
||||
<div className="mt-3">
|
||||
|
||||
Reference in New Issue
Block a user