diff --git a/frontend/src/components/canvas/nodes/index.tsx b/frontend/src/components/canvas/nodes/index.tsx
index 6bf6eff..1cb114b 100644
--- a/frontend/src/components/canvas/nodes/index.tsx
+++ b/frontend/src/components/canvas/nodes/index.tsx
@@ -1,7 +1,7 @@
import { type NodeProps, type Node } from '@xyflow/react'
import {
Globe, Router, Network, Server, Layers, Box, Container,
- HardDrive, Cpu, Wifi, Circle, Cctv, Printer, Monitor, PlugZap, Anchor, Package, Flame, Radio, Antenna,
+ HardDrive, Cpu, Wifi, Circle, Cctv, Printer, Monitor, Laptop, Smartphone, PlugZap, Anchor, Package, Flame, Radio, Antenna,
} from 'lucide-react'
import { BaseNode } from './BaseNode'
import type { NodeData } from '@/types'
@@ -22,6 +22,8 @@ export const ApNode = (props: N) =>
export const CameraNode = (props: N) =>
export const PrinterNode = (props: N) =>
export const ComputerNode = (props: N) =>
+export const LaptopNode = (props: N) =>
+export const MobileNode = (props: N) =>
export const CplNode = (props: N) =>
export const DockerHostNode = (props: N) =>
export const DockerContainerNode = (props: N) =>
diff --git a/frontend/src/components/canvas/nodes/nodeTypes.ts b/frontend/src/components/canvas/nodes/nodeTypes.ts
index 34d25ec..38e427f 100644
--- a/frontend/src/components/canvas/nodes/nodeTypes.ts
+++ b/frontend/src/components/canvas/nodes/nodeTypes.ts
@@ -1,4 +1,4 @@
-import { IspNode, RouterNode, FirewallNode, SwitchNode, ServerNode, VmNode, LxcNode, NasNode, IotNode, ApNode, CameraNode, PrinterNode, ComputerNode, CplNode, DockerHostNode, DockerContainerNode, GenericNode, ZigbeeCoordinatorNode, ZigbeeRouterNode, ZigbeeEndDeviceNode } from './index'
+import { IspNode, RouterNode, FirewallNode, SwitchNode, ServerNode, VmNode, LxcNode, NasNode, IotNode, ApNode, CameraNode, PrinterNode, ComputerNode, LaptopNode, MobileNode, CplNode, DockerHostNode, DockerContainerNode, GenericNode, ZigbeeCoordinatorNode, ZigbeeRouterNode, ZigbeeEndDeviceNode } from './index'
import { ProxmoxGroupNode } from './ProxmoxGroupNode'
import { GroupRectNode } from './GroupRectNode'
import { GroupNode } from './GroupNode'
@@ -19,6 +19,8 @@ export const nodeTypes = {
camera: CameraNode,
printer: PrinterNode,
computer: ComputerNode,
+ laptop: LaptopNode,
+ mobile: MobileNode,
cpl: CplNode,
docker_host: DockerHostNode,
docker_container: DockerContainerNode,
diff --git a/frontend/src/components/modals/CustomStyleModal.tsx b/frontend/src/components/modals/CustomStyleModal.tsx
index 20aaac1..09391a0 100644
--- a/frontend/src/components/modals/CustomStyleModal.tsx
+++ b/frontend/src/components/modals/CustomStyleModal.tsx
@@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'
import { toast } from 'sonner'
import {
Globe, Router, Network, Server, Layers, Box, Container, HardDrive,
- Cpu, Wifi, Camera, Printer, Monitor, PlugZap, Anchor, Package, Circle, Flame,
+ Cpu, Wifi, Camera, Printer, Monitor, Laptop, Smartphone, PlugZap, Anchor, Package, Circle, Flame,
Radio, Zap, Lightbulb,
type LucideIcon,
} from 'lucide-react'
@@ -21,7 +21,7 @@ import { NODE_TYPE_LABELS, EDGE_TYPE_LABELS } from '@/types'
const EDITABLE_NODE_TYPES: NodeType[] = [
'isp', 'router', 'firewall', 'switch', 'server', 'proxmox', 'vm', 'lxc', 'nas',
- 'iot', 'ap', 'camera', 'printer', 'computer', 'cpl', 'docker_host',
+ 'iot', 'ap', 'camera', 'printer', 'computer', 'laptop', 'mobile', 'cpl', 'docker_host',
'docker_container', 'zigbee_coordinator', 'zigbee_router', 'zigbee_enddevice',
'generic',
]
@@ -31,7 +31,7 @@ const EDITABLE_EDGE_TYPES: EdgeType[] = ['ethernet', 'wifi', 'iot', 'vlan', 'vir
const NODE_ICONS: Record = {
isp: Globe, router: Router, firewall: Flame, switch: Network, server: Server, proxmox: Layers,
vm: Box, lxc: Container, nas: HardDrive, iot: Cpu, ap: Wifi,
- camera: Camera, printer: Printer, computer: Monitor, cpl: PlugZap,
+ camera: Camera, printer: Printer, computer: Monitor, laptop: Laptop, mobile: Smartphone, cpl: PlugZap,
docker_host: Anchor, docker_container: Package,
zigbee_coordinator: Radio, zigbee_router: Zap, zigbee_enddevice: Lightbulb,
generic: Circle,
diff --git a/frontend/src/components/modals/NodeModal.tsx b/frontend/src/components/modals/NodeModal.tsx
index d5b851f..274954b 100644
--- a/frontend/src/components/modals/NodeModal.tsx
+++ b/frontend/src/components/modals/NodeModal.tsx
@@ -18,7 +18,8 @@ const NODE_TYPE_GROUPS: { label: string; types: NodeType[] }[] = [
{ label: 'Virtualization', types: ['proxmox', 'vm', 'lxc', 'docker_host', 'docker_container'] },
{ label: 'IoT', types: ['iot', 'camera', 'cpl'] },
{ label: 'Zigbee', types: ['zigbee_coordinator', 'zigbee_router', 'zigbee_enddevice'] },
- { label: 'Generic', types: ['computer', 'generic', 'groupRect'] },
+ { label: 'Personal', types: ['computer', 'laptop', 'mobile'] },
+ { label: 'Generic', types: ['generic', 'groupRect'] },
]
const CHECK_METHODS: CheckMethod[] = ['none', 'ping', 'http', 'https', 'tcp', 'ssh', 'prometheus', 'health']
diff --git a/frontend/src/types/__tests__/types.test.ts b/frontend/src/types/__tests__/types.test.ts
index 57e6efd..0ccf129 100644
--- a/frontend/src/types/__tests__/types.test.ts
+++ b/frontend/src/types/__tests__/types.test.ts
@@ -4,7 +4,7 @@ import type { CheckMethod } from '@/types'
describe('NODE_TYPE_LABELS', () => {
it('has an entry for every node type', () => {
- const expectedTypes = ['isp', 'router', 'firewall', 'switch', 'server', 'proxmox', 'vm', 'lxc', 'nas', 'iot', 'ap', 'camera', 'generic']
+ const expectedTypes = ['isp', 'router', 'firewall', 'switch', 'server', 'proxmox', 'vm', 'lxc', 'nas', 'iot', 'ap', 'camera', 'laptop', 'mobile', 'generic']
expectedTypes.forEach((t) => {
expect(NODE_TYPE_LABELS).toHaveProperty(t)
expect(typeof NODE_TYPE_LABELS[t as keyof typeof NODE_TYPE_LABELS]).toBe('string')
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index 8e79fa6..e159821 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -13,6 +13,8 @@ export type NodeType =
| 'camera'
| 'printer'
| 'computer'
+ | 'laptop'
+ | 'mobile'
| 'cpl'
| 'docker_host'
| 'docker_container'
@@ -137,6 +139,8 @@ export const NODE_TYPE_LABELS: Record = {
camera: 'Camera',
printer: 'Printer',
computer: 'Computer',
+ laptop: 'Laptop',
+ mobile: 'Phone / Mobile',
cpl: 'CPL / Powerline',
docker_host: 'Docker Host',
docker_container: 'Docker Container',
diff --git a/frontend/src/utils/__tests__/themes.test.ts b/frontend/src/utils/__tests__/themes.test.ts
index 1a4b103..74e7937 100644
--- a/frontend/src/utils/__tests__/themes.test.ts
+++ b/frontend/src/utils/__tests__/themes.test.ts
@@ -4,7 +4,7 @@ import type { NodeType, EdgeType, NodeStatus } from '@/types'
const NODE_TYPES: NodeType[] = [
'isp', 'router', 'firewall', 'switch', 'server', 'proxmox', 'vm', 'lxc',
- 'nas', 'iot', 'ap', 'camera', 'printer', 'computer', 'cpl', 'docker_host', 'docker_container', 'generic', 'groupRect',
+ 'nas', 'iot', 'ap', 'camera', 'printer', 'computer', 'laptop', 'mobile', 'cpl', 'docker_host', 'docker_container', 'generic', 'groupRect',
]
const EDGE_TYPES: EdgeType[] = ['ethernet', 'wifi', 'iot', 'vlan', 'virtual', 'cluster']
const STATUS_TYPES: NodeStatus[] = ['online', 'offline', 'pending', 'unknown']
diff --git a/frontend/src/utils/nodeIcons.ts b/frontend/src/utils/nodeIcons.ts
index c781d0a..6a5d2a3 100644
--- a/frontend/src/utils/nodeIcons.ts
+++ b/frontend/src/utils/nodeIcons.ts
@@ -24,7 +24,7 @@ import {
// Communications
Mail, MessageSquare, Phone,
// Misc devices
- Printer, Smartphone, Search, Filter, BookOpen, PlugZap, Type,
+ Printer, Smartphone, Laptop, Search, Filter, BookOpen, PlugZap, Type,
} from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
@@ -50,6 +50,7 @@ export const ICON_REGISTRY: IconEntry[] = [
{ key: 'wifi', label: 'Access Point', category: 'Infrastructure', icon: Wifi },
{ key: 'circle', label: 'Generic', category: 'Infrastructure', icon: Circle },
{ key: 'monitor', label: 'Workstation', category: 'Infrastructure', icon: Monitor },
+ { key: 'laptop', label: 'Laptop', category: 'Infrastructure', icon: Laptop },
{ key: 'smartphone', label: 'Phone / Mobile', category: 'Infrastructure', icon: Smartphone },
{ key: 'printer', label: 'Printer', category: 'Infrastructure', icon: Printer },
{ key: 'plugzap', label: 'CPL / Powerline', category: 'Infrastructure', icon: PlugZap },
@@ -167,6 +168,8 @@ export const NODE_TYPE_DEFAULT_ICONS: Record = {
camera: Cctv,
printer: Printer,
computer: Monitor,
+ laptop: Laptop,
+ mobile: Smartphone,
cpl: PlugZap,
docker_host: Anchor,
docker_container: Package,
diff --git a/frontend/src/utils/themes.ts b/frontend/src/utils/themes.ts
index 82c03e4..3762731 100644
--- a/frontend/src/utils/themes.ts
+++ b/frontend/src/utils/themes.ts
@@ -56,6 +56,8 @@ export const THEMES: Record = {
camera: { border: '#8b949e', icon: '#8b949e' },
printer: { border: '#8b949e', icon: '#8b949e' },
computer: { border: '#a855f7', icon: '#a855f7' },
+ laptop: { border: '#a855f7', icon: '#a855f7' },
+ mobile: { border: '#ec4899', icon: '#ec4899' },
cpl: { border: '#e3b341', icon: '#e3b341' },
docker_host: { border: '#2496ED', icon: '#2496ED' },
docker_container: { border: '#0ea5e9', icon: '#0ea5e9' },
@@ -117,6 +119,8 @@ export const THEMES: Record = {
camera: { border: '#94a3b8', icon: '#94a3b8' },
printer: { border: '#94a3b8', icon: '#94a3b8' },
computer: { border: '#c084fc', icon: '#c084fc' },
+ laptop: { border: '#c084fc', icon: '#c084fc' },
+ mobile: { border: '#ec4899', icon: '#ec4899' },
cpl: { border: '#fbbf24', icon: '#fbbf24' },
docker_host: { border: '#2496ED', icon: '#2496ED' },
docker_container: { border: '#38bdf8', icon: '#38bdf8' },
@@ -178,6 +182,8 @@ export const THEMES: Record = {
camera: { border: '#6b7280', icon: '#6b7280' },
printer: { border: '#6b7280', icon: '#6b7280' },
computer: { border: '#7c3aed', icon: '#7c3aed' },
+ laptop: { border: '#7c3aed', icon: '#7c3aed' },
+ mobile: { border: '#ec4899', icon: '#ec4899' },
cpl: { border: '#b45309', icon: '#b45309' },
docker_host: { border: '#2496ED', icon: '#2496ED' },
docker_container: { border: '#0369a1', icon: '#0369a1' },
@@ -239,6 +245,8 @@ export const THEMES: Record = {
camera: { border: '#8888ff', icon: '#8888ff' },
printer: { border: '#8888ff', icon: '#8888ff' },
computer: { border: '#ff00ff', icon: '#ff00ff' },
+ laptop: { border: '#ff00ff', icon: '#ff00ff' },
+ mobile: { border: '#ec4899', icon: '#ec4899' },
cpl: { border: '#ffff00', icon: '#ffff00' },
docker_host: { border: '#00aaff', icon: '#00aaff' },
docker_container: { border: '#00ddff', icon: '#00ddff' },
@@ -300,6 +308,8 @@ export const THEMES: Record = {
camera: { border: '#005500', icon: '#005500' },
printer: { border: '#005500', icon: '#005500' },
computer: { border: '#008822', icon: '#008822' },
+ laptop: { border: '#008822', icon: '#008822' },
+ mobile: { border: '#ec4899', icon: '#ec4899' },
cpl: { border: '#66ff33', icon: '#66ff33' },
docker_host: { border: '#00cc88', icon: '#00cc88' },
docker_container: { border: '#00aacc', icon: '#00aacc' },
@@ -361,6 +371,8 @@ export const THEMES: Record = {
camera: { border: '#8b949e', icon: '#8b949e' },
printer: { border: '#8b949e', icon: '#8b949e' },
computer: { border: '#a855f7', icon: '#a855f7' },
+ laptop: { border: '#a855f7', icon: '#a855f7' },
+ mobile: { border: '#ec4899', icon: '#ec4899' },
cpl: { border: '#e3b341', icon: '#e3b341' },
docker_host: { border: '#2496ED', icon: '#2496ED' },
docker_container: { border: '#0ea5e9', icon: '#0ea5e9' },