trimmed weight edit
This commit is contained in:
+15
-1
@@ -306,6 +306,15 @@ const weightSchema = z.object({
|
|||||||
recordedOn: dateStringSchema,
|
recordedOn: dateStringSchema,
|
||||||
notes: z.string().trim().max(280).optional().or(z.literal('')),
|
notes: z.string().trim().max(280).optional().or(z.literal('')),
|
||||||
});
|
});
|
||||||
|
const weightEditWindowDays = 3;
|
||||||
|
|
||||||
|
const getWeightEditEarliestDate = () => {
|
||||||
|
const earliestDate = new Date();
|
||||||
|
earliestDate.setDate(earliestDate.getDate() - (weightEditWindowDays - 1));
|
||||||
|
return earliestDate.toISOString().slice(0, 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWeightDateEditable = (recordedOn: string) => recordedOn >= getWeightEditEarliestDate();
|
||||||
|
|
||||||
const vetVisitSchema = z.object({
|
const vetVisitSchema = z.object({
|
||||||
visitedOn: dateStringSchema,
|
visitedOn: dateStringSchema,
|
||||||
@@ -4094,6 +4103,11 @@ app.put(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isWeightDateEditable(parsed.data.recordedOn)) {
|
||||||
|
res.status(409).json({ error: 'Weight entries can only be edited for the last 3 days.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const weight = await updateWeightForBird(
|
const weight = await updateWeightForBird(
|
||||||
req.params.weightId,
|
req.params.weightId,
|
||||||
req.params.birdId,
|
req.params.birdId,
|
||||||
@@ -4103,7 +4117,7 @@ app.put(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!weight) {
|
if (!weight) {
|
||||||
res.status(404).json({ error: 'Weight entry not found.' });
|
res.status(404).json({ error: 'Weight entry not found or no longer editable.' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -925,6 +925,7 @@ export const updateWeightForBird = async (
|
|||||||
notes = $5
|
notes = $5
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
AND bird_id = $2
|
AND bird_id = $2
|
||||||
|
AND recorded_on >= CURRENT_DATE - (2 * INTERVAL '1 day')
|
||||||
RETURNING id, bird_id, weight_grams, recorded_on::text, notes`,
|
RETURNING id, bird_id, weight_grams, recorded_on::text, notes`,
|
||||||
[weightId, birdId, weightGrams, recordedOn, notes],
|
[weightId, birdId, weightGrams, recordedOn, notes],
|
||||||
);
|
);
|
||||||
|
|||||||
+40
-19
@@ -876,6 +876,13 @@ const formatAuditAction = (value: string) =>
|
|||||||
const formatWeight = (value: number | null) => (value ? `${value.toFixed(1)} g` : 'Pending');
|
const formatWeight = (value: number | null) => (value ? `${value.toFixed(1)} g` : 'Pending');
|
||||||
const formatRange = (minGrams: number, maxGrams: number) => `${minGrams.toFixed(0)}-${maxGrams.toFixed(0)} g`;
|
const formatRange = (minGrams: number, maxGrams: number) => `${minGrams.toFixed(0)}-${maxGrams.toFixed(0)} g`;
|
||||||
const parseDateValue = (value: string) => new Date(`${value}T00:00:00`);
|
const parseDateValue = (value: string) => new Date(`${value}T00:00:00`);
|
||||||
|
const WEIGHT_EDIT_WINDOW_DAYS = 3;
|
||||||
|
const getWeightEditEarliestDate = () => {
|
||||||
|
const earliestDate = new Date();
|
||||||
|
earliestDate.setDate(earliestDate.getDate() - (WEIGHT_EDIT_WINDOW_DAYS - 1));
|
||||||
|
return earliestDate.toISOString().slice(0, 10);
|
||||||
|
};
|
||||||
|
const isWeightEditable = (weight: WeightRecord) => weight.recordedOn >= getWeightEditEarliestDate();
|
||||||
const daysBetweenDates = (startDate: string, endDate: string) =>
|
const daysBetweenDates = (startDate: string, endDate: string) =>
|
||||||
Math.abs(parseDateValue(endDate).getTime() - parseDateValue(startDate).getTime()) / (24 * 60 * 60 * 1000);
|
Math.abs(parseDateValue(endDate).getTime() - parseDateValue(startDate).getTime()) / (24 * 60 * 60 * 1000);
|
||||||
const addYearsToDate = (date: Date, years: number) => {
|
const addYearsToDate = (date: Date, years: number) => {
|
||||||
@@ -3411,6 +3418,11 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEditWeight = (weight: WeightRecord) => {
|
const handleEditWeight = (weight: WeightRecord) => {
|
||||||
|
if (!isWeightEditable(weight)) {
|
||||||
|
setError('Weight entries can only be edited for the last 3 days.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setEditingWeightId(weight.id);
|
setEditingWeightId(weight.id);
|
||||||
setWeightForm({
|
setWeightForm({
|
||||||
weightGrams: String(weight.weightGrams),
|
weightGrams: String(weight.weightGrams),
|
||||||
@@ -6550,11 +6562,12 @@ function App() {
|
|||||||
<label>
|
<label>
|
||||||
Recorded on
|
Recorded on
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={weightForm.recordedOn}
|
value={weightForm.recordedOn}
|
||||||
onChange={(event) => setWeightForm({ ...weightForm, recordedOn: event.target.value })}
|
min={editingWeightId ? getWeightEditEarliestDate() : undefined}
|
||||||
required
|
onChange={(event) => setWeightForm({ ...weightForm, recordedOn: event.target.value })}
|
||||||
/>
|
required
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="wide-field">
|
<label className="wide-field">
|
||||||
Notes
|
Notes
|
||||||
@@ -6577,20 +6590,28 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div className="recent-list">
|
<div className="recent-list">
|
||||||
{[...weights]
|
{[...weights]
|
||||||
.sort((left, right) => right.recordedOn.localeCompare(left.recordedOn))
|
.sort((left, right) => right.recordedOn.localeCompare(left.recordedOn))
|
||||||
.map((weight) => (
|
.map((weight) => {
|
||||||
<article className="vet-visit-card" key={weight.id}>
|
const weightCanBeEdited = isWeightEditable(weight);
|
||||||
<strong>{formatWeight(weight.weightGrams)}</strong>
|
|
||||||
<span>{formatDate(weight.recordedOn)}</span>
|
return (
|
||||||
<small>{weight.notes || 'No notes recorded.'}</small>
|
<article className="vet-visit-card" key={weight.id}>
|
||||||
<div className="button-row">
|
<strong>{formatWeight(weight.weightGrams)}</strong>
|
||||||
<button className="secondary-button" onClick={() => handleEditWeight(weight)} type="button">
|
<span>{formatDate(weight.recordedOn)}</span>
|
||||||
Edit
|
<small>{weight.notes || 'No notes recorded.'}</small>
|
||||||
</button>
|
{weightCanBeEdited ? (
|
||||||
</div>
|
<div className="button-row">
|
||||||
</article>
|
<button className="secondary-button" onClick={() => handleEditWeight(weight)} type="button">
|
||||||
))}
|
Edit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<small>Editing available for the last 3 days only.</small>
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{!weights.length ? <p className="muted">No weight entries recorded yet.</p> : null}
|
{!weights.length ? <p className="muted">No weight entries recorded yet.</p> : null}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user