Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3bde9de
feat(dashboard): add dashboard
matm89 Dec 2, 2025
5e499de
Merge remote-tracking branch 'origin/dev' into feat/dashboard
matm89 Dec 2, 2025
5e9de53
Merge remote-tracking branch 'origin/dev' into feat/dashboard
matm89 Dec 2, 2025
4eeac51
refactor(dashboard): add actual dashboard
matm89 Dec 2, 2025
6a9d7b3
feat(removed asterisk): removed Asterisk
AlfredoChavez Dec 2, 2025
81c9631
fix(sign up fixed hook): sign Up Fixed Hook
AlfredoChavez Dec 2, 2025
46943db
feat(immersiveviewpage style): immersiveViewPage Cohesive Style
AlfredoChavez Dec 2, 2025
a852bf8
feat(roomservice): add update
matm89 Dec 2, 2025
165cedf
chore(login-form): comment console.log
AlfredoChavez Dec 2, 2025
e4c3eb6
refactor(roomstore): add new methods
matm89 Dec 2, 2025
6445fea
feat(wip immersiveview): wIP ImmersiveView
AlfredoChavez Dec 2, 2025
ded5949
Merge branch 'feature/immersive-room' of github.com:Home-Pulse-App/fr…
AlfredoChavez Dec 2, 2025
adb3496
feat(wip sending room splat): wIP sending room splat
AlfredoChavez Dec 2, 2025
94ce3fd
feat(immersivepage): fixed ImmersivePage to pass roomID
AlfredoChavez Dec 2, 2025
c08e7ab
refactor(immersiveview): add room views
matm89 Dec 2, 2025
9f6d48d
Merge branch 'feature/immersive-room' of github.com:Home-Pulse-App/fr…
AlfredoChavez Dec 2, 2025
68e9cad
feat(room splat implementation): add new Splat to rooms
matm89 Dec 2, 2025
dc3fa58
Merge branch 'feature/immersive-room' of github.com:Home-Pulse-App/fr…
AlfredoChavez Dec 2, 2025
0d050c9
test:some changes
matm89 Dec 3, 2025
b9be97d
Merge remote-tracking branch 'origin/dev' into feat/dashboard
matm89 Dec 3, 2025
ae41201
Merge branch 'feature/immersive-room' of https://github.com/Home-Puls…
matm89 Dec 3, 2025
4eb8e8a
Merge remote-tracking branch 'origin/feat/dashboard' into feature/imm…
matm89 Dec 3, 2025
1335596
feat(immersive view): add devices with DB
matm89 Dec 3, 2025
2790ea1
style(multiple files): lint fix
AlfredoChavez Dec 3, 2025
49db7be
feat(add immersive devices): add Immersive Devices
AlfredoChavez Dec 3, 2025
cdc5dd9
fix(room navigation): fixed Room navigation on first time
AlfredoChavez Dec 3, 2025
2684027
feat(add login form autocomplete required by dom): add login form aut…
AlfredoChavez Dec 3, 2025
6f6d8c5
feat(remove unnecessary navigation data): remove unnecessary navigati…
AlfredoChavez Dec 3, 2025
bdf38fe
chore(remove console.log): remove console.log
AlfredoChavez Dec 3, 2025
0378983
fix(devices): fixed read devices from DB
matm89 Dec 3, 2025
225632b
Merge branch 'feature/immersive-room' of https://github.com/Home-Puls…
matm89 Dec 3, 2025
9fd0781
feat: dashboard
koepadam Dec 3, 2025
d9f8f76
refactor(dashboard): make it interactive
matm89 Dec 3, 2025
76460c9
fix: linted
matm89 Dec 3, 2025
e028d39
fix: CI build errors, eliminated:nav-user,nav-main,nav-secondary
matm89 Dec 3, 2025
e9f32e9
fix: removed: nav-secondary
matm89 Dec 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,316 changes: 1,286 additions & 30 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,51 @@
"preview": "vite preview"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.4.0",
"@sparkjsdev/spark": "^0.1.10",
"@tabler/icons-react": "^3.35.0",
"@tailwindcss/vite": "^4.1.17",
"@tanstack/react-table": "^8.21.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.24",
"gsap": "^3.13.0",
"lucide-react": "^0.555.0",
"media-chrome": "^4.16.0",
"next-themes": "^0.4.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-hook-form": "^7.66.1",
"react-icons": "^5.5.0",
"react-router-dom": "^7.9.6",
"react-router-hash-link": "^2.4.3",
"recharts": "^2.15.4",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"three": "^0.181.2",
"valtio": "^2.2.0",
"vaul": "^1.1.2",
"vite-plugin-svgr": "^4.5.0",
"zod": "^4.1.13",
"zustand": "^5.0.9"
Expand All @@ -60,6 +77,7 @@
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"postcss": "^8.5.6",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.1.17",
"tw-animate-css": "^1.4.0",
"typescript": "~5.9.3",
Expand All @@ -71,4 +89,4 @@
"path": "./node_modules/cz-conventional-changelog"
}
}
}
}
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function App() {
<Route element={<ProtectedRoute />}>
<Route path='/dashboard' element={<Dashboard />} />
<Route path='/ImmersiveView' element={<ImmersiveViewPage />} />
<Route path='/ImmersiveView/:roomId' element={<ImmersiveViewPage />} />
<Route path='/viewer' element={<SparkComponent />} />
<Route path='/homes' element={<HomesPage />} />
<Route path='/homes/:id' element={<HomeDetailsPage />} />
Expand Down
39 changes: 28 additions & 11 deletions src/components/Devices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { useThree } from '@react-three/fiber';
import { TransformControls, useGLTF, useCursor, Outlines } from '@react-three/drei';
import { proxy, useSnapshot } from 'valtio';
import * as THREE from 'three';
import { deviceDataService } from '@/services/deviceDataService';
import type { SensorData } from '@/types/sensorsDataTypes';
import type { DeviceData } from '@/types/device';
import { useRoomStore } from '@/store/roomStore';
import { useDeviceDataStore } from '@/store/sensorStore';

const modes = ['translate', 'rotate', 'scale'] as const;

Expand Down Expand Up @@ -148,11 +149,14 @@ interface DevicesProps {
initialDevices?: DeviceData[];
onDevicesChange?: (devices: DeviceData[]) => void;
onSensorDataUpdate?: (deviceId: string, sensorData: SensorData) => void;
roomId?: string;
}

export default function Devices({ deviceToSpawn, onSpawned, initialDevices = [], onDevicesChange, onSensorDataUpdate }: DevicesProps) {
export default function Devices({ deviceToSpawn, onSpawned, initialDevices = [], onDevicesChange, onSensorDataUpdate, roomId }: DevicesProps) {
const [devices, setDevices] = useState<DeviceData[]>(initialDevices);
const camera = useThree((state) => state.camera);
const { devices: iotDevices, fetchRoom } = useRoomStore();
const { latestData, fetchLatestData } = useDeviceDataStore();

//* Update internal state if initialDevices changes (e.g. loaded from server)
useEffect(() => {
Expand Down Expand Up @@ -201,7 +205,7 @@ export default function Devices({ deviceToSpawn, onSpawned, initialDevices = [],
//* Handle Backspace
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
console.log('Key pressed:', event.key, 'Current device:', deviceState.current);
// console.log('Key pressed:', event.key, 'Current device:', deviceState.current);
if (event.key === 'Backspace' && deviceState.current) {
const idToRemove = deviceState.current;
const nameToRemove = deviceState.name;
Expand All @@ -220,14 +224,13 @@ export default function Devices({ deviceToSpawn, onSpawned, initialDevices = [],
setDevices(prev => prev.map(d =>
d.id === deviceId ? { ...d, sensorData } : d,
));
// console.log(devices); // Removed to avoid dependency on 'devices'
// console.log(devices);
onSensorDataUpdate?.(deviceId, sensorData);
console.log('📈', sensorData);
}, [onSensorDataUpdate]);

//* Expose updateDeviceSensorData to parent via callback
useEffect(() => {

if (onSensorDataUpdate) {
(window as any).__updateDeviceSensorData = updateDeviceSensorData;
}
Expand All @@ -242,14 +245,29 @@ export default function Devices({ deviceToSpawn, onSpawned, initialDevices = [],
devicesRef.current = devices;
}, [devices]);

//* Keep a ref to latestData to access latest sensor data inside interval without resetting it
const latestDataRef = useRef(latestData);
useEffect(() => {
latestDataRef.current = latestData;
}, [latestData]);

useEffect(() => {
const responseData = async () => {
try {
const deviceId = 'iot1';
const newData = await deviceDataService.getLatest(deviceId);
//* Use the ref to get the latest devices list
let deviceId = 'iot1';
if (roomId) {
fetchRoom(roomId);
if (iotDevices[0].length > 0) {
console.log('IoTDevices🔧:', iotDevices);
deviceId = iotDevices[0] as unknown as string;
}
}
console.log('IoT🔧:', deviceId, roomId);
await fetchLatestData(deviceId);
//* Use the refs to get the latest state without causing re-renders
// console.log('IoT🔧:', latestDataRef.current);
devicesRef.current.forEach(d => {
updateDeviceSensorData(d.id, newData.data.latest.sensorsData);
updateDeviceSensorData(d.id, latestDataRef.current);
});
} catch (error) {
console.error('Error fetching device data:', error);
Expand Down Expand Up @@ -291,5 +309,4 @@ export default function Devices({ deviceToSpawn, onSpawned, initialDevices = [],
))}
</Suspense>
);
}

}
21 changes: 17 additions & 4 deletions src/components/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useState, useRef } from 'react';
import { useNavigate } from 'react-router';
import Folder from '../components/ui/Folder';
import { mockServer } from '../services/localDBService';
// import { mockServer } from '../services/localDBService';
import { useRoomStore } from '@/store/roomStore';

type FileUploadProps = {
onFileUpload?: (fileData: FileData) => void;
roomId: string;
};

export interface FileData {
Expand All @@ -14,11 +16,12 @@ export interface FileData {
url: string;
}

function FileUpload({ onFileUpload }: FileUploadProps) {
function FileUpload({ onFileUpload, roomId }: FileUploadProps) {
//* Trick to avoid unused variables
const [, setIsDragging] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const navigate = useNavigate();
const { updateRoom } = useRoomStore();

const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -72,10 +75,20 @@ function FileUpload({ onFileUpload }: FileUploadProps) {
reader.onload = async () => {
const base64Data = reader.result as string;
console.log('[FileUpload] File read complete, base64 length:', base64Data.length);
await mockServer.saveSplatData('default-user', base64Data);
// await mockServer.saveSplatData('default-user', base64Data);

// Validate roomId before making API call
if (!roomId || roomId === 'null' || roomId === 'undefined') {
console.error('[FileUpload] Invalid roomId:', roomId);
console.error('[FileUpload] Cannot save splat data without a valid room ID');
return;
}

const response = await updateRoom(roomId, { viewSplat: base64Data });
console.log(response);
console.log('[FileUpload] Save complete, navigating...');
//* Navigate ONLY after saving is complete
navigate('/viewer', { state: { file: fileData } });
navigate('/viewer', { state: { file: fileData, roomId } });
};
reader.onerror = (error) => {
console.error('[FileUpload] FileReader error:', error);
Expand Down
1 change: 0 additions & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export default function Navbar() {
logoHref='/'
navigationLinks={[
{ label: 'Dashboard', href: '/dashboard' },
{ label: 'Immersive View', href: '/ImmersiveView' },
{ label: 'Homes', href: '/homes' },
{ label: 'Devices', href: '/devices' },
{ label: 'Getting Started', href: '/getting-started' },
Expand Down
2 changes: 0 additions & 2 deletions src/components/Process.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Asterisk } from 'lucide-react';
import React from 'react';
import { FaEye, FaArrowRight, FaHome, FaPeopleCarry, FaLightbulb } from 'react-icons/fa';

Expand Down Expand Up @@ -49,7 +48,6 @@ const Process = () => {
<div className='relative w-fit text-5xl font-semibold tracking-tight lg:text-7xl'>
{' '}
<h1 className='w-fit'>The Home Pulse Journey</h1>
<Asterisk className='absolute -right-2 -top-2 size-5 text-orange-500 md:size-10 lg:-right-14' />
</div>
<p className='text-foreground/50 text-base text-justify'>
Home Pulse is the result of curiosity, collaboration,
Expand Down
12 changes: 11 additions & 1 deletion src/components/RoomCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export default function RoomCard({ room, homeId, onDelete }: RoomCardProps) {
navigate(`/homes/${homeId}/rooms/${room._id}`);
}

function handleClick() {
navigate(`/ImmersiveView/${room._id}`);
}

return (
<div
onClick={handleCardClick}
Expand Down Expand Up @@ -69,7 +73,13 @@ export default function RoomCard({ room, homeId, onDelete }: RoomCardProps) {
</div>
<div className='flex items-center justify-between mt-4'>
<p className='text-gray-600'>Devices: {room.devices ? room.devices.length : 0}</p>
<button className='px-3 py-1 rounded-md font-medium bg-gray-200 hover:bg-gray-300 text-gray-700'>
<button
onClick={(e) => {
e.stopPropagation();
handleClick();
}}
className='px-3 py-1 rounded-md font-medium bg-gray-200 hover:bg-gray-300 text-gray-700'
>
Immersive View
</button>
</div>
Expand Down
19 changes: 12 additions & 7 deletions src/components/SparkComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import Dock from './ui/Dock';
import { FaHome, FaKeyboard, FaLightbulb, FaSun, FaThermometerHalf, FaWater } from 'react-icons/fa';
import Instructions from './ui/Instructions/Instructions';
import Devices, { deviceState } from './Devices';
import { mockServer } from '../services/localDBService';
import { type DeviceData } from '@/types/device';
import SensorControlPanel from './ui/SensorControlPanel';
import { useSnapshot } from 'valtio';
import type { SensorData } from '@/types/sensorsDataTypes';
import { Progress } from '@/components/ui/Progress';
import { useRoomStore } from '@/store/roomStore';

//* Export deviceState so other components can check transformation status
export { deviceState };
Expand All @@ -31,6 +31,8 @@ function SparkComponent() {
const [deviceToSpawn, setDeviceToSpawn] = useState<string | null>(null);
const [initialDevices, setInitialDevices] = useState<DeviceData[]>([]);
const [devices, setDevices] = useState<DeviceData[]>([]);
const { viewDevices, fetchRoom, updateRoom } = useRoomStore();
const roomId = location.state?.roomId;

const snap = useSnapshot(deviceState);

Expand All @@ -42,8 +44,10 @@ function SparkComponent() {
}

//* Check for initial devices passed from Load Session
if (location.state?.devices) {
setInitialDevices(location.state.devices);
//! BIG TODO: Refactor to work with more than one device
if (roomId) {
fetchRoom(roomId);
setInitialDevices(viewDevices);
}
}, [location.state, navigate]);

Expand Down Expand Up @@ -76,17 +80,17 @@ function SparkComponent() {

const handleDevicesChange = useCallback((devices: DeviceData[]) => {
setDevices(devices);
//TODO: Manage real userID when connecting to the backend
mockServer.saveDevices('default-user', devices);
const updatedRoom = { viewDevices: devices };
updateRoom(roomId, updatedRoom);
}, []);

const handleSensorDataUpdate = useCallback(async (deviceId: string, sensorData: SensorData) => {
setDevices(prev => {
const updatedDevices = prev.map(d =>
d.id === deviceId ? { ...d, sensorData } : d,
);
//TODO: Manage real userID when connecting to the backend
mockServer.saveDevices('default-user', updatedDevices);
const updatedRoom = { viewDevices: updatedDevices };
updateRoom(roomId, updatedRoom);
return updatedDevices;
});
}, []);
Expand Down Expand Up @@ -186,6 +190,7 @@ function SparkComponent() {
initialDevices={initialDevices}
onDevicesChange={handleDevicesChange}
onSensorDataUpdate={handleSensorDataUpdate}
roomId={roomId}
/>
)}
</Canvas>
Expand Down
2 changes: 1 addition & 1 deletion src/components/TextType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const TextType = ({

setCurrentTextIndex(prev => (prev + 1) % textArray.length);
setCurrentCharIndex(0);
timeout = setTimeout(() => {}, pauseDuration);
timeout = setTimeout(() => { }, pauseDuration);
} else {
timeout = setTimeout(() => {
setDisplayedText(prev => prev.slice(0, -1));
Expand Down
Loading