feat(layout): Add Ceiling Height and Tiers to Floor creation
- Added ceilingHeight and defaultTiers to FacilityFloor model - Updated API routes and frontend client - Updated AddFloorModal with new fields and clearer labels
This commit is contained in:
parent
0c970cadb7
commit
da93320ccf
5 changed files with 94 additions and 8 deletions
40
.agent/artifacts/deployment_summary_2025_12_11.md
Normal file
40
.agent/artifacts/deployment_summary_2025_12_11.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Deployment & Verification Summary
|
||||||
|
|
||||||
|
**Date:** 2025-12-11
|
||||||
|
**Environment:** `nexus-vector` (Demo)
|
||||||
|
**URL:** [https://777wolfpack.runfoo.run](https://777wolfpack.runfoo.run)
|
||||||
|
|
||||||
|
## Key Achievements
|
||||||
|
|
||||||
|
1. **Layout Designer Enhancements**
|
||||||
|
* **Property Creation:** Implemented "Add Property" workflow directly within the Layout Designer.
|
||||||
|
* **Building Creation:** Added `AddBuildingModal` and "Add New Building" button in the floor selector.
|
||||||
|
* **Floor Creation:** Integrated `AddFloorModal` for creating new floors on the fly.
|
||||||
|
* **API Updates:** Fixed `layout.routes.ts` to support complex nested queries and proper address generation.
|
||||||
|
|
||||||
|
2. **Backend & Database Fixes**
|
||||||
|
* **Schema Update:** Added bi-directional relation between `Batch` and `FacilityPlant` in `schema.prisma`.
|
||||||
|
* **Seed Script Fixed:** Corrected `seed.js` to use valid `RoleEnum` values (`GROWER`, `STAFF`) and `SupplyCategory` (`OTHER`), and removed invalid `createdById` field in Task creation.
|
||||||
|
* **Type Safety:** Resolved TypeScript errors in `audit.routes.ts` (null vs undefined), `insights.routes.ts` (Batch relations), and `messaging.routes.ts` (duplicate keys).
|
||||||
|
|
||||||
|
3. **Deployment Success**
|
||||||
|
* Successfully deployed the latest code to `nexus-vector`.
|
||||||
|
* Database migrations applied and seed data populated successfully.
|
||||||
|
* Verified frontend application loads and Layout Designer components are active.
|
||||||
|
|
||||||
|
## Verification Steps Performed
|
||||||
|
|
||||||
|
* **Build:** Backend and Frontend Docker builds completed successfully.
|
||||||
|
* **Database:** `prisma db push` and `node prisma/seed.js` executed without errors.
|
||||||
|
* **Frontend:** Verified access to Layout Designer and presence of "Setup Property" / creation controls.
|
||||||
|
|
||||||
|
## Usage Instructions
|
||||||
|
|
||||||
|
To test the new features:
|
||||||
|
|
||||||
|
1. Login as `admin@runfoo.run` / `password123`.
|
||||||
|
2. Navigate to **Layout Designer**.
|
||||||
|
3. Open the top-left dropdown (Floor Selector).
|
||||||
|
4. Use **"Add New Property"** to set up a facility.
|
||||||
|
5. Use **"Add New Building"** to add structures.
|
||||||
|
6. Use **"Add Floor"** to define levels.
|
||||||
|
|
@ -545,6 +545,8 @@ model FacilityFloor {
|
||||||
number Int // 1, 2, 3...
|
number Int // 1, 2, 3...
|
||||||
width Int // feet
|
width Int // feet
|
||||||
height Int // feet
|
height Int // feet
|
||||||
|
ceilingHeight Float? // feet
|
||||||
|
defaultTiers Int @default(1)
|
||||||
rooms FacilityRoom[]
|
rooms FacilityRoom[]
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
|
||||||
|
|
@ -155,9 +155,17 @@ export async function layoutRoutes(fastify: FastifyInstance, options: FastifyPlu
|
||||||
fastify.post('/floors', {
|
fastify.post('/floors', {
|
||||||
handler: async (request, reply) => {
|
handler: async (request, reply) => {
|
||||||
try {
|
try {
|
||||||
const { buildingId, name, number, width, height } = request.body as any;
|
const { buildingId, name, number, width, height, ceilingHeight, defaultTiers } = request.body as any;
|
||||||
const floor = await prisma.facilityFloor.create({
|
const floor = await prisma.facilityFloor.create({
|
||||||
data: { buildingId, name, number, width: width || 200, height: height || 150 },
|
data: {
|
||||||
|
buildingId,
|
||||||
|
name,
|
||||||
|
number,
|
||||||
|
width: width || 200,
|
||||||
|
height: height || 150,
|
||||||
|
ceilingHeight: ceilingHeight || null,
|
||||||
|
defaultTiers: defaultTiers || 1
|
||||||
|
},
|
||||||
include: { rooms: true }
|
include: { rooms: true }
|
||||||
});
|
});
|
||||||
return reply.status(201).send(floor);
|
return reply.status(201).send(floor);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFlo
|
||||||
name: '',
|
name: '',
|
||||||
number: 1,
|
number: 1,
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100
|
height: 100,
|
||||||
|
ceilingHeight: undefined,
|
||||||
|
defaultTiers: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isOpen || !buildingId) return null;
|
if (!isOpen || !buildingId) return null;
|
||||||
|
|
@ -30,12 +32,14 @@ export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFlo
|
||||||
name: formData.name.trim(),
|
name: formData.name.trim(),
|
||||||
number: formData.number,
|
number: formData.number,
|
||||||
width: formData.width,
|
width: formData.width,
|
||||||
height: formData.height
|
height: formData.height,
|
||||||
|
ceilingHeight: formData.ceilingHeight,
|
||||||
|
defaultTiers: formData.defaultTiers
|
||||||
});
|
});
|
||||||
onCreated(floor.id);
|
onCreated(floor.id);
|
||||||
onClose();
|
onClose();
|
||||||
// Reset form
|
// Reset form
|
||||||
setFormData({ name: '', number: formData.number + 1, width: 100, height: 100 });
|
setFormData({ name: '', number: formData.number + 1, width: 100, height: 100, ceilingHeight: undefined, defaultTiers: 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create floor:', error);
|
console.error('Failed to create floor:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -75,7 +79,7 @@ export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFlo
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-slate-400 mb-2">Number</label>
|
<label className="block text-sm text-slate-400 mb-2">Number</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -85,6 +89,18 @@ export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFlo
|
||||||
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
|
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm text-slate-400 mb-2">Default Tiers</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.defaultTiers}
|
||||||
|
onChange={(e) => setFormData({ ...formData, defaultTiers: parseInt(e.target.value) || 1 })}
|
||||||
|
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-slate-400 mb-2">Width (ft)</label>
|
<label className="block text-sm text-slate-400 mb-2">Width (ft)</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -95,7 +111,7 @@ export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFlo
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-slate-400 mb-2">Height (ft)</label>
|
<label className="block text-sm text-slate-400 mb-2">Length (ft)</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={formData.height}
|
value={formData.height}
|
||||||
|
|
@ -103,6 +119,16 @@ export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFlo
|
||||||
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
|
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm text-slate-400 mb-2">Ceiling (ft)</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.ceilingHeight ?? ''}
|
||||||
|
onChange={(e) => setFormData({ ...formData, ceilingHeight: e.target.value ? parseInt(e.target.value) : undefined })}
|
||||||
|
placeholder="Optional"
|
||||||
|
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ export interface LayoutFloor {
|
||||||
number: number;
|
number: number;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
ceilingHeight?: number;
|
||||||
|
defaultTiers?: number;
|
||||||
rooms: LayoutRoom[];
|
rooms: LayoutRoom[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,7 +100,15 @@ export const layoutApi = {
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async createFloor(data: { buildingId: string; name: string; number: number; width?: number; height?: number }): Promise<LayoutFloor> {
|
async createFloor(data: {
|
||||||
|
buildingId: string;
|
||||||
|
name: string;
|
||||||
|
number: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
ceilingHeight?: number;
|
||||||
|
defaultTiers?: number;
|
||||||
|
}): Promise<LayoutFloor> {
|
||||||
const response = await api.post('/api/layout/floors', data);
|
const response = await api.post('/api/layout/floors', data);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue