Moved edit bird profile to flock page rather than settings
This commit is contained in:
+449
-6
@@ -1414,6 +1414,7 @@ function App() {
|
|||||||
const [memorializedBirds, setMemorializedBirds] = useState<Bird[]>([]);
|
const [memorializedBirds, setMemorializedBirds] = useState<Bird[]>([]);
|
||||||
const [selectedBirdId, setSelectedBirdId] = useState<string>('');
|
const [selectedBirdId, setSelectedBirdId] = useState<string>('');
|
||||||
const [editingBirdId, setEditingBirdId] = useState<string>('');
|
const [editingBirdId, setEditingBirdId] = useState<string>('');
|
||||||
|
const [birdEditorOpen, setBirdEditorOpen] = useState(false);
|
||||||
const [weights, setWeights] = useState<WeightRecord[]>([]);
|
const [weights, setWeights] = useState<WeightRecord[]>([]);
|
||||||
const [vetVisits, setVetVisits] = useState<VetVisit[]>([]);
|
const [vetVisits, setVetVisits] = useState<VetVisit[]>([]);
|
||||||
const [medications, setMedications] = useState<Medication[]>([]);
|
const [medications, setMedications] = useState<Medication[]>([]);
|
||||||
@@ -1526,7 +1527,7 @@ function App() {
|
|||||||
[allBirdWeights, birds, overviewWindowStartDate],
|
[allBirdWeights, birds, overviewWindowStartDate],
|
||||||
);
|
);
|
||||||
|
|
||||||
const showFlockDetailColumn = bulkWeightOpen || Boolean(selectedBird);
|
const showFlockDetailColumn = bulkWeightOpen || birdEditorOpen || Boolean(selectedBird);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!publicProfile || !authSession || workspace?.id !== publicProfile.workspaceId || !birds.some((bird) => bird.id === publicProfile.id)) {
|
if (!publicProfile || !authSession || workspace?.id !== publicProfile.workspaceId || !birds.some((bird) => bird.id === publicProfile.id)) {
|
||||||
@@ -2344,12 +2345,13 @@ function App() {
|
|||||||
|
|
||||||
const startCreateBird = () => {
|
const startCreateBird = () => {
|
||||||
setEditingBirdId('');
|
setEditingBirdId('');
|
||||||
|
setBirdEditorOpen(true);
|
||||||
setBirdForm(emptyBirdForm);
|
setBirdForm(emptyBirdForm);
|
||||||
setBirdPhotoName('');
|
setBirdPhotoName('');
|
||||||
setPhotoCrop(null);
|
setPhotoCrop(null);
|
||||||
setPhotoDrag(null);
|
setPhotoDrag(null);
|
||||||
setError('');
|
setError('');
|
||||||
setActivePage('settings');
|
setActivePage('flock');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAuthSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
const handleAuthSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
@@ -2671,12 +2673,13 @@ function App() {
|
|||||||
const startEditBird = (bird: Bird) => {
|
const startEditBird = (bird: Bird) => {
|
||||||
setSelectedBirdId(bird.id);
|
setSelectedBirdId(bird.id);
|
||||||
setEditingBirdId(bird.id);
|
setEditingBirdId(bird.id);
|
||||||
|
setBirdEditorOpen(true);
|
||||||
setBirdForm(toBirdForm(bird));
|
setBirdForm(toBirdForm(bird));
|
||||||
setBirdPhotoName('');
|
setBirdPhotoName('');
|
||||||
setPhotoCrop(null);
|
setPhotoCrop(null);
|
||||||
setPhotoDrag(null);
|
setPhotoDrag(null);
|
||||||
setError('');
|
setError('');
|
||||||
setActivePage('settings');
|
setActivePage('flock');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBirdPhotoChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleBirdPhotoChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@@ -2969,9 +2972,10 @@ function App() {
|
|||||||
});
|
});
|
||||||
setSelectedBirdId(isEditing ? savedBird.id : '');
|
setSelectedBirdId(isEditing ? savedBird.id : '');
|
||||||
setEditingBirdId(savedBird.id);
|
setEditingBirdId(savedBird.id);
|
||||||
|
setBirdEditorOpen(false);
|
||||||
setBirdForm(toBirdForm(savedBird));
|
setBirdForm(toBirdForm(savedBird));
|
||||||
setBirdPhotoName('');
|
setBirdPhotoName('');
|
||||||
setActivePage(isEditing ? 'settings' : 'flock');
|
setActivePage('flock');
|
||||||
} catch (submitError) {
|
} catch (submitError) {
|
||||||
setError(submitError instanceof Error ? submitError.message : 'Unable to save flock member.');
|
setError(submitError instanceof Error ? submitError.message : 'Unable to save flock member.');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -4771,7 +4775,446 @@ function App() {
|
|||||||
</section>
|
</section>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{selectedBird ? (
|
{birdEditorOpen ? (
|
||||||
|
<section className="panel flock-member-panel flock-profile-editor">
|
||||||
|
<div className="panel-header">
|
||||||
|
<div>
|
||||||
|
<p className="eyebrow">Bird profile</p>
|
||||||
|
<h2>{editingBird ? `Edit ${editingBird.name}` : 'Add flock member'}</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="secondary-button"
|
||||||
|
onClick={() => {
|
||||||
|
setBirdEditorOpen(false);
|
||||||
|
setEditingBirdId('');
|
||||||
|
setBirdPhotoName('');
|
||||||
|
setPhotoCrop(null);
|
||||||
|
setPhotoDrag(null);
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form className="form-panel settings-nested-stack" onSubmit={handleBirdSubmit}>
|
||||||
|
<section className="settings-nested-card">
|
||||||
|
<div className="settings-nested-header">
|
||||||
|
<p className="eyebrow">Identity</p>
|
||||||
|
<h3>Profile details</h3>
|
||||||
|
</div>
|
||||||
|
<div className="settings-nested-grid">
|
||||||
|
<label>
|
||||||
|
Bird name
|
||||||
|
<input value={birdForm.name} onChange={(event) => setBirdForm({ ...birdForm, name: event.target.value })} required />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Band ID
|
||||||
|
<input
|
||||||
|
value={birdForm.tagId}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, tagId: event.target.value })}
|
||||||
|
placeholder="Optional if unknown"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="species-picker-field wide-field">
|
||||||
|
Species
|
||||||
|
<div className="species-picker">
|
||||||
|
<input
|
||||||
|
value={birdForm.species}
|
||||||
|
onChange={(event) => {
|
||||||
|
setBirdForm({ ...birdForm, species: event.target.value });
|
||||||
|
setSpeciesPickerOpen(true);
|
||||||
|
}}
|
||||||
|
onFocus={() => setSpeciesPickerOpen(true)}
|
||||||
|
onBlur={() => {
|
||||||
|
window.setTimeout(() => setSpeciesPickerOpen(false), 120);
|
||||||
|
}}
|
||||||
|
placeholder="Start typing a species"
|
||||||
|
autoComplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{speciesPickerOpen ? (
|
||||||
|
<div className="species-picker-menu">
|
||||||
|
{filteredSpeciesOptions.length ? (
|
||||||
|
filteredSpeciesOptions.map((speciesOption) => (
|
||||||
|
<button
|
||||||
|
key={speciesOption}
|
||||||
|
className={`species-picker-option ${birdForm.species === speciesOption ? 'active' : ''}`}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setBirdForm({ ...birdForm, species: speciesOption });
|
||||||
|
setSpeciesPickerOpen(false);
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{speciesOption}
|
||||||
|
</button>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="species-picker-empty">No matching species yet. Keep typing to add a custom entry.</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<div className="segmented-field wide-field">
|
||||||
|
<span>Gender</span>
|
||||||
|
<div className="segmented-control" role="radiogroup" aria-label="Bird gender">
|
||||||
|
{(['unknown', 'male', 'female'] as BirdGender[]).map((gender) => (
|
||||||
|
<button
|
||||||
|
key={gender}
|
||||||
|
className={`segmented-option ${birdForm.gender === gender ? 'active' : ''}`}
|
||||||
|
onClick={() => setBirdForm({ ...birdForm, gender })}
|
||||||
|
type="button"
|
||||||
|
role="radio"
|
||||||
|
aria-checked={birdForm.gender === gender}
|
||||||
|
>
|
||||||
|
<span className={`gender-symbol ${gender}`} aria-hidden="true">
|
||||||
|
{getBirdGenderSymbol({ gender })}
|
||||||
|
</span>
|
||||||
|
{getBirdGenderLabel({ gender })}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-inline-header wide-field">
|
||||||
|
<p className="eyebrow">Dates</p>
|
||||||
|
<h3>Milestones and reminders</h3>
|
||||||
|
</div>
|
||||||
|
<label>
|
||||||
|
Hatch Day
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={birdForm.dateOfBirth}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, dateOfBirth: event.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Gotcha day
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={birdForm.gotchaDay}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, gotchaDay: event.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="toggle-card">
|
||||||
|
<span>Notify on Hatch Day</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={birdForm.notifyOnDob}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, notifyOnDob: event.target.checked })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="toggle-card">
|
||||||
|
<span>Notify on gotcha day</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={birdForm.notifyOnGotchaDay}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, notifyOnGotchaDay: event.target.checked })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Favorite snack
|
||||||
|
<input
|
||||||
|
value={birdForm.favoriteSnack}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, favoriteSnack: event.target.value })}
|
||||||
|
placeholder="Optional"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="wide-field">
|
||||||
|
Motivators
|
||||||
|
<div className="profile-list-fields">
|
||||||
|
{getBirdProfileListFields(birdForm.motivators).map((motivator, index) => (
|
||||||
|
<input
|
||||||
|
key={`flock-motivator-${index}`}
|
||||||
|
value={motivator}
|
||||||
|
onChange={(event) =>
|
||||||
|
setBirdForm({
|
||||||
|
...birdForm,
|
||||||
|
motivators: updateBirdProfileListField(birdForm.motivators, index, event.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder={index === 0 ? 'Training reward, sound, person, toy, or routine' : `Motivator ${index + 1}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label className="wide-field">
|
||||||
|
Demotivators
|
||||||
|
<div className="profile-list-fields">
|
||||||
|
{getBirdProfileListFields(birdForm.demotivators).map((demotivator, index) => (
|
||||||
|
<input
|
||||||
|
key={`flock-demotivator-${index}`}
|
||||||
|
value={demotivator}
|
||||||
|
onChange={(event) =>
|
||||||
|
setBirdForm({
|
||||||
|
...birdForm,
|
||||||
|
demotivators: updateBirdProfileListField(birdForm.demotivators, index, event.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder={index === 0 ? 'Stressor, disliked handling, noise, or situation' : `Demotivator ${index + 1}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section className="settings-nested-card">
|
||||||
|
<div className="settings-nested-header">
|
||||||
|
<p className="eyebrow">Display</p>
|
||||||
|
<h3>Chart color and photo</h3>
|
||||||
|
</div>
|
||||||
|
<div className="settings-nested-grid">
|
||||||
|
<label>
|
||||||
|
Graph color
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={birdForm.chartColor}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, chartColor: event.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="toggle-field wide-field">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={birdForm.publicProfileEnabled}
|
||||||
|
onChange={(event) => setBirdForm({ ...birdForm, publicProfileEnabled: event.target.checked })}
|
||||||
|
/>
|
||||||
|
<span>Enable QR code</span>
|
||||||
|
</label>
|
||||||
|
<div className="photo-editor wide-field">
|
||||||
|
<div className="photo-preview-shell">
|
||||||
|
{photoCrop ? (
|
||||||
|
<div
|
||||||
|
className={`crop-preview-frame ${photoDrag ? 'dragging' : ''}`}
|
||||||
|
onPointerDown={handlePhotoCropPointerDown}
|
||||||
|
onPointerMove={handlePhotoCropPointerMove}
|
||||||
|
onPointerUp={handlePhotoCropPointerUp}
|
||||||
|
onPointerCancel={handlePhotoCropPointerUp}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="crop-preview-image"
|
||||||
|
src={photoCrop.sourceDataUrl}
|
||||||
|
alt="Crop preview"
|
||||||
|
style={{
|
||||||
|
width: `${getPhotoCropMetrics(photoCrop).displayWidth}px`,
|
||||||
|
height: `${getPhotoCropMetrics(photoCrop).displayHeight}px`,
|
||||||
|
left: `${getPhotoCropMetrics(photoCrop).left}px`,
|
||||||
|
top: `${getPhotoCropMetrics(photoCrop).top}px`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="crop-preview-overlay" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<img className="profile-photo" src={birdForm.photoDataUrl || defaultBirdPhoto} alt="Bird preview" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="photo-copy">
|
||||||
|
<label className="file-picker">
|
||||||
|
Photo
|
||||||
|
<input accept="image/png,image/jpeg,image/jpg,image/webp,image/gif" onChange={handleBirdPhotoChange} type="file" />
|
||||||
|
</label>
|
||||||
|
{photoCrop ? (
|
||||||
|
<div className="crop-control-stack">
|
||||||
|
<p className="muted">{photoCrop.fileName} ready to crop.</p>
|
||||||
|
<label>
|
||||||
|
Zoom
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="3"
|
||||||
|
step="0.01"
|
||||||
|
value={photoCrop.zoom}
|
||||||
|
onChange={(event) =>
|
||||||
|
setPhotoCrop((current) => (current ? { ...current, zoom: Number(event.target.value) } : current))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="button-row">
|
||||||
|
<button className="primary-button" onClick={handleApplyPhotoCrop} type="button" disabled={applyingPhotoCrop}>
|
||||||
|
{applyingPhotoCrop ? 'Applying crop...' : 'Apply crop'}
|
||||||
|
</button>
|
||||||
|
<button className="secondary-button" onClick={handleCancelPhotoCrop} type="button" disabled={applyingPhotoCrop}>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{birdForm.photoDataUrl ? (
|
||||||
|
<button className="secondary-button" onClick={handleRemovePhoto} type="button">
|
||||||
|
Remove photo
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div className="settings-save-row">
|
||||||
|
<button className="primary-button" type="submit" disabled={savingBird}>
|
||||||
|
{savingBird ? 'Saving...' : editingBird ? 'Save profile changes' : 'Save bird profile'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{editingBird && selectedBird ? (
|
||||||
|
<section className="settings-nested-card">
|
||||||
|
<div className="settings-nested-header">
|
||||||
|
<p className="eyebrow">Medication</p>
|
||||||
|
<h3>Medication configuration</h3>
|
||||||
|
</div>
|
||||||
|
<form className="form-panel inline-form care-entry-form" onSubmit={handleMedicationSubmit}>
|
||||||
|
<label>
|
||||||
|
Medication
|
||||||
|
<input
|
||||||
|
value={medicationForm.name}
|
||||||
|
onChange={(event) => setMedicationForm({ ...medicationForm, name: event.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Dosage
|
||||||
|
<input
|
||||||
|
value={medicationForm.dosage}
|
||||||
|
onChange={(event) => setMedicationForm({ ...medicationForm, dosage: event.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Frequency
|
||||||
|
<select
|
||||||
|
value={medicationForm.frequency}
|
||||||
|
onChange={(event) => {
|
||||||
|
const frequency = event.target.value as MedicationFrequency;
|
||||||
|
setMedicationForm({
|
||||||
|
...medicationForm,
|
||||||
|
frequency,
|
||||||
|
doseSchedule: getDefaultMedicationDoseSchedule(frequency),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
{medicationFrequencyOptions.map((option) => (
|
||||||
|
<option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Route
|
||||||
|
<input value={medicationForm.route} onChange={(event) => setMedicationForm({ ...medicationForm, route: event.target.value })} />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Start date
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={medicationForm.startDate}
|
||||||
|
onChange={(event) => setMedicationForm({ ...medicationForm, startDate: event.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
End date
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={medicationForm.endDate}
|
||||||
|
onChange={(event) => setMedicationForm({ ...medicationForm, endDate: event.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="wide-field">
|
||||||
|
Dose labels and times
|
||||||
|
<div className="dose-schedule-editor">
|
||||||
|
{medicationForm.doseSchedule.map((slot, index) => (
|
||||||
|
<div className="dose-schedule-row" key={slot.key}>
|
||||||
|
<input
|
||||||
|
value={slot.label}
|
||||||
|
onChange={(event) =>
|
||||||
|
setMedicationForm({
|
||||||
|
...medicationForm,
|
||||||
|
doseSchedule: medicationForm.doseSchedule.map((currentSlot, currentIndex) =>
|
||||||
|
currentIndex === index ? { ...currentSlot, label: event.target.value } : currentSlot,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="time"
|
||||||
|
value={slot.time}
|
||||||
|
onChange={(event) =>
|
||||||
|
setMedicationForm({
|
||||||
|
...medicationForm,
|
||||||
|
doseSchedule: medicationForm.doseSchedule.map((currentSlot, currentIndex) =>
|
||||||
|
currentIndex === index ? { ...currentSlot, time: event.target.value } : currentSlot,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label className="wide-field">
|
||||||
|
Notes
|
||||||
|
<textarea
|
||||||
|
rows={3}
|
||||||
|
value={medicationForm.notes}
|
||||||
|
onChange={(event) => setMedicationForm({ ...medicationForm, notes: event.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="button-row care-form-actions">
|
||||||
|
<button className="primary-button" type="submit">
|
||||||
|
{editingMedicationId ? 'Save medication changes' : 'Save medication'}
|
||||||
|
</button>
|
||||||
|
{editingMedicationId ? (
|
||||||
|
<button className="secondary-button" onClick={handleCancelMedicationEdit} type="button">
|
||||||
|
Cancel edit
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div className="recent-list">{renderMedicationList({ showActions: true })}</div>
|
||||||
|
</section>
|
||||||
|
) : null}
|
||||||
|
{editingBird && selectedBird ? (
|
||||||
|
<section className="settings-nested-card settings-danger-card">
|
||||||
|
<div className="settings-nested-header">
|
||||||
|
<p className="eyebrow">Danger zone</p>
|
||||||
|
<h3>Destructive profile actions</h3>
|
||||||
|
</div>
|
||||||
|
<form className="form-panel inline-form care-entry-form" onSubmit={handleMemorializeBird}>
|
||||||
|
<label>
|
||||||
|
Memorial date
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={memorializeBirdForm.memorializedOn}
|
||||||
|
onChange={(event) => setMemorializeBirdForm({ ...memorializeBirdForm, memorializedOn: event.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="wide-field">
|
||||||
|
Memorial note
|
||||||
|
<textarea
|
||||||
|
rows={3}
|
||||||
|
value={memorializeBirdForm.memorialNote}
|
||||||
|
onChange={(event) => setMemorializeBirdForm({ ...memorializeBirdForm, memorialNote: event.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="button-row care-form-actions">
|
||||||
|
<button className="danger-button" type="submit" disabled={memorializingBird || activeMembership?.role !== 'owner'}>
|
||||||
|
{memorializingBird ? 'Memorializing...' : `Memorialize ${selectedBird.name}`}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p className="muted">Removing permanently deletes weight records, vet visits, and medication history for this bird.</p>
|
||||||
|
<div className="button-row">
|
||||||
|
<button className="danger-button" onClick={handleRemoveBird} type="button" disabled={deletingBird}>
|
||||||
|
{deletingBird ? 'Removing...' : 'Remove from flock'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
) : null}
|
||||||
|
</section>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{selectedBird && !birdEditorOpen ? (
|
||||||
<section className="panel flock-member-panel">
|
<section className="panel flock-member-panel">
|
||||||
<div className="panel-header">
|
<div className="panel-header">
|
||||||
<div>
|
<div>
|
||||||
@@ -5813,7 +6256,7 @@ function App() {
|
|||||||
) : null}
|
) : null}
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article className="panel form-panel settings-card-bird-profiles">
|
<article className="panel form-panel settings-card-bird-profiles" hidden>
|
||||||
<div className="panel-header">
|
<div className="panel-header">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Bird profiles</p>
|
<p className="eyebrow">Bird profiles</p>
|
||||||
|
|||||||
@@ -614,6 +614,10 @@ textarea {
|
|||||||
order: 1;
|
order: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-card-bird-profiles[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-card-collaborators {
|
.settings-card-collaborators {
|
||||||
order: 2;
|
order: 2;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user