Route boards to circuit-first editor
This commit is contained in:
@@ -239,3 +239,29 @@ Acceptance criteria:
|
||||
|
||||
- the codebase can later add rule-based sizing without redesigning circuits/devices
|
||||
- users can manually override calculated suggestions
|
||||
|
||||
## Phase 9: Development Environment and Docker
|
||||
|
||||
Goal:
|
||||
|
||||
Make short development sessions quick to start and stop.
|
||||
|
||||
Tasks:
|
||||
|
||||
- add a Docker-based local development setup
|
||||
- start frontend, API and SQLite persistence with one command
|
||||
- keep the database in an explicit persistent volume
|
||||
- run or document pending database migrations during startup
|
||||
- add concise start, stop and reset instructions
|
||||
- retain a non-Docker development path for direct debugging
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- a new development session starts with one documented command
|
||||
- stopping and restarting does not lose the local database
|
||||
- frontend and API health can be checked without manual port setup
|
||||
- the development workflow works consistently across short sessions
|
||||
|
||||
Note:
|
||||
|
||||
This phase may be moved earlier if repeated local startup overhead begins to slow down feature development.
|
||||
|
||||
@@ -11,18 +11,21 @@ export default function CircuitTreeEditPage() {
|
||||
<main className="container py-4">
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h1 className="h4 mb-1">Circuit Tree Editor</h1>
|
||||
<p className="text-secondary mb-0">Basic editing for the circuit-first model.</p>
|
||||
<h1 className="h4 mb-1">Stromkreisliste</h1>
|
||||
<p className="text-secondary mb-0">Stromkreise und zugeordnete Geräte bearbeiten.</p>
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
<Link
|
||||
href={`/projects/${params.projectId}/circuit-lists/${params.circuitListId}/tree`}
|
||||
className="btn btn-outline-secondary btn-sm"
|
||||
>
|
||||
Read-only tree
|
||||
Strukturvorschau
|
||||
</Link>
|
||||
<Link href={`/projects/${params.projectId}`} className="btn btn-outline-secondary btn-sm">
|
||||
Zum Projekt
|
||||
</Link>
|
||||
<Link href={`/projects/${params.projectId}/circuit-lists`} className="btn btn-outline-secondary btn-sm">
|
||||
Legacy editor
|
||||
Legacy-Editor
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
createProjectDevice,
|
||||
createRoom,
|
||||
deleteProjectDevice,
|
||||
listCircuitLists,
|
||||
listDistributionBoards,
|
||||
listFloors,
|
||||
listGlobalDevices,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
updateProjectSettings,
|
||||
} from "../../../frontend/utils/api";
|
||||
import type {
|
||||
CircuitListDto,
|
||||
CreateProjectDeviceInput,
|
||||
DistributionBoardDto,
|
||||
FloorDto,
|
||||
@@ -56,6 +58,7 @@ export default function ProjectDetailPage() {
|
||||
const [projectId, setProjectId] = useState("");
|
||||
const [project, setProject] = useState<ProjectDto | null>(null);
|
||||
const [boards, setBoards] = useState<DistributionBoardDto[]>([]);
|
||||
const [circuitLists, setCircuitLists] = useState<CircuitListDto[]>([]);
|
||||
const [floors, setFloors] = useState<FloorDto[]>([]);
|
||||
const [rooms, setRooms] = useState<RoomDto[]>([]);
|
||||
const [projectDevices, setProjectDevices] = useState<ProjectDeviceDto[]>([]);
|
||||
@@ -94,12 +97,21 @@ export default function ProjectDetailPage() {
|
||||
Promise.all([
|
||||
listProjects(),
|
||||
listDistributionBoards(projectId),
|
||||
listCircuitLists(projectId),
|
||||
listFloors(projectId),
|
||||
listRooms(projectId),
|
||||
listProjectDevices(projectId),
|
||||
listGlobalDevices(),
|
||||
])
|
||||
.then(([projects, distributionBoards, loadedFloors, loadedRooms, loadedProjectDevices, loadedGlobalDevices]) => {
|
||||
.then(([
|
||||
projects,
|
||||
distributionBoards,
|
||||
loadedCircuitLists,
|
||||
loadedFloors,
|
||||
loadedRooms,
|
||||
loadedProjectDevices,
|
||||
loadedGlobalDevices,
|
||||
]) => {
|
||||
const currentProject = projects.find((item) => item.id === projectId) ?? null;
|
||||
setProject(currentProject);
|
||||
if (currentProject) {
|
||||
@@ -107,6 +119,7 @@ export default function ProjectDetailPage() {
|
||||
setThreePhaseVoltageV(String(currentProject.threePhaseVoltageV));
|
||||
}
|
||||
setBoards(distributionBoards);
|
||||
setCircuitLists(loadedCircuitLists);
|
||||
setFloors(loadedFloors);
|
||||
setRooms(loadedRooms);
|
||||
setProjectDevices(loadedProjectDevices);
|
||||
@@ -123,6 +136,10 @@ export default function ProjectDetailPage() {
|
||||
const roomCount = useMemo(() => rooms.length, [rooms.length]);
|
||||
const projectDeviceCount = useMemo(() => projectDevices.length, [projectDevices.length]);
|
||||
const floorById = useMemo(() => new Map(floors.map((item) => [item.id, item])), [floors]);
|
||||
const circuitListByBoardId = useMemo(
|
||||
() => new Map(circuitLists.map((circuitList) => [circuitList.distributionBoardId, circuitList])),
|
||||
[circuitLists]
|
||||
);
|
||||
|
||||
async function handleCreateBoard(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
@@ -134,6 +151,7 @@ export default function ProjectDetailPage() {
|
||||
try {
|
||||
const created = await createDistributionBoard(projectId, boardName.trim());
|
||||
setBoards((current) => [...current, created]);
|
||||
setCircuitLists(await listCircuitLists(projectId));
|
||||
setBoardName("");
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Verteilung konnte nicht erstellt werden.");
|
||||
@@ -415,19 +433,36 @@ export default function ProjectDetailPage() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{boards.map((board) => (
|
||||
<tr key={board.id}>
|
||||
<td>{board.name}</td>
|
||||
<td className="text-end">
|
||||
<Link
|
||||
className="btn btn-sm btn-outline-primary"
|
||||
href={`/projects/${projectId}/circuit-lists?boardId=${board.id}`}
|
||||
>
|
||||
Stromkreisliste öffnen
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{boards.map((board) => {
|
||||
const circuitList = circuitListByBoardId.get(board.id);
|
||||
return (
|
||||
<tr key={board.id}>
|
||||
<td>{board.name}</td>
|
||||
<td className="text-end">
|
||||
<div className="d-inline-flex gap-2">
|
||||
{circuitList ? (
|
||||
<Link
|
||||
className="btn btn-sm btn-primary"
|
||||
href={`/projects/${projectId}/circuit-lists/${circuitList.id}/tree-edit`}
|
||||
>
|
||||
Stromkreisliste öffnen
|
||||
</Link>
|
||||
) : (
|
||||
<span className="text-secondary small align-self-center">
|
||||
Keine Stromkreisliste vorhanden
|
||||
</span>
|
||||
)}
|
||||
<Link
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
href={`/projects/${projectId}/circuit-lists?boardId=${board.id}`}
|
||||
>
|
||||
Legacy
|
||||
</Link>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
{!boards.length ? (
|
||||
<tr>
|
||||
<td colSpan={2} className="text-center text-secondary py-4">
|
||||
|
||||
Reference in New Issue
Block a user