diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 180265c..c8d001a 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -23,6 +23,7 @@ jobs: node-version: 22.x cache: 'pnpm' - run: pnpm install --frozen-lockfile + - run: pnpx playwright install chromium --with-deps - run: pnpm build - run: pnpm lint - run: pnpm test diff --git a/.prettierignore b/.prettierignore index 345efd9..b232436 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,6 @@ dist node_modules coverage/ reports/ +.metals +.bloop +target diff --git a/eslint.config.js b/eslint.config.js index 6aeb3a6..d5ae6b6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -22,14 +22,6 @@ export default tseslint.config( }, rules: { // Custom rules here - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - - // Most of these rules _should_ be turned on, but they are disabled for now - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', }, settings: { react: { version: 'detect' }, diff --git a/package.json b/package.json index 0cb42f2..9e2930e 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "build": "tsc && vite build", "preview": "vite preview", "deploy": "./deploy.sh", - "test": "vitest --ui", + "test": "vitest", "coverage": "vitest run --coverage", - "lint": "npm run lint:prettier && npm run lint:eslint", + "lint": "pnpm lint:prettier && pnpm lint:eslint", "lint:prettier": "prettier --check .", "lint:eslint": "eslint .", "codegen": "graphql-codegen --config tasks/codegen.ts", @@ -49,28 +49,29 @@ "@swc-jotai/debug-label": "0.1.1", "@swc-jotai/react-refresh": "0.2.0", "@swc/core": "1.6.13", - "@testing-library/react": "16.0.1", "@types/eslint__js": "8.42.3", "@types/react": "18.3.10", "@types/react-dom": "18.3.0", "@types/uuid": "10.0.0", "@vitejs/plugin-react-swc": "3.7.0", + "@vitest/browser": "2.1.1", "@vitest/coverage-v8": "2.1.1", "@vitest/ui": "2.1.1", "eslint": "9.11.1", "eslint-plugin-react": "7.37.0", "globals": "15.9.0", - "happy-dom": "15.7.4", "husky": "9.1.6", "lint-staged": "15.2.10", "lucuma-schemas": "0.100.0", + "playwright": "1.47.2", "prettier": "3.3.3", "sass": "1.79.4", "typescript": "5.6.2", "typescript-eslint": "8.7.0", "vite": "5.4.8", "vite-plugin-mkcert": "1.17.6", - "vitest": "2.1.1" + "vitest": "2.1.1", + "vitest-browser-react": "0.0.1" }, "lint-staged": { "*.{js,mjs,ts,tsx,jsx,css,md,json,yml}": "prettier --write" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0119c65..d56fee6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,9 +75,6 @@ importers: '@swc/core': specifier: 1.6.13 version: 1.6.13 - '@testing-library/react': - specifier: 16.0.1 - version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/eslint__js': specifier: 8.42.3 version: 8.42.3 @@ -93,9 +90,12 @@ importers: '@vitejs/plugin-react-swc': specifier: 3.7.0 version: 3.7.0(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)) + '@vitest/browser': + specifier: 2.1.1 + version: 2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1) '@vitest/coverage-v8': specifier: 2.1.1 - version: 2.1.1(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.79.4)) + version: 2.1.1(@vitest/browser@2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1))(vitest@2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4)) '@vitest/ui': specifier: 2.1.1 version: 2.1.1(vitest@2.1.1) @@ -108,9 +108,6 @@ importers: globals: specifier: 15.9.0 version: 15.9.0 - happy-dom: - specifier: 15.7.4 - version: 15.7.4 husky: specifier: 9.1.6 version: 9.1.6 @@ -120,6 +117,9 @@ importers: lucuma-schemas: specifier: 0.100.0 version: 0.100.0 + playwright: + specifier: 1.47.2 + version: 1.47.2 prettier: specifier: 3.3.3 version: 3.3.3 @@ -140,7 +140,10 @@ importers: version: 1.17.6(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)) vitest: specifier: 2.1.1 - version: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.79.4) + version: 2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4) + vitest-browser-react: + specifier: 0.0.1 + version: 0.0.1(@types/react-dom@18.3.0)(@types/react@18.3.10)(@vitest/browser@2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4)) packages: @@ -448,6 +451,15 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bundled-es-modules/cookie@2.0.0': + resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -837,6 +849,26 @@ packages: resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} + '@inquirer/confirm@3.2.0': + resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==} + engines: {node: '>=18'} + + '@inquirer/core@9.2.1': + resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==} + engines: {node: '>=18'} + + '@inquirer/figures@1.0.6': + resolution: {integrity: sha512-yfZzps3Cso2UbM7WlxKwZQh2Hs6plrbjs1QnzQDZhK2DgyCo6D8AaHps9olkNcUFlcYERMqU3uJSp1gmy3s/qQ==} + engines: {node: '>=18'} + + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} + engines: {node: '>=18'} + + '@inquirer/type@2.0.0': + resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==} + engines: {node: '>=18'} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -866,6 +898,10 @@ packages: '@kamilkisiela/fast-url-parser@1.1.4': resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} + '@mswjs/interceptors@0.35.8': + resolution: {integrity: sha512-PFfqpHplKa7KMdoQdj5td03uG05VK2Ng1dG0sP4pT9h0dGSX2v9txYt/AnrzPb/vAmfyBBC0NQV7VaBEX+efgQ==} + engines: {node: '>=18'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -930,6 +966,15 @@ packages: '@octokit/types@13.6.0': resolution: {integrity: sha512-CrooV/vKCXqwLa+osmHLIMUb87brpgUqlqkPGc6iE2wCkUvTrHiXFMhAKoDDaAAYJrtKtrFTgSQTg5nObBEaew==} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@parcel/watcher-android-arm64@2.4.1': resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} engines: {node: '>= 10.0.0'} @@ -1232,24 +1277,18 @@ packages: resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} - '@testing-library/react@16.0.1': - resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==} - engines: {node: '>=18'} + '@testing-library/user-event@14.5.2': + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} + engines: {node: '>=12', npm: '>=6'} peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 - '@types/react-dom': ^18.0.0 - react: ^18.0.0 - react-dom: ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@testing-library/dom': '>=7.21.4' '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/d3-array@3.2.1': resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} @@ -1361,6 +1400,12 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + + '@types/node@22.5.5': + resolution: {integrity: sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==} + '@types/node@22.7.4': resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} @@ -1376,9 +1421,18 @@ packages: '@types/react@18.3.10': resolution: {integrity: sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==} + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + '@types/ws@8.5.12': resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} @@ -1444,6 +1498,21 @@ packages: peerDependencies: vite: ^4 || ^5 + '@vitest/browser@2.1.1': + resolution: {integrity: sha512-wLKqohwlZI24xMIEZAPwv9SVliv1avaIBeE0ou471D++BRPhiw2mubKBczFFIDHXuSL7UXb8/JQK9Ui6ttW9bQ==} + peerDependencies: + playwright: '*' + safaridriver: '*' + vitest: 2.1.1 + webdriverio: '*' + peerDependenciesMeta: + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + '@vitest/coverage-v8@2.1.1': resolution: {integrity: sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==} peerDependencies: @@ -1782,6 +1851,10 @@ packages: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} @@ -1834,6 +1907,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -2244,6 +2321,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2396,6 +2478,9 @@ packages: header-case@2.0.4: resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -2551,6 +2636,9 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} @@ -2878,9 +2966,23 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.4.9: + resolution: {integrity: sha512-1m8xccT6ipN4PTqLinPwmzhxQREuxaEJYdx4nIbggxP8aM7r1e71vE7RtOUSQoAm1LydjGfZKy7370XD/tsuYg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2979,6 +3081,9 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3058,6 +3163,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3085,6 +3193,16 @@ packages: engines: {node: '>=0.10'} hasBin: true + playwright-core@1.47.2: + resolution: {integrity: sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.47.2: + resolution: {integrity: sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==} + engines: {node: '>=18'} + hasBin: true + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -3129,6 +3247,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -3143,6 +3264,9 @@ packages: resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} engines: {node: '>=6.0.0'} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3235,6 +3359,9 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3397,6 +3524,10 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} @@ -3404,6 +3535,9 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -3540,6 +3674,10 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -3570,6 +3708,10 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + type-fest@4.26.1: + resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} + engines: {node: '>=16'} + typed-array-buffer@1.0.2: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} @@ -3617,6 +3759,10 @@ packages: universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + unixify@1.0.0: resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} engines: {node: '>=0.10.0'} @@ -3636,6 +3782,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + urlpattern-polyfill@10.0.0: resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} @@ -3700,6 +3849,22 @@ packages: terser: optional: true + vitest-browser-react@0.0.1: + resolution: {integrity: sha512-075hxhouuuMeCqHRyX4POzZWEDD3aPxFxDcwENN+T2ZlJ4lh5vUlgbkV4C4A0KZWinHb/Wsj3pYEJlDgIO23CQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + '@types/react': '>18.0.0' + '@types/react-dom': '>18.0.0' + '@vitest/browser': ^2.1.0-beta.4 + react: '>18.0.0' + react-dom: '>18.0.0' + vitest: ^2.1.0-beta.4 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + vitest@2.1.1: resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3862,6 +4027,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + zen-observable-ts@1.2.5: resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} @@ -4283,6 +4452,19 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@bundled-es-modules/cookie@2.0.0': + dependencies: + cookie: 0.5.0 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + '@esbuild/aix-ppc64@0.21.5': optional: true @@ -4824,6 +5006,36 @@ snapshots: '@humanwhocodes/retry@0.3.0': {} + '@inquirer/confirm@3.2.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + + '@inquirer/core@9.2.1': + dependencies: + '@inquirer/figures': 1.0.6 + '@inquirer/type': 2.0.0 + '@types/mute-stream': 0.0.4 + '@types/node': 22.5.5 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 1.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + + '@inquirer/figures@1.0.6': {} + + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + + '@inquirer/type@2.0.0': + dependencies: + mute-stream: 1.0.0 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -4854,6 +5066,15 @@ snapshots: '@kamilkisiela/fast-url-parser@1.1.4': {} + '@mswjs/interceptors@0.35.8': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4929,6 +5150,15 @@ snapshots: dependencies: '@octokit/openapi-types': 22.2.0 + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + '@parcel/watcher-android-arm64@2.4.1': optional: true @@ -5205,18 +5435,14 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': dependencies: - '@babel/runtime': 7.25.6 '@testing-library/dom': 10.4.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.10 - '@types/react-dom': 18.3.0 '@types/aria-query@5.0.4': {} + '@types/cookie@0.6.0': {} + '@types/d3-array@3.2.1': {} '@types/d3-axis@3.0.6': @@ -5351,6 +5577,14 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 22.5.5 + + '@types/node@22.5.5': + dependencies: + undici-types: 6.19.8 + '@types/node@22.7.4': dependencies: undici-types: 6.19.8 @@ -5370,8 +5604,14 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 + '@types/statuses@2.0.5': {} + + '@types/tough-cookie@4.0.5': {} + '@types/uuid@10.0.0': {} + '@types/wrap-ansi@3.0.0': {} + '@types/ws@8.5.12': dependencies: '@types/node': 22.7.4 @@ -5464,7 +5704,28 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - '@vitest/coverage-v8@2.1.1(vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.79.4))': + '@vitest/browser@2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1)': + dependencies: + '@testing-library/dom': 10.4.0 + '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)) + '@vitest/utils': 2.1.1 + magic-string: 0.30.11 + msw: 2.4.9(typescript@5.6.2) + sirv: 2.0.4 + tinyrainbow: 1.2.0 + vitest: 2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4) + ws: 8.18.0 + optionalDependencies: + playwright: 1.47.2 + transitivePeerDependencies: + - '@vitest/spy' + - bufferutil + - typescript + - utf-8-validate + - vite + + '@vitest/coverage-v8@2.1.1(@vitest/browser@2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1))(vitest@2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -5478,7 +5739,9 @@ snapshots: std-env: 3.7.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.79.4) + vitest: 2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4) + optionalDependencies: + '@vitest/browser': 2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1) transitivePeerDependencies: - supports-color @@ -5489,12 +5752,13 @@ snapshots: chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))': + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))': dependencies: '@vitest/spy': 2.1.1 estree-walker: 3.0.3 magic-string: 0.30.11 optionalDependencies: + msw: 2.4.9(typescript@5.6.2) vite: 5.4.8(@types/node@22.7.4)(sass@1.79.4) '@vitest/pretty-format@2.1.1': @@ -5525,7 +5789,7 @@ snapshots: sirv: 2.0.4 tinyglobby: 0.2.6 tinyrainbow: 1.2.0 - vitest: 2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.79.4) + vitest: 2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4) '@vitest/utils@2.1.1': dependencies: @@ -5915,6 +6179,8 @@ snapshots: cli-width@3.0.0: {} + cli-width@4.1.0: {} + cliui@6.0.0: dependencies: string-width: 4.2.3 @@ -5963,6 +6229,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@0.5.0: {} + cosmiconfig@8.3.6(typescript@5.6.2): dependencies: import-fresh: 3.3.0 @@ -6129,7 +6397,8 @@ snapshots: emoji-regex@9.2.2: {} - entities@4.5.0: {} + entities@4.5.0: + optional: true env-paths@2.2.1: {} @@ -6486,6 +6755,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -6621,6 +6893,7 @@ snapshots: entities: 4.5.0 webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 + optional: true has-bigints@1.0.2: {} @@ -6649,6 +6922,8 @@ snapshots: capital-case: 1.0.4 tslib: 2.7.0 + headers-polyfill@4.0.3: {} + hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -6802,6 +7077,8 @@ snapshots: is-negative-zero@2.0.3: {} + is-node-process@1.2.0: {} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -7113,8 +7390,32 @@ snapshots: ms@2.1.3: {} + msw@2.4.9(typescript@5.6.2): + dependencies: + '@bundled-es-modules/cookie': 2.0.0 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 3.2.0 + '@mswjs/interceptors': 0.35.8 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + chalk: 4.1.2 + graphql: 16.9.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + strict-event-emitter: 0.5.1 + type-fest: 4.26.1 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.6.2 + mute-stream@0.0.8: {} + mute-stream@1.0.0: {} + nanoid@3.3.7: {} natural-compare@1.4.0: {} @@ -7222,6 +7523,8 @@ snapshots: os-tmpdir@1.0.2: {} + outvariant@1.4.3: {} + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -7299,6 +7602,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -7313,6 +7618,14 @@ snapshots: pidtree@0.6.0: {} + playwright-core@1.47.2: {} + + playwright@1.47.2: + dependencies: + playwright-core: 1.47.2 + optionalDependencies: + fsevents: 2.3.2 + possible-typed-array-names@1.0.0: {} postcss@8.4.47: @@ -7354,6 +7667,8 @@ snapshots: proxy-from-env@1.1.0: {} + psl@1.9.0: {} + punycode@1.4.1: {} punycode@2.3.1: {} @@ -7364,6 +7679,8 @@ snapshots: pvutils@1.1.3: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} react-dom@18.3.1(react@18.3.1): @@ -7465,6 +7782,8 @@ snapshots: require-main-filename@2.0.0: {} + requires-port@1.0.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -7648,10 +7967,14 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.1: {} + std-env@3.7.0: {} streamsearch@1.1.0: {} + strict-event-emitter@0.5.1: {} + string-argv@0.3.2: {} string-env-interpolation@1.0.1: {} @@ -7800,6 +8123,13 @@ snapshots: totalist@3.0.1: {} + tough-cookie@4.1.4: + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + tr46@0.0.3: {} ts-api-utils@1.3.0(typescript@5.6.2): @@ -7822,6 +8152,8 @@ snapshots: type-fest@0.21.3: {} + type-fest@4.26.1: {} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 @@ -7882,6 +8214,8 @@ snapshots: universal-user-agent@6.0.1: {} + universalify@0.2.0: {} + unixify@1.0.0: dependencies: normalize-path: 2.1.1 @@ -7904,6 +8238,11 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + urlpattern-polyfill@10.0.0: {} urlpattern-polyfill@8.0.2: {} @@ -7955,10 +8294,20 @@ snapshots: fsevents: 2.3.3 sass: 1.79.4 - vitest@2.1.1(@types/node@22.7.4)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.79.4): + vitest-browser-react@0.0.1(@types/react-dom@18.3.0)(@types/react@18.3.10)(@vitest/browser@2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4)): + dependencies: + '@vitest/browser': 2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + vitest: 2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4) + optionalDependencies: + '@types/react': 18.3.10 + '@types/react-dom': 18.3.0 + + vitest@2.1.1(@types/node@22.7.4)(@vitest/browser@2.1.1)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.2))(sass@1.79.4): dependencies: '@vitest/expect': 2.1.1 - '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)) + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4)) '@vitest/pretty-format': 2.1.1 '@vitest/runner': 2.1.1 '@vitest/snapshot': 2.1.1 @@ -7978,6 +8327,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.7.4 + '@vitest/browser': 2.1.1(@vitest/spy@2.1.1)(playwright@1.47.2)(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(sass@1.79.4))(vitest@2.1.1) '@vitest/ui': 2.1.1(vitest@2.1.1) happy-dom: 15.7.4 transitivePeerDependencies: @@ -8007,9 +8357,11 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@7.0.0: {} + webidl-conversions@7.0.0: + optional: true - whatwg-mimetype@3.0.0: {} + whatwg-mimetype@3.0.0: + optional: true whatwg-url@5.0.0: dependencies: @@ -8140,6 +8492,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors-cjs@2.1.2: {} + zen-observable-ts@1.2.5: dependencies: zen-observable: 0.8.15 diff --git a/src/App.tsx b/src/App.tsx index 443b230..3c58408 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,7 @@ import Home from './components/Layout/Home/Home'; import Layout from './components/Layout/Layout'; import Login from './components/Login/Login'; import Token from './components/Token/Token'; +import { ToastProvider } from './Helpers/toast'; const router = createBrowserRouter([ { path: '/', element: , children: [{ index: true, element: }] }, @@ -29,8 +30,10 @@ export function App({ environment }: { environment: Environment }) { return ( - - + + + + ); } diff --git a/src/Helpers/toast.tsx b/src/Helpers/toast.tsx new file mode 100644 index 0000000..04a1572 --- /dev/null +++ b/src/Helpers/toast.tsx @@ -0,0 +1,25 @@ +import { Toast } from 'primereact/toast'; +import { createContext, useContext, useEffect, useRef, useState } from 'react'; + +const ToastContext = createContext(null); + +export function useToast() { + return useContext(ToastContext); +} + +export function ToastProvider({ children }: { children: React.ReactNode }) { + const ref = useRef(null); + const [toast, setToast] = useState(null); + + // We have to wrap the ref in a state to force a re-render on initial mount, otherwise the context value will be null + useEffect(() => { + if (ref.current) setToast(ref.current); + }, [ref.current]); + + return ( + <> + + {children} + + ); +} diff --git a/src/components/Contexts/Auth/Authentication.ts b/src/components/Contexts/Auth/Authentication.ts index 1e67037..21e4191 100644 --- a/src/components/Contexts/Auth/Authentication.ts +++ b/src/components/Contexts/Auth/Authentication.ts @@ -37,7 +37,7 @@ export const Authentication = { }); if (res.status === 200) { - const data: User = await res.json(); + const data = (await res.json()) as User; localStorage.setItem('user', JSON.stringify(data)); return [data, null]; } else { diff --git a/src/components/Contexts/Variables/Modals/Instrument.tsx b/src/components/Contexts/Variables/Modals/Instrument.tsx index 624856b..29f6324 100644 --- a/src/components/Contexts/Variables/Modals/Instrument.tsx +++ b/src/components/Contexts/Variables/Modals/Instrument.tsx @@ -10,7 +10,7 @@ export function Instrument() { const [importInstrument, setImportInstrument] = useImportInstrument(); const getNames = useGetDistinctInstruments(); const getPorts = useGetDistinctPorts(); - const getInstuments = useGetInstruments(); + const getInstruments = useGetInstruments(); const [nameOptions, setNameOptions] = useState([]); const [name, setName] = useState(''); const [portOptions, setPortOptions] = useState([]); @@ -20,7 +20,7 @@ export function Instrument() { useEffect(() => { if (importInstrument) - getNames({ + void getNames({ onCompleted: (data) => { setNameOptions(data.distinctInstruments.map((e) => e.name)); }, @@ -28,9 +28,9 @@ export function Instrument() { }, [importInstrument]); useEffect(() => { - if (name !== '') { + if (name) { setPort(0); - getPorts({ + void getPorts({ variables: { name: name }, onCompleted: (data) => { setPortOptions(data.distinctPorts.map((e) => e.issPort)); @@ -40,8 +40,8 @@ export function Instrument() { }, [name]); useEffect(() => { - if (port > 0 && name !== '') { - getInstuments({ + if (port && name) { + void getInstruments({ variables: { name: name, issPort: port }, onCompleted: (data) => { setInstrumentOptions(data.instruments); @@ -76,7 +76,7 @@ export function Instrument() { }); let table: JSX.Element | null = null; - if (port > 0 && name !== '') { + if (port && name) { table = ( @@ -109,7 +109,7 @@ export function Instrument() { setName(e.target.value)} + onChange={(e) => setName(e.target.value as string)} placeholder="Select instrument" /> issPort @@ -117,7 +117,7 @@ export function Instrument() { disabled={portOptions.length <= 0} value={port} options={portOptions} - onChange={(e) => setPort(e.target.value)} + onChange={(e) => setPort(e.target.value as number)} placeholder="Select port" /> @@ -133,7 +133,7 @@ function InstrumentDetails({ selectedPk, }: { instrument: InstrumentType; - setInstrument(_: InstrumentType): void; + setInstrument(this: void, _: InstrumentType): void; selectedPk: number; }) { return ( diff --git a/src/components/Contexts/Variables/Modals/OdbImport/OdbImport.tsx b/src/components/Contexts/Variables/Modals/OdbImport/OdbImport.tsx index e4bbb9d..30bc688 100644 --- a/src/components/Contexts/Variables/Modals/OdbImport/OdbImport.tsx +++ b/src/components/Contexts/Variables/Modals/OdbImport/OdbImport.tsx @@ -23,7 +23,7 @@ export function OdbImport() { const rotator = useRotator().data?.rotator; function updateObs() { - updateConfiguration({ + void updateConfiguration({ variables: { ...(configuration as ConfigurationType), obsId: selectedObservation.id, @@ -31,9 +31,9 @@ export function OdbImport() { obsSubtitle: selectedObservation.subtitle, obsInstrument: selectedObservation.instrument, }, - onCompleted() { + async onCompleted() { setOdbVisible(false); - removeAndCreateBaseTargets({ + await removeAndCreateBaseTargets({ variables: { targets: [ { @@ -46,8 +46,8 @@ export function OdbImport() { }, ], }, - onCompleted(t) { - updateConfiguration({ + async onCompleted(t) { + await updateConfiguration({ variables: { pk: configuration?.pk ?? 1, selectedTarget: t.removeAndCreateBaseTargets[0].pk, @@ -56,7 +56,7 @@ export function OdbImport() { }, }); if (rotator) { - updateRotator({ + await updateRotator({ variables: { pk: rotator?.pk, angle: selectedObservation.posAngleConstraint.angle.degrees, @@ -73,7 +73,7 @@ export function OdbImport() {
+ Previous panel + ); } let nextPanelDisplay = null; if (nextPanel) { nextPanelDisplay = ( -
+
+ Next panel + ); } return ( diff --git a/src/gql/ApolloConfigs.ts b/src/gql/ApolloConfigs.ts index afb2706..561d46a 100644 --- a/src/gql/ApolloConfigs.ts +++ b/src/gql/ApolloConfigs.ts @@ -1,5 +1,6 @@ // Apollo import { ApolloClient, InMemoryCache, ApolloLink, HttpLink, defaultDataIdFromObject } from '@apollo/client'; +import { onError } from '@apollo/client/link/error'; // Subscription channel import { WebSocketLink } from '@apollo/client/link/ws'; @@ -18,6 +19,19 @@ const withAbsoluteUri = (uri: string, isWs = false) => { return isWs ? newUri.replace(/^http/, 'ws') : newUri; }; +// Log errors to the console +const errorLink = onError(({ graphQLErrors, networkError }) => { + if (graphQLErrors) + graphQLErrors.forEach(({ message, locations, path }) => + console.warn( + `[GraphQL error]: ${message.trim()} + Path: ${(path ?? [])?.join(', ')} ${(locations ?? [])?.map((l) => `[${l.line}:${l.column}]`).join(', ')}`, + ), + ); + + if (networkError) console.error(`[Network error]`, networkError); +}); + export function createClient(env: Environment) { const navigateCommandServer = new HttpLink({ uri: withAbsoluteUri(env.navigateServerURI) }); @@ -29,24 +43,27 @@ export function createClient(env: Environment) { return new ApolloClient({ name: 'navigate-ui', - link: ApolloLink.split( - (operation) => operation.getContext().clientName === 'odb', - odbLink, + link: ApolloLink.from([ + errorLink, ApolloLink.split( - (operation) => operation.getContext().clientName === 'navigateConfigs', - navigateConfigs, + (operation) => operation.getContext().clientName === 'odb', + odbLink, ApolloLink.split( - ({ query }) => { - const definition = getMainDefinition(query); - return ( - definition.kind === Kind.OPERATION_DEFINITION && definition.operation === OperationTypeNode.SUBSCRIPTION - ); - }, - wsLink, - navigateCommandServer, + (operation) => operation.getContext().clientName === 'navigateConfigs', + navigateConfigs, + ApolloLink.split( + ({ query }) => { + const definition = getMainDefinition(query); + return ( + definition.kind === Kind.OPERATION_DEFINITION && definition.operation === OperationTypeNode.SUBSCRIPTION + ); + }, + wsLink, + navigateCommandServer, + ), ), ), - ), + ]), cache: new InMemoryCache({ dataIdFromObject(responseObject) { // Configure primary-key fields for cache normalization to use 'pk' field diff --git a/src/gql/configs/Configuration.ts b/src/gql/configs/Configuration.ts index 099576a..3344939 100644 --- a/src/gql/configs/Configuration.ts +++ b/src/gql/configs/Configuration.ts @@ -1,7 +1,7 @@ import { useMutation, useQuery } from '@apollo/client'; import { graphql } from './gen'; -const GET_CONFIGURATION = graphql(` +export const GET_CONFIGURATION = graphql(` query getConfiguration { configuration { pk diff --git a/src/gql/configs/Instrument.ts b/src/gql/configs/Instrument.ts index 1bb4f0a..b68d6ec 100644 --- a/src/gql/configs/Instrument.ts +++ b/src/gql/configs/Instrument.ts @@ -34,7 +34,7 @@ export function useGetDistinctPorts() { return queryFunction; } -const GET_INSTRUMENTS = graphql(` +export const GET_INSTRUMENTS = graphql(` query getInstruments($name: String!, $issPort: Int!) { instruments(name: $name, issPort: $issPort) { pk @@ -59,8 +59,8 @@ export function useGetInstruments() { return queryFunction; } -const GET_INSTRUMENT = graphql(` - query getInstrument($name: String!, $issPort: Int!, $wfs: WfsType) { +export const GET_INSTRUMENT = graphql(` + query getInstrument($name: String, $issPort: Int, $wfs: WfsType) { instrument(name: $name, issPort: $issPort, wfs: $wfs) { pk name diff --git a/src/gql/configs/Rotator.ts b/src/gql/configs/Rotator.ts index 928096a..0ed8c07 100644 --- a/src/gql/configs/Rotator.ts +++ b/src/gql/configs/Rotator.ts @@ -1,7 +1,7 @@ import { useMutation, useQuery } from '@apollo/client'; import { graphql } from './gen'; -const GET_ROTATOR = graphql(` +export const GET_ROTATOR = graphql(` query getRotator { rotator { pk diff --git a/src/gql/configs/Target.ts b/src/gql/configs/Target.ts index 89e4170..63e9b5b 100644 --- a/src/gql/configs/Target.ts +++ b/src/gql/configs/Target.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery } from '@apollo/client'; import { graphql } from './gen'; import { isBaseTarget, isOiTarget, isP1Target, isP2Target } from '@gql/util'; import { useMemo } from 'react'; +import { Target } from './gen/graphql'; const GET_TARGETS = graphql(` query getTargets { @@ -38,12 +39,13 @@ export function useTargets() { }); const filteredData = useMemo(() => { - const targets = result.data?.targets ?? []; + const targets: Target[] = result.data?.targets ?? []; return { baseTargets: targets.filter(isBaseTarget), oiTargets: targets.filter(isOiTarget), p1Targets: targets.filter(isP1Target), p2Targets: targets.filter(isP2Target), + allTargets: targets, }; }, [result.data]); diff --git a/src/gql/configs/gen/gql.ts b/src/gql/configs/gen/gql.ts index c3e6fa4..8b912cb 100644 --- a/src/gql/configs/gen/gql.ts +++ b/src/gql/configs/gen/gql.ts @@ -47,7 +47,7 @@ const documents = { types.GetDistinctPortsDocument, '\n query getInstruments($name: String!, $issPort: Int!) {\n instruments(name: $name, issPort: $issPort) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n': types.GetInstrumentsDocument, - '\n query getInstrument($name: String!, $issPort: Int!, $wfs: WfsType) {\n instrument(name: $name, issPort: $issPort, wfs: $wfs) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n': + '\n query getInstrument($name: String, $issPort: Int, $wfs: WfsType) {\n instrument(name: $name, issPort: $issPort, wfs: $wfs) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n': types.GetInstrumentDocument, '\n query getMechanism {\n mechanism {\n pk\n mcs\n mcsPark\n mcsUnwrap\n scs\n crcs\n crcsPark\n crcsUnwrap\n pwfs1\n pwfs1Park\n pwfs1Unwrap\n pwfs2\n pwfs2Park\n pwfs2Unwrap\n oiwfs\n oiwfsPark\n odgw\n odgwPark\n aowfs\n aowfsPark\n dome\n domePark\n domeMode\n shutters\n shuttersPark\n shutterMode\n shutterAperture\n wVGate\n wVGateClose\n wVGateValue\n eVGate\n eVGateClose\n eVGateValue\n agScienceFoldPark\n agAoFoldPark\n agAcPickoffPark\n agParkAll\n }\n }\n': types.GetMechanismDocument, @@ -193,8 +193,8 @@ export function graphql( * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql( - source: '\n query getInstrument($name: String!, $issPort: Int!, $wfs: WfsType) {\n instrument(name: $name, issPort: $issPort, wfs: $wfs) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n', -): (typeof documents)['\n query getInstrument($name: String!, $issPort: Int!, $wfs: WfsType) {\n instrument(name: $name, issPort: $issPort, wfs: $wfs) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n']; + source: '\n query getInstrument($name: String, $issPort: Int, $wfs: WfsType) {\n instrument(name: $name, issPort: $issPort, wfs: $wfs) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n', +): (typeof documents)['\n query getInstrument($name: String, $issPort: Int, $wfs: WfsType) {\n instrument(name: $name, issPort: $issPort, wfs: $wfs) {\n pk\n name\n iaa\n issPort\n focusOffset\n wfs\n originX\n originY\n ao\n extraParams\n }\n }\n']; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/src/gql/configs/gen/graphql.ts b/src/gql/configs/gen/graphql.ts index 32bd88d..56bb3fa 100644 --- a/src/gql/configs/gen/graphql.ts +++ b/src/gql/configs/gen/graphql.ts @@ -907,8 +907,8 @@ export type GetInstrumentsQuery = { }; export type GetInstrumentQueryVariables = Exact<{ - name: Scalars['String']['input']; - issPort: Scalars['Int']['input']; + name?: InputMaybe; + issPort?: InputMaybe; wfs?: InputMaybe; }>; @@ -2510,12 +2510,12 @@ export const GetInstrumentDocument = { { kind: 'VariableDefinition', variable: { kind: 'Variable', name: { kind: 'Name', value: 'name' } }, - type: { kind: 'NonNullType', type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, }, { kind: 'VariableDefinition', variable: { kind: 'Variable', name: { kind: 'Name', value: 'issPort' } }, - type: { kind: 'NonNullType', type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } }, }, { kind: 'VariableDefinition', diff --git a/src/gql/odb/gen/graphql.ts b/src/gql/odb/gen/graphql.ts index 0225a01..69e5325 100644 --- a/src/gql/odb/gen/graphql.ts +++ b/src/gql/odb/gen/graphql.ts @@ -6158,10 +6158,22 @@ export type TargetEnvironment = { * range. In this case, the `end` of one period will be the same as the `start` of the next period. */ guideAvailability: Array; - /** The guide star(s) and related information */ - guideEnvironment?: Maybe; + /** + * The guide target(s) and related information. + * If a guide target has been set via `guideTargetName`, that target will be + * returned. If it not found or not usable, an error will be returned. + * If no guide target has been set, or it has been invalidated by observation/target + * changes, Gaia will be searched for the best guide target available. + */ + guideEnvironment: GuideEnvironment; /** The guide star(s) and related information */ guideEnvironments: Array; + /** + * The name of the guide target, if any, set by `setGuideTargetName`. + * If the name is no longer valid or a sequence cannot be generated, null will + * be returned. + */ + guideTargetName?: Maybe; }; export type TargetEnvironmentAsterismArgs = { @@ -6177,10 +6189,6 @@ export type TargetEnvironmentGuideAvailabilityArgs = { start: Scalars['Timestamp']['input']; }; -export type TargetEnvironmentGuideEnvironmentArgs = { - lookupIfUndefined?: Scalars['Boolean']['input']; -}; - export type TargetEnvironmentGuideEnvironmentsArgs = { observationTime: Scalars['Timestamp']['input']; }; diff --git a/src/gql/render.tsx b/src/gql/render.tsx index 212d00a..24d6f26 100644 --- a/src/gql/render.tsx +++ b/src/gql/render.tsx @@ -1,8 +1,8 @@ import { MockedProvider, MockedResponse } from '@apollo/client/testing'; -import { render, RenderOptions } from '@testing-library/react'; import { createStore, Provider, WritableAtom } from 'jotai'; import { useHydrateAtoms } from 'jotai/utils'; import { PropsWithChildren, ReactElement } from 'react'; +import { ComponentRenderOptions, render } from 'vitest-browser-react'; import { GET_SLEW_FLAGS } from './configs/SlewFlags'; interface CreateOptions { @@ -24,7 +24,7 @@ function HydrateAtoms({ export function renderWithContext( ui: ReactElement, createOptions: CreateOptions = {}, - options?: RenderOptions, + options?: ComponentRenderOptions, ) { const store = createStore(); const renderResult = render( diff --git a/src/gql/server/Buttons.tsx b/src/gql/server/Buttons.tsx index 19a5e61..69a1015 100644 --- a/src/gql/server/Buttons.tsx +++ b/src/gql/server/Buttons.tsx @@ -2,10 +2,9 @@ import { useMutation, OperationVariables, DocumentNode } from '@apollo/client'; import { VariablesOf } from '@graphql-typed-document-node/core'; -import { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { Button, ButtonProps } from 'primereact/button'; import { SlewFlagsType } from '@/types'; -import { Toast } from 'primereact/toast'; import { BTN_CLASSES } from '@/Helpers/constants'; import { graphql } from './gen'; import { Instrument, MechSystemState } from './gen/graphql'; @@ -17,6 +16,7 @@ import { useInstrument } from '@gql/configs/Instrument'; import { clsx } from 'clsx'; import { MOUNT_FOLLOW_MUTATION, OIWFS_FOLLOW_MUTATION, ROTATOR_FOLLOW_MUTATION, SCS_FOLLOW_MUTATION } from './follow'; import { ROTATOR_PARK_MUTATION, MOUNT_PARK_MUTATION, OIWFS_PARK_MUTATION } from './park'; +import { useToast } from '@/Helpers/toast'; // Generic mutation button function MutationButton({ @@ -28,28 +28,23 @@ function MutationButton({ variables: VariablesOf extends OperationVariables ? VariablesOf : never; } & ButtonProps) { const TOAST_LIFE = 5000; - const toast = useRef(null); + const toast = useToast(); const [mutationFunction, { loading, error }] = useMutation(mutation, { variables: variables, }); useEffect(() => { if (error) { - toast.current?.show({ + toast?.show({ severity: 'error', - summary: 'Error', + summary: error.name, detail: error.message, life: TOAST_LIFE, }); } }, [error]); - return ( - <> - -