diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e534ccf..6ff020a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,4 +42,4 @@ rockcraft pack -v version=$(yq '.version' rockcraft.yaml) sudo rockcraft.skopeo --insecure-policy copy oci-archive:gocert_${version}_amd64.rock docker-daemon:gocert:${version} docker run gocert:${version} -``` +``` \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index af2fb43..2e76764 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -7,8 +7,10 @@ "name": "gocert", "dependencies": { "next": "14.2.3", + "pkijs": "^3.1.0", "react": "^18", "react-dom": "^18", + "react-query": "^3.39.3", "sass": "^1.77.4", "vanilla-framework": "^4.11.0" }, @@ -392,7 +394,6 @@ "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1982,6 +1983,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "dependencies": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2039,8 +2053,15 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -2057,7 +2078,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2074,6 +2094,21 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/browserslist": { "version": "4.23.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", @@ -2117,6 +2152,14 @@ "node": ">=10.16.0" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2291,8 +2334,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/confbox": { "version": "0.1.7", @@ -2501,6 +2543,11 @@ "node": ">=6" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -3397,8 +3444,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -3836,7 +3882,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3845,8 +3890,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.7", @@ -4286,6 +4330,11 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4524,6 +4573,15 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4552,6 +4610,11 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4589,7 +4652,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4633,6 +4695,14 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -4877,11 +4947,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -4985,7 +5059,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5081,6 +5154,21 @@ "pathe": "^1.1.2" } }, + "node_modules/pkijs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.1.0.tgz", + "integrity": "sha512-N+OCWUp6xrg7OkG+4DIiZUOsp3qMztjq8RGCc1hSY92dsUG8cTlAo7pEkfRGjcdyBv2c1Y9bjAzqdTJAlctuNg==", + "dependencies": { + "asn1js": "^3.0.5", + "bytestreamjs": "^2.0.0", + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -5184,6 +5272,22 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", + "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "dependencies": { + "tslib": "^2.6.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -5239,6 +5343,31 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -5283,8 +5412,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", @@ -5304,6 +5432,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -5359,7 +5492,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -5374,7 +5506,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6201,6 +6332,15 @@ "node": ">= 4.0.0" } }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", @@ -6696,8 +6836,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { "version": "8.17.1", diff --git a/ui/package.json b/ui/package.json index 348263e..cbf3c62 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,8 +12,10 @@ }, "dependencies": { "next": "14.2.3", + "pkijs": "^3.1.0", "react": "^18", "react-dom": "^18", + "react-query": "^3.39.3", "sass": "^1.77.4", "vanilla-framework": "^4.11.0" }, diff --git a/ui/src/app/certificate_requests/page.test.tsx b/ui/src/app/certificate_requests/page.test.tsx deleted file mode 100644 index 50eaaba..0000000 --- a/ui/src/app/certificate_requests/page.test.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { expect, test } from 'vitest' -import { render, screen } from '@testing-library/react' -import CertificateRequests from './page' - -test('CertificateRequestsPage', () => { - render(< CertificateRequests />) - expect(screen.getByRole('table', {})).toBeDefined() -}) \ No newline at end of file diff --git a/ui/src/app/certificate_requests/page.tsx b/ui/src/app/certificate_requests/page.tsx index a0e69a4..70be761 100644 --- a/ui/src/app/certificate_requests/page.tsx +++ b/ui/src/app/certificate_requests/page.tsx @@ -1,9 +1,45 @@ "use client" +import { useQuery } from "react-query" import { CertificateRequestsTable } from "./table" +import { getCertificateRequests } from "./queries" +import { CSREntry } from "./types" + +function Error({ msg }: { msg: string }) { + return ( + +
+
+
+

An error occured trying to load certificate requests

+

{msg}

