Initial scaffold: full-stack RackMapper application

Complete project scaffold with working auth, REST API, Prisma/SQLite
schema, Docker config, and React frontend for both Rack Planner and
Service Mapper modules. Both server and client pass TypeScript strict
mode with zero errors. Initial migration applied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 21:48:56 -05:00
parent 61a4d37d94
commit 231de3d005
79 changed files with 12983 additions and 0 deletions
@@ -0,0 +1,56 @@
import { memo } from 'react';
import { Handle, Position, type NodeProps } from '@xyflow/react';
import type { Module } from '../../../types';
import { MODULE_TYPE_COLORS, MODULE_TYPE_LABELS } from '../../../lib/constants';
import { Badge } from '../../ui/Badge';
export interface DeviceNodeData {
label: string;
module?: Module;
[key: string]: unknown;
}
export const DeviceNode = memo(({ data, selected }: NodeProps) => {
const nodeData = data as DeviceNodeData;
const mod = nodeData.module;
const colors = mod ? MODULE_TYPE_COLORS[mod.type] : null;
return (
<div
className={`min-w-[160px] max-w-[200px] bg-slate-800 border rounded-lg shadow-lg overflow-hidden transition-all ${
selected ? 'ring-2 ring-blue-500 border-blue-500' : 'border-slate-600'
} ${colors ? colors.border : ''}`}
>
<Handle type="target" position={Position.Top} className="!bg-slate-400 !border-slate-600" />
{/* Colored accent strip */}
{colors && <div className={`h-1 w-full ${colors.bg}`} />}
<div className="px-3 py-2">
<div className="flex items-center gap-1.5 mb-1">
<svg width="12" height="12" viewBox="0 0 18 18" fill="none" className="shrink-0 opacity-60">
<rect x="1" y="2" width="16" height="3" rx="1" fill="currentColor" />
<rect x="1" y="7" width="16" height="3" rx="1" fill="currentColor" opacity="0.7" />
<rect x="1" y="12" width="16" height="3" rx="1" fill="currentColor" opacity="0.4" />
</svg>
<span className="text-xs font-semibold text-slate-100 truncate">{nodeData.label}</span>
</div>
{mod && (
<div className="flex flex-wrap gap-1">
<Badge variant="slate" className="text-[10px]">{MODULE_TYPE_LABELS[mod.type]}</Badge>
{mod.ipAddress && (
<span className="text-[10px] text-slate-400 font-mono">{mod.ipAddress}</span>
)}
</div>
)}
{!mod && (
<span className="text-[10px] text-slate-500">Unlinked device</span>
)}
</div>
<Handle type="source" position={Position.Bottom} className="!bg-slate-400 !border-slate-600" />
</div>
);
});
DeviceNode.displayName = 'DeviceNode';