feat: host detail page + FM host context
CI / Lint · Typecheck · Test · Build (push) Successful in 44s
CI / Playwright (smoke) (push) Has been skipped
CI / Build & push images (push) Successful in 1m5s

Add /hosts/:id detail page with unified timeline (HostEvents + FMs + Repairs
+ part arrivals/departures) and a deployed-parts table. Hosts list rows now
link to the page. FM list + detail surface inline State/Stack badges next
to the asset ID, with the asset ID linking to the host page.

HostEvent audit model added; create/update in the hosts service now diff
and log state, stack, and field changes the same way parts.ts does.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 14:04:07 -04:00
parent 60255f20bb
commit b0e9c5d1d0
19 changed files with 1228 additions and 91 deletions
@@ -0,0 +1,19 @@
-- CreateTable
CREATE TABLE "HostEvent" (
"id" TEXT NOT NULL PRIMARY KEY,
"hostId" TEXT NOT NULL,
"userId" TEXT,
"type" TEXT NOT NULL,
"field" TEXT,
"oldValue" TEXT,
"newValue" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "HostEvent_hostId_fkey" FOREIGN KEY ("hostId") REFERENCES "Host" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "HostEvent_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "HostEvent_hostId_createdAt_idx" ON "HostEvent"("hostId", "createdAt" DESC);
-- CreateIndex
CREATE INDEX "HostEvent_userId_idx" ON "HostEvent"("userId");
+18
View File
@@ -24,6 +24,7 @@ model User {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
partEvents PartEvent[]
hostEvents HostEvent[]
refreshTokens RefreshToken[]
custodyParts Part[] @relation("Custody")
repairs Repair[]
@@ -198,11 +199,28 @@ model Host {
parts Part[]
fms Fm[]
repairs Repair[]
events HostEvent[]
@@index([state])
@@index([stack])
}
model HostEvent {
id String @id @default(uuid())
hostId String
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade)
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
type String
field String?
oldValue String?
newValue String?
createdAt DateTime @default(now())
@@index([hostId, createdAt(sort: Desc)])
@@index([userId])
}
model Fm {
id String @id @default(uuid())
hostId String