This commit is contained in:
@@ -68,35 +68,38 @@ export function Onboarding({ onComplete }: Props) {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="pi">PI first name</Label>
|
<Label htmlFor="pi">
|
||||||
|
PI first name <span className="text-muted-foreground font-normal">(optional)</span>
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="pi"
|
id="pi"
|
||||||
type="text"
|
type="text"
|
||||||
value={piFirstName}
|
value={piFirstName}
|
||||||
onChange={e => setPiFirstName(e.target.value)}
|
onChange={e => setPiFirstName(e.target.value)}
|
||||||
required
|
|
||||||
placeholder="e.g. Smith"
|
placeholder="e.g. Smith"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="bldg">Building code</Label>
|
<Label htmlFor="bldg">
|
||||||
|
Building code <span className="text-muted-foreground font-normal">(optional)</span>
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="bldg"
|
id="bldg"
|
||||||
type="text"
|
type="text"
|
||||||
value={bldgCode}
|
value={bldgCode}
|
||||||
onChange={e => setBldgCode(e.target.value)}
|
onChange={e => setBldgCode(e.target.value)}
|
||||||
required
|
|
||||||
placeholder="e.g. EER"
|
placeholder="e.g. EER"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="lab">Lab</Label>
|
<Label htmlFor="lab">
|
||||||
|
Lab <span className="text-muted-foreground font-normal">(optional)</span>
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="lab"
|
id="lab"
|
||||||
type="text"
|
type="text"
|
||||||
value={lab}
|
value={lab}
|
||||||
onChange={e => setLab(e.target.value)}
|
onChange={e => setLab(e.target.value)}
|
||||||
required
|
|
||||||
placeholder="e.g. 3.822"
|
placeholder="e.g. 3.822"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -112,10 +112,16 @@ CREATE INDEX IF NOT EXISTS protocols_user_id_idx ON protocols(user_id);
|
|||||||
-- User profile — lab defaults pre-filled on chemical entries
|
-- User profile — lab defaults pre-filled on chemical entries
|
||||||
CREATE TABLE IF NOT EXISTS user_profile (
|
CREATE TABLE IF NOT EXISTS user_profile (
|
||||||
user_id TEXT NOT NULL PRIMARY KEY REFERENCES "user"("id") ON DELETE CASCADE,
|
user_id TEXT NOT NULL PRIMARY KEY REFERENCES "user"("id") ON DELETE CASCADE,
|
||||||
pi_first_name TEXT NOT NULL,
|
pi_first_name TEXT,
|
||||||
bldg_code TEXT NOT NULL,
|
bldg_code TEXT,
|
||||||
lab TEXT NOT NULL,
|
lab TEXT,
|
||||||
contact TEXT,
|
contact TEXT,
|
||||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- Allow existing deployments to have nullable profile fields.
|
||||||
|
-- DROP NOT NULL on an already-nullable column is a no-op in Postgres.
|
||||||
|
ALTER TABLE user_profile ALTER COLUMN pi_first_name DROP NOT NULL;
|
||||||
|
ALTER TABLE user_profile ALTER COLUMN bldg_code DROP NOT NULL;
|
||||||
|
ALTER TABLE user_profile ALTER COLUMN lab DROP NOT NULL;
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ router.get('/', requireAuth, async (req, res) => {
|
|||||||
|
|
||||||
router.post('/', requireAuth, async (req, res) => {
|
router.post('/', requireAuth, async (req, res) => {
|
||||||
const { pi_first_name, bldg_code, lab, contact } = req.body;
|
const { pi_first_name, bldg_code, lab, contact } = req.body;
|
||||||
if (!pi_first_name || !bldg_code || !lab) {
|
// Coerce empty strings to null so the DB stores a clean null rather than "".
|
||||||
return res.status(400).json({ error: 'pi_first_name, bldg_code, and lab are required' });
|
const toNull = (v: unknown) => (typeof v === 'string' && v.trim() !== '' ? v.trim() : null);
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const result = await pool.query(
|
const result = await pool.query(
|
||||||
`INSERT INTO user_profile (user_id, pi_first_name, bldg_code, lab, contact)
|
`INSERT INTO user_profile (user_id, pi_first_name, bldg_code, lab, contact)
|
||||||
@@ -35,7 +34,7 @@ router.post('/', requireAuth, async (req, res) => {
|
|||||||
contact = EXCLUDED.contact,
|
contact = EXCLUDED.contact,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
RETURNING *`,
|
RETURNING *`,
|
||||||
[req.user!.id, pi_first_name, bldg_code, lab, contact || null]
|
[req.user!.id, toNull(pi_first_name), toNull(bldg_code), toNull(lab), toNull(contact)]
|
||||||
);
|
);
|
||||||
res.json(result.rows[0]);
|
res.json(result.rows[0]);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user