+
+
+
+ + ) +} + +function Loading() { + return ( + +
+
+
+

Loading...

+
+
+
+ + ) +} export default function CertificateRequests() { + const query = useQuery('csrs', getCertificateRequests) + if (query.status == "loading") { return } + if (query.status == "error") { return } + const csrs = Array.from(query.data ? query.data : []) return ( - + ) } \ No newline at end of file diff --git a/ui/src/app/certificate_requests/queries.ts b/ui/src/app/certificate_requests/queries.ts new file mode 100644 index 0000000..3702304 --- /dev/null +++ b/ui/src/app/certificate_requests/queries.ts @@ -0,0 +1,9 @@ +import { CSREntry } from "./types" + +export async function getCertificateRequests(): Promise { + const response = await fetch("/api/v1/certificate_requests") + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() +} \ No newline at end of file diff --git a/ui/src/app/certificate_requests/row.test.tsx b/ui/src/app/certificate_requests/row.test.tsx index dbe8269..edabc82 100644 --- a/ui/src/app/certificate_requests/row.test.tsx +++ b/ui/src/app/certificate_requests/row.test.tsx @@ -1,11 +1,67 @@ import { expect, test } from 'vitest' -import { render, screen } from '@testing-library/react' +import { Dispatch, SetStateAction } from "react" +import { render, screen, fireEvent } from '@testing-library/react' import Row from './row' +const csr = +{ + 'ID': 1, + 'CSR': `-----BEGIN CERTIFICATE REQUEST----- +MIIC5zCCAc8CAQAwRzEWMBQGA1UEAwwNMTAuMTUyLjE4My41MzEtMCsGA1UELQwk +MzlhY2UxOTUtZGM1YS00MzJiLTgwOTAtYWZlNmFiNGI0OWNmMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjM5Wz+HRtDveRzeDkEDM4ornIaefe8d8nmFi +pUat9qCU3U9798FR460DHjCLGxFxxmoRitzHtaR4ew5H036HlGB20yas/CMDgSUI +69DyAsyPwEJqOWBGO1LL50qXdl5/jOkO2voA9j5UsD1CtWSklyhbNhWMpYqj2ObW +XcaYj9Gx/TwYhw8xsJ/QRWyCrvjjVzH8+4frfDhBVOyywN7sq+I3WwCbyBBcN8uO +yae0b/q5+UJUiqgpeOAh/4Y7qI3YarMj4cm7dwmiCVjedUwh65zVyHtQUfLd8nFW +Kl9775mNBc1yicvKDU3ZB5hZ1MZtpbMBwaA1yMSErs/fh5KaXwIDAQABoFswWQYJ +KoZIhvcNAQkOMUwwSjBIBgNVHREEQTA/hwQKmLc1gjd2YXVsdC1rOHMtMC52YXVs +dC1rOHMtZW5kcG9pbnRzLnZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsMA0GCSqGSIb3 +DQEBCwUAA4IBAQCJt8oVDbiuCsik4N5AOJIT7jKsMb+j0mizwjahKMoCHdx+zv0V +FGkhlf0VWPAdEu3gHdJfduX88WwzJ2wBBUK38UuprAyvfaZfaYUgFJQNC6DH1fIa +uHYEhvNJBdFJHaBvW7lrSFi57fTA9IEPrB3m/XN3r2F4eoHnaJJqHZmMwqVHck87 +cAQXk3fvTWuikHiCHqqdSdjDYj/8cyiwCrQWpV245VSbOE0WesWoEnSdFXVUfE1+ +RSKeTRuuJMcdGqBkDnDI22myj0bjt7q8eqBIjTiLQLnAFnQYpcCrhc8dKU9IJlv1 +H9Hay4ZO9LRew3pEtlx2WrExw/gpUcWM8rTI +-----END CERTIFICATE REQUEST-----`, + 'Certificate': `-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgIURKr+jf7hj60SyAryIeN++9wDdtkwDQYJKoZIhvcNAQEL +BQAwOTELMAkGA1UEBhMCVVMxKjAoBgNVBAMMIXNlbGYtc2lnbmVkLWNlcnRpZmlj +YXRlcy1vcGVyYXRvcjAeFw0yNDAzMjcxMjQ4MDRaFw0yNTAzMjcxMjQ4MDRaMEcx +FjAUBgNVBAMMDTEwLjE1Mi4xODMuNTMxLTArBgNVBC0MJDM5YWNlMTk1LWRjNWEt +NDMyYi04MDkwLWFmZTZhYjRiNDljZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAIzOVs/h0bQ73kc3g5BAzOKK5yGnn3vHfJ5hYqVGrfaglN1Pe/fBUeOt +Ax4wixsRccZqEYrcx7WkeHsOR9N+h5RgdtMmrPwjA4ElCOvQ8gLMj8BCajlgRjtS +y+dKl3Zef4zpDtr6APY+VLA9QrVkpJcoWzYVjKWKo9jm1l3GmI/Rsf08GIcPMbCf +0EVsgq7441cx/PuH63w4QVTsssDe7KviN1sAm8gQXDfLjsmntG/6uflCVIqoKXjg +If+GO6iN2GqzI+HJu3cJoglY3nVMIeuc1ch7UFHy3fJxVipfe++ZjQXNconLyg1N +2QeYWdTGbaWzAcGgNcjEhK7P34eSml8CAwEAAaOBnTCBmjAhBgNVHSMEGjAYgBYE +FN/vgl9cAapV7hH9lEyM7qYS958aMB0GA1UdDgQWBBRJJDZkHr64VqTC24DPQVld +Ba3iPDAMBgNVHRMBAf8EAjAAMEgGA1UdEQRBMD+CN3ZhdWx0LWs4cy0wLnZhdWx0 +LWs4cy1lbmRwb2ludHMudmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWyHBAqYtzUwDQYJ +KoZIhvcNAQELBQADggEBAEH9NTwDiSsoQt/QXkWPMBrB830K0dlwKl5WBNgVxFP+ +hSfQ86xN77jNSp2VxOksgzF9J9u/ubAXvSFsou4xdP8MevBXoFJXeqMERq5RW3gc +WyhXkzguv3dwH+n43GJFP6MQ+n9W/nPZCUQ0Iy7ueAvj0HFhGyZzAE2wxNFZdvCs +gCX3nqYpp70oZIFDrhmYwE5ij5KXlHD4/1IOfNUKCDmQDgGPLI1tVtwQLjeRq7Hg +XVelpl/LXTQawmJyvDaVT/Q9P+WqoDiMjrqF6Sy7DzNeeccWVqvqX5TVS6Ky56iS +Mvo/+PAJHkBciR5Xn+Wg2a+7vrZvT6CBoRSOTozlLSM= +-----END CERTIFICATE-----` +} + +let actionMenuExpanded = 0 +const setActionMenuExpanded = (val: number) => { + actionMenuExpanded = val +} + test('Certificate Requests Table Row', () => { - render() - expect(screen.getByText('1')).toBeDefined() -}) -// TODO: when certificate rejected => rejected status -// TODO: when certificate empty => outstanding status -// TODO: when certificate anything else => certificate.NotAfter \ No newline at end of file + render(>} />) + expect(screen.getByText('10.152.183.53')).toBeDefined() // Common name of CSR + expect(screen.getByLabelText('certificate-expiry-date').innerHTML).toMatch(/^Thu Mar 27/) + const openActionsButton = screen.getByLabelText("action-menu-button") + fireEvent.click(openActionsButton); + expect(actionMenuExpanded).toBe(1) + render(>} />) + expect(screen.getByText('rejected')).toBeDefined() + render(>} />) + expect(screen.getByText('outstanding')).toBeDefined() +}) \ No newline at end of file diff --git a/ui/src/app/certificate_requests/row.tsx b/ui/src/app/certificate_requests/row.tsx index 0ef4a03..9ca6891 100644 --- a/ui/src/app/certificate_requests/row.tsx +++ b/ui/src/app/certificate_requests/row.tsx @@ -1,11 +1,6 @@ import { useState, Dispatch, SetStateAction } from "react" -const extractCSR = (csrPemString: string) => { - //TODO -} +import { extractCSR, extractCert } from "../utils" -const extractCert = (certPemString: string) => { - //TODO -} type rowProps = { id: number, @@ -18,40 +13,44 @@ type rowProps = { export default function Row({ id, csr, certificate, ActionMenuExpanded, setActionMenuExpanded }: rowProps) { const [detailsMenuOpen, setDetailsMenuOpen] = useState(false) + const csrObj = extractCSR(csr) + const certObj = extractCert(certificate) + const toggleActionMenu = () => { if (ActionMenuExpanded == id) { setActionMenuExpanded(0) - }else{ + } else { setActionMenuExpanded(id) } } return ( - {id} + {id} - - example.com + {csrObj.subjects.find((e) => e.type == "Common Name")?.value} - {certificate == "" ? "outstanding" : (certificate == "rejected" ? "rejected" : "fulfilled")} - {certificate == "" ? "" : (certificate == "rejected" ? "" : "date")} + {certificate == "" ? "outstanding" : (certificate == "rejected" ? "rejected" : "fulfilled")} + {certificate == "" ? "" : (certificate == "rejected" ? "" : certObj?.notAfter)} - - + @@ -65,11 +64,10 @@ export default function Row({ id, csr, certificate, ActionMenuExpanded, setActio - +
-

