Adds first version of the app. Local storage only.

This commit is contained in:
2026-09-01 14:44:49 -05:00
commit 9710f1ee5a
44 changed files with 5865 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
import { useRef } from 'react'
import { downloadProjectFile, readProjectFile, ProjectImportError } from '../../data/exportImport'
import { useProjectStore } from '../../state/projectStore'
export default function TopBar() {
const project = useProjectStore((s) => s.project)
const renameProject = useProjectStore((s) => s.renameProject)
const newProject = useProjectStore((s) => s.newProject)
const importProject = useProjectStore((s) => s.importProject)
const fileInputRef = useRef<HTMLInputElement>(null)
const handleNewProject = () => {
if (project.devices.length > 0 || project.connections.length > 0) {
const confirmed = window.confirm('Start a new project? Unsaved changes to the current one will be lost (unless exported).')
if (!confirmed) return
}
newProject()
}
const handleExport = () => downloadProjectFile(project)
const handleImportClick = () => fileInputRef.current?.click()
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
event.target.value = ''
if (!file) return
try {
const imported = await readProjectFile(file)
importProject(imported)
} catch (err) {
const message = err instanceof ProjectImportError ? err.message : 'Could not read that file.'
window.alert(`Import failed: ${message}`)
}
}
return (
<header className="flex h-12 shrink-0 items-center justify-between border-b border-slate-200 bg-white px-3">
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-indigo-700">AV Planner</span>
<input
value={project.name}
onChange={(e) => renameProject(e.target.value)}
className="rounded border border-transparent px-2 py-1 text-sm text-slate-700 hover:border-slate-200 focus:border-slate-300 focus:outline-none"
/>
</div>
<div className="flex items-center gap-1.5">
<button onClick={handleNewProject} className="rounded px-2.5 py-1.5 text-xs text-slate-600 hover:bg-slate-100">
New
</button>
<button onClick={handleImportClick} className="rounded px-2.5 py-1.5 text-xs text-slate-600 hover:bg-slate-100">
Import
</button>
<input ref={fileInputRef} type="file" accept="application/json,.json" className="hidden" onChange={handleFileChange} />
<button
onClick={handleExport}
className="rounded bg-indigo-600 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-indigo-500"
>
Export
</button>
</div>
</header>
)
}