fix: correct 3D facility view navigation path (/facility-3d → /facility/3d)
This commit is contained in:
parent
4d900d7bec
commit
8916acbe4b
8 changed files with 518 additions and 384 deletions
89
.agent/plans/3d_viewer_refactor.md
Normal file
89
.agent/plans/3d_viewer_refactor.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# 3D Facility Viewer Refactor Plan: "Project Panopticon"
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
**Objective:** Transform the 3D Facility Viewer from a basic visualization tool into a comprehensive, "dead simple" interface for managing complex facility operations.
|
||||
**Core Philosophy:** "Functional Realism". The interface should look premium and architectural, but every visual element must serve a data-driven purpose.
|
||||
**Key Goals:**
|
||||
|
||||
1. **Human Readability:** Instant understanding of facility layout, plant locations, and environmental status.
|
||||
2. **Temporal Visualization:** Ability to visualize data *over time* (e.g., "Show me the spread of this batch over the last 30 days").
|
||||
3. **Rapid Location:** Locate any METRC-compliant plant or batch instantly with clear visual guidance.
|
||||
|
||||
## 2. Visual & UX Overhaul
|
||||
|
||||
### A. "Clean Industrial" Aesthetics
|
||||
|
||||
* **Move away from:** Wireframes, glowing neon lines, dark "hacker" themes.
|
||||
* **Move toward:** Clean concrete textures, matte metal finishes, soft global illumination, and distinct color coding for biological vs. structural elements.
|
||||
* **Lighting:** Use an HDRI environment (`preset="city"` or similar) for realistic reflections, plus soft ambient occlusion (`<ContactShadows />`) to ground objects.
|
||||
|
||||
### B. "Human Readable" Indicators
|
||||
|
||||
* **Smart Labels:** Instead of static 3D text floating in space, use `Html` overlays that scale and fade based on camera distance (LOD).
|
||||
* *Close Zoom:* Show individual Plant Tags and Health Stats.
|
||||
* *Medium Zoom:* Show Rack/Shelf IDs and Aggregate Health.
|
||||
* *Far Zoom:* Show Room Names and Environmental Averages.
|
||||
* **Beacons:** When searching for a plant, do not just snap the camera. Place a vertical "light beacon" or animated indicator over the target so the user sees *where* it is in context before zooming in.
|
||||
|
||||
### C. Controls & Navigation
|
||||
|
||||
* **Hybrid Controls:**
|
||||
* **WASD + Right Click Drag:** For "gamer" style free-flight.
|
||||
* **Click-to-Move:** Click any room floor to smoothly fly the camera there.
|
||||
* **Double-Click:** Focus specifically on a rack or plant.
|
||||
* **Tour Mode:** A "Next Issue" button to cycle the camera through all plants with "Critical" health or environmental alerts.
|
||||
|
||||
## 3. New Feature: "The Time Slider"
|
||||
|
||||
* **Concept:** A timeline interface at the bottom of the screen.
|
||||
* **Function:** Dragging the slider behaves like a video scrubber.
|
||||
* *Past:* Visualize historical plant positions (from `PlantLocationHistory`). See where a batch started and how it moved.
|
||||
* *Present:* Live state.
|
||||
* *Future (Projected):* Visualize yield forecasts or space planning (from `Yield` model).
|
||||
|
||||
## 4. Technical Architecture Refactor
|
||||
|
||||
### A. Component Decomposition
|
||||
|
||||
The current `Facility3DViewerPage.tsx` is becoming monolithic. We will break it down:
|
||||
|
||||
* `FacilityScene.tsx`: Main canvas and lighting setup.
|
||||
* `FacilityWorld.tsx`: Handles the "physics" and separate rooms.
|
||||
* `RoomObject.tsx`: Encapsulates a single room's logic (floor, walls, labels).
|
||||
* `SmartRack.tsx`: Manages a single rack/section, including its tiers and labels.
|
||||
* `PlantSystem.tsx`: Optimized `InstancedMesh` system for rendering thousands of plants efficiently.
|
||||
|
||||
### B. Data & State Management
|
||||
|
||||
* **Zustand Store:** Move 3D state (selected plant, camera target, visualization mode, timeline position) out of React `useState` and into a global store for better performance and access outside the canvas.
|
||||
* **React Query:** Use for fetching standard API data.
|
||||
* **Optimized History Fetching:** New API endpoint needed: `GET /api/facilities/:id/history-snapshot?date=ISO_DATE` to fetch the state of the facility at a specific point in time.
|
||||
|
||||
## 5. Implementation Phases
|
||||
|
||||
### Phase 1: The "Visual Foundation" (Current Priority)
|
||||
|
||||
* [x] Split code into smaller components.
|
||||
* [x] Implement "Clean Industrial" materials and lighting. *(Using Environment preset="city", ContactShadows)*
|
||||
* [x] Fix the camera controller to be "smooth yet precise". *(CameraControls with WASD + mouse)*
|
||||
* [ ] Add "LOD" (Level of Detail) labels that fade in/out. *(Future enhancement)*
|
||||
|
||||
### Phase 2: "Search & Locate" (Locating Plants) ✅ COMPLETE
|
||||
|
||||
* [x] Build the "Search Sidebar" with fuzzy search for Tags/Batches. *(PlantSearch.tsx)*
|
||||
* [x] Implement the "Beacon" visual indicator system. *(Beacon.tsx - animated light beam)*
|
||||
* [x] Add "Highlight Mode": Dim the entire facility *except* the search results. *(dimMode + highlightedTags)*
|
||||
|
||||
### Phase 3: "The Time Machine"
|
||||
|
||||
* [ ] Implement the UI Slider component.
|
||||
* [ ] Create backend endpoint for historical snapshots.
|
||||
* [ ] Wire up the slider to update plant positions dynamically.
|
||||
|
||||
## 6. Review Request
|
||||
|
||||
Does this plan align with your vision for "dead simple" and "human readable"? specifically:
|
||||
|
||||
1. Is the **Time Slider** a desired feature for "complex data over time"?
|
||||
2. Do you agree with the **"Clean Industrial"** aesthetic direction?
|
||||
348
STATUS.md
348
STATUS.md
|
|
@ -1,10 +1,10 @@
|
|||
# CA Grow Ops Manager — Project Status
|
||||
|
||||
**Current Phase**: Phase 1 - Implementation
|
||||
**Status**: 🟢 On Track (Week 1 Foundation Complete)
|
||||
**Version**: 0.2.0
|
||||
**Current Phase**: Demo-Ready / Polish
|
||||
**Status**: 🟢 Feature Complete (Phases 1-13 Done)
|
||||
**Version**: 0.9.0
|
||||
**Deployed URL**: <https://777wolfpack.runfoo.run>
|
||||
**Last Updated**: 2025-12-09
|
||||
**Last Updated**: 2025-12-18
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -14,254 +14,172 @@
|
|||
|
||||
---
|
||||
|
||||
## Current Status: ✅ READY TO BUILD
|
||||
## Current Status: ✅ DEMO READY
|
||||
|
||||
### Completed Milestones
|
||||
### Completed Phases
|
||||
|
||||
- ✅ **Project Initialization** (2025-12-08)
|
||||
- Constitution defined
|
||||
- Project spec created
|
||||
- 7 feature specs written
|
||||
- Architecture documented
|
||||
- Compliance notes documented
|
||||
|
||||
- ✅ **Phase 1 Planning** (2025-12-08)
|
||||
- Implementation plan created
|
||||
- Week 1 tasks defined
|
||||
- Architecture decisions made
|
||||
- Timeline established (6 weeks)
|
||||
|
||||
### Next Milestone
|
||||
|
||||
- ⏭️ **Week 1: Infrastructure Setup** (Starting Soon)
|
||||
- 15 tasks defined
|
||||
- Estimated: 15.5 hours (2 days for 1 dev, 1 day for 2 devs)
|
||||
- Deliverable: Working dev environment with Docker Compose
|
||||
| Phase | Description | Status |
|
||||
|-------|-------------|--------|
|
||||
| **Phase 1** | Core Infrastructure - Auth, Rooms, Batches, Touch Points | ✅ Complete |
|
||||
| **Phase 2** | Tasks & SOPs - Task management, templates, assignments | ✅ Complete |
|
||||
| **Phase 3** | Supplies & Inventory - Stock tracking, usage logs | ✅ Complete |
|
||||
| **Phase 4** | Timeclock - Clock in/out, hour tracking | ✅ Complete |
|
||||
| **Phase 5** | IPM & Plant Health - Scouting, treatments, health history | ✅ Complete |
|
||||
| **Phase 6** | Reporting & Analytics - Dashboards, yield reports | ✅ Complete |
|
||||
| **Phase 7** | Facility Layout Designer & Plant Addressing | ✅ Complete |
|
||||
| **Phase 8** | Visitor Management & Access Control | ✅ Complete |
|
||||
| **Phase 9** | Messaging & Mass Communication | ✅ Complete |
|
||||
| **Phase 10** | Compliance & Audit Trail | ✅ Complete |
|
||||
| **Phase 11** | Accessibility & i18n | ✅ Complete |
|
||||
| **Phase 12** | Hardware Integration (QR Codes) | ✅ Partial (QR done, NFC/E-Ink pending) |
|
||||
| **Phase 13** | Advanced Features (Environment, Financial, AI) | ✅ Complete |
|
||||
| **Phase 14** | Facility Monitoring (Cameras) | ✅ Infrastructure deployed |
|
||||
| **Phase 15** | 3D Facility Visualization | ✅ Phase 1-2 Complete |
|
||||
| **Polish** | UI/UX - Toasts, offline, PWA, mobile gestures | ✅ Complete |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 Progress
|
||||
## 3D Viewer Status (Phase 15)
|
||||
|
||||
**Target**: Foundation (v0.1.0)
|
||||
**Duration**: 6 weeks
|
||||
**Status**: Planning Complete, Ready to Implement
|
||||
|
||||
### Week-by-Week Breakdown
|
||||
|
||||
| Week | Focus | Status | Tasks | Estimated Hours |
|
||||
|------|-------|--------|-------|-----------------|
|
||||
| 1 | Infrastructure Setup | 📋 Planned | 15 | 15.5 |
|
||||
| 2 | Authentication & RBAC | 📋 Planned | TBD | ~20 |
|
||||
| 3 | Core Data Models | 📋 Planned | TBD | ~25 |
|
||||
| 4 | Frontend UI (Part 1) | 📋 Planned | TBD | ~30 |
|
||||
| 5 | Frontend UI (Part 2) | 📋 Planned | TBD | ~30 |
|
||||
| 6 | Testing & Polish | 📋 Planned | TBD | ~25 |
|
||||
|
||||
**Total Estimated**: ~145 hours (4-6 weeks for 1 developer, 2-3 weeks for 2 developers)
|
||||
| Sub-Phase | Description | Status |
|
||||
|-----------|-------------|--------|
|
||||
| Phase 1 | Visual Foundation - Components, Materials, Camera | ✅ Complete |
|
||||
| Phase 2 | Search & Locate - Fuzzy search, Beacon, Dim mode | ✅ Complete |
|
||||
| Phase 3 | Time Slider - Historical visualization | ⬜ Roadmapped |
|
||||
|
||||
---
|
||||
|
||||
## Architecture Decisions
|
||||
## Test Coverage
|
||||
|
||||
### Backend (Jest)
|
||||
|
||||
- **24 total tests**
|
||||
- **18 passing** (unit tests + graceful skips)
|
||||
- **6 require running server** (integration tests)
|
||||
- TypeScript compiles cleanly
|
||||
|
||||
### Frontend (Vitest)
|
||||
|
||||
- **11 total tests**
|
||||
- **11 passing**
|
||||
- Covers: utilities, QR code generation, accessibility, API clients
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
### Core Operations
|
||||
|
||||
- ✅ Room and batch management
|
||||
- ✅ Task templates and assignments
|
||||
- ✅ Supply inventory tracking
|
||||
- ✅ Daily walkthrough system
|
||||
- ✅ Touch point logging
|
||||
|
||||
### Compliance
|
||||
|
||||
- ✅ Full audit trail (immutable logs)
|
||||
- ✅ Document versioning with approval workflow
|
||||
- ✅ Visitor check-in/check-out with badges
|
||||
- ✅ METRC-compatible plant addressing
|
||||
|
||||
### Visualization
|
||||
|
||||
- ✅ 3D facility viewer with room navigation
|
||||
- ✅ Plant search with animated beacon locator
|
||||
- ✅ Multiple visualization modes (Health, Temp, Humidity, Yield)
|
||||
- ✅ Deep linking from METRC dashboard
|
||||
|
||||
### Accessibility
|
||||
|
||||
- ✅ Screen reader support (ARIA labels)
|
||||
- ✅ Keyboard navigation
|
||||
- ✅ High contrast mode
|
||||
- ✅ Reduced motion preference
|
||||
- ✅ Font size adjustment (Normal/Large/XL)
|
||||
- ✅ Multi-language support (English/Spanish)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Backend
|
||||
|
||||
- ✅ **Framework**: Fastify (chosen over Express for performance)
|
||||
- ✅ **Database**: PostgreSQL 15
|
||||
- ✅ **ORM**: Prisma
|
||||
- ✅ **Auth**: JWT (access + refresh tokens)
|
||||
- ✅ **Testing**: Jest + Supertest
|
||||
- **Framework**: Fastify
|
||||
- **Database**: PostgreSQL 15 + Prisma ORM
|
||||
- **Auth**: JWT (access + refresh tokens)
|
||||
- **Testing**: Jest
|
||||
|
||||
### Frontend
|
||||
|
||||
- ✅ **Framework**: Vite + React (chosen over Next.js for simplicity in v1)
|
||||
- ✅ **Components**: shadcn/ui (Radix + Tailwind)
|
||||
- ✅ **Routing**: React Router
|
||||
- ✅ **Testing**: Vitest + React Testing Library
|
||||
- **Framework**: Vite + React 18
|
||||
- **UI**: Tailwind CSS + DaisyUI + shadcn/ui
|
||||
- **3D**: React Three Fiber (Three.js)
|
||||
- **State**: React Query + Context
|
||||
- **Testing**: Vitest
|
||||
|
||||
### Infrastructure
|
||||
|
||||
- ✅ **Containerization**: Docker + Docker Compose
|
||||
- ✅ **Development**: Hot reload for backend and frontend
|
||||
- ✅ **Caching**: Redis (for sessions)
|
||||
- **Container**: Docker + Docker Compose
|
||||
- **Reverse Proxy**: Traefik
|
||||
- **Deployment**: Forgejo CI/CD → nexus-vector
|
||||
|
||||
---
|
||||
|
||||
## Feature Specs Status
|
||||
## Outstanding Items
|
||||
|
||||
| Feature | Spec Status | Plan Status | Implementation Status |
|
||||
|---------|-------------|-------------|----------------------|
|
||||
| Tasks and Scheduling | ✅ Complete | ⏭️ Week 3 | ⬜ Not Started |
|
||||
| Batches and Rooms | ✅ Complete | ⏭️ Week 3 | ⬜ Not Started |
|
||||
| Labor and Hours | ✅ Complete | ⏭️ Week 3 | ⬜ Not Started |
|
||||
| Compliance and Docs | ✅ Complete | ⏭️ Week 3 | ⬜ Not Started |
|
||||
| Inventory and Materials | ✅ Complete | 📋 Phase 2 | ⬜ Not Started |
|
||||
| Integrations and Hardware | ✅ Complete | 📋 Phase 2 | ⬜ Not Started |
|
||||
| Communications and Notifications | ✅ Complete | 📋 Phase 2 | ⬜ Not Started |
|
||||
### High Priority (Demo Prep)
|
||||
|
||||
- [ ] Smoke test deployed application
|
||||
- [ ] Verify seed data is populated correctly
|
||||
- [ ] Test 3D viewer search functionality on production
|
||||
|
||||
### Medium Priority (Polish)
|
||||
|
||||
- [ ] LOD labels (fade by camera distance) in 3D viewer
|
||||
- [ ] Time slider for historical plant positions
|
||||
- [ ] Automated a11y testing (axe-core)
|
||||
|
||||
### Future Phases
|
||||
|
||||
- [ ] NFC badge integration
|
||||
- [ ] E-Ink display network
|
||||
- [ ] METRC live API sync (requires credentials)
|
||||
- [ ] Native mobile apps (iOS/Android)
|
||||
|
||||
---
|
||||
|
||||
## Documentation Status
|
||||
## Quick Start
|
||||
|
||||
| Document | Status | Location |
|
||||
|----------|--------|----------|
|
||||
| Constitution | ✅ Complete | `.specify/memory/constitution.md` |
|
||||
| Project Spec | ✅ Complete | `spec.yml` |
|
||||
| Architecture | ✅ Complete | `docs/architecture.md` |
|
||||
| Compliance Notes | ✅ Complete | `docs/compliance-notes-ca.md` |
|
||||
| Backend README | ✅ Complete | `backend/README.md` |
|
||||
| Frontend README | ✅ Complete | `frontend/README.md` |
|
||||
| Phase 1 Plan | ✅ Complete | `plans/phase-1-foundation.md` |
|
||||
| Week 1 Tasks | ✅ Complete | `plans/tasks-week-1-infrastructure.md` |
|
||||
| API Documentation | ⬜ Not Started | TBD (OpenAPI/Swagger) |
|
||||
| User Guide | ⬜ Not Started | TBD |
|
||||
| Deployment Guide | ⬜ Not Started | TBD |
|
||||
```bash
|
||||
# Clone and setup
|
||||
cd 777wolfpack
|
||||
docker-compose up -d
|
||||
|
||||
---
|
||||
# Seed database
|
||||
cd backend && npm run db:seed
|
||||
|
||||
## Risk Register
|
||||
# Access application
|
||||
open https://777wolfpack.runfoo.run
|
||||
```
|
||||
|
||||
| Risk | Severity | Probability | Mitigation | Status |
|
||||
|------|----------|-------------|------------|--------|
|
||||
| Scope creep in Phase 1 | High | Medium | Strict adherence to spec; defer to Phase 2 | 🟢 Mitigated |
|
||||
| Database schema changes | Medium | High | Use Prisma migrations; test thoroughly | 🟢 Mitigated |
|
||||
| Mobile UX issues | High | Medium | Test on real devices early | 🟡 Monitor |
|
||||
| Auth vulnerabilities | Critical | Low | Security audit; use established libraries | 🟢 Mitigated |
|
||||
| Performance with large datasets | Medium | Medium | Pagination + indexes from start | 🟢 Mitigated |
|
||||
| Team capacity | Medium | Medium | Clear task breakdown; realistic timeline | 🟢 Mitigated |
|
||||
### Demo Credentials
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Phase 1 Targets
|
||||
|
||||
**Functionality**:
|
||||
|
||||
- [ ] All Phase 1 features implemented
|
||||
- [ ] All acceptance criteria met
|
||||
- [ ] Zero critical bugs
|
||||
|
||||
**Quality**:
|
||||
|
||||
- [ ] 80%+ test coverage (backend and frontend)
|
||||
- [ ] WCAG 2.1 AA compliance
|
||||
- [ ] Zero security vulnerabilities (critical/high)
|
||||
|
||||
**Performance**:
|
||||
|
||||
- [ ] API response time < 200ms (p95)
|
||||
- [ ] Page load time < 2s on 3G
|
||||
- [ ] Lighthouse score > 90
|
||||
|
||||
**Documentation**:
|
||||
|
||||
- [ ] API documentation complete
|
||||
- [ ] User guide complete
|
||||
- [ ] Deployment guide complete
|
||||
|
||||
---
|
||||
|
||||
## Team & Roles
|
||||
|
||||
**Recommended Team Composition**:
|
||||
|
||||
- **1x Backend Developer**: Fastify, Prisma, PostgreSQL, Auth
|
||||
- **1x Frontend Developer**: React, Tailwind, shadcn/ui, Mobile UX
|
||||
- **1x Full-Stack Developer** (optional): Can support both sides
|
||||
- **1x Product Manager** (part-time): Spec review, stakeholder communication
|
||||
- **1x QA/Tester** (part-time): Testing, bug reporting
|
||||
|
||||
**Minimum Viable Team**:
|
||||
|
||||
- **1x Full-Stack Developer**: Can complete Phase 1 in 6 weeks solo
|
||||
|
||||
---
|
||||
|
||||
## Communication & Tracking
|
||||
|
||||
**Recommended Tools**:
|
||||
|
||||
- **Project Management**: GitHub Projects, Linear, or Jira
|
||||
- **Communication**: Slack or Discord
|
||||
- **Documentation**: Notion or Confluence
|
||||
- **Code Repository**: GitHub or GitLab
|
||||
- **CI/CD**: GitHub Actions or GitLab CI
|
||||
|
||||
**Meeting Cadence** (if team):
|
||||
|
||||
- **Daily Standup**: 15 min (async or sync)
|
||||
- **Weekly Planning**: 1 hour (review tasks, plan next week)
|
||||
- **Bi-Weekly Demo**: 30 min (show progress to stakeholders)
|
||||
- **Retrospective**: 1 hour (end of each phase)
|
||||
|
||||
---
|
||||
|
||||
## Next Actions
|
||||
|
||||
### Immediate (This Week)
|
||||
|
||||
1. ✅ Review Phase 1 plan with team/stakeholders
|
||||
2. ✅ Review Week 1 tasks
|
||||
3. ⏭️ **Start TASK-001**: Initialize Backend Project
|
||||
4. ⏭️ **Start TASK-006**: Initialize Frontend Project
|
||||
5. ⏭️ **Start TASK-013**: Create Git Repository
|
||||
|
||||
### Short-Term (Next 2 Weeks)
|
||||
|
||||
1. Complete Week 1: Infrastructure Setup
|
||||
2. Complete Week 2: Authentication & RBAC
|
||||
3. Create Week 2 task breakdown
|
||||
4. Set up project tracking (GitHub Projects)
|
||||
|
||||
### Medium-Term (Next 6 Weeks)
|
||||
|
||||
1. Complete Phase 1: Foundation
|
||||
2. Deploy to staging environment
|
||||
3. Conduct stakeholder demo
|
||||
4. Plan Phase 2: Operations
|
||||
- **Admin**: `admin@777wolfpack.com` / `admin123`
|
||||
- **Manager**: `manager@777wolfpack.com` / `manager123`
|
||||
- **Worker**: `worker@777wolfpack.com` / `worker123`
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
|
||||
- [Constitution](.specify/memory/constitution.md)
|
||||
- [Project Spec](spec.yml)
|
||||
- [Roadmap](docs/ROADMAP.md)
|
||||
- [Architecture](docs/architecture.md)
|
||||
- [Compliance Notes](docs/compliance-notes-ca.md)
|
||||
|
||||
### Plans
|
||||
|
||||
- [Phase 1 Plan](plans/phase-1-foundation.md)
|
||||
- [Week 1 Tasks](plans/tasks-week-1-infrastructure.md)
|
||||
|
||||
### Specs
|
||||
|
||||
- [Tasks and Scheduling](specs/tasks-and-scheduling.md)
|
||||
- [Batches and Rooms](specs/batches-and-rooms.md)
|
||||
- [Labor and Hours](specs/labor-and-hours.md)
|
||||
- [Compliance and Docs](specs/compliance-and-docs.md)
|
||||
- [Inventory and Materials](specs/inventory-and-materials.md)
|
||||
- [Integrations and Hardware](specs/integrations-and-hardware.md)
|
||||
- [Communications and Notifications](specs/communications-and-notifications.md)
|
||||
- [3D Viewer Refactor Plan](.agent/plans/3d_viewer_refactor.md)
|
||||
- [Verification Guide](VERIFICATION_GUIDE.md)
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2025-12-08
|
||||
|
||||
- ✅ Project initialized with Spec Kit
|
||||
- ✅ Constitution created
|
||||
- ✅ 7 feature specs written
|
||||
- ✅ Architecture documented
|
||||
- ✅ Phase 1 plan created
|
||||
- ✅ Week 1 tasks defined
|
||||
- ✅ Architecture decisions made (Fastify, Vite, shadcn/ui)
|
||||
|
||||
---
|
||||
|
||||
**Status**: 🟢 ON TRACK
|
||||
**Next Milestone**: Week 1 Complete (TBD)
|
||||
**Phase 1 Target**: 6 weeks from start
|
||||
**Status**: 🟢 DEMO READY
|
||||
**Next Steps**: Smoke test deployment, prepare for stakeholder demo
|
||||
|
|
|
|||
40
VERIFICATION_GUIDE.md
Normal file
40
VERIFICATION_GUIDE.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# 3D Facility Viewer & METRC Integration - Verification Guide
|
||||
|
||||
## 1. Feature Overview
|
||||
|
||||
We have integrated METRC plant tracking with the 3D facility viewer and added advanced data visualizations.
|
||||
|
||||
- **Deep Linking**: Click "View in 3D" from the METRC Dashboard to fly directly to a plant's location.
|
||||
- **Plant History**: View a timeline of plant movements in the METRC Dashboard.
|
||||
- **Data Overlays**: Toggle "Health", "Temperature", "Humidity", and "Yield" modes in the 3D viewer.
|
||||
|
||||
## 2. Verification Steps
|
||||
|
||||
### A. METRC Dashboard Integration
|
||||
|
||||
1. Navigate to `/metrc`.
|
||||
2. Find a plant in the "Live Plants" table.
|
||||
3. **Test History**: Click the **"History"** button.
|
||||
- Verify a modal appears showing the plant's current location and movement log.
|
||||
- Click **"Locate in 3D"** within the modal.
|
||||
4. **Test Direct Link**: Click the **"View in 3D"** button (box icon) in the table row.
|
||||
- Verify you are redirected to `/facility/3d`.
|
||||
- Verify the camera automatically flies to the room containing the plant.
|
||||
- Verify the specific plant is selected (popup overlay appears).
|
||||
|
||||
### B. 3D Data Visualization
|
||||
|
||||
1. Navigate to `/facility/3d`.
|
||||
2. Look for the floating control panel on the **right side** of the screen.
|
||||
3. Test the Modes:
|
||||
- **Standard**: Default view, plants colored by stage (Veg/Flower).
|
||||
- **Health**: Plants colored Green (Good), Yellow (Warning), Red (Critical). *Note: Uses simulated data for demo.*
|
||||
- **Temp**: Rooms colored by temperature heatmap (Blue -> Red). Hover/Look for temperature labels.
|
||||
- **VPD (Humidity)**: Rooms colored by humidity heatmap.
|
||||
- **Yield**: Plants colored by maturity gradient.
|
||||
4. **Legend**: Verify the legend at the top of the screen updates to reflect the active mode.
|
||||
|
||||
## 3. Configuration & Troubleshooting
|
||||
|
||||
- **Data Source**: The environment and health data are currently simulated (`getMockRoomEnv`, `getMockPlantHealth`) for demonstration purposes. To connect real sensors, update the `useEffect` hook in `Facility3DViewerPage.tsx` to fetch from your IoT API.
|
||||
- **Missing Plants**: If a plant is not found when deep-linking, a warning is logged to the console, and the viewer stays at the default view. Ensure the `seed-plants.js` script has been run to populate the facility.
|
||||
|
|
@ -8,7 +8,7 @@ describe('CA Grow Ops Manager API Tests', () => {
|
|||
describe('Health Check', () => {
|
||||
it('should return ok status', async () => {
|
||||
const response = await fetch(`${API_BASE}/healthz`);
|
||||
const data = await response.json();
|
||||
const data = await response.json() as { status: string; timestamp: string };
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.status).toBe('ok');
|
||||
|
|
@ -52,10 +52,10 @@ describe('CA Grow Ops Manager API Tests', () => {
|
|||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = await response.json();
|
||||
const data = await response.json() as { token: string; user?: { id: string } };
|
||||
expect(data.token).toBeDefined();
|
||||
authToken = data.token;
|
||||
testUserId = data.user?.id;
|
||||
testUserId = data.user?.id || '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -160,7 +160,7 @@ describe('CA Grow Ops Manager API Tests', () => {
|
|||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
const data = await response.json();
|
||||
const data = await response.json() as { sizes: unknown };
|
||||
expect(data.sizes).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -198,7 +198,7 @@ describe('CA Grow Ops Manager API Tests', () => {
|
|||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
const data = await response.json();
|
||||
const data = await response.json() as { transactions: unknown[] };
|
||||
expect(data.transactions).toBeDefined();
|
||||
});
|
||||
|
||||
|
|
|
|||
368
frontend/package-lock.json
generated
368
frontend/package-lock.json
generated
|
|
@ -13,8 +13,8 @@
|
|||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.4.2",
|
||||
"@react-three/drei": "9.107.2",
|
||||
"@react-three/fiber": "8.16.8",
|
||||
"axios": "^1.6.2",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
"react-konva": "^18.2.10",
|
||||
"react-router-dom": "^7.10.1",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"three": "^0.182.0",
|
||||
"three": "0.165.0",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -1183,9 +1183,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mediapipe/tasks-vision": {
|
||||
"version": "0.10.17",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz",
|
||||
"integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==",
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.8.tgz",
|
||||
"integrity": "sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@monogrid/gainmap-js": {
|
||||
|
|
@ -2375,39 +2375,115 @@
|
|||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-three/drei": {
|
||||
"version": "10.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz",
|
||||
"integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==",
|
||||
"node_modules/@react-spring/animated": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz",
|
||||
"integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mediapipe/tasks-vision": "0.10.17",
|
||||
"@monogrid/gainmap-js": "^3.0.6",
|
||||
"@use-gesture/react": "^10.3.1",
|
||||
"camera-controls": "^3.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"detect-gpu": "^5.0.56",
|
||||
"glsl-noise": "^0.0.0",
|
||||
"hls.js": "^1.5.17",
|
||||
"maath": "^0.10.8",
|
||||
"meshline": "^3.3.1",
|
||||
"stats-gl": "^2.2.8",
|
||||
"stats.js": "^0.17.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"three-mesh-bvh": "^0.8.3",
|
||||
"three-stdlib": "^2.35.6",
|
||||
"troika-three-text": "^0.52.4",
|
||||
"tunnel-rat": "^0.1.2",
|
||||
"use-sync-external-store": "^1.4.0",
|
||||
"utility-types": "^3.11.0",
|
||||
"zustand": "^5.0.1"
|
||||
"@react-spring/shared": "~9.6.1",
|
||||
"@react-spring/types": "~9.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/fiber": "^9.0.0",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"three": ">=0.159"
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/core": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz",
|
||||
"integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/animated": "~9.6.1",
|
||||
"@react-spring/rafz": "~9.6.1",
|
||||
"@react-spring/shared": "~9.6.1",
|
||||
"@react-spring/types": "~9.6.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/react-spring/donate"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/rafz": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz",
|
||||
"integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-spring/shared": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz",
|
||||
"integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/rafz": "~9.6.1",
|
||||
"@react-spring/types": "~9.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/three": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz",
|
||||
"integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/animated": "~9.6.1",
|
||||
"@react-spring/core": "~9.6.1",
|
||||
"@react-spring/shared": "~9.6.1",
|
||||
"@react-spring/types": "~9.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/fiber": ">=6.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"three": ">=0.126"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/types": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz",
|
||||
"integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-three/drei": {
|
||||
"version": "9.107.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.107.2.tgz",
|
||||
"integrity": "sha512-MQqRhq3bFKkYuhwp812JwdcYRy/601+jokri2m+PmuuWGy6Xi9n001pzeH47iy1Gb8KCiGHAilGXiXF87A/t4A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@mediapipe/tasks-vision": "0.10.8",
|
||||
"@monogrid/gainmap-js": "^3.0.5",
|
||||
"@react-spring/three": "~9.6.1",
|
||||
"@use-gesture/react": "^10.2.24",
|
||||
"camera-controls": "^2.4.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"detect-gpu": "^5.0.28",
|
||||
"glsl-noise": "^0.0.0",
|
||||
"hls.js": "1.3.5",
|
||||
"maath": "^0.10.7",
|
||||
"meshline": "^3.1.6",
|
||||
"react-composer": "^5.0.3",
|
||||
"stats-gl": "^2.0.0",
|
||||
"stats.js": "^0.17.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"three-mesh-bvh": "^0.7.0",
|
||||
"three-stdlib": "^2.29.9",
|
||||
"troika-three-text": "^0.49.0",
|
||||
"tunnel-rat": "^0.1.2",
|
||||
"utility-types": "^3.10.0",
|
||||
"uuid": "^9.0.1",
|
||||
"zustand": "^3.7.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/fiber": ">=8.0",
|
||||
"react": ">=18.0",
|
||||
"react-dom": ">=18.0",
|
||||
"three": ">=0.137"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
|
|
@ -2416,62 +2492,49 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-three/drei/node_modules/zustand": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.9.tgz",
|
||||
"integrity": "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber": {
|
||||
"version": "9.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.4.2.tgz",
|
||||
"integrity": "sha512-H4B4+FDNHpvIb4FmphH4ubxOfX5bxmfOw0+3pkQwR9u9wFiyMS7wUDkNn0m4RqQuiLWeia9jfN1eBvtyAVGEog==",
|
||||
"version": "8.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.16.8.tgz",
|
||||
"integrity": "sha512-Lc8fjATtvQEfSd8d5iKdbpHtRm/aPMeFj7jQvp6TNHfpo8IQTW3wwcE1ZMrGGoUH+w2mnyS+0MK1NLPLnuzGkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.8",
|
||||
"@types/react-reconciler": "^0.32.0",
|
||||
"@types/react-reconciler": "^0.26.7",
|
||||
"@types/webxr": "*",
|
||||
"base64-js": "^1.5.1",
|
||||
"buffer": "^6.0.3",
|
||||
"its-fine": "^2.0.0",
|
||||
"react-reconciler": "^0.31.0",
|
||||
"react-use-measure": "^2.1.7",
|
||||
"scheduler": "^0.25.0",
|
||||
"its-fine": "^1.0.6",
|
||||
"react-reconciler": "^0.27.0",
|
||||
"react-use-measure": "^2.1.1",
|
||||
"scheduler": "^0.21.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"use-sync-external-store": "^1.4.0",
|
||||
"zustand": "^5.0.3"
|
||||
"zustand": "^3.7.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": ">=43.0",
|
||||
"expo-asset": ">=8.4",
|
||||
"expo-file-system": ">=11.0",
|
||||
"expo-gl": ">=11.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-native": ">=0.78",
|
||||
"three": ">=0.156"
|
||||
"react": ">=18.0",
|
||||
"react-dom": ">=18.0",
|
||||
"react-native": ">=0.64",
|
||||
"three": ">=0.133"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"expo": {
|
||||
|
|
@ -2495,82 +2558,53 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/@types/react-reconciler": {
|
||||
"version": "0.32.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.32.3.tgz",
|
||||
"integrity": "sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/its-fine": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz",
|
||||
"integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==",
|
||||
"version": "0.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
|
||||
"integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react-reconciler": "^0.28.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/its-fine/node_modules/@types/react-reconciler": {
|
||||
"version": "0.28.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz",
|
||||
"integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/react-reconciler": {
|
||||
"version": "0.31.0",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz",
|
||||
"integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==",
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
|
||||
"integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/scheduler": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||
"license": "MIT"
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
|
||||
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/zustand": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.9.tgz",
|
||||
"integrity": "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -3105,7 +3139,6 @@
|
|||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/qrcode": {
|
||||
|
|
@ -3122,7 +3155,6 @@
|
|||
"version": "18.3.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
|
|
@ -3133,7 +3165,7 @@
|
|||
"version": "18.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
|
||||
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.0.0"
|
||||
|
|
@ -4025,14 +4057,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/camera-controls": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz",
|
||||
"integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==",
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.10.1.tgz",
|
||||
"integrity": "sha512-KnaKdcvkBJ1Irbrzl8XD6WtZltkRjp869Jx8c0ujs9K+9WD+1D7ryBsCiVqJYUqt6i/HR5FxT7RLASieUD+Q5w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
"npm": ">=10.5.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.126.1"
|
||||
}
|
||||
|
|
@ -4342,7 +4370,6 @@
|
|||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
|
|
@ -5499,9 +5526,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/hls.js": {
|
||||
"version": "1.6.15",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz",
|
||||
"integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==",
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.3.5.tgz",
|
||||
"integrity": "sha512-uybAvKS6uDe0MnWNEPnO0krWVr+8m2R0hJ/viql8H3MVK+itq8gGQuIYoFHL3rECkIpNH98Lw8YuuWMKZxp3Ew==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/html-encoding-sniffer": {
|
||||
|
|
@ -6655,7 +6682,6 @@
|
|||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
|
@ -7200,6 +7226,23 @@
|
|||
"lie": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types/node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
|
|
@ -7295,6 +7338,18 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-composer": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz",
|
||||
"integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
|
|
@ -8268,18 +8323,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.182.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz",
|
||||
"integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==",
|
||||
"version": "0.165.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.165.0.tgz",
|
||||
"integrity": "sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/three-mesh-bvh": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz",
|
||||
"integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==",
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.6.tgz",
|
||||
"integrity": "sha512-rCjsnxEqR9r1/C/lCqzGLS67NDty/S/eT6rAJfDvsanrIctTWdNoR4ZOGWewCB13h1QkVo2BpmC0wakj1+0m8A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.159.0"
|
||||
"three": ">= 0.151.0"
|
||||
}
|
||||
},
|
||||
"node_modules/three-stdlib": {
|
||||
|
|
@ -8423,14 +8478,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/troika-three-text": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
||||
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
||||
"version": "0.49.1",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.49.1.tgz",
|
||||
"integrity": "sha512-lXGWxgjJP9kw4i4Wh+0k0Q/7cRfS6iOME4knKht/KozPu9GcFA9NnNpRvehIhrUawq9B0ZRw+0oiFHgRO+4Wig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bidi-js": "^1.0.2",
|
||||
"troika-three-utils": "^0.52.4",
|
||||
"troika-worker-utils": "^0.52.0",
|
||||
"troika-three-utils": "^0.49.0",
|
||||
"troika-worker-utils": "^0.49.0",
|
||||
"webgl-sdf-generator": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
@ -8438,18 +8493,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/troika-three-utils": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
||||
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
||||
"version": "0.49.0",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.49.0.tgz",
|
||||
"integrity": "sha512-umitFL4cT+Fm/uONmaQEq4oZlyRHWwVClaS6ZrdcueRvwc2w+cpNQ47LlJKJswpqtMFWbEhOLy0TekmcPZOdYA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-worker-utils": {
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
||||
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==",
|
||||
"version": "0.49.0",
|
||||
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.49.0.tgz",
|
||||
"integrity": "sha512-1xZHoJrG0HFfCvT/iyN41DvI/nRykiBtHqFkGaGgJwq5iXfIZFBiPPEHFpPpgyKM3Oo5ITHXP5wM2TNQszYdVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
|
|
@ -8527,7 +8582,7 @@
|
|||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
|
@ -8681,6 +8736,19 @@
|
|||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/uzip": {
|
||||
"version": "0.20201231.0",
|
||||
"resolved": "https://registry.npmjs.org/uzip/-/uzip-0.20201231.0.tgz",
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export const NAV_SECTIONS: NavSection[] = [
|
|||
{ id: 'audit', label: 'Audit Log', path: '/compliance/audit', icon: ClipboardList },
|
||||
{ id: 'documents', label: 'SOP Library', path: '/compliance/documents', icon: FileText },
|
||||
{ id: 'metrc', label: 'METRC Integration', path: '/metrc', icon: Cloud, minRole: 'MANAGER' },
|
||||
{ id: 'facility-3d', label: '3D Facility View', path: '/facility-3d', icon: Grid3X3, minRole: 'MANAGER' },
|
||||
{ id: 'facility-3d', label: '3D Facility View', path: '/facility/3d', icon: Grid3X3, minRole: 'MANAGER' },
|
||||
{ id: 'layout', label: 'Layout Designer', path: '/layout-designer', icon: Grid3X3, minRole: 'ADMIN' },
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Shield, Clock, MapPin, Loader, AlertTriangle, User, RotateCcw, QrCode, LogOut } from 'lucide-react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { Shield, Clock, MapPin, Loader, AlertTriangle, User, RotateCcw, QrCode, LogOut, LayoutDashboard } from 'lucide-react';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { visitorsApi, Visitor, VisitorLog } from '../lib/visitorsApi';
|
||||
|
||||
|
|
@ -151,9 +151,9 @@ export default function BadgePage() {
|
|||
|
||||
{/* Type Badge */}
|
||||
<span className={`px-4 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-4 ${visitor.type === 'VISITOR' ? 'bg-blue-100 text-blue-700' :
|
||||
visitor.type === 'CONTRACTOR' ? 'bg-amber-100 text-amber-700' :
|
||||
visitor.type === 'INSPECTOR' ? 'bg-red-100 text-red-700' :
|
||||
'bg-slate-100 text-slate-700'
|
||||
visitor.type === 'CONTRACTOR' ? 'bg-amber-100 text-amber-700' :
|
||||
visitor.type === 'INSPECTOR' ? 'bg-red-100 text-red-700' :
|
||||
'bg-slate-100 text-slate-700'
|
||||
}`}>
|
||||
{visitor.type}
|
||||
</span>
|
||||
|
|
@ -262,10 +262,19 @@ export default function BadgePage() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Branding */}
|
||||
<p className="text-slate-600 text-xs mt-8 text-center">
|
||||
Digital Badge powered by 777 Wolfpack Grow Ops Manager
|
||||
</p>
|
||||
{/* Bottom Navigation */}
|
||||
<div className="mt-8 flex flex-col items-center gap-3">
|
||||
<Link
|
||||
to="/"
|
||||
className="flex items-center gap-2 text-slate-400 hover:text-emerald-400 text-sm transition-colors px-4 py-2 rounded-lg hover:bg-white/5"
|
||||
>
|
||||
<LayoutDashboard size={16} />
|
||||
<span>Dashboard</span>
|
||||
</Link>
|
||||
<p className="text-slate-600 text-xs text-center">
|
||||
Digital Badge powered by 777 Wolfpack Grow Ops Manager
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useState, useEffect, useRef } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { QRCodeSVG as QRCode } from 'qrcode.react';
|
||||
import { User, Building, Clock, CheckCircle, XCircle, UserPlus, LogOut, Search, Shield, AlertTriangle, Camera, Trash2, Download } from 'lucide-react';
|
||||
import { User, Building, Clock, CheckCircle, XCircle, UserPlus, LogOut, Search, Shield, AlertTriangle, Camera, Trash2, Download, LayoutDashboard } from 'lucide-react';
|
||||
import { visitorsApi, Visitor, ActiveVisitor } from '../lib/visitorsApi';
|
||||
|
||||
type KioskMode = 'home' | 'new-visitor' | 'returning' | 'check-in' | 'check-out' | 'success';
|
||||
|
|
@ -221,9 +222,18 @@ export default function VisitorKioskPage() {
|
|||
<p className="text-xs text-slate-400">777 Wolfpack Facility</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-slate-400 text-sm">
|
||||
<Clock size={16} />
|
||||
<span>{new Date().toLocaleTimeString()}</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-slate-400 text-sm">
|
||||
<Clock size={16} />
|
||||
<span>{new Date().toLocaleTimeString()}</span>
|
||||
</div>
|
||||
<Link
|
||||
to="/"
|
||||
className="flex items-center gap-2 text-slate-400 hover:text-emerald-400 text-sm transition-colors px-3 py-1.5 rounded-lg hover:bg-white/5"
|
||||
>
|
||||
<LayoutDashboard size={16} />
|
||||
<span className="hidden sm:inline">Dashboard</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue