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:
@@ -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';
|
||||
Reference in New Issue
Block a user