Ongoing adaptation to new backend structure
This commit is contained in:
@@ -45,7 +45,7 @@ const AnuncioCard = ({ anuncio, isNew = false, onCreate, onUpdate, onDelete, onC
|
||||
const [formData, setFormData] = useState({
|
||||
body: anuncio.body || '',
|
||||
priority: anuncio.priority ?? 1,
|
||||
published_by: JSON.parse(localStorage.getItem('user'))?.user_id,
|
||||
publishedBy: JSON.parse(localStorage.getItem('identity'))?.user?.userId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -53,7 +53,7 @@ const AnuncioCard = ({ anuncio, isNew = false, onCreate, onUpdate, onDelete, onC
|
||||
setFormData({
|
||||
body: anuncio.body || '',
|
||||
priority: anuncio.priority ?? 1,
|
||||
published_by: JSON.parse(localStorage.getItem('user'))?.user_id,
|
||||
publishedBy: JSON.parse(localStorage.getItem('identity'))?.user?.userId,
|
||||
});
|
||||
}
|
||||
}, [anuncio, editMode]);
|
||||
@@ -63,7 +63,7 @@ const AnuncioCard = ({ anuncio, isNew = false, onCreate, onUpdate, onDelete, onC
|
||||
setEditMode(true);
|
||||
};
|
||||
|
||||
const handleDelete = () => typeof onDelete === 'function' && onDelete(anuncio.announce_id);
|
||||
const handleDelete = () => typeof onDelete === 'function' && onDelete(anuncio.announceId);
|
||||
|
||||
const handleCancel = () => {
|
||||
if (onClearError) onClearError();
|
||||
@@ -77,12 +77,12 @@ const AnuncioCard = ({ anuncio, isNew = false, onCreate, onUpdate, onDelete, onC
|
||||
formData.body = sanitizedBody;
|
||||
const updated = { ...anuncio, ...formData };
|
||||
if (createMode && typeof onCreate === 'function') return onCreate(updated);
|
||||
if (typeof onUpdate === 'function') return onUpdate(updated, anuncio.announce_id);
|
||||
if (typeof onUpdate === 'function') return onUpdate(updated, anuncio.announceId);
|
||||
};
|
||||
|
||||
const handleChange = (field, value) => setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
|
||||
const { date, time } = formatDateTime(anuncio.created_at);
|
||||
const { date, time } = formatDateTime(anuncio.createdAt);
|
||||
const priorityInfo = PRIORITY_CONFIG[formData.priority] || PRIORITY_CONFIG[1];
|
||||
const isLongBody = formData.body.length > 300;
|
||||
const displayBody = isLongBody && !showFullBody
|
||||
@@ -101,11 +101,15 @@ const AnuncioCard = ({ anuncio, isNew = false, onCreate, onUpdate, onDelete, onC
|
||||
<Card className="anuncio-card rounded-4 border-0 shadow-sm mb-4">
|
||||
<Card.Header className="d-flex justify-content-between align-items-center rounded-top-4 px-3 py-2">
|
||||
<div className="d-flex flex-column">
|
||||
<span className="fw-bold">📢 Anuncio #{anuncio.announce_id}</span>
|
||||
<small className="muted">
|
||||
Publicado el {date} a las {time} por{' '}
|
||||
<span className="fw-semibold">#{anuncio.published_by}</span>
|
||||
</small>
|
||||
<span className="fw-bold">📢 Anuncio {!createMode ? ("#"+anuncio.idx) : ("")}</span>
|
||||
{!createMode ? (
|
||||
<small className="muted">
|
||||
Publicado el {date} a las {time} por{' '}
|
||||
<span className="fw-semibold">{anuncio.publishedByName}</span>
|
||||
</small>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
{!createMode && !editMode && (
|
||||
<AnimatedDropdown
|
||||
|
||||
@@ -32,7 +32,6 @@ function App() {
|
||||
<NavBar />
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
{/*
|
||||
<Route path="/lista-espera" element={<ListaEspera />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/gestion/socios" element={
|
||||
@@ -80,7 +79,6 @@ function App() {
|
||||
<Correo />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
*/}
|
||||
<Route path="/*" element={<Maintenance />} />
|
||||
</Routes>
|
||||
{routesWithFooter.includes(useLocation().pathname) ? <Footer /> : null}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useAuth } from "../../hooks/useAuth.js";
|
||||
|
||||
const IfRole = ({ roles, children }) => {
|
||||
const { user, authStatus } = useAuth();
|
||||
const { identity, authStatus } = useAuth();
|
||||
|
||||
if (authStatus !== "authenticated") return null;
|
||||
|
||||
const userRole = user?.role;
|
||||
const userRole = identity?.metadata?.role;
|
||||
|
||||
return roles.includes(userRole) ? children : null;
|
||||
};
|
||||
|
||||
@@ -11,15 +11,15 @@ import CustomContainer from '../CustomContainer.jsx';
|
||||
import ContentWrapper from '../ContentWrapper.jsx';
|
||||
|
||||
import '../../css/LoginForm.css';
|
||||
import { CONSTANTS } from '../../util/constants.js';
|
||||
|
||||
const LoginForm = () => {
|
||||
const { login, error } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [formState, setFormState] = useState({
|
||||
emailOrUserName: "",
|
||||
password: "",
|
||||
keepLoggedIn: false
|
||||
username: "",
|
||||
password: ""
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
@@ -30,19 +30,12 @@ const LoginForm = () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formState.emailOrUserName);
|
||||
|
||||
const loginBody = {
|
||||
username: formState.username,
|
||||
password: formState.password,
|
||||
keepLoggedIn: Boolean(formState.keepLoggedIn),
|
||||
serviceId: CONSTANTS.SERVICE_ID
|
||||
};
|
||||
|
||||
if (isEmail) {
|
||||
loginBody.email = formState.emailOrUserName;
|
||||
} else {
|
||||
loginBody.userName = formState.emailOrUserName;
|
||||
}
|
||||
|
||||
try {
|
||||
await login(loginBody);
|
||||
navigate("/");
|
||||
@@ -63,15 +56,15 @@ const LoginForm = () => {
|
||||
label={
|
||||
<>
|
||||
<FontAwesomeIcon icon={faUser} className="me-2" />
|
||||
Usuario o Email
|
||||
Usuario
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder=""
|
||||
name="emailOrUserName"
|
||||
value={formState.emailOrUserName}
|
||||
name="username"
|
||||
value={formState.username}
|
||||
onChange={handleChange}
|
||||
className="rounded-4"
|
||||
/>
|
||||
@@ -83,7 +76,7 @@ const LoginForm = () => {
|
||||
name="password"
|
||||
/>
|
||||
|
||||
<div className="d-flex flex-column flex-sm-row justify-content-between align-items-center gap-2">
|
||||
{/*<div className="d-flex flex-column flex-sm-row justify-content-between align-items-center gap-2">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
name="keepLoggedIn"
|
||||
@@ -92,10 +85,10 @@ const LoginForm = () => {
|
||||
value={formState.keepLoggedIn}
|
||||
onChange={(e) => { formState.keepLoggedIn = e.target.checked; setFormState({ ...formState }) }}
|
||||
/>
|
||||
{/*<Link disabled to="#" className="muted">
|
||||
<Link disabled to="#" className="muted">
|
||||
Olvidé mi contraseña
|
||||
</Link>*/}
|
||||
</div>
|
||||
</Link>
|
||||
</div>*/}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
||||
@@ -6,10 +6,10 @@ import { faSpinner } from "@fortawesome/free-solid-svg-icons";
|
||||
const ProtectedRoute = ({ minimumRoles, children }) => {
|
||||
const { authStatus } = useAuth();
|
||||
|
||||
if (authStatus === "checking") return <FontAwesomeIcon icon={faSpinner} />; // o un loader si quieres
|
||||
if (authStatus === "checking") return <FontAwesomeIcon icon={faSpinner} />;
|
||||
if (authStatus === "unauthenticated") return <Navigate to="/login" replace />;
|
||||
if (authStatus === "authenticated" && minimumRoles) {
|
||||
const userRole = JSON.parse(localStorage.getItem("user"))?.role;
|
||||
const userRole = JSON.parse(localStorage.getItem("identity"))?.metadata?.role;
|
||||
if (!minimumRoles.includes(userRole)) return <Navigate to="/" replace />;
|
||||
}
|
||||
return children;
|
||||
|
||||
@@ -67,17 +67,17 @@ const formatCurrency = (value) =>
|
||||
|
||||
export const BalancePDF = ({ balance }) => {
|
||||
const {
|
||||
initial_bank,
|
||||
initial_cash,
|
||||
total_bank_expenses,
|
||||
total_cash_expenses,
|
||||
total_bank_incomes,
|
||||
total_cash_incomes,
|
||||
created_at
|
||||
initialBank,
|
||||
initialCash,
|
||||
totalBankExpenses,
|
||||
totalCashExpenses,
|
||||
totalBankIncomes,
|
||||
totalCashIncomes,
|
||||
createdAt
|
||||
} = balance;
|
||||
|
||||
const final_bank = initial_bank + total_bank_incomes - total_bank_expenses;
|
||||
const final_cash = initial_cash + total_cash_incomes - total_cash_expenses;
|
||||
const finalBank = initialBank + totalBankIncomes - totalBankExpenses;
|
||||
const finalCash = initialCash + totalCashIncomes - totalCashExpenses;
|
||||
|
||||
return (
|
||||
<Document>
|
||||
@@ -91,22 +91,22 @@ export const BalancePDF = ({ balance }) => {
|
||||
</View>
|
||||
|
||||
<Text style={styles.sectionTitle}>Banco</Text>
|
||||
<View style={styles.row}><Text style={styles.label}>Saldo inicial</Text><Text style={styles.value}>{formatCurrency(initial_bank)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Ingresos</Text><Text style={styles.value}>{formatCurrency(total_bank_incomes)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Gastos</Text><Text style={styles.value}>{formatCurrency(total_bank_expenses)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Saldo inicial</Text><Text style={styles.value}>{formatCurrency(initialBank)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Ingresos</Text><Text style={styles.value}>{formatCurrency(totalBankIncomes)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Gastos</Text><Text style={styles.value}>{formatCurrency(totalBankExpenses)}</Text></View>
|
||||
|
||||
<Text style={styles.sectionTitle}>Caja</Text>
|
||||
<View style={styles.row}><Text style={styles.label}>Saldo inicial</Text><Text style={styles.value}>{formatCurrency(initial_cash)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Ingresos</Text><Text style={styles.value}>{formatCurrency(total_cash_incomes)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Gastos</Text><Text style={styles.value}>{formatCurrency(total_cash_expenses)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Saldo inicial</Text><Text style={styles.value}>{formatCurrency(initialCash)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Ingresos</Text><Text style={styles.value}>{formatCurrency(totalCashIncomes)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Gastos</Text><Text style={styles.value}>{formatCurrency(totalCashExpenses)}</Text></View>
|
||||
|
||||
<Text style={styles.sectionTitle}>Total</Text>
|
||||
<View style={styles.row}><Text style={styles.label}>Banco</Text><Text style={styles.value}>{formatCurrency(final_bank)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Caja</Text><Text style={styles.value}>{formatCurrency(final_cash)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Total</Text><Text style={styles.value}>{formatCurrency(final_bank + final_cash)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Banco</Text><Text style={styles.value}>{formatCurrency(finalBank)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Caja</Text><Text style={styles.value}>{formatCurrency(finalCash)}</Text></View>
|
||||
<View style={styles.row}><Text style={styles.label}>Total</Text><Text style={styles.value}>{formatCurrency(finalBank + finalCash)}</Text></View>
|
||||
|
||||
<Text style={[styles.label, { marginTop: 20 }]}>
|
||||
Última actualización: {format(new Date(created_at), 'dd/MM/yyyy HH:mm')}
|
||||
Última actualización: {format(new Date(createdAt), 'dd/MM/yyyy HH:mm')}
|
||||
</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
|
||||
@@ -24,17 +24,17 @@ const BalanceReport = ({ balance }) => {
|
||||
const closePDFModal = () => setShowPDF(false);
|
||||
|
||||
const {
|
||||
initial_bank,
|
||||
initial_cash,
|
||||
total_bank_expenses,
|
||||
total_cash_expenses,
|
||||
total_bank_incomes,
|
||||
total_cash_incomes,
|
||||
created_at
|
||||
initialBank,
|
||||
initialCash,
|
||||
totalBankExpenses,
|
||||
totalCashExpenses,
|
||||
totalBankIncomes,
|
||||
totalCashIncomes,
|
||||
createdAt
|
||||
} = balance;
|
||||
|
||||
const final_bank = initial_bank + total_bank_incomes - total_bank_expenses;
|
||||
const final_cash = initial_cash + total_cash_incomes - total_cash_expenses;
|
||||
const finalBank = initialBank + totalBankIncomes - totalBankExpenses;
|
||||
const finalCash = initialCash + totalCashIncomes - totalCashExpenses;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -56,20 +56,20 @@ const BalanceReport = ({ balance }) => {
|
||||
<Col md={6}>
|
||||
<div className="balance-box">
|
||||
<h4><FontAwesomeIcon icon={faPiggyBank} className="me-2" />Banco</h4>
|
||||
<p>Saldo inicial: <span className="balance-value">{formatCurrency(initial_bank)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowUp} className="me-1 text-success" />Ingresos: <span className="balance-value">{formatCurrency(total_bank_incomes)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowDown} className="me-1 text-danger" />Gastos: <span className="balance-value">{formatCurrency(total_bank_expenses)}</span></p>
|
||||
<p className="fw-bold mt-3">💰 Saldo final: {formatCurrency(final_bank)}</p>
|
||||
<p>Saldo inicial: <span className="balance-value">{formatCurrency(initialBank)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowUp} className="me-1 text-success" />Ingresos: <span className="balance-value">{formatCurrency(totalBankIncomes)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowDown} className="me-1 text-danger" />Gastos: <span className="balance-value">{formatCurrency(totalBankExpenses)}</span></p>
|
||||
<p className="fw-bold mt-3">💰 Saldo final: {formatCurrency(finalBank)}</p>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<div className="balance-box">
|
||||
<h4><FontAwesomeIcon icon={faCoins} className="me-2" />Caja</h4>
|
||||
<p>Saldo inicial: <span className="balance-value">{formatCurrency(initial_cash)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowUp} className="me-1 text-success" />Ingresos: <span className="balance-value">{formatCurrency(total_cash_incomes)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowDown} className="me-1 text-danger" />Gastos: <span className="balance-value">{formatCurrency(total_cash_expenses)}</span></p>
|
||||
<p className="fw-bold mt-3">💵 Saldo final: {formatCurrency(final_cash)}</p>
|
||||
<p>Saldo inicial: <span className="balance-value">{formatCurrency(initialCash)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowUp} className="me-1 text-success" />Ingresos: <span className="balance-value">{formatCurrency(totalCashIncomes)}</span></p>
|
||||
<p><FontAwesomeIcon icon={faArrowDown} className="me-1 text-danger" />Gastos: <span className="balance-value">{formatCurrency(totalCashExpenses)}</span></p>
|
||||
<p className="fw-bold mt-3">💵 Saldo final: {formatCurrency(finalCash)}</p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -77,7 +77,7 @@ const BalanceReport = ({ balance }) => {
|
||||
<Row className="mt-4">
|
||||
<Col className="text-end balance-timestamp">
|
||||
<FontAwesomeIcon icon={faClock} className="me-2" />
|
||||
Última actualización: {format(new Date(created_at), 'dd/MM/yyyy HH:mm')}
|
||||
Última actualización: {format(new Date(createdAt), 'dd/MM/yyyy HH:mm')}
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
@@ -25,19 +25,19 @@ const File = ({ file, onDelete }) => {
|
||||
return (
|
||||
<Card
|
||||
className="file-card col-sm-3 col-lg-2 col-xxl-1 m-0 p-0 position-relative text-decoration-none bg-transparent"
|
||||
onClick={() => window.open(`https://miarma.net/files/huertos/${file.file_name}`, "_blank")}
|
||||
onClick={() => window.open(`https://miarma.net/files/huertos/${file.fileName}`, "_blank")}
|
||||
>
|
||||
<Card.Body className="text-center">
|
||||
<img
|
||||
src={getIcon(file.mime_type)}
|
||||
alt={file.file_name}
|
||||
src={getIcon(file.mimeType)}
|
||||
alt={file.fileName}
|
||||
className="img-fluid mb-2"
|
||||
/>
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
overlay={<Tooltip>{file.file_name}</Tooltip>}
|
||||
overlay={<Tooltip>{file.fileName}</Tooltip>}
|
||||
>
|
||||
<p className="m-0 p-0 text-truncate">{file.file_name}</p>
|
||||
<p className="m-0 p-0 text-truncate">{file.fileName}</p>
|
||||
</OverlayTrigger>
|
||||
</Card.Body>
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
supplier: gasto.supplier || '',
|
||||
invoice: gasto.invoice || '',
|
||||
type: gasto.type ?? 0,
|
||||
created_at: gasto.created_at?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
createdAt: gasto.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -60,7 +60,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
supplier: gasto.supplier || '',
|
||||
invoice: gasto.invoice || '',
|
||||
type: gasto.type ?? 0,
|
||||
created_at: gasto.created_at?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
createdAt: gasto.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -68,7 +68,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
|
||||
const handleChange = (field, value) => setFormData(prev => ({ ...prev, [field]: value }));
|
||||
|
||||
const handleDelete = () => typeof onDelete === 'function' && onDelete(gasto.expense_id);
|
||||
const handleDelete = () => typeof onDelete === 'function' && onDelete(gasto.expenseId);
|
||||
|
||||
const handleCancel = () => {
|
||||
if (onClearError) onClearError();
|
||||
@@ -80,7 +80,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
if (onClearError) onClearError();
|
||||
const newExpense = { ...gasto, ...formData };
|
||||
if (createMode && typeof onCreate === 'function') return onCreate(newExpense);
|
||||
if (typeof onUpdate === 'function') return onUpdate(newExpense, gasto.expense_id);
|
||||
if (typeof onUpdate === 'function') return onUpdate(newExpense, gasto.expenseId);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -102,13 +102,13 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
<small>
|
||||
{editMode ? (
|
||||
<SpanishDateTimePicker
|
||||
selected={new Date(formData.created_at)}
|
||||
selected={new Date(formData.createdAt)}
|
||||
onChange={(date) =>
|
||||
handleChange('created_at', date.toISOString().slice(0, 16))
|
||||
handleChange('createdAt', date.toISOString().slice(0, 16))
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
DateParser.isoToStringWithTime(formData.created_at)
|
||||
DateParser.isoToStringWithTime(formData.createdAt)
|
||||
)}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
@@ -104,7 +104,7 @@ export const GastosPDF = ({ gastos }) => (
|
||||
{ borderBottomRightRadius: idx === gastos.length - 1 ? 10 : 0 },
|
||||
]}
|
||||
>
|
||||
<Text style={[styles.cell, { flex: 2 }]}>{parseDate(gasto.created_at)}</Text>
|
||||
<Text style={[styles.cell, { flex: 2 }]}>{parseDate(gasto.createdAt)}</Text>
|
||||
<Text style={[styles.cell, { flex: 4 }]}>{gasto.concept}</Text>
|
||||
<Text style={[styles.cell, { flex: 2 }]}>{gasto.amount.toFixed(2)} €</Text>
|
||||
<Text style={[styles.cell, { flex: 3 }]}>{gasto.supplier}</Text>
|
||||
|
||||
@@ -63,8 +63,8 @@ const IngresoCard = ({
|
||||
amount: income.amount || 0,
|
||||
type: income.type ?? CONSTANTS.PAYMENT_TYPE_CASH,
|
||||
frequency: income.frequency ?? CONSTANTS.PAYMENT_FREQUENCY_YEARLY,
|
||||
member_number: income.member_number,
|
||||
created_at: income.created_at?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
memberNumber: income.memberNumber,
|
||||
createdAt: income.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -74,9 +74,9 @@ const IngresoCard = ({
|
||||
amount: income.amount || 0,
|
||||
type: income.type ?? CONSTANTS.PAYMENT_TYPE_CASH,
|
||||
frequency: income.frequency ?? CONSTANTS.PAYMENT_FREQUENCY_YEARLY,
|
||||
display_name: income.display_name,
|
||||
member_number: income.member_number,
|
||||
created_at: income.created_at?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
displayName: income.displayName,
|
||||
memberNumber: income.memberNumber,
|
||||
createdAt: income.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -95,14 +95,14 @@ const IngresoCard = ({
|
||||
if (onClearError) onClearError();
|
||||
const newIncome = { ...income, ...formData };
|
||||
if (createMode && typeof onCreate === 'function') return onCreate(newIncome);
|
||||
if (typeof onUpdate === 'function') return onUpdate(newIncome, income.income_id);
|
||||
if (typeof onUpdate === 'function') return onUpdate(newIncome, income.incomeId);
|
||||
};
|
||||
|
||||
const handleDelete = () => typeof onDelete === 'function' && onDelete(income.income_id);
|
||||
const handleDelete = () => typeof onDelete === 'function' && onDelete(income.incomeId);
|
||||
|
||||
const uniqueMembers = Array.from(
|
||||
new Map(members.map(item => [item.member_number, item])).values()
|
||||
).sort((a, b) => a.member_number - b.member_number);
|
||||
new Map(members.map(item => [item.memberNumber, item])).values()
|
||||
).sort((a, b) => a.memberNumber - b.memberNumber);
|
||||
|
||||
return (
|
||||
<MotionCard className={`ingreso-card shadow-sm rounded-4 border-0 h-100 ${className}`}>
|
||||
@@ -125,13 +125,13 @@ const IngresoCard = ({
|
||||
<small>
|
||||
{editMode ? (
|
||||
<SpanishDateTimePicker
|
||||
selected={new Date(formData.created_at)}
|
||||
selected={new Date(formData.createdAt)}
|
||||
onChange={(date) =>
|
||||
handleChange('created_at', date.toISOString().slice(0, 16))
|
||||
handleChange('createdAt', date.toISOString().slice(0, 16))
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
DateParser.isoToStringWithTime(formData.created_at)
|
||||
DateParser.isoToStringWithTime(formData.createdAt)
|
||||
)}
|
||||
</small>
|
||||
</div>
|
||||
@@ -167,13 +167,13 @@ const IngresoCard = ({
|
||||
<Form.Select
|
||||
className="themed-input"
|
||||
size="sm"
|
||||
value={formData.member_number}
|
||||
onChange={(e) => handleChange('member_number', parseInt(e.target.value))}
|
||||
value={formData.memberNumber}
|
||||
onChange={(e) => handleChange('memberNumber', parseInt(e.target.value))}
|
||||
style={{ maxWidth: '300px', display: 'inline-block' }}
|
||||
>
|
||||
{uniqueMembers.map((m) => (
|
||||
<option key={m.member_number} value={m.member_number}>
|
||||
{`${m.display_name} (${m.member_number})`}
|
||||
<option key={m.memberNumber} value={m.memberNumber}>
|
||||
{`${m.displayName} (${m.memberNumber})`}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
@@ -187,24 +187,24 @@ const IngresoCard = ({
|
||||
disabled
|
||||
size="sm"
|
||||
type="text"
|
||||
value={`${formData.display_name || 'Socio'} (${formData.member_number})`}
|
||||
value={`${formData.displayName || 'Socio'} (${formData.memberNumber})`}
|
||||
style={{ maxWidth: '300px', display: 'inline-block' }}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
) : (
|
||||
formData.display_name ? (
|
||||
formData.displayName ? (
|
||||
<>
|
||||
<OverlayTrigger
|
||||
placement="top"
|
||||
overlay={<Tooltip>{formData.display_name}</Tooltip>}
|
||||
overlay={<Tooltip>{formData.displayName}</Tooltip>}
|
||||
>
|
||||
<span className="text-truncate d-inline-block" style={{ maxWidth: '200px', verticalAlign: 'middle' }}>
|
||||
{formData.display_name}
|
||||
{formData.displayName}
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
({formData.member_number})
|
||||
({formData.memberNumber})
|
||||
</>
|
||||
) : formData.member_number
|
||||
) : formData.memberNumber
|
||||
)}
|
||||
</Card.Text>
|
||||
|
||||
|
||||
@@ -105,12 +105,12 @@ export const IngresosPDF = ({ ingresos }) => (
|
||||
{ borderBottomRightRadius: idx === ingresos.length - 1 ? 10 : 0 },
|
||||
]}
|
||||
>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{ing.member_number}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{ing.memberNumber}</Text>
|
||||
<Text style={[styles.cell, { flex: 3 }]}>{ing.concept}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{ing.amount.toFixed(2)} €</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{getTypeLabel(ing.type)}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{getFreqLabel(ing.frequency)}</Text>
|
||||
<Text style={[styles.cell, { flex: 2 }]}>{parseDate(ing.created_at)}</Text>
|
||||
<Text style={[styles.cell, { flex: 2 }]}>{parseDate(ing.createdAt)}</Text>
|
||||
</View>
|
||||
))}
|
||||
</Page>
|
||||
|
||||
@@ -27,7 +27,7 @@ import AnimatedDropdown from '../AnimatedDropdown.jsx';
|
||||
import { CONSTANTS } from '../../util/constants.js';
|
||||
|
||||
const NavBar = () => {
|
||||
const { user, logout } = useAuth();
|
||||
const { identity, logout } = useAuth();
|
||||
const [showingUserDropdown, setShowingUserDropdown] = useState(false);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [isLg, setIsLg] = useState(window.innerWidth >= 992);
|
||||
@@ -138,7 +138,7 @@ const NavBar = () => {
|
||||
onToggle={(isOpen) => setShowingUserDropdown(isOpen)}
|
||||
trigger={
|
||||
<Link className="nav-link dropdown-toggle fw-bold">
|
||||
@{user?.user_name}
|
||||
@{identity?.account?.username}
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -45,16 +45,16 @@ const renderDateField = (label, icon, dateValue, editMode, fieldKey, handleChang
|
||||
};
|
||||
|
||||
const getFechas = (formData, editMode, handleChange) => {
|
||||
const { created_at, assigned_at, deactivated_at } = formData;
|
||||
const { createdAt, assignedAt, deactivatedAt } = formData;
|
||||
|
||||
// Si no hay fechas y no está en modo edición, no muestres nada
|
||||
if (!editMode && !created_at && !assigned_at && !deactivated_at) return null;
|
||||
if (!editMode && !createdAt && !assignedAt && !deactivatedAt) return null;
|
||||
|
||||
return (
|
||||
<ListGroup className="mt-2 border-1 rounded-3 shadow-sm">
|
||||
{renderDateField("ALTA", faCalendar, created_at, editMode, "created_at", handleChange)}
|
||||
{renderDateField("ENTREGA", faCalendar, assigned_at, editMode, "assigned_at", handleChange)}
|
||||
{renderDateField("BAJA", faCalendar, deactivated_at, editMode, "deactivated_at", handleChange)}
|
||||
{renderDateField("ALTA", faCalendar, createdAt, editMode, "createdAt", handleChange)}
|
||||
{renderDateField("ENTREGA", faCalendar, assignedAt, editMode, "assignedAt", handleChange)}
|
||||
{renderDateField("BAJA", faCalendar, deactivatedAt, editMode, "deactivatedAt", handleChange)}
|
||||
</ListGroup>
|
||||
);
|
||||
};
|
||||
@@ -91,7 +91,7 @@ const getPFP = (tipo) => {
|
||||
|
||||
const MotionCard = _motion.create(Card);
|
||||
|
||||
const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCancel, onViewIncomes, error, onClearError, positionIfWaitlist }) => {
|
||||
const SocioCard = ({ identity, isNew = false, onCreate, onUpdate, onDelete, onCancel, onViewIncomes, error, onClearError, positionIfWaitlist }) => {
|
||||
const createMode = isNew;
|
||||
const [editMode, setEditMode] = useState(isNew);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
@@ -99,60 +99,59 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
const { getData } = useDataContext();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
display_name: socio.display_name,
|
||||
user_name: socio.user_name,
|
||||
email: socio.email || '',
|
||||
dni: socio.dni,
|
||||
phone: socio.phone,
|
||||
member_number: socio.member_number || latestNumber,
|
||||
plot_number: socio.plot_number,
|
||||
notes: socio.notes || '',
|
||||
status: socio.status,
|
||||
type: socio.type,
|
||||
created_at: socio.created_at?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
assigned_at: socio.assigned_at?.slice(0, 16) || undefined,
|
||||
deactivated_at: socio.deactivated_at?.slice(0, 16) || undefined,
|
||||
global_role: 0,
|
||||
displayName: identity?.user.displayName,
|
||||
userName: identity?.account.username,
|
||||
email: identity?.account.email || '',
|
||||
dni: identity?.metadata.dni,
|
||||
phone: identity?.metadata.phone,
|
||||
memberNumber: identity?.metadata.memberNumber || latestNumber,
|
||||
plotNumber: identity?.metadata.plotNumber,
|
||||
notes: identity?.metadata.notes || '',
|
||||
status: identity?.account.status,
|
||||
type: identity?.metadata.type,
|
||||
createdAt: identity?.metadata.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
assignedAt: identity?.metadata.assignedAt?.slice(0, 16) || undefined,
|
||||
deactivatedAt: identity?.metadata.deactivatedAt?.slice(0, 16) || undefined,
|
||||
globalRole: 0,
|
||||
password: createMode && !editMode ? generateSecurePassword() : null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!editMode) {
|
||||
setFormData({
|
||||
display_name: socio.display_name,
|
||||
user_name: socio.user_name,
|
||||
email: socio.email || '',
|
||||
dni: socio.dni,
|
||||
phone: socio.phone,
|
||||
member_number: socio.member_number,
|
||||
plot_number: socio.plot_number,
|
||||
notes: socio.notes || '',
|
||||
status: socio.status,
|
||||
type: socio.type,
|
||||
created_at: socio.created_at?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
assigned_at: socio.assigned_at?.slice(0, 16) || undefined,
|
||||
deactivated_at: socio.deactivated_at?.slice(0, 16) || undefined,
|
||||
global_role: 0,
|
||||
displayName: identity.user.displayName,
|
||||
userName: identity.account.username,
|
||||
email: identity.account.email || '',
|
||||
dni: identity.metadata.dni,
|
||||
phone: identity.metadata.phone,
|
||||
memberNumber: identity.metadata.memberNumber,
|
||||
plotNumber: identity.metadata.plotNumber,
|
||||
notes: identity.metadata.notes || '',
|
||||
status: identity.account.status,
|
||||
type: identity.metadat.type,
|
||||
createdAt: identity.metadata.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''),
|
||||
assignedAt: identity.metadata.assignedAt?.slice(0, 16) || undefined,
|
||||
deactivatedAt: identity.metadata.deactivatedAt?.slice(0, 16) || undefined,
|
||||
globalRole: 0,
|
||||
password: createMode ? generateSecurePassword() : ''
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [socio, editMode]);
|
||||
}, [identity, editMode]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLastNumber = async () => {
|
||||
try {
|
||||
if (!(createMode || editMode)) return;
|
||||
|
||||
const { data, error } = await getData("https://api.huertosbellavista.es/v1/members/latest-number");
|
||||
if (error) throw new Error(error);
|
||||
const latestNumber = await getData("http://localhost:8081/v2/huertos/users/latest-number");
|
||||
|
||||
const nuevoNumero = data.lastMemberNumber + 1;
|
||||
const nuevoNumero = latestNumber + 1;
|
||||
setLatestNumber(nuevoNumero);
|
||||
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
member_number: prev.member_number || nuevoNumero
|
||||
memberNumber: prev.memberNumber || nuevoNumero
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error("Error al obtener el número de socio:", err);
|
||||
@@ -167,7 +166,7 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
setEditMode(true);
|
||||
};
|
||||
|
||||
const handleDelete = () => typeof onDelete === "function" && onDelete(socio.user_id);
|
||||
const handleDelete = () => typeof onDelete === "function" && onDelete(identity.user.userId);
|
||||
|
||||
const handleCancel = () => {
|
||||
if (onClearError) onClearError();
|
||||
@@ -177,16 +176,16 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
|
||||
const handleSave = () => {
|
||||
if (onClearError) onClearError();
|
||||
const newSocio = { ...socio, ...formData };
|
||||
const newSocio = { ...identity, ...formData };
|
||||
if (createMode && typeof onCreate === 'function') return onCreate(newSocio);
|
||||
if (typeof onUpdate === 'function') return onUpdate(newSocio, socio.user_id);
|
||||
if (typeof onUpdate === 'function') return onUpdate(newSocio, identity.user.userId);
|
||||
};
|
||||
|
||||
const handleChange = (field, value) => {
|
||||
if (["member_number"].includes(field)) {
|
||||
if (["memberNumber"].includes(field)) {
|
||||
value = value === "" ? latestNumber : parseInt(value);
|
||||
}
|
||||
if (field === "display_name") {
|
||||
if (field === "displayName") {
|
||||
value = value.toUpperCase();
|
||||
}
|
||||
if (field === "dni") {
|
||||
@@ -196,7 +195,7 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
};
|
||||
|
||||
const handleViewIncomes = () => {
|
||||
onViewIncomes(socio.user_id);
|
||||
onViewIncomes(identity.user.userId);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -206,7 +205,7 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
{editMode ? (
|
||||
<TipoSocioDropdown value={formData.type} onChange={(val) => handleChange('type', val)} />
|
||||
) : (
|
||||
positionIfWaitlist && socio.type === 0 ? (
|
||||
positionIfWaitlist && identity.metadata.type === 0 ? (
|
||||
<OverlayTrigger
|
||||
placement="top"
|
||||
overlay={
|
||||
@@ -225,8 +224,8 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
<div className='d-flex flex-column gap-1'>
|
||||
<Card.Title className="m-0">
|
||||
{editMode ? (
|
||||
<Form.Control className="themed-input" size="sm" value={formData.display_name} onChange={(e) => handleChange('display_name', e.target.value)} style={{ maxWidth: '220px' }} />
|
||||
) : formData.display_name}
|
||||
<Form.Control className="themed-input" size="sm" value={formData.displayName} onChange={(e) => handleChange('displayName', e.target.value)} style={{ maxWidth: '220px' }} />
|
||||
) : formData.displayName}
|
||||
</Card.Title>
|
||||
{editMode ? (
|
||||
<Form.Select className="themed-input" size="sm" value={formData.status} onChange={(e) => handleChange('status', parseInt(e.target.value))} style={{ maxWidth: '8rem' }}>
|
||||
@@ -269,9 +268,9 @@ const SocioCard = ({ socio, isNew = false, onCreate, onUpdate, onDelete, onCance
|
||||
{[{
|
||||
label: 'DNI', clazz: '', icon: faIdCard, value: formData.dni, field: 'dni', type: 'text', maxWidth: '180px'
|
||||
}, {
|
||||
label: 'SOCIO Nº', clazz: '', icon: faUser, value: formData.member_number || latestNumber, field: 'member_number', type: 'number', maxWidth: '100px'
|
||||
label: 'SOCIO Nº', clazz: '', icon: faUser, value: formData.memberNumber || latestNumber, field: 'memberNumber', type: 'number', maxWidth: '100px'
|
||||
}, {
|
||||
label: 'HUERTO Nº', clazz: '', icon: faSunPlantWilt, value: formData.plot_number, field: 'plot_number', type: 'number', maxWidth: '100px'
|
||||
label: 'HUERTO Nº', clazz: '', icon: faSunPlantWilt, value: formData.plotNumber, field: 'plotNumber', type: 'number', maxWidth: '100px'
|
||||
}, {
|
||||
label: 'TLF.', clazz: '', icon: faPhone, value: formData.phone, field: 'phone', type: 'number', maxWidth: '200px'
|
||||
}, {
|
||||
|
||||
@@ -107,13 +107,13 @@ export const SociosPDF = ({ socios }) => (
|
||||
{ borderBottomRightRadius: idx === socios.length - 1 ? 10 : 0 },
|
||||
]}
|
||||
>
|
||||
<Text style={[styles.cell, { flex: 0.2 }]}>{socio?.member_number}</Text>
|
||||
<Text style={[styles.cell, { flex: 0.2 }]}>{socio?.plot_number}</Text>
|
||||
<Text style={[styles.cell, { flex: 3 }]}>{socio?.display_name}</Text>
|
||||
<Text style={[styles.cell, { flex: 0.2 }]}>{socio?.memberNumber}</Text>
|
||||
<Text style={[styles.cell, { flex: 0.2 }]}>{socio?.plotNumber}</Text>
|
||||
<Text style={[styles.cell, { flex: 3 }]}>{socio?.displayName}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{socio?.dni}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{socio?.phone}</Text>
|
||||
<Text style={[styles.cell, { flex: 3 }]}>{socio?.email || ''}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{parseDate(socio?.created_at?.split('T')[0] || '')}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>{parseDate(socio?.createdAt?.split('T')[0] || '')}</Text>
|
||||
<Text style={[styles.cell, { flex: 1 }]}>
|
||||
{(() => {
|
||||
switch (socio?.type) {
|
||||
|
||||
@@ -3,22 +3,25 @@ import { Form, Row, Col, Button } from 'react-bootstrap';
|
||||
import { useDataContext } from '../../hooks/useDataContext';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
import { generateSecurePassword } from '../../util/passwordGenerator';
|
||||
|
||||
|
||||
const PreUserForm = ({ onSubmit, userType, plotNumber, errors = {} }) => {
|
||||
const { getData } = useDataContext();
|
||||
const fetchedOnce = useRef(false);
|
||||
|
||||
const [form, setForm] = useState({
|
||||
user_name: '',
|
||||
display_name: '',
|
||||
userName: '',
|
||||
password: generateSecurePassword(8),
|
||||
displayName: '',
|
||||
dni: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
address: '',
|
||||
zip_code: '',
|
||||
zipCode: '',
|
||||
city: '',
|
||||
member_number: '',
|
||||
plot_number: plotNumber,
|
||||
memberNumber: '',
|
||||
plotNumber: plotNumber,
|
||||
type: userType,
|
||||
status: 1,
|
||||
role: 0
|
||||
@@ -30,12 +33,10 @@ const PreUserForm = ({ onSubmit, userType, plotNumber, errors = {} }) => {
|
||||
fetchedOnce.current = true;
|
||||
|
||||
try {
|
||||
const { data, error } = await getData("https://api.huertosbellavista.es/v1/members/latest-number");
|
||||
if (error) throw new Error(error);
|
||||
|
||||
const latestNumber = await getData("http://localhost:8081/v2/huertos/users/latest-number");
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
member_number: data.lastMemberNumber + 1
|
||||
memberNumber: latestNumber + 1
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error("Error al obtener el número de socio:", err);
|
||||
@@ -47,21 +48,21 @@ const PreUserForm = ({ onSubmit, userType, plotNumber, errors = {} }) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const trimmedName = form.display_name?.trim() ?? "";
|
||||
const trimmedName = form.displayName?.trim() ?? "";
|
||||
|
||||
const nuevoUsername = trimmedName
|
||||
? trimmedName.split(' ')[0].toLowerCase() : "";
|
||||
|
||||
if (form.user_name !== nuevoUsername) {
|
||||
setForm(prev => ({ ...prev, user_name: nuevoUsername }));
|
||||
if (form.userName !== nuevoUsername) {
|
||||
setForm(prev => ({ ...prev, userName: nuevoUsername }));
|
||||
}
|
||||
}, [form.member_number, form.display_name, form.user_name]);
|
||||
}, [form.memberNumber, form.displayName, form.userName]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value, type } = e.target;
|
||||
let updatedValue = value;
|
||||
|
||||
if (name === 'display_name' || name === 'dni') {
|
||||
if (name === 'displayName' || name === 'dni') {
|
||||
updatedValue = value.toUpperCase();
|
||||
}
|
||||
|
||||
@@ -84,13 +85,13 @@ const PreUserForm = ({ onSubmit, userType, plotNumber, errors = {} }) => {
|
||||
<Row className="gy-3">
|
||||
|
||||
{[
|
||||
{ label: 'Nombre completo', name: 'display_name', type: 'text', required: true },
|
||||
{ label: 'Nombre de usuario', name: 'user_name', type: 'text', required: true },
|
||||
{ label: 'Nombre completo', name: 'displayName', type: 'text', required: true },
|
||||
{ label: 'Nombre de usuario', name: 'userName', type: 'text', required: true },
|
||||
{ label: 'DNI', name: 'dni', type: 'text', required: true, maxLength: 9 },
|
||||
{ label: 'Teléfono', name: 'phone', type: 'tel', required: true },
|
||||
{ label: 'Correo electrónico', name: 'email', type: 'email', required: true },
|
||||
{ label: 'Domicilio', name: 'address', type: 'text' },
|
||||
{ label: 'Código Postal', name: 'zip_code', type: 'text' },
|
||||
{ label: 'Código Postal', name: 'zipCode', type: 'text' },
|
||||
{ label: 'Ciudad', name: 'city', type: 'text' }
|
||||
].map(({ label, name, type, required, maxLength }) => (
|
||||
<Col md={4} key={name}>
|
||||
@@ -120,8 +121,8 @@ const PreUserForm = ({ onSubmit, userType, plotNumber, errors = {} }) => {
|
||||
className="shadow-sm"
|
||||
disabled
|
||||
type="number"
|
||||
name="member_number"
|
||||
value={form.member_number}
|
||||
name="memberNumber"
|
||||
value={form.memberNumber}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -35,15 +35,15 @@ const getPFP = (tipo) => {
|
||||
};
|
||||
|
||||
const renderDescripcionSolicitud = (data, onProfile) => {
|
||||
const { request_type, request_status, requested_by_name, pre_display_name } = data;
|
||||
const { type, status, requestedByName, preDisplayName } = data;
|
||||
|
||||
switch (request_type) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
if (requested_by_name) {
|
||||
return `${requested_by_name} quiere darse de alta.`;
|
||||
} else if (request_status !== 1 && pre_display_name) {
|
||||
return `${pre_display_name} quiere darse de alta.`;
|
||||
} else if (request_status !== 1) {
|
||||
if (requestedByName) {
|
||||
return `${requestedByName} quiere darse de alta.`;
|
||||
} else if (status !== 1 && preDisplayName) {
|
||||
return `${preDisplayName} quiere darse de alta.`;
|
||||
} else if (status !== 1) {
|
||||
return `Alguien quiere darse de alta.`;
|
||||
} else {
|
||||
return `Se ha aceptado esta solicitud de alta.`;
|
||||
@@ -52,30 +52,30 @@ const renderDescripcionSolicitud = (data, onProfile) => {
|
||||
case 1:
|
||||
return onProfile
|
||||
? "Has solicitado darte de baja."
|
||||
: requested_by_name
|
||||
? `${requested_by_name} quiere darse de baja.`
|
||||
: request_status !== 1
|
||||
: requestedByName
|
||||
? `${requestedByName} quiere darse de baja.`
|
||||
: status !== 1
|
||||
? `Alguien quiere darse de baja.`
|
||||
: `Se ha aceptado esta solicitud de baja.`;
|
||||
|
||||
case 2:
|
||||
if (onProfile) {
|
||||
switch (request_status) {
|
||||
switch (status) {
|
||||
case 0: return "Has solicitado añadir un colaborador.";
|
||||
case 1: return "Tu solicitud de colaborador ha sido aceptada.";
|
||||
case 2: return "Tu solicitud de colaborador ha sido rechazada.";
|
||||
default: return "Solicitud de colaborador desconocida.";
|
||||
}
|
||||
} else {
|
||||
switch (request_status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return requested_by_name
|
||||
? `${requested_by_name} quiere añadir a ${pre_display_name || "un colaborador"} como colaborador.`
|
||||
: `Alguien quiere añadir a ${pre_display_name || "un colaborador"} como colaborador.`;
|
||||
return requestedByName
|
||||
? `${requestedByName} quiere añadir a ${preDisplayName || "un colaborador"} como colaborador.`
|
||||
: `Alguien quiere añadir a ${preDisplayName || "un colaborador"} como colaborador.`;
|
||||
case 1:
|
||||
return `La solicitud de colaborador de ${requested_by_name || "alguien"} ha sido aceptada.`;
|
||||
return `La solicitud de colaborador de ${requestedByName || "alguien"} ha sido aceptada.`;
|
||||
case 2:
|
||||
return `La solicitud de colaborador de ${requested_by_name || "alguien"} ha sido rechazada.`;
|
||||
return `La solicitud de colaborador de ${requestedByName || "alguien"} ha sido rechazada.`;
|
||||
default:
|
||||
return "Solicitud de colaborador desconocida.";
|
||||
}
|
||||
@@ -84,27 +84,27 @@ const renderDescripcionSolicitud = (data, onProfile) => {
|
||||
case 3:
|
||||
return onProfile
|
||||
? "Has solicitado quitar tu colaborador."
|
||||
: requested_by_name
|
||||
? `${requested_by_name} quiere quitar su colaborador.`
|
||||
: request_status !== 1
|
||||
: requestedByName
|
||||
? `${requestedByName} quiere quitar su colaborador.`
|
||||
: status !== 1
|
||||
? `Alguien quiere quitar su colaborador.`
|
||||
: `Se ha aceptado esta solicitud de baja de colaborador.`;
|
||||
|
||||
case 4:
|
||||
return onProfile
|
||||
? "Has solicitado una parcela en el invernadero."
|
||||
: requested_by_name
|
||||
? `${requested_by_name} quiere una parcela en el invernadero.`
|
||||
: request_status !== 1
|
||||
: requestedByName
|
||||
? `${requestedByName} quiere una parcela en el invernadero.`
|
||||
: status !== 1
|
||||
? `Alguien quiere una parcela en el invernadero.`
|
||||
: `Se ha aceptado esta solicitud de parcela en el invernadero.`;
|
||||
|
||||
case 5:
|
||||
return onProfile
|
||||
? "Has solicitado dejar tu parcela del invernadero."
|
||||
: requested_by_name
|
||||
? `${requested_by_name} quiere dejar su parcela del invernadero.`
|
||||
: request_status !== 1
|
||||
: requestedByName
|
||||
? `${requestedByName} quiere dejar su parcela del invernadero.`
|
||||
: status !== 1
|
||||
? `Alguien quiere dejar su parcela del invernadero.`
|
||||
: `Se ha aceptado esta solicitud de salida del invernadero.`;
|
||||
|
||||
@@ -114,18 +114,18 @@ const renderDescripcionSolicitud = (data, onProfile) => {
|
||||
};
|
||||
|
||||
const SolicitudCard = ({ data, onAccept, onReject, onDelete, editable = true, onProfile = false }) => {
|
||||
const handleDelete = () => typeof onDelete === "function" && onDelete(data.request_id);
|
||||
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(data.pre_type)} width="36" className="rounded me-3" alt="PFP" />
|
||||
<img src={getPFP(data.preType)} width="36" className="rounded me-3" alt="PFP" />
|
||||
<div>
|
||||
<Card.Title className="mb-0">
|
||||
Solicitud #{data.request_id} - {getTipoSolicitud(data.request_type)}
|
||||
Solicitud #{data.requestId} - {getTipoSolicitud(data.type)}
|
||||
</Card.Title>
|
||||
<small className='state-small'>Estado: <strong>{getEstadoSolicitud(data.request_status)}</strong></small>
|
||||
<small className='state-small'>Estado: <strong>{getEstadoSolicitud(data.status)}</strong></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +147,7 @@ const SolicitudCard = ({ data, onAccept, onReject, onDelete, editable = true, on
|
||||
<ListGroup variant="flush" className="border rounded-3 mb-3">
|
||||
<ListGroup.Item>
|
||||
<FontAwesomeIcon icon={faCalendar} className="me-2" />
|
||||
Fecha de solicitud: <strong>{parseDate(data.request_created_at)}</strong>
|
||||
Fecha de solicitud: <strong>{parseDate(data.request_createdAt)}</strong>
|
||||
</ListGroup.Item>
|
||||
</ListGroup>
|
||||
|
||||
@@ -157,24 +157,24 @@ const SolicitudCard = ({ data, onAccept, onReject, onDelete, editable = true, on
|
||||
</ListGroup.Item>
|
||||
</ListGroup>
|
||||
|
||||
{data.pre_display_name && (
|
||||
{data.preDisplayName && (
|
||||
<>
|
||||
<Card.Subtitle className="card-subtitle mt-3 mb-2">Datos del futuro socio</Card.Subtitle>
|
||||
<ListGroup variant="flush" className="border rounded-3">
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faUser} className="me-2" />Nombre: <strong>{data.pre_display_name}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faIdCard} className="me-2" />DNI: <strong>{data.pre_dni}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faPhone} className="me-2" />Teléfono: <strong>{data.pre_phone}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faEnvelope} className="me-2" />Email: <strong>{data.pre_email}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faHome} className="me-2" />Dirección: <strong>{data.pre_address ?? 'NO'}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faMapMarkerAlt} className="me-2" />Ciudad: <strong>{data.pre_city ?? 'NO'} ({data.pre_zip_code ?? 'NO'})</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faHashtag} className="me-2" />Nº socio: <strong>{data.pre_member_number ?? 'NO'}</strong> | Nº huerto: <strong>{data.pre_plot_number ?? 'NO'}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faSeedling} className="me-2" />Tipo: <strong>{['Lista de Espera', 'Hortelano', 'Hortelano + Invernadero', 'Colaborador'][data.pre_type]}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faUserShield} className="me-2" />Rol: <strong>{['Usuario', 'Admin', 'Desarrollador'][data.pre_role]}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faUser} className="me-2" />Nombre: <strong>{data.preDisplayName}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faIdCard} className="me-2" />DNI: <strong>{data.preDni}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faPhone} className="me-2" />Teléfono: <strong>{data.prePhone}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faEnvelope} className="me-2" />Email: <strong>{data.preEmail}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faHome} className="me-2" />Dirección: <strong>{data.preAddress ?? 'NO'}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faMapMarkerAlt} className="me-2" />Ciudad: <strong>{data.preCity ?? 'NO'} ({data.preZipCode ?? 'NO'})</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faHashtag} className="me-2" />Nº socio: <strong>{data.preMemberNumber ?? 'NO'}</strong> | Nº huerto: <strong>{data.prePlotNumber ?? 'NO'}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faSeedling} className="me-2" />Tipo: <strong>{['Lista de Espera', 'Hortelano', 'Hortelano + Invernadero', 'Colaborador'][data.preType]}</strong></ListGroup.Item>
|
||||
<ListGroup.Item><FontAwesomeIcon icon={faUserShield} className="me-2" />Rol: <strong>{['Usuario', 'Admin', 'Desarrollador'][data.preRole]}</strong></ListGroup.Item>
|
||||
</ListGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{editable && data.request_status === 0 && (
|
||||
{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>
|
||||
|
||||
Reference in New Issue
Block a user