automated dev db build
This commit is contained in:
+120
-81
@@ -472,6 +472,23 @@ const parseImportGender = (value: unknown): BirdGender | null => {
|
||||
const getBirdImportKey = (name: string, tagId: string) => (tagId ? `band:${tagId.toLowerCase()}` : `name:${name.toLowerCase()}`);
|
||||
|
||||
const mergeImportText = (current: string, next: string) => current || next;
|
||||
const birdProfileListLimit = 3;
|
||||
|
||||
const parseBirdProfileList = (value: string | null | undefined) =>
|
||||
(value ?? '')
|
||||
.split(/\r?\n/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, birdProfileListLimit);
|
||||
|
||||
const getBirdProfileListFields = (value: string) =>
|
||||
Array.from({ length: birdProfileListLimit }, (_, index) => parseBirdProfileList(value)[index] ?? '');
|
||||
|
||||
const updateBirdProfileListField = (value: string, index: number, nextItem: string) => {
|
||||
const items = getBirdProfileListFields(value);
|
||||
items[index] = nextItem;
|
||||
return items.map((item) => item.trim()).filter(Boolean).join('\n');
|
||||
};
|
||||
|
||||
const parseBirdImportRows = (rows: Record<string, unknown>[]): BirdImportPreview => {
|
||||
const errors: string[] = [];
|
||||
@@ -701,8 +718,8 @@ const toBirdForm = (bird: Bird): BirdFormState => ({
|
||||
name: bird.name,
|
||||
tagId: bird.tagId ?? '',
|
||||
species: bird.species,
|
||||
motivators: bird.motivators ?? '',
|
||||
demotivators: bird.demotivators ?? '',
|
||||
motivators: parseBirdProfileList(bird.motivators).join('\n'),
|
||||
demotivators: parseBirdProfileList(bird.demotivators).join('\n'),
|
||||
favoriteSnack: bird.favoriteSnack ?? '',
|
||||
gender: bird.gender,
|
||||
dateOfBirth: bird.dateOfBirth ?? '',
|
||||
@@ -4804,26 +4821,33 @@ function App() {
|
||||
<span>Gotcha day</span>
|
||||
<strong>{formatDate(selectedBird.gotchaDay)}</strong>
|
||||
</article>
|
||||
<article className="detail-card">
|
||||
<span>Gender</span>
|
||||
<strong className="detail-gender">
|
||||
<span aria-hidden="true" className={`gender-symbol ${selectedBird.gender}`}>
|
||||
{getBirdGenderSymbol(selectedBird)}
|
||||
</span>
|
||||
{getBirdGenderLabel(selectedBird)}
|
||||
</strong>
|
||||
</article>
|
||||
<article className="detail-card">
|
||||
<span>Favorite snack</span>
|
||||
<strong>{selectedBird.favoriteSnack || 'Not recorded'}</strong>
|
||||
</article>
|
||||
<article className="detail-card">
|
||||
<span>Motivators</span>
|
||||
<strong>{selectedBird.motivators || 'Not recorded'}</strong>
|
||||
{parseBirdProfileList(selectedBird.motivators).length ? (
|
||||
<ul className="detail-item-list">
|
||||
{parseBirdProfileList(selectedBird.motivators).map((motivator, index) => (
|
||||
<li key={`${motivator}-${index}`}>{motivator}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<strong>Not recorded</strong>
|
||||
)}
|
||||
</article>
|
||||
<article className="detail-card">
|
||||
<span>Demotivators</span>
|
||||
<strong>{selectedBird.demotivators || 'Not recorded'}</strong>
|
||||
{parseBirdProfileList(selectedBird.demotivators).length ? (
|
||||
<ul className="detail-item-list">
|
||||
{parseBirdProfileList(selectedBird.demotivators).map((demotivator, index) => (
|
||||
<li key={`${demotivator}-${index}`}>{demotivator}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<strong>Not recorded</strong>
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -5577,7 +5601,7 @@ function App() {
|
||||
</article>
|
||||
) : null}
|
||||
<article className="summary-card">
|
||||
<strong>{workspace?.billingEmail || authSession.user.email}</strong>
|
||||
<strong className="billing-contact-email">{workspace?.billingEmail || authSession.user.email}</strong>
|
||||
<span>Billing contact for invoices, receipts, and account notices.</span>
|
||||
</article>
|
||||
<article className="summary-card">
|
||||
@@ -5895,30 +5919,6 @@ function App() {
|
||||
</div>
|
||||
<small className="muted">Search or select a species so alerts and chart references stay consistent.</small>
|
||||
</label>
|
||||
<label>
|
||||
Favorite snack
|
||||
<input
|
||||
value={birdForm.favoriteSnack}
|
||||
onChange={(event) => setBirdForm({ ...birdForm, favoriteSnack: event.target.value })}
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</label>
|
||||
<label className="wide-field">
|
||||
Motivators
|
||||
<textarea
|
||||
value={birdForm.motivators}
|
||||
onChange={(event) => setBirdForm({ ...birdForm, motivators: event.target.value })}
|
||||
placeholder="Training rewards, sounds, people, toys, routines"
|
||||
/>
|
||||
</label>
|
||||
<label className="wide-field">
|
||||
Demotivates
|
||||
<textarea
|
||||
value={birdForm.demotivators}
|
||||
onChange={(event) => setBirdForm({ ...birdForm, demotivators: event.target.value })}
|
||||
placeholder="Stressors, disliked handling, noises, situations"
|
||||
/>
|
||||
</label>
|
||||
<div className="segmented-field wide-field">
|
||||
<span>Gender</span>
|
||||
<div className="segmented-control" role="radiogroup" aria-label="Bird gender">
|
||||
@@ -5961,49 +5961,88 @@ function App() {
|
||||
</div>
|
||||
<small className="muted">Shown on the bird profile card as a symbol.</small>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="settings-nested-card">
|
||||
<div className="settings-nested-header">
|
||||
<p className="eyebrow">Dates</p>
|
||||
<h3>Milestones and reminders</h3>
|
||||
</div>
|
||||
<div className="settings-nested-grid">
|
||||
<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 })}
|
||||
/>
|
||||
<small className="muted">Send a reminder on this bird's Hatch Day each year.</small>
|
||||
</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 })}
|
||||
/>
|
||||
<small className="muted">Send a reminder on this bird's gotcha day anniversary.</small>
|
||||
</label>
|
||||
<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 })}
|
||||
/>
|
||||
<small className="muted">Send a reminder on this bird's Hatch Day each year.</small>
|
||||
</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 })}
|
||||
/>
|
||||
<small className="muted">Send a reminder on this bird's gotcha day anniversary.</small>
|
||||
</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={`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={`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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user