235 lines
7.4 KiB
JavaScript
235 lines
7.4 KiB
JavaScript
import { Card, ListGroup, Button } from 'react-bootstrap';
|
||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||
import {
|
||
faUser, faIdCard, faEnvelope, faPhone, faHome, faMapMarkerAlt, faHashtag,
|
||
faSeedling, faUserShield, faCalendar,
|
||
faTrash, faEllipsisVertical
|
||
} from '@fortawesome/free-solid-svg-icons';
|
||
import PropTypes from 'prop-types';
|
||
import { motion as _motion } from 'framer-motion';
|
||
import AnimatedDropdown from '../../components/AnimatedDropdown';
|
||
import '../../css/SolicitudCard.css';
|
||
|
||
const MotionCard = _motion.create(Card);
|
||
|
||
const parseDate = (date) => {
|
||
if (!date) return 'NO';
|
||
const d = new Date(date);
|
||
return `${d.getDate().toString().padStart(2, '0')}/${(d.getMonth() + 1)
|
||
.toString().padStart(2, '0')}/${d.getFullYear()}`;
|
||
};
|
||
|
||
const getTipoSolicitud = (tipo) =>
|
||
['Alta', 'Baja', 'Añadir Colaborador', 'Quitar Colaborador', 'Añadir parcela invernadero', 'Dejar parcela invernadero'][tipo]
|
||
?? 'Desconocido';
|
||
|
||
const getEstadoSolicitud = (estado) =>
|
||
['Pendiente', 'Aceptada', 'Rechazada'][estado] ?? 'Desconocido';
|
||
|
||
const getPFP = (tipo) => {
|
||
const base = '/images/icons/';
|
||
const map = {
|
||
0: 'list.svg',
|
||
1: 'farmer.svg',
|
||
2: 'green_house.svg',
|
||
3: 'join.svg'
|
||
};
|
||
return base + (map[tipo] || 'farmer.svg');
|
||
};
|
||
|
||
const renderDescripcionSolicitud = (data, onProfile) => {
|
||
const m = data.metadata;
|
||
|
||
switch (data.type) {
|
||
case 0:
|
||
return data.status === 1
|
||
? 'Se ha aceptado esta solicitud de alta.'
|
||
: `${m?.displayName ?? 'Alguien'} quiere darse de alta.`;
|
||
|
||
case 1:
|
||
return onProfile
|
||
? 'Has solicitado darte de baja.'
|
||
: `${m?.displayName ?? 'Alguien'} quiere darse de baja.`;
|
||
|
||
case 2:
|
||
if (onProfile) {
|
||
return [
|
||
'Has solicitado añadir un colaborador.',
|
||
'Tu solicitud de colaborador ha sido aceptada.',
|
||
'Tu solicitud de colaborador ha sido rechazada.'
|
||
][data.status] ?? 'Solicitud de colaborador.';
|
||
}
|
||
return data.status === 0
|
||
? `${m?.displayName ?? 'Alguien'} quiere añadir un colaborador.`
|
||
: `La solicitud de colaborador ha sido ${
|
||
data.status === 1 ? 'aceptada' : 'rechazada'
|
||
}.`;
|
||
|
||
case 3:
|
||
return `${m?.displayName ?? 'Alguien'} quiere quitar su colaborador.`;
|
||
|
||
case 4:
|
||
return `${m?.displayName ?? 'Alguien'} quiere una parcela en el invernadero.`;
|
||
|
||
case 5:
|
||
return `${m?.displayName ?? 'Alguien'} quiere dejar su parcela del invernadero.`;
|
||
|
||
default:
|
||
return 'Tipo de solicitud desconocido.';
|
||
}
|
||
};
|
||
|
||
const SolicitudCard = ({
|
||
data,
|
||
onAccept,
|
||
onReject,
|
||
onDelete,
|
||
editable = true,
|
||
onProfile = false
|
||
}) => {
|
||
const m = data.metadata;
|
||
|
||
const handleDelete = () =>
|
||
typeof onDelete === 'function' && onDelete(data.requestId);
|
||
|
||
return (
|
||
<MotionCard className="solicitud-card shadow-sm rounded-4 h-100">
|
||
<Card.Header className="rounded-top-4 d-flex justify-content-between align-items-center">
|
||
<div className="d-flex align-items-center">
|
||
<img
|
||
src={getPFP(m?.type)}
|
||
width="36"
|
||
className="rounded me-3"
|
||
alt="PFP"
|
||
/>
|
||
<div>
|
||
<Card.Title className="mb-0">
|
||
Solicitud #{data.requestId?.slice(0, 8)} – {getTipoSolicitud(data.type)}
|
||
</Card.Title>
|
||
<small className="state-small">
|
||
Estado: <strong>{getEstadoSolicitud(data.status)}</strong>
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
{!onProfile && (
|
||
<AnimatedDropdown
|
||
className="end-0"
|
||
buttonStyle="card-button"
|
||
icon={<FontAwesomeIcon icon={faEllipsisVertical} className="fa-xl" />}
|
||
>
|
||
{({ closeDropdown }) => (
|
||
<div
|
||
className="dropdown-item d-flex align-items-center text-danger"
|
||
onClick={() => {
|
||
handleDelete();
|
||
closeDropdown();
|
||
}}
|
||
>
|
||
<FontAwesomeIcon icon={faTrash} className="me-2" />
|
||
Eliminar
|
||
</div>
|
||
)}
|
||
</AnimatedDropdown>
|
||
)}
|
||
</Card.Header>
|
||
|
||
<Card.Body>
|
||
<ListGroup variant="flush" className="border rounded-3 mb-3">
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faCalendar} className="me-2" />
|
||
Fecha de solicitud: <strong>{parseDate(data.createdAt)}</strong>
|
||
</ListGroup.Item>
|
||
</ListGroup>
|
||
|
||
<ListGroup variant="flush" className="border rounded-3 mb-3">
|
||
<ListGroup.Item>
|
||
{renderDescripcionSolicitud(data, onProfile)}
|
||
</ListGroup.Item>
|
||
</ListGroup>
|
||
|
||
{m && (
|
||
<>
|
||
<Card.Subtitle className="card-subtitle mt-3 mb-2">
|
||
Datos asociados a la solicitud
|
||
</Card.Subtitle>
|
||
|
||
<ListGroup variant="flush" className="border rounded-3">
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faUser} className="me-2" />
|
||
Nombre: <strong>{m.displayName}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faIdCard} className="me-2" />
|
||
DNI: <strong>{m.dni}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faPhone} className="me-2" />
|
||
Teléfono: <strong>{m.phone ?? 'NO'}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faEnvelope} className="me-2" />
|
||
Email: <strong>{m.email}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faUserShield} className="me-2" />
|
||
Usuario: <strong>{m.username}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faHome} className="me-2" />
|
||
Dirección: <strong>{m.address ?? 'NO'}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faMapMarkerAlt} className="me-2" />
|
||
Ciudad:{' '}
|
||
<strong>
|
||
{m.city ?? 'NO'} ({m.zipCode ?? 'NO'})
|
||
</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faHashtag} className="me-2" />
|
||
Nº socio: <strong>{m.memberNumber ?? 'NO'}</strong> | Nº huerto:{' '}
|
||
<strong>{m.plotNumber ?? 'NO'}</strong>
|
||
</ListGroup.Item>
|
||
|
||
<ListGroup.Item>
|
||
<FontAwesomeIcon icon={faSeedling} className="me-2" />
|
||
Tipo: <strong>{['Lista de espera', 'Hortelano', 'Hortelano + Invernadero', 'Colaborador'][m.type]}</strong>
|
||
</ListGroup.Item>
|
||
</ListGroup>
|
||
</>
|
||
)}
|
||
|
||
{editable && data.status === 0 && (
|
||
<div className="d-flex justify-content-end gap-2 mt-3">
|
||
<Button variant="danger" size="sm" onClick={() => onReject?.(data)}>
|
||
Rechazar
|
||
</Button>
|
||
<Button variant="success" size="sm" onClick={() => onAccept?.(data)}>
|
||
Aceptar
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</Card.Body>
|
||
</MotionCard>
|
||
);
|
||
};
|
||
|
||
SolicitudCard.propTypes = {
|
||
data: PropTypes.object.isRequired,
|
||
onAccept: PropTypes.func,
|
||
onReject: PropTypes.func,
|
||
onDelete: PropTypes.func,
|
||
editable: PropTypes.bool,
|
||
onProfile: PropTypes.bool
|
||
};
|
||
|
||
export default SolicitudCard;
|