Common Name: example.com

-

Subject Alternative Names: example.com, 127.0.0.1, 1.2.3.4.5.56

+

Common Name: {csrObj.subjects.find((e) => e.type == "Common Name")?.value}

diff --git a/ui/src/app/certificate_requests/table.test.tsx b/ui/src/app/certificate_requests/table.test.tsx new file mode 100644 index 0000000..6a7c08c --- /dev/null +++ b/ui/src/app/certificate_requests/table.test.tsx @@ -0,0 +1,99 @@ +import { expect, test } from 'vitest' +import { render, screen } from '@testing-library/react' +import { CertificateRequestsTable } from './table' + +const rows = [ + { + 'ID': 1, + 'CSR': `-----BEGIN CERTIFICATE REQUEST----- +MIICszCCAZsCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDC5KgrADpuOUPwSh0YLmpWF66VTcciIGC2HcGn +oJknL7pm5q9qhfWGIdvKKlIA6cBB32jPd0QcYDsx7+AvzEvBuO7mq7v2Q1sPU4Q+ +L0s2pLJges6/cnDWvk/p5eBjDLOqHhUNzpMUga9SgIod8yymTZm3eqQvt1ABdwTg +FzBs5QdSm2Ny1fEbbcRE+Rv5rqXyJb2isXSujzSuS22VqslDIyqnY5WaLg+pjZyR ++0j13ecJsdh6/MJMUZWheimV2Yv7SFtxzFwbzBMO9YFS098sy4F896eBHLNe9cUC ++d1JDtLaewlMogjHBHAxmP54dhe6vvc78anElKKP4hm5N5nlAgMBAAGgWDBWBgkq +hkiG9w0BCQ4xSTBHMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcD +AQYIKwYBBQUHAwIwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEL +BQADggEBACP1VKEGVYKoVLMDJS+EZ0CPwIYWsO4xBXgK6atHe8WIChVn/8I7eo60 +cuMDiy4LR70G++xL1tpmYGRbx21r9d/shL2ehp9VdClX06qxlcGxiC/F8eThRuS5 +zHcdNqSVyMoLJ0c7yWHJahN5u2bn1Lov34yOEqGGpWCGF/gT1nEvM+p/v30s89f2 +Y/uPl4g3jpGqLCKTASWJDGnZLroLICOzYTVs5P3oj+VueSUwYhGK5tBnS2x5FHID +uMNMgwl0fxGMQZjrlXyCBhXBm1k6PmwcJGJF5LQ31c+5aTTMFU7SyZhlymctB8mS +y+ErBQsRpcQho6Ok+HTXQQUcx7WNcwI= +-----END CERTIFICATE REQUEST-----`, + 'Certificate': "" + }, + { + 'ID': 2, + 'CSR': `-----BEGIN CERTIFICATE REQUEST----- +MIIDGjCCAgICAQAwajEVMBMGA1UEAwwMZXhlbXBsYXIuY29tMQswCQYDVQQGEwJV +UzESMBAGA1UECAwJTG91aXNpYW5hMRQwEgYDVQQHDAtOZXcgT3JsZWFuczEaMBgG +A1UECgwRQ2VydGlmaWNhdGUgVG9vbHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDZD+5Kz84PVxW6zUEd0wwWPC4h0vxSMX1kuDM+NibZdgB/kE+g9OI5 +qvK7bOrGrXEs0GOuI4M7pl/6e42+it/oE0v5tWBZxka8J+bqYE5J8NGal/oIgOo7 +evpx5QPFFlJlJOJdH4bFJfp6GMO/3tO3Ip7O0Q3iitTnDA2gJC6aQW7hclVCk4ls +lWFVyUFRRubKW0/LEmgNl9DNuQyfLp1yB3159r1NiKT9M+/ATrmBYF2ZiCWBWz4C +ySja6+r4UB/LZYwdp/n7rRtwX1R/B6HPsXw/nGsjU6+OyYS/oDJwNWpT3+dsa7sD +cSm1SNhJuKwC74nYGfH0y4FNsPW3cpiZAgMBAAGgazBpBgkqhkiG9w0BCQ4xXDBa +MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +KQYDVR0RBCIwIIIMZXhlbXBsYXIuY29tghB3d3cuZXhlbXBsYXIuY29tMA0GCSqG +SIb3DQEBCwUAA4IBAQAuqwxmBklZ86ypTkchZJmUsyF8y/THqncFDW8RGWIB3Usi +tK9qb8EE92MoWboo4m4bcX74y+eUo3xBev6ZZwdScy8OHLhA/MMI8EElpeYt+Hc2 +2gvIs7WNemo3cCTpOtwvROWYpzxMp/z2/Zui9D57oTFcTdBjlJPyU5K4bCz+nNGV +81ifHK1xAUECfJp1IR7hFv2c2JbkwwD3KSCsyqc+/xtQLbrEPGWF1R0Gp9N1hxKv +WsDOAOH6qKQKQg3BO/xmRoohC6GL4CuhP7HYGi7+wziNhNZQa4GtE/k9DyIXVtJy +yuf2PnfXCKnaIWRJNoEqDCZRVMfA5BFSwTPITqyo +-----END CERTIFICATE REQUEST-----`, + 'Certificate': "rejected" + }, + { + 'ID': 3, + 'CSR': `-----BEGIN CERTIFICATE REQUEST----- +MIIC5zCCAc8CAQAwRzEWMBQGA1UEAwwNMTAuMTUyLjE4My41MzEtMCsGA1UELQwk +MzlhY2UxOTUtZGM1YS00MzJiLTgwOTAtYWZlNmFiNGI0OWNmMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjM5Wz+HRtDveRzeDkEDM4ornIaefe8d8nmFi +pUat9qCU3U9798FR460DHjCLGxFxxmoRitzHtaR4ew5H036HlGB20yas/CMDgSUI +69DyAsyPwEJqOWBGO1LL50qXdl5/jOkO2voA9j5UsD1CtWSklyhbNhWMpYqj2ObW +XcaYj9Gx/TwYhw8xsJ/QRWyCrvjjVzH8+4frfDhBVOyywN7sq+I3WwCbyBBcN8uO +yae0b/q5+UJUiqgpeOAh/4Y7qI3YarMj4cm7dwmiCVjedUwh65zVyHtQUfLd8nFW +Kl9775mNBc1yicvKDU3ZB5hZ1MZtpbMBwaA1yMSErs/fh5KaXwIDAQABoFswWQYJ +KoZIhvcNAQkOMUwwSjBIBgNVHREEQTA/hwQKmLc1gjd2YXVsdC1rOHMtMC52YXVs +dC1rOHMtZW5kcG9pbnRzLnZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsMA0GCSqGSIb3 +DQEBCwUAA4IBAQCJt8oVDbiuCsik4N5AOJIT7jKsMb+j0mizwjahKMoCHdx+zv0V +FGkhlf0VWPAdEu3gHdJfduX88WwzJ2wBBUK38UuprAyvfaZfaYUgFJQNC6DH1fIa +uHYEhvNJBdFJHaBvW7lrSFi57fTA9IEPrB3m/XN3r2F4eoHnaJJqHZmMwqVHck87 +cAQXk3fvTWuikHiCHqqdSdjDYj/8cyiwCrQWpV245VSbOE0WesWoEnSdFXVUfE1+ +RSKeTRuuJMcdGqBkDnDI22myj0bjt7q8eqBIjTiLQLnAFnQYpcCrhc8dKU9IJlv1 +H9Hay4ZO9LRew3pEtlx2WrExw/gpUcWM8rTI +-----END CERTIFICATE REQUEST-----`, + 'Certificate': `-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgIURKr+jf7hj60SyAryIeN++9wDdtkwDQYJKoZIhvcNAQEL +BQAwOTELMAkGA1UEBhMCVVMxKjAoBgNVBAMMIXNlbGYtc2lnbmVkLWNlcnRpZmlj +YXRlcy1vcGVyYXRvcjAeFw0yNDAzMjcxMjQ4MDRaFw0yNTAzMjcxMjQ4MDRaMEcx +FjAUBgNVBAMMDTEwLjE1Mi4xODMuNTMxLTArBgNVBC0MJDM5YWNlMTk1LWRjNWEt +NDMyYi04MDkwLWFmZTZhYjRiNDljZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAIzOVs/h0bQ73kc3g5BAzOKK5yGnn3vHfJ5hYqVGrfaglN1Pe/fBUeOt +Ax4wixsRccZqEYrcx7WkeHsOR9N+h5RgdtMmrPwjA4ElCOvQ8gLMj8BCajlgRjtS +y+dKl3Zef4zpDtr6APY+VLA9QrVkpJcoWzYVjKWKo9jm1l3GmI/Rsf08GIcPMbCf +0EVsgq7441cx/PuH63w4QVTsssDe7KviN1sAm8gQXDfLjsmntG/6uflCVIqoKXjg +If+GO6iN2GqzI+HJu3cJoglY3nVMIeuc1ch7UFHy3fJxVipfe++ZjQXNconLyg1N +2QeYWdTGbaWzAcGgNcjEhK7P34eSml8CAwEAAaOBnTCBmjAhBgNVHSMEGjAYgBYE +FN/vgl9cAapV7hH9lEyM7qYS958aMB0GA1UdDgQWBBRJJDZkHr64VqTC24DPQVld +Ba3iPDAMBgNVHRMBAf8EAjAAMEgGA1UdEQRBMD+CN3ZhdWx0LWs4cy0wLnZhdWx0 +LWs4cy1lbmRwb2ludHMudmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWyHBAqYtzUwDQYJ +KoZIhvcNAQELBQADggEBAEH9NTwDiSsoQt/QXkWPMBrB830K0dlwKl5WBNgVxFP+ +hSfQ86xN77jNSp2VxOksgzF9J9u/ubAXvSFsou4xdP8MevBXoFJXeqMERq5RW3gc +WyhXkzguv3dwH+n43GJFP6MQ+n9W/nPZCUQ0Iy7ueAvj0HFhGyZzAE2wxNFZdvCs +gCX3nqYpp70oZIFDrhmYwE5ij5KXlHD4/1IOfNUKCDmQDgGPLI1tVtwQLjeRq7Hg +XVelpl/LXTQawmJyvDaVT/Q9P+WqoDiMjrqF6Sy7DzNeeccWVqvqX5TVS6Ky56iS +Mvo/+PAJHkBciR5Xn+Wg2a+7vrZvT6CBoRSOTozlLSM= +-----END CERTIFICATE-----` + }, +] + +test('CertificateRequestsPage', () => { + render(< CertificateRequestsTable csrs={rows} />) + expect(screen.getByRole('table', {})).toBeDefined() + expect(screen.getByText('example.com')).toBeDefined() // Common Name of one of the CSR's +}) \ No newline at end of file diff --git a/ui/src/app/certificate_requests/table.tsx b/ui/src/app/certificate_requests/table.tsx index 51142d4..e7fe679 100644 --- a/ui/src/app/certificate_requests/table.tsx +++ b/ui/src/app/certificate_requests/table.tsx @@ -1,16 +1,30 @@ -import { useContext, useState } from "react" +import { useContext, useState, Dispatch, SetStateAction } from "react" import { AsideContext } from "../nav" import Row from "./row" +import { CSREntry } from "./types" -type CSREntry = { - id: number, - csr: string, - certificate: string +function EmptyState({ asideOpen, setAsideOpen }: { asideOpen: boolean, setAsideOpen: Dispatch> }) { + return ( + +
+
+
+

No CSRs available yet.

+ +
+
+
+ + ) +} + +type TableProps = { + csrs: CSREntry[] } function sortByCSRStatus(a: CSREntry, b: CSREntry) { - const aCSRStatus = a.certificate == "" ? "outstanding" : (a.certificate == "rejected" ? "rejected" : "fulfilled") - const bCSRStatus = b.certificate == "" ? "outstanding" : (b.certificate == "rejected" ? "rejected" : "fulfilled") + const aCSRStatus = a.Certificate == "" ? "outstanding" : (a.Certificate == "rejected" ? "rejected" : "fulfilled") + const bCSRStatus = b.Certificate == "" ? "outstanding" : (b.Certificate == "rejected" ? "rejected" : "fulfilled") if (aCSRStatus < bCSRStatus) { return -1; } else if (aCSRStatus > bCSRStatus) { @@ -21,8 +35,8 @@ function sortByCSRStatus(a: CSREntry, b: CSREntry) { } function sortByCertStatus(a: CSREntry, b: CSREntry) { - const aCertStatus = (a.certificate == "" ? "" : (a.certificate == "rejected" ? "" : "date")) - const bCertStatus = (b.certificate == "" ? "" : (b.certificate == "rejected" ? "" : "date")) + const aCertStatus = (a.Certificate == "" ? "" : (a.Certificate == "rejected" ? "" : "date")) + const bCertStatus = (b.Certificate == "" ? "" : (b.Certificate == "rejected" ? "" : "date")) if (aCertStatus < bCertStatus) { return -1; } else if (aCertStatus > bCertStatus) { @@ -32,26 +46,18 @@ function sortByCertStatus(a: CSREntry, b: CSREntry) { } } -export function CertificateRequestsTable() { +export function CertificateRequestsTable({ csrs: rows }: TableProps) { const { isOpen: isAsideOpen, setIsOpen: setAsideIsOpen } = useContext(AsideContext) const [actionsMenuExpanded, setActionsMenuExpanded] = useState(0) const [sortedColumn, setSortedColumn] = useState('none') const [sortDescending, setSortDescending] = useState(true) - const rows = [ - {'id':1, 'csr':"csr1",'certificate':""}, - {'id':2, 'csr':"csr2",'certificate':"rejected"}, - {'id':3, 'csr':"csr3",'certificate':"a real cert"}, - {'id':4, 'csr':"csr3",'certificate':"a real cert"}, - {'id':5, 'csr':"csr3",'certificate':"a real cert"}, - {'id':6, 'csr':"csr3",'certificate':"a real cert"}, - ] const sortedRows = () => { switch (sortedColumn) { case "csr": - return (sortDescending? rows.sort(sortByCSRStatus).reverse() : rows.sort(sortByCSRStatus)) + return (sortDescending ? rows.sort(sortByCSRStatus).reverse() : rows.sort(sortByCSRStatus)) case "cert": - return (sortDescending? rows.sort(sortByCertStatus).reverse() : rows.sort(sortByCertStatus)) + return (sortDescending ? rows.sort(sortByCertStatus).reverse() : rows.sort(sortByCertStatus)) default: return rows } @@ -61,7 +67,7 @@ export function CertificateRequestsTable() {

Certificate Requests

- + {rows.length > 0 && }
@@ -71,8 +77,8 @@ export function CertificateRequestsTable() { ID Details - {setSortedColumn('csr');setSortDescending(!sortDescending)}}>CSR Status - {setSortedColumn('cert');setSortDescending(!sortDescending)}}>Certificate Expiry Date + { setSortedColumn('csr'); setSortDescending(!sortDescending) }}>CSR Status + { setSortedColumn('cert'); setSortDescending(!sortDescending) }}>Certificate Expiry Date Actions @@ -80,10 +86,11 @@ export function CertificateRequestsTable() { { sortedRows().map((row) => ( - + ) - )} + )} + {rows.length == 0 && }
diff --git a/ui/src/app/certificate_requests/types.ts b/ui/src/app/certificate_requests/types.ts new file mode 100644 index 0000000..8f2704f --- /dev/null +++ b/ui/src/app/certificate_requests/types.ts @@ -0,0 +1,5 @@ +export type CSREntry = { + ID: number, + CSR: string, + Certificate: string +} \ No newline at end of file diff --git a/ui/src/app/nav.test.tsx b/ui/src/app/nav.test.tsx index 08bb5de..fa0fd1c 100644 --- a/ui/src/app/nav.test.tsx +++ b/ui/src/app/nav.test.tsx @@ -1,11 +1,11 @@ import { expect, describe, it } from "vitest"; -import {render, fireEvent, screen} from '@testing-library/react' +import { render, fireEvent, screen } from '@testing-library/react' import Navigation from "./nav"; import { CertificateRequestsTable } from "./certificate_requests/table"; describe('Navigation', () => { it('should open aside when clicking button', () => { - render() + render() const addCSRButton = screen.getByLabelText(/add-csr-button/i) expect(screen.getByLabelText(/aside-panel/i).className.endsWith('is-collapsed')).toBe(true) fireEvent.click(addCSRButton) diff --git a/ui/src/app/nav.tsx b/ui/src/app/nav.tsx index d905608..82bf2d7 100644 --- a/ui/src/app/nav.tsx +++ b/ui/src/app/nav.tsx @@ -1,6 +1,7 @@ "use client" -import { SetStateAction, Dispatch, useState, createContext, useEffect , ChangeEvent} from "react" +import { SetStateAction, Dispatch, useState, createContext, useEffect, ChangeEvent } from "react" +import { QueryClient, QueryClientProvider } from "react-query"; import Image from "next/image"; type AsideContextType = { @@ -24,10 +25,10 @@ export function Aside({ isOpen, setIsOpen }: { isOpen: boolean, setIsOpen: Dispa setCSRPEMString(e.target.result.toString()); } } - }; - reader.readAsText(file); + }; + reader.readAsText(file); } - }; + }; return (