Fix P2 issues

This commit is contained in:
jason
2026-03-27 13:42:35 -05:00
parent 3eac74f28c
commit 308a4c5641
6 changed files with 544 additions and 87 deletions

View File

@@ -2,8 +2,8 @@ import { useState, useEffect, useMemo, type FormEvent } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Modal } from '../ui/Modal'; import { Modal } from '../ui/Modal';
import { Button } from '../ui/Button'; import { Button } from '../ui/Button';
import { ConfirmDialog } from '../ui/ConfirmDialog';
import { useRackStore } from '../../store/useRackStore'; import { useRackStore } from '../../store/useRackStore';
import type { Connection } from '../../types';
interface ConnectionConfigModalProps { interface ConnectionConfigModalProps {
connectionId: string | null; connectionId: string | null;
@@ -29,8 +29,9 @@ const PRESET_COLORS = [
]; ];
export function ConnectionConfigModal({ connectionId, open, onClose }: ConnectionConfigModalProps) { export function ConnectionConfigModal({ connectionId, open, onClose }: ConnectionConfigModalProps) {
const { racks, setCablingFromPortId, updateConnection, deleteConnection } = useRackStore(); const { racks, updateConnection, deleteConnection } = useRackStore();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
// Synchronously find the connection from the global store // Synchronously find the connection from the global store
const connection = useMemo(() => { const connection = useMemo(() => {
@@ -77,11 +78,11 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
async function handleDelete() { async function handleDelete() {
if (!connectionId) return; if (!connectionId) return;
if (!confirm('Are you sure you want to remove this connection?')) return;
setLoading(true); setLoading(true);
try { try {
await deleteConnection(connectionId); await deleteConnection(connectionId);
toast.success('Connection removed'); toast.success('Connection removed');
setConfirmDeleteOpen(false);
onClose(); onClose();
} catch (err) { } catch (err) {
toast.error(err instanceof Error ? err.message : 'Delete failed'); toast.error(err instanceof Error ? err.message : 'Delete failed');
@@ -93,6 +94,7 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
if (!open || !connection) return null; if (!open || !connection) return null;
return ( return (
<>
<Modal open={open} onClose={() => !loading && onClose()} title="Edit Connection"> <Modal open={open} onClose={() => !loading && onClose()} title="Edit Connection">
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
<p className="text-sm text-slate-400"> <p className="text-sm text-slate-400">
@@ -100,10 +102,10 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
</p> </p>
<form id="connection-form" onSubmit={handleSubmit} className="flex flex-col gap-5"> <form id="connection-form" onSubmit={handleSubmit} className="flex flex-col gap-5">
{/* Label Name */}
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<label className="text-sm font-semibold text-slate-300">Cable Label (Optional)</label> <label htmlFor="connection-label" className="text-sm font-semibold text-slate-300">Cable Label (Optional)</label>
<input <input
id="connection-label"
type="text" type="text"
value={label} value={label}
onChange={(e) => setLabel(e.target.value)} onChange={(e) => setLabel(e.target.value)}
@@ -112,7 +114,6 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
/> />
</div> </div>
{/* Color Picker */}
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<label className="text-sm font-semibold text-slate-300">Cable Color</label> <label className="text-sm font-semibold text-slate-300">Cable Color</label>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
@@ -135,12 +136,12 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
onChange={(e) => setColor(e.target.value)} onChange={(e) => setColor(e.target.value)}
className="absolute -top-2 -left-2 w-12 h-12 cursor-pointer" className="absolute -top-2 -left-2 w-12 h-12 cursor-pointer"
title="Custom color" title="Custom color"
aria-label="Select custom cable color"
/> />
</div> </div>
</div> </div>
</div> </div>
{/* Edge Style */}
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<label className="text-sm font-semibold text-slate-300">Curve Style</label> <label className="text-sm font-semibold text-slate-300">Curve Style</label>
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
@@ -154,6 +155,7 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
? 'bg-blue-500/10 border-blue-500 text-blue-400' ? 'bg-blue-500/10 border-blue-500 text-blue-400'
: 'bg-slate-900 border-slate-700 text-slate-400 hover:border-slate-500 hover:text-slate-200' : 'bg-slate-900 border-slate-700 text-slate-400 hover:border-slate-500 hover:text-slate-200'
}`} }`}
aria-pressed={edgeType === style.value}
> >
{style.label} {style.label}
</button> </button>
@@ -163,7 +165,13 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
</form> </form>
<div className="flex items-center justify-between pt-4 border-t border-slate-800"> <div className="flex items-center justify-between pt-4 border-t border-slate-800">
<Button type="button" variant="ghost" className="text-red-400 hover:text-red-300" onClick={handleDelete} disabled={loading}> <Button
type="button"
variant="ghost"
className="text-red-400 hover:text-red-300"
onClick={() => setConfirmDeleteOpen(true)}
disabled={loading}
>
Delete Connection Delete Connection
</Button> </Button>
<div className="flex gap-2"> <div className="flex gap-2">
@@ -177,5 +185,16 @@ export function ConnectionConfigModal({ connectionId, open, onClose }: Connectio
</div> </div>
</div> </div>
</Modal> </Modal>
<ConfirmDialog
open={confirmDeleteOpen}
onClose={() => !loading && setConfirmDeleteOpen(false)}
onConfirm={handleDelete}
title="Delete Connection"
message="Are you sure you want to remove this connection?"
confirmLabel="Delete Connection"
loading={loading}
/>
</>
); );
} }

View File

@@ -1,6 +1,5 @@
import { useEffect, useState, useMemo, useCallback } from 'react'; import { useEffect, useState, useMemo, useCallback } from 'react';
import { useRackStore } from '../../store/useRackStore'; import { useRackStore } from '../../store/useRackStore';
import { cn } from '../../lib/utils';
export function ConnectionLayer() { export function ConnectionLayer() {
const { racks, cablingFromPortId, setActiveConfigConnectionId } = useRackStore(); const { racks, cablingFromPortId, setActiveConfigConnectionId } = useRackStore();
@@ -171,12 +170,21 @@ export function ConnectionLayer() {
strokeWidth="10" strokeWidth="10"
fill="none" fill="none"
className={isShiftPressed ? 'pointer-events-auto cursor-pointer' : 'pointer-events-none'} className={isShiftPressed ? 'pointer-events-auto cursor-pointer' : 'pointer-events-none'}
tabIndex={0}
role="button"
aria-label="Edit connection"
onClick={(e) => { onClick={(e) => {
if (e.shiftKey) { if (e.shiftKey) {
e.stopPropagation(); e.stopPropagation();
setActiveConfigConnectionId(conn.id); setActiveConfigConnectionId(conn.id);
} }
}} }}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setActiveConfigConnectionId(conn.id);
}
}}
/> />
</g> </g>
); );

43
eslint.config.js Normal file
View File

@@ -0,0 +1,43 @@
const js = require('@eslint/js');
const globals = require('globals');
const tseslint = require('typescript-eslint');
module.exports = tseslint.config(
{
ignores: ['dist/**', 'client/**', 'node_modules/**'],
},
js.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
files: ['server/**/*.ts', 'scripts/**/*.ts', 'prisma/seed.ts'],
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
globals: {
...globals.node,
},
},
rules: {
'@typescript-eslint/no-misused-promises': [
'error',
{
checksVoidReturn: {
arguments: false,
},
},
],
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-unsafe-assignment': 'off',
},
}
);

389
package-lock.json generated
View File

@@ -17,6 +17,7 @@
"jsonwebtoken": "^9.0.2" "jsonwebtoken": "^9.0.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.4",
"@types/better-sqlite3": "^7.6.12", "@types/better-sqlite3": "^7.6.12",
"@types/cookie-parser": "^1.4.8", "@types/cookie-parser": "^1.4.8",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
@@ -25,11 +26,13 @@
"@types/node": "^22.9.0", "@types/node": "^22.9.0",
"concurrently": "^9.1.0", "concurrently": "^9.1.0",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"globals": "^17.4.0",
"nodemon": "^3.1.7", "nodemon": "^3.1.7",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prisma": "^5.22.0", "prisma": "^5.22.0",
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "^5.6.3", "typescript": "^5.6.3",
"typescript-eslint": "^8.57.2",
"vitest": "^2.1.5" "vitest": "^2.1.5"
} }
}, },
@@ -582,6 +585,19 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.39.4", "version": "9.39.4",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
@@ -1276,6 +1292,288 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz",
"integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/type-utils": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.57.2",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz",
"integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.57.2",
"@typescript-eslint/types": "^8.57.2",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz",
"integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz",
"integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.57.2",
"@typescript-eslint/tsconfig-utils": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/visitor-keys": "8.57.2",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz",
"integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.57.2",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@vitest/expect": { "node_modules/@vitest/expect": {
"version": "2.1.9", "version": "2.1.9",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
@@ -2741,9 +3039,9 @@
} }
}, },
"node_modules/globals": { "node_modules/globals": {
"version": "14.0.0", "version": "17.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -4337,6 +4635,54 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tinypool": { "node_modules/tinypool": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
@@ -4409,6 +4755,19 @@
"tree-kill": "cli.js" "tree-kill": "cli.js"
} }
}, },
"node_modules/ts-api-utils": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
"integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.12"
},
"peerDependencies": {
"typescript": ">=4.8.4"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -4488,6 +4847,30 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/typescript-eslint": {
"version": "8.57.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz",
"integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.57.2",
"@typescript-eslint/parser": "8.57.2",
"@typescript-eslint/typescript-estree": "8.57.2",
"@typescript-eslint/utils": "8.57.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/undefsafe": { "node_modules/undefsafe": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",

View File

@@ -27,6 +27,7 @@
"jsonwebtoken": "^9.0.2" "jsonwebtoken": "^9.0.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.4",
"@types/better-sqlite3": "^7.6.12", "@types/better-sqlite3": "^7.6.12",
"@types/cookie-parser": "^1.4.8", "@types/cookie-parser": "^1.4.8",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
@@ -35,11 +36,13 @@
"@types/node": "^22.9.0", "@types/node": "^22.9.0",
"concurrently": "^9.1.0", "concurrently": "^9.1.0",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"globals": "^17.4.0",
"nodemon": "^3.1.7", "nodemon": "^3.1.7",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prisma": "^5.22.0", "prisma": "^5.22.0",
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "^5.6.3", "typescript": "^5.6.3",
"typescript-eslint": "^8.57.2",
"vitest": "^2.1.5" "vitest": "^2.1.5"
}, },
"prisma": { "prisma": {

View File

@@ -1,5 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as connService from '../services/connectionService'; import * as connService from '../services/connectionService';
import { ok } from '../types/index';
const router = Router(); const router = Router();
@@ -8,7 +9,7 @@ router.post('/', async (req, res, next) => {
try { try {
const { fromPortId, toPortId, color, label, edgeType } = req.body; const { fromPortId, toPortId, color, label, edgeType } = req.body;
const conn = await connService.createConnection({ fromPortId, toPortId, color, label, edgeType }); const conn = await connService.createConnection({ fromPortId, toPortId, color, label, edgeType });
res.status(201).json(conn); res.status(201).json(ok(conn));
} catch (err) { } catch (err) {
next(err); next(err);
} }
@@ -19,7 +20,7 @@ router.put('/:id', async (req, res, next) => {
try { try {
const { color, label, edgeType } = req.body; const { color, label, edgeType } = req.body;
const conn = await connService.updateConnection(req.params.id, { color, label, edgeType }); const conn = await connService.updateConnection(req.params.id, { color, label, edgeType });
res.json(conn); res.json(ok(conn));
} catch (err) { } catch (err) {
next(err); next(err);
} }
@@ -29,7 +30,7 @@ router.put('/:id', async (req, res, next) => {
router.delete('/:id', async (req, res, next) => { router.delete('/:id', async (req, res, next) => {
try { try {
await connService.deleteConnection(req.params.id); await connService.deleteConnection(req.params.id);
res.json({ success: true }); res.json(ok(null));
} catch (err) { } catch (err) {
next(err); next(err);
} }
@@ -39,7 +40,7 @@ router.delete('/:id', async (req, res, next) => {
router.delete('/ports/:p1/:p2', async (req, res, next) => { router.delete('/ports/:p1/:p2', async (req, res, next) => {
try { try {
await connService.deleteByPorts(req.params.p1, req.params.p2); await connService.deleteByPorts(req.params.p1, req.params.p2);
res.json({ success: true }); res.json(ok(null));
} catch (err) { } catch (err) {
next(err); next(err);
} }