Value by Category
{chartData.byCategory.length > 0 ? ( ) : (Add items to see category breakdowns.
Total Insured Value Over Time
{chartData.timeline.length > 0 ? ( ) : (Add items to see your insured value grow over time.
/* @component-map * App β Main container, tab navigation [app.jsx] * InventoryList β Main inventory list with search/filter [components/InventoryList.jsx] * AddItemForm β Modal form to add/edit inventory items [components/AddItemForm.jsx] * Dashboard β Summary stats and total insured value [components/Dashboard.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"cron": [], "triggers": [{"name": "notify_when_total_insured_value_exceeds_threshold", "on": "collection.add", "collection": "insurance_policies", "actions": [{"type": "email", "to": "user@example.com", "subject": "Action needed: update your insurance policy", "body": "Your total insured value has exceeded the configured threshold. Please review and update your insurance policy."}]}]} import { useEffect, useMemo, useState } from 'react'; import { useAuth, useCollection, renderChart } from '@deplixo/sdk'; import { Dashboard } from './components/Dashboard.jsx'; import { InventoryList } from './components/InventoryList.jsx'; import { AddItemForm } from './components/AddItemForm.jsx'; function App() { const { user, loading, logout } = useAuth(); const { items: householdUsers, loading: householdUsersLoading, add: addHouseholdUser, search: searchHouseholdUsers, } = useCollection('household_users', { personal: true }); const { items: inventoryItems, loading: inventoryLoading, add: addInventorySeed, } = useCollection('inventory_items', { personal: true }); const [activeTab, setActiveTab] = useState('inventory'); const [showAddForm, setShowAddForm] = useState(false); const [editItem, setEditItem] = useState(null); const [householdName, setHouseholdName] = useState(''); const [householdStatus, setHouseholdStatus] = useState(''); const householdUser = useMemo(() => { if (!user) return null; return householdUsers?.find((item) => item.email === user.email) || null; }, [householdUsers, user]); const chartData = useMemo(() => { const items = Array.isArray(inventoryItems) ? inventoryItems : []; const categoryTotals = items.reduce((acc, item) => { const category = item?.category || 'Uncategorized'; const value = Number(item?.value) || 0; acc[category] = (acc[category] || 0) + value; return acc; }, {}); const byCategory = Object.entries(categoryTotals) .map(([label, value]) => ({ label, value })) .sort((a, b) => b.value - a.value); const timeline = [...items] .filter((item) => item?.createdAt || item?.date) .sort((a, b) => new Date(a.createdAt || a.date) - new Date(b.createdAt || b.date)) .map((item) => ({ label: new Date(item.createdAt || item.date).toLocaleDateString(), value: Number(item.value) || 0, })) .reduce((acc, point) => { const last = acc[acc.length - 1]; const runningTotal = (last?.value || 0) + point.value; acc.push({ label: point.label, value: runningTotal }); return acc; }, []); return { byCategory, timeline }; }, [inventoryItems]); const categoryChartRef = useMemo(() => ({ current: null }), []); const valueChartRef = useMemo(() => ({ current: null }), []); useEffect(() => { if (categoryChartRef.current && chartData.byCategory.length > 0) { renderChart(categoryChartRef.current, { type: 'pie', data: { labels: chartData.byCategory.map((d) => d.label), datasets: [ { label: 'Value by Category', data: chartData.byCategory.map((d) => d.value), }, ], }, }); } }, [chartData.byCategory, categoryChartRef]); useEffect(() => { if (valueChartRef.current && chartData.timeline.length > 0) { renderChart(valueChartRef.current, { type: 'line', data: { labels: chartData.timeline.map((d) => d.label), datasets: [ { label: 'Total Insured Value', data: chartData.timeline.map((d) => d.value), }, ], }, }); } }, [chartData.timeline, valueChartRef]); useEffect(() => { if (!user || householdUsersLoading) return; const existing = householdUsers?.find((item) => item.email === user.email); if (existing) { setHouseholdName(existing.householdName || existing.name || user.name || 'Household'); setHouseholdStatus(''); return; } const createHouseholdUser = async () => { try { setHouseholdStatus('Setting up your household account...'); const payload = { name: user.name, email: user.email, avatar: user.avatar, householdName: householdName || `${user.name || 'My'} Household`, role: 'owner', linkedProvider: 'google', createdAt: new Date().toISOString(), }; await addHouseholdUser(payload); setHouseholdStatus(''); } catch (error) { setHouseholdStatus(''); } }; createHouseholdUser(); }, [user, householdUsersLoading, householdUsers, addHouseholdUser, householdName]); useEffect(() => { if (!user || inventoryLoading) return; if (inventoryItems && inventoryItems.length === 0) { const seedItem = async () => { try { await addInventorySeed({ name: 'Welcome Home Inventory', category: 'Documents', value: 0, date: new Date().toISOString().split('T')[0], createdAt: new Date().toISOString(), description: 'This household inventory is ready for your items.', householdEmail: user.email, createdBy: user.email, }); } catch (error) {} }; seedItem(); } }, [user, inventoryLoading, inventoryItems, addInventorySeed]); const tabs = [ { id: 'inventory', label: 'Inventory', icon: 'π¦' }, { id: 'dashboard', label: 'Dashboard', icon: 'π' }, ]; const handleEdit = (item) => { setEditItem(item); setShowAddForm(true); }; const handleCloseForm = () => { setShowAddForm(false); setEditItem(null); }; if (loading) return
Sign in with Google to access your household inventory.
Insurance-ready asset tracker
{householdStatus}
} {isHouseholdReady && (Linked to {householdUser.householdName || 'your household'}
)}Add items to see category breakdowns.
Add items to see your insured value grow over time.
Weβre linking your Google account to this household inventory. Please wait a moment.