mirror of
https://github.com/go-sigma/sigma.git
synced 2026-10-28 14:34:34 +08:00
refactor(web): replace @headlessui/react with shadcn components
Swap every headlessui usage for the shadcn/base-ui components that are
already bundled in the app, then drop the dependency:
- menus (8): Menu/MenuButton/MenuItems/MenuItem + Transition -> DropdownMenu;
role checks that guarded the original clicks are preserved as
DropdownMenuItem `disabled`
- dialogs (14): Dialog/DialogPanel/DialogTitle + Transition* -> Dialog,
DialogContent, DialogTitle, DialogDescription, DialogFooter and Button
- listboxes (6): Listbox -> Select; the multiple-selection one becomes a
DropdownMenu with DropdownMenuCheckboxItem
- comboboxes (9): Combobox -> Popover + Command, wiring the existing search
state into CommandInput and keeping shouldFilter={false} for server-side
search
- remove @headlessui/react from package.json/bun.lock
Also included:
- drop imports that became unused (Table/Button in Builder/RunnerList,
Separator, useSidebar, dayjs, Link/useParams, Check/ChevronsUpDown and the
leftover shadcn imports in the webhook pages)
- replace Tailwind v4 arbitrary sizes that have spacing-scale equivalents
(w-[350px] -> w-87.5, h-[36px] -> h-9, after:top-[2px] -> after:top-0.5, ...);
h-[18.4px] is intentionally kept since 4.6 is not a multiple of 0.25
- fix the webhook log delete-confirm label (was "Resend")
- add oxlint with `lint` and `lint:fix` scripts
Verification: web: `tsc --noEmit`; `vite build`; `oxlint` (0 errors,
72 pre-existing warnings); scripted scan reports 0 unused imports and no
remaining @headlessui imports.
This commit is contained in:
1 parent
959b2f2ee6
commit
3ca56e7b67
24 files changed
+2214
-3438
No files matched your search
+41
-29
@@ -9,7 +9,6 @@
|
||||
"@bytemd/plugin-gfm": "^1.22.0",
|
||||
"@bytemd/react": "^1.22.0",
|
||||
"@fontsource-variable/geist": "^5.3.0",
|
||||
"@headlessui/react": "^2.2.10",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@shikijs/core": "^4.4.3",
|
||||
"@shikijs/engine-javascript": "^4.4.3",
|
||||
@@ -52,6 +51,7 @@
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"autoprefixer": "^10.5.6",
|
||||
"esbuild": "^0.28.1",
|
||||
"oxlint": "^1.83.0",
|
||||
"postcss": "^8.5.28",
|
||||
"postcss-import": "^16.2.0",
|
||||
"postcss-nesting": "^13.0.2",
|
||||
@@ -197,26 +197,16 @@
|
||||
|
||||
"@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="],
|
||||
|
||||
"@floating-ui/react": ["@floating-ui/react@0.26.28", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw=="],
|
||||
|
||||
"@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg=="],
|
||||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="],
|
||||
|
||||
"@fontsource-variable/geist": ["@fontsource-variable/geist@5.3.0", "", {}, "sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA=="],
|
||||
|
||||
"@headlessui/react": ["@headlessui/react@2.2.10", "", { "dependencies": { "@floating-ui/react": "^0.26.16", "@react-aria/focus": "^3.20.2", "@react-aria/interactions": "^3.25.0", "@tanstack/react-virtual": "^3.13.9", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-5pVLNK9wlpxTUTy9GpgbX/SdcRh+HBnPktjM2wbiLTH4p+2EPHBO1aoSryUCuKUIItdDWO9ITlhUL8UnUN/oIA=="],
|
||||
|
||||
"@heroicons/react": ["@heroicons/react@2.2.0", "", { "peerDependencies": { "react": ">= 16 || ^19.0.0-rc" } }, "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@2.1.1", "", { "peerDependencies": { "hono": "^4" } }, "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg=="],
|
||||
|
||||
"@internationalized/date": ["@internationalized/date@3.12.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ=="],
|
||||
|
||||
"@internationalized/number": ["@internationalized/number@3.6.8", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-8UmMFia46DUt+k97zKd9fKWXcWHR+k8ae3eYzILETuT2KbIvLyOfac7zesw+sJdRAAZ7Q9pM1Mk22aXp2LD0Ig=="],
|
||||
|
||||
"@internationalized/string": ["@internationalized/string@3.2.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
@@ -237,6 +227,44 @@
|
||||
|
||||
"@oxc-project/types": ["@oxc-project/types@0.149.0", "", {}, "sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA=="],
|
||||
|
||||
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.83.0", "", { "os": "android", "cpu": "arm" }, "sha512-0yGY24EwsLk5YDe6F+VkmZyRHSwJDALa3nIrPpq7FXmp2lV2d0TzvBCGeZk+wgiULRGr5blhyr4QMp5KCXJUqA=="],
|
||||
|
||||
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.83.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hHfJ0vc17A4iUjH5p9BsTUPYbYRNxGpvD2lbu1aBRk54bzNIx9o5TtYF39QPZcV95DagZd+4DEAw2RH3G2ZsMg=="],
|
||||
|
||||
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.83.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hsOjYjszLb/3zym/TkzUMPAoQlTJcuzSyEPOAyA+skXJIX9M0o+4JfOtqopX/Vf4hSLrJ98j0nvFo23gzk8auQ=="],
|
||||
|
||||
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.83.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-mjh5oH2EA+wl5yRJYT9K9G61O2zFlpuv+yf2JwZOi0+dq2FnTUtm1h8i+5Ik0fXPWIu/k84I1psZR9aQsLAnyA=="],
|
||||
|
||||
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.83.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fNHr64/YaO8YssuoDVC8+F4Uk5enR86q5uxfHkQrjAPs1dbAILOrD2uaud+J7MO8Fx774g44ERLD0IGIvZE48w=="],
|
||||
|
||||
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.83.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Qpwy3zzAwMj+8/lyYItHmkSMwbkprFNWTK7jPYDOxSyxEhaSLOWYUTCMkjF334J8/WD0nznCCsoBbIH6hpsuIw=="],
|
||||
|
||||
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.83.0", "", { "os": "linux", "cpu": "arm" }, "sha512-s+BirYLFq7JL2k9sP0XI3ZXJ9dYvJ8sX3jLCLoag7tt+zrSHpZxP0jqznfL+Gdgwu7ay0dYgGYJXrQvq3iWloA=="],
|
||||
|
||||
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.83.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-7lihXt3vKr+GIyapNbHrnFHm/biiW30le6Zv/DExbAFPF6YwCQXVFlONPFehxs0CpGO4CBfYPM9rdDT+XMoIlg=="],
|
||||
|
||||
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.83.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-q63JalLYVkZiZvls1z3PPUnpmQluOMXp0khqQMznCeAPLGydfNY8JhvuA4WlK57JfrvikU8wB5lPVveqpIXvew=="],
|
||||
|
||||
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.83.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-krQmDF+dRbxvdqVPV88ZuOoPPu8X5BuqDA8Hd+qcS4YMRQCb+nexA57DazgGsc/rGdKBe3QmV0mnv0bdpW/p5g=="],
|
||||
|
||||
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.83.0", "", { "os": "linux", "cpu": "none" }, "sha512-MmOl8Y6txEAXZU1RG8Rr264jQ6D7VPmqFsU/45x/FeWsGe32hklTqGrLE6UxHzp5Rjt0wP+20tY8YXKgSFB3mw=="],
|
||||
|
||||
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.83.0", "", { "os": "linux", "cpu": "none" }, "sha512-u1rMymh0W3JZkq370kzQsYPULGWqhE09pZRqnZvUSoYaI9pVO5yVX+iYIslmWuEgwuzH9YAaOsScJiobWCHoOw=="],
|
||||
|
||||
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.83.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-y0zK3HNwGysu7rqtE+BQG/d0bx5gh/KwlOtghN8oWeK1KcWzeaLqtZrbm8owqdma1lFyrce/hTO5ismuNu+INQ=="],
|
||||
|
||||
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.83.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rS5gM0NgD7ngmuJmbIehsidtrOwKkLFwCQbKEeb9KuyQrrWNq5Zkn0uV6AYdXOMJ0grrWEiLwBuvMxt8w5vsNw=="],
|
||||
|
||||
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.83.0", "", { "os": "linux", "cpu": "x64" }, "sha512-W2IH4EtpcPaWcvNGCA95YoDg4vxqE/ZiPCi3arrxEEpsK7+JQN9WYwrlYFx9pcdP6KPXqRqkv3zdQPHcx7b6YQ=="],
|
||||
|
||||
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.83.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6LyKkUyoajssTPLlZmDbZIbu4IZ5B4bGuRUnBgCGpEvHP3FQMaYITncHA/unPUo7q+Z+pIu2HhdkQ+8d1SG7iA=="],
|
||||
|
||||
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.83.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Uz/fObEtF0jmNJQJ8CGRBKfefYstS0/wjD3s6IGzP8nUwsJykHQJBiN3npHwKiGRGn/vvBEgNr4B3cCzmmatvg=="],
|
||||
|
||||
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.83.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-u7XcvPW6Bk58tY5iWs2ESb0vJjoE/kuSpHxopbwp/p3ZtWVQXZ6wor5w3ssVTHOqd/v8b+QdhSFWQ4grEUNWpA=="],
|
||||
|
||||
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.83.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LZRubd7ph13QmAg4fFecTYVZkiYbROR2Htaxh/ufWRkDhPOm2wrwaEYR89e0YpPFD3dqBrPoxS7myBw5hmYA7Q=="],
|
||||
|
||||
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
|
||||
|
||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="],
|
||||
@@ -271,12 +299,6 @@
|
||||
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="],
|
||||
|
||||
"@react-aria/focus": ["@react-aria/focus@3.22.1", "", { "dependencies": { "@swc/helpers": "^0.5.0", "react-aria": "^3.48.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-CPxtkyrBi/HYY5P3lE/57sQ6qfa0lN8E55TOm89H0kNGv0lKt+/0zP7lWERzBjRr5IxBVrQX4gFEowBN52LPaA=="],
|
||||
|
||||
"@react-aria/interactions": ["@react-aria/interactions@3.28.1", "", { "dependencies": { "@react-types/shared": "^3.34.0", "@swc/helpers": "^0.5.0", "react-aria": "^3.48.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-Bqb+HrD5I5MHS2SKBhISYqo2SW8Y2dfzgF/Y1lIJq7xqLxheo9vzxPGEHhz+XzkgGfoqEJx8A6a3C7uiqS3HWA=="],
|
||||
|
||||
"@react-types/shared": ["@react-types/shared@3.36.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag=="],
|
||||
|
||||
"@remix-run/router": ["@remix-run/router@1.23.4", "", {}, "sha512-q7j5geK7xs3UJSdm9/iytUNclBnLmYx1EnSeCFXHPeutdqgIMeFeHtUZgS3EhlKxdBEAu8OwtJCwmLrEzpSs7Q=="],
|
||||
|
||||
"@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.8", "", { "os": "android", "cpu": "arm" }, "sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw=="],
|
||||
@@ -329,8 +351,6 @@
|
||||
|
||||
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="],
|
||||
@@ -365,10 +385,6 @@
|
||||
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.102.8", "", { "dependencies": { "@tanstack/query-core": "5.102.8" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A=="],
|
||||
|
||||
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.14.12", "", { "dependencies": { "@tanstack/virtual-core": "3.17.10" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-EOJkBss/FRqzFrCpX8M96lIh+ZMc0jPt0LILhxWQ3tOHQ3XdVvuE0jceS2yZXpDtayNfpjeg2EnuYPm90CxIlA=="],
|
||||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.17.10", "", {}, "sha512-eMorKWIi2nekElu+8DgSMJR4iR+sTDeh+W8VjKiMH79yUfEDJ3W9aL/96/wwbxNjyV6EfN39HKszSxjd90zAqQ=="],
|
||||
|
||||
"@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="],
|
||||
|
||||
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||
@@ -1025,6 +1041,8 @@
|
||||
|
||||
"ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="],
|
||||
|
||||
"oxlint": ["oxlint@1.83.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.83.0", "@oxlint/binding-android-arm64": "1.83.0", "@oxlint/binding-darwin-arm64": "1.83.0", "@oxlint/binding-darwin-x64": "1.83.0", "@oxlint/binding-freebsd-x64": "1.83.0", "@oxlint/binding-linux-arm-gnueabihf": "1.83.0", "@oxlint/binding-linux-arm-musleabihf": "1.83.0", "@oxlint/binding-linux-arm64-gnu": "1.83.0", "@oxlint/binding-linux-arm64-musl": "1.83.0", "@oxlint/binding-linux-ppc64-gnu": "1.83.0", "@oxlint/binding-linux-riscv64-gnu": "1.83.0", "@oxlint/binding-linux-riscv64-musl": "1.83.0", "@oxlint/binding-linux-s390x-gnu": "1.83.0", "@oxlint/binding-linux-x64-gnu": "1.83.0", "@oxlint/binding-linux-x64-musl": "1.83.0", "@oxlint/binding-openharmony-arm64": "1.83.0", "@oxlint/binding-win32-arm64-msvc": "1.83.0", "@oxlint/binding-win32-ia32-msvc": "1.83.0", "@oxlint/binding-win32-x64-msvc": "1.83.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2001", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-cyDzSzaw3uzP0TeCeq3lLRPPoaUxkbB4ZOXj+kn+5r+BX9V+4bNVGk9lxer+WrgcpebH4JxLlJ3KQjveVztOLQ=="],
|
||||
|
||||
"p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||
|
||||
"p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
|
||||
@@ -1091,8 +1109,6 @@
|
||||
|
||||
"react": ["react@19.3.0", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="],
|
||||
|
||||
"react-aria": ["react-aria@3.52.1", "", { "dependencies": { "@internationalized/date": "^3.12.4", "@internationalized/number": "^3.6.8", "@internationalized/string": "^3.2.10", "@react-types/shared": "^3.36.1", "@swc/helpers": "^0.5.0", "aria-hidden": "^1.2.3", "clsx": "^2.0.0", "react-stately": "3.50.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-fdZZruC9/x/joCg0mhKGs5aHpwrXLCSZ4GOJmhhYyiE0ffyEsk9MLFt9LCOCF9tn8ErTD+UDQP4oDUSlZkmpCg=="],
|
||||
|
||||
"react-dom": ["react-dom@19.3.0", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.0" } }, "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q=="],
|
||||
|
||||
"react-fast-compare": ["react-fast-compare@3.2.2", "", {}, "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="],
|
||||
@@ -1111,8 +1127,6 @@
|
||||
|
||||
"react-router-dom": ["react-router-dom@6.30.6", "", { "dependencies": { "@remix-run/router": "1.23.4", "react-router": "6.30.6" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-0RHKZz7wwffvkU+2MFVT2NnjK44ssLEV+m0CAJaS2Ksmorrwj7WxH00jO0SOCW26/tINUnJHToXblDs33I38YQ=="],
|
||||
|
||||
"react-stately": ["react-stately@3.50.0", "", { "dependencies": { "@internationalized/date": "^3.12.4", "@internationalized/number": "^3.6.8", "@internationalized/string": "^3.2.10", "@react-types/shared": "^3.36.1", "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-TnckvpDQGU0672wEaLZqdzvhuSeXWjgW6vijMWxiKOxc8CosQUfPGISdcZyfHqjX3XMjEtRxj4w9HumvX4h4mw=="],
|
||||
|
||||
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||
|
||||
"react-universal-interface": ["react-universal-interface@0.6.2", "", { "peerDependencies": { "react": "*", "tslib": "*" } }, "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw=="],
|
||||
@@ -1247,8 +1261,6 @@
|
||||
|
||||
"systeminformation": ["systeminformation@5.33.10", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-/NXbMVASt2UbSVgmWto4aBTIAeuYY7zOELpZybmNxqpsrbo5uYHpuEPf5nkyrPng0uG5YOb+Yv6s0FyKY4mDQg=="],
|
||||
|
||||
"tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="],
|
||||
|
||||
+4
-2
@@ -6,14 +6,15 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.8.0",
|
||||
"@bytemd/plugin-gfm": "^1.22.0",
|
||||
"@bytemd/react": "^1.22.0",
|
||||
"@fontsource-variable/geist": "^5.3.0",
|
||||
"@headlessui/react": "^2.2.10",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@shikijs/core": "^4.4.3",
|
||||
"@shikijs/engine-javascript": "^4.4.3",
|
||||
@@ -56,6 +57,7 @@
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"autoprefixer": "^10.5.6",
|
||||
"esbuild": "^0.28.1",
|
||||
"oxlint": "^1.83.0",
|
||||
"postcss": "^8.5.28",
|
||||
"postcss-import": "^16.2.0",
|
||||
"postcss-nesting": "^13.0.2",
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
export default function Header({ title, breadcrumb, props }: { title: ReactNode, breadcrumb?: ReactNode, props?: ReactNode }) {
|
||||
return (
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarProvider,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
||||
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -133,7 +133,7 @@ function DropdownMenuSubContent({
|
||||
return (
|
||||
<DropdownMenuContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
||||
className={cn("w-auto min-w-24 rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
|
||||
@@ -16,7 +16,7 @@ function ScrollArea({
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
data-slot="scroll-area-viewport"
|
||||
className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
|
||||
className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
|
||||
@@ -289,7 +289,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
||||
onClick={toggleSidebar}
|
||||
title="Toggle Sidebar"
|
||||
className={cn(
|
||||
"absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2",
|
||||
"absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-0.5 hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2",
|
||||
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
||||
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
||||
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar",
|
||||
|
||||
@@ -16,7 +16,7 @@ function Switch({
|
||||
data-slot="switch"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
||||
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -22,7 +22,7 @@ function Tabs({
|
||||
}
|
||||
|
||||
const tabsListVariants = cva(
|
||||
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-9 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
|
||||
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-0.75 text-muted-foreground group-data-horizontal/tabs:h-9 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -56,7 +56,7 @@ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
||||
<TabsPrimitive.Tab
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
||||
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
|
||||
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
||||
|
||||
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const toggleVariants = cva(
|
||||
"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
import axios from "axios";
|
||||
import dayjs from "dayjs";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Dialog, DialogContent, DialogFooter } from "@/components/ui/dialog";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Helmet, HelmetProvider } from "react-helmet-async";
|
||||
import { Link, useParams, useSearchParams } from "react-router-dom";
|
||||
import { Tooltip } from '../../utils';
|
||||
@@ -322,171 +321,133 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
</div>
|
||||
</div >
|
||||
|
||||
<Transition.Root show={createRunnerModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setCreateRunnerModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
|
||||
<label htmlFor="tagTemplate" className="block text-sm font-medium leading-6 text-gray-900">
|
||||
<span className="text-red-600">*</span>Tag
|
||||
</label>
|
||||
<div className="relative mt-2 rounded-md shadow-sm">
|
||||
<input
|
||||
type="text"
|
||||
id="tagTemplate"
|
||||
name="tagTemplate"
|
||||
placeholder="tag template"
|
||||
className={(tagTemplateTextValid ? "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" : "block w-full rounded-md border-0 py-1.5 pr-10 text-red-900 ring-1 ring-inset ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500 sm:text-sm sm:leading-6")}
|
||||
value={tagTemplateText}
|
||||
onChange={e => {
|
||||
setTagTemplateText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{
|
||||
tagTemplateTextValid ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5 text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{
|
||||
tagTemplateTextValid ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<span>
|
||||
Not a valid tag template, you can try 'main', '{{.ScmRef}}', '{{.ScmBrach}}'.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
{
|
||||
builderObj?.source !== "Dockerfile" ? (
|
||||
<>
|
||||
<label htmlFor="first-name" className="block text-sm font-medium leading-6 text-gray-900">
|
||||
<span className="text-red-600">*</span>Branch
|
||||
</label>
|
||||
<div className="relative mt-2 rounded-md shadow-sm">
|
||||
<input
|
||||
type="text"
|
||||
name="namespace"
|
||||
placeholder="1-64 characters"
|
||||
className={(branchTextValid ? "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" : "block w-full rounded-md border-0 py-1.5 pr-10 text-red-900 ring-1 ring-inset ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500 sm:text-sm sm:leading-6")}
|
||||
value={branchText}
|
||||
onChange={e => {
|
||||
setBranchText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{
|
||||
branchTextValid ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5 text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{
|
||||
branchTextValid ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<span>
|
||||
Not a valid branch, you can try 'main', 'master', 'dev'.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
<label htmlFor="first-name" className="block text-sm font-medium text-gray-700">
|
||||
Description
|
||||
</label>
|
||||
<div className="relative mt-2 rounded-md shadow-sm">
|
||||
<textarea
|
||||
name="description"
|
||||
placeholder="30 characters"
|
||||
className={(descriptionTextValid ? "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" : "block w-full rounded-md border-0 py-1.5 pr-10 text-red-900 ring-1 ring-inset ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500 sm:text-sm sm:leading-6")}
|
||||
value={descriptionText}
|
||||
onChange={e => setDescriptionText(e.target.value)}
|
||||
/>
|
||||
{
|
||||
descriptionTextValid ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5 text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{
|
||||
descriptionTextValid ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<span>
|
||||
Not a valid description, max 50 characters.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-500 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:bg-indigo-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
onClick={() => createRunner()}
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
|
||||
onClick={() => setCreateRunnerModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
<Dialog open={createRunnerModal} onOpenChange={setCreateRunnerModal}>
|
||||
<DialogContent className="sm:max-w-lg" aria-label="Create runner">
|
||||
<label htmlFor="tagTemplate" className="block text-sm font-medium leading-6 text-gray-900">
|
||||
<span className="text-red-600">*</span>Tag
|
||||
</label>
|
||||
<div className="relative mt-2 rounded-md shadow-sm">
|
||||
<input
|
||||
type="text"
|
||||
id="tagTemplate"
|
||||
name="tagTemplate"
|
||||
placeholder="tag template"
|
||||
className={(tagTemplateTextValid ? "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" : "block w-full rounded-md border-0 py-1.5 pr-10 text-red-900 ring-1 ring-inset ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500 sm:text-sm sm:leading-6")}
|
||||
value={tagTemplateText}
|
||||
onChange={e => {
|
||||
setTagTemplateText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{
|
||||
tagTemplateTextValid ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5 text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{
|
||||
tagTemplateTextValid ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<span>
|
||||
Not a valid tag template, you can try 'main', '{{.ScmRef}}', '{{.ScmBrach}}'.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
{
|
||||
builderObj?.source !== "Dockerfile" ? (
|
||||
<>
|
||||
<label htmlFor="first-name" className="block text-sm font-medium leading-6 text-gray-900">
|
||||
<span className="text-red-600">*</span>Branch
|
||||
</label>
|
||||
<div className="relative mt-2 rounded-md shadow-sm">
|
||||
<input
|
||||
type="text"
|
||||
name="namespace"
|
||||
placeholder="1-64 characters"
|
||||
className={(branchTextValid ? "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" : "block w-full rounded-md border-0 py-1.5 pr-10 text-red-900 ring-1 ring-inset ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500 sm:text-sm sm:leading-6")}
|
||||
value={branchText}
|
||||
onChange={e => {
|
||||
setBranchText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{
|
||||
branchTextValid ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5 text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{
|
||||
branchTextValid ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<span>
|
||||
Not a valid branch, you can try 'main', 'master', 'dev'.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
<label htmlFor="first-name" className="block text-sm font-medium text-gray-700">
|
||||
Description
|
||||
</label>
|
||||
<div className="relative mt-2 rounded-md shadow-sm">
|
||||
<textarea
|
||||
name="description"
|
||||
placeholder="30 characters"
|
||||
className={(descriptionTextValid ? "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" : "block w-full rounded-md border-0 py-1.5 pr-10 text-red-900 ring-1 ring-inset ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500 sm:text-sm sm:leading-6")}
|
||||
value={descriptionText}
|
||||
onChange={e => setDescriptionText(e.target.value)}
|
||||
/>
|
||||
{
|
||||
descriptionTextValid ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5 text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{
|
||||
descriptionTextValid ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<span>
|
||||
Not a valid description, max 50 characters.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setCreateRunnerModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => createRunner()}>Create</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
+473
-775
File diff suppressed because it is too large.
Load diff
@@ -22,7 +22,6 @@ import {
|
||||
ICodeRepositoryOwnerItem,
|
||||
ICodeRepositoryOwnerList,
|
||||
ICodeRepositoryUser3rdParty,
|
||||
IHTTPError,
|
||||
IOrder
|
||||
} from '../../interfaces';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
@@ -134,7 +133,7 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
<HelmetProvider>
|
||||
<Helmet><title>{t("common.codeRepository")}</title></Helmet>
|
||||
</HelmetProvider>
|
||||
<div className="min-h-screen flex overflow-hidden bg-background min-w-[1600px]">
|
||||
<div className="min-h-screen flex overflow-hidden bg-background min-w-400">
|
||||
<HeaderMenu localServer={localServer} item="coderepos" />
|
||||
<div className="flex flex-col flex-1 max-h-screen">
|
||||
<main className="relative focus:outline-none" tabIndex={0}>
|
||||
|
||||
@@ -115,7 +115,7 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
<title>{t("common.codeRepository")}</title>
|
||||
</Helmet>
|
||||
</HelmetProvider>
|
||||
<div className="min-h-screen flex overflow-hidden bg-white dark:bg-gray-950 min-w-[1600px]">
|
||||
<div className="min-h-screen flex overflow-hidden bg-white dark:bg-gray-950 min-w-400">
|
||||
<Menu localServer={localServer} item="coderepos" />
|
||||
<div className="flex flex-col flex-1 max-h-screen">
|
||||
<main className="relative z-0 focus:outline-none" tabIndex={0}>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import dayjs from 'dayjs';
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { Link, useLocation, useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
import Toast from 'react-hot-toast';
|
||||
import axios from "axios";
|
||||
import dayjs from 'dayjs';
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { Link, useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
+738
-1068
File diff suppressed because it is too large.
Load diff
+245
-457
@@ -19,13 +19,37 @@ import Toast from 'react-hot-toast';
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { Link, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { CheckIcon, ChevronUpDownIcon, EllipsisVerticalIcon } from "@heroicons/react/20/solid";
|
||||
import { ChevronUpDownIcon, EllipsisVerticalIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Combobox, ComboboxButton, ComboboxInput, ComboboxOption,
|
||||
ComboboxOptions, Dialog, DialogPanel, DialogTitle, Listbox,
|
||||
ListboxButton, ListboxOption, ListboxOptions, Menu, MenuItem,
|
||||
MenuItems, Transition, TransitionChild
|
||||
} from "@headlessui/react";
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
import Header from "../../components/Header";
|
||||
import IMenu from "../../components/Menu";
|
||||
@@ -200,7 +224,7 @@ export default function Member({ localServer }: { localServer: string }) {
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div className="pt-1 pb-1 flex justify-between items-center min-h-[60px]">
|
||||
<div className="pt-1 pb-1 flex justify-between items-center min-h-15">
|
||||
<div className="px-4">
|
||||
<div className="flex gap-4">
|
||||
<div className="relative mt-2 flex items-center">
|
||||
@@ -272,216 +296,107 @@ export default function Member({ localServer }: { localServer: string }) {
|
||||
<Pagination limit={Settings.PageSize} page={page} setPage={setPage} total={total} />
|
||||
</div>
|
||||
</div>
|
||||
<Transition show={createUserNamespaceModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setCreateUserNamespaceModal}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</TransitionChild>
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<DialogPanel className="relative transform rounded-lg bg-white px-6 pb-4 text-left shadow-xl transition-all">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900 border-b pt-4 pb-4"
|
||||
>
|
||||
Add member
|
||||
</DialogTitle>
|
||||
<div className="flex flex-col gap-0 mt-4">
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">User</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Combobox value={userSelected}
|
||||
onChange={(user: IUserItem | null) => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
setUserSelected(user);
|
||||
}}>
|
||||
<div className="relative mt-1">
|
||||
<div className="w-full relative cursor-default overflow-hidden rounded-lg bg-white text-left shadow focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm">
|
||||
<ComboboxInput
|
||||
id="namespace"
|
||||
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
|
||||
displayValue={(user: IUserItem) => user.username}
|
||||
onChange={event => {
|
||||
setUserSearch(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<ComboboxButton className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</ComboboxButton>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
afterLeave={() => setUserSearch('')}
|
||||
>
|
||||
<ComboboxOptions className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm z-10">
|
||||
{
|
||||
userList?.length === 0 ? (
|
||||
<div className="relative cursor-default select-none py-2 px-4 text-gray-700">
|
||||
Nothing found.
|
||||
</div>
|
||||
) : (
|
||||
userList?.map(user => (
|
||||
<ComboboxOption
|
||||
key={user.id}
|
||||
className={`relative cursor-pointer select-none`}
|
||||
value={user}
|
||||
>
|
||||
{({ focus, selected }) => (
|
||||
<span className={`block truncate font-normal py-2 px-4 ${focus ? 'bg-gray-200 text-gray-800' : 'text-gray-900'}`}
|
||||
>
|
||||
{user.username}
|
||||
</span>
|
||||
)}
|
||||
</ComboboxOption>
|
||||
))
|
||||
)
|
||||
}
|
||||
</ComboboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Combobox>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2"></div>
|
||||
<div className="col-span-4">
|
||||
{
|
||||
userSelectedValid ? null : (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
<span>
|
||||
Please select a user.
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-4 mt-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">Role</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Listbox
|
||||
value={addNamespaceRoleRole}
|
||||
onChange={(source: string) => {
|
||||
setAddNamespaceRoleRole(source);
|
||||
}}>
|
||||
<div className="relative w-full">
|
||||
<ListboxButton
|
||||
className={() => {
|
||||
let cursor = ''
|
||||
if ((searchParams.get('code_repository_stick') || '') === 'true') {
|
||||
cursor = 'cursor-not-allowed ';
|
||||
} else {
|
||||
cursor = 'cursor-pointer ';
|
||||
}
|
||||
return cursor + "relative w-full rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm"
|
||||
}}
|
||||
>
|
||||
<span className="block truncate">{addNamespaceRoleRole}</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
<ListboxOptions
|
||||
className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm z-10"
|
||||
>
|
||||
{
|
||||
namespaceRoles.map(source => (
|
||||
<ListboxOption key={source.name} value={source.name} className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-10 pr-4 ${active ? 'bg-gray-100 text-gray-900' : 'text-gray-900'
|
||||
}`
|
||||
}>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${selected ? 'font-medium' : 'font-normal'
|
||||
}`}
|
||||
>
|
||||
{source.name}
|
||||
</span>
|
||||
{
|
||||
selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-600">
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
) : null
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))
|
||||
}
|
||||
</ListboxOptions>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row-reverse mt-5">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-500 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:bg-indigo-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
onClick={e => addMember()}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
|
||||
onClick={e => { setCreateUserNamespaceModal(false) }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<Dialog open={createUserNamespaceModal} onOpenChange={setCreateUserNamespaceModal}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogTitle className="border-b pb-4">Add member</DialogTitle>
|
||||
<div className="flex flex-col gap-0 mt-4">
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">User</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button variant="outline" className="w-full justify-between font-normal">
|
||||
{userSelected?.username || "Select user"}
|
||||
<ChevronUpDownIcon className="size-4 text-muted-foreground" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<PopoverContent align="start" className="w-(--anchor-width) p-0">
|
||||
<Command shouldFilter={false}>
|
||||
<CommandInput
|
||||
placeholder="Search user"
|
||||
value={userSearch}
|
||||
onValueChange={setUserSearch}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty>Nothing found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{userList?.map(user => (
|
||||
<CommandItem
|
||||
key={user.id}
|
||||
value={user.username}
|
||||
onSelect={() => setUserSelected(user)}
|
||||
>
|
||||
{user.username}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2"></div>
|
||||
<div className="col-span-4">
|
||||
{
|
||||
userSelectedValid ? null : (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
<span>
|
||||
Please select a user.
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-4 mt-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">Role</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Select
|
||||
value={addNamespaceRoleRole}
|
||||
onValueChange={(source) => { if (source) setAddNamespaceRoleRole(source); }}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{namespaceRoles.map(source => (
|
||||
<SelectItem key={source.name} value={source.name}>
|
||||
{source.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setCreateUserNamespaceModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => addMember()}>Add</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Fragment >
|
||||
);
|
||||
}
|
||||
@@ -547,254 +462,127 @@ function TableItem({ localServer, index, namespace, userSelectedArg, member, set
|
||||
<td className="pr-3 whitespace-nowrap" onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<Menu as="div" className="relative flex-none" onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<Menu.Button className="mx-auto -m-2.5 block p-2.5 text-gray-500 hover:text-gray-900 margin">
|
||||
<span className="sr-only">Open options</span>
|
||||
<EllipsisVerticalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</Menu.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems className={(index > 10 ? "menu-action-top" : "mt-2") + " text-left absolute right-0 z-10 w-20 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none"} >
|
||||
<MenuItem>
|
||||
{({ active }) => (
|
||||
<div
|
||||
className={
|
||||
(active ? 'bg-gray-100' : '') +
|
||||
' block px-3 py-1 text-sm leading-6 text-gray-900 cursor-pointer'
|
||||
}
|
||||
onClick={e => setUpdateUserNamespaceModal(true)}
|
||||
>
|
||||
Update
|
||||
</div>
|
||||
)}
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{({ active }) => (
|
||||
<div
|
||||
className={
|
||||
(active ? 'bg-gray-50' : '') + ' block px-3 py-1 text-sm leading-6 text-gray-900 hover:text-white hover:bg-red-600 cursor-pointer'
|
||||
}
|
||||
onClick={e => deleteMember()}
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
)}
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="icon-sm" className="text-gray-500">
|
||||
<span className="sr-only">Open options</span>
|
||||
<EllipsisVerticalIcon className="size-5" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="w-28">
|
||||
<DropdownMenuItem onClick={() => setUpdateUserNamespaceModal(true)}>
|
||||
Update
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem variant="destructive" onClick={() => deleteMember()}>
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</td>
|
||||
<td className="absolute hidden" onClick={e => { e.preventDefault() }}>
|
||||
<Transition show={updateUserNamespaceModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setUpdateUserNamespaceModal}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</TransitionChild>
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<DialogPanel className="relative transform rounded-lg bg-white px-6 pb-4 text-left shadow-xl transition-all">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900 border-b pt-4 pb-4"
|
||||
>
|
||||
Update member
|
||||
</DialogTitle>
|
||||
<div className="flex flex-col gap-0 mt-4">
|
||||
{/* <div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">User</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Combobox value={userSelected}
|
||||
onChange={(user: IUserItem) => {
|
||||
setUserSelected(user);
|
||||
}}>
|
||||
<div className="relative mt-1">
|
||||
<div className="w-full relative cursor-default overflow-hidden rounded-lg bg-white text-left shadow focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm">
|
||||
<Combobox.Input
|
||||
id="namespace"
|
||||
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
|
||||
displayValue={(user: IUserItem) => user.username}
|
||||
onChange={event => {
|
||||
setUserSearch(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Combobox.Button>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
afterLeave={() => setUserSearch('')}
|
||||
>
|
||||
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm z-10">
|
||||
{
|
||||
userList.length === 0 ? (
|
||||
<div className="relative cursor-default select-none py-2 px-4 text-gray-700">
|
||||
Nothing found.
|
||||
</div>
|
||||
) : (
|
||||
userList.map(user => (
|
||||
<Combobox.Option
|
||||
key={user.id}
|
||||
className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-4 pr-4 ${active ? 'bg-gray-200 text-gray-800' : 'text-gray-900'
|
||||
}`
|
||||
}
|
||||
value={user}
|
||||
>
|
||||
<span className={`block truncate font-normal`}>
|
||||
{user.username}
|
||||
</span>
|
||||
</Combobox.Option>
|
||||
))
|
||||
)
|
||||
}
|
||||
</Combobox.Options>
|
||||
</Transition>
|
||||
</div>
|
||||
</Combobox>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* <div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2"></div>
|
||||
<div className="col-span-4">
|
||||
{
|
||||
userSelectedValid ? null : (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
<span>
|
||||
Please select a user.
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">Role</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Listbox
|
||||
value={addNamespaceRoleRole}
|
||||
onChange={(source: string) => {
|
||||
setAddNamespaceRoleRole(source);
|
||||
}}>
|
||||
<div className="relative w-full">
|
||||
<ListboxButton
|
||||
className={() => {
|
||||
let cursor = 'cursor-pointer '
|
||||
return cursor + "relative w-full rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm min-w-[200px]"
|
||||
}}
|
||||
>
|
||||
<span className="block truncate">{addNamespaceRoleRole}</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
<ListboxOptions className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm z-10">
|
||||
|
||||
{
|
||||
namespaceRoles.map(source => (
|
||||
<ListboxOption key={source.name} value={source.name} className={({ active }) =>
|
||||
`relative cursor-pointer select-none py-2 pl-10 pr-4 ${active ? 'bg-gray-100 text-gray-900' : 'text-gray-900'
|
||||
}`
|
||||
}>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${selected ? 'font-medium' : 'font-normal'
|
||||
}`}
|
||||
>
|
||||
{source.name}
|
||||
</span>
|
||||
{
|
||||
selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-600">
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
) : null
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))
|
||||
}
|
||||
</ListboxOptions>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row-reverse mt-5">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-500 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:bg-indigo-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
onClick={e => updateMember()}
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
|
||||
onClick={e => { setUpdateUserNamespaceModal(false) }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<Dialog open={updateUserNamespaceModal} onOpenChange={setUpdateUserNamespaceModal}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogTitle className="border-b pb-4">Update member</DialogTitle>
|
||||
<div className="flex flex-col gap-0 mt-4">
|
||||
{/* <div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">User</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button variant="outline" className="w-full justify-between font-normal">
|
||||
{userSelected?.username || "Select user"}
|
||||
<ChevronUpDownIcon className="size-4 text-muted-foreground" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<PopoverContent align="start" className="w-(--anchor-width) p-0">
|
||||
<Command shouldFilter={false}>
|
||||
<CommandInput
|
||||
placeholder="Search user"
|
||||
value={userSearch}
|
||||
onValueChange={setUserSearch}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty>Nothing found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{userList?.map(user => (
|
||||
<CommandItem
|
||||
key={user.id}
|
||||
value={user.username}
|
||||
onSelect={() => setUserSelected(user)}
|
||||
>
|
||||
{user.username}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* <div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2"></div>
|
||||
<div className="col-span-4">
|
||||
{
|
||||
userSelectedValid ? null : (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
<span>
|
||||
Please select a user.
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-2 flex flex-row">
|
||||
<label htmlFor="usernameText" className="block text-sm font-medium leading-6 text-gray-900 my-auto">
|
||||
<div className="flex">
|
||||
<span className="text-red-600">*</span>
|
||||
<span className="leading-6 ">Role</span>
|
||||
<span>:</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-span-4">
|
||||
<Select
|
||||
value={addNamespaceRoleRole}
|
||||
onValueChange={(source) => { if (source) setAddNamespaceRoleRole(source); }}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{namespaceRoles.map(source => (
|
||||
<SelectItem key={source.name} value={source.name}>
|
||||
{source.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setUpdateUserNamespaceModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => updateMember()}>Update</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -199,7 +199,7 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
</Tabs>
|
||||
}
|
||||
/>
|
||||
<div className="pt-3 pb-3 flex justify-between items-center min-h-[60px]">
|
||||
<div className="pt-3 pb-3 flex justify-between items-center min-h-15">
|
||||
<div className="px-4">
|
||||
<div className="relative flex items-center">
|
||||
<Label htmlFor="repositorySearch" className="absolute -top-2 left-2 inline-block bg-background px-1 text-xs font-medium text-foreground z-10">
|
||||
|
||||
@@ -18,7 +18,6 @@ import axios from "axios";
|
||||
import dayjs from "dayjs";
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
|
||||
import Header from "../../components/Header";
|
||||
import Menu from "../../components/Menu";
|
||||
|
||||
+73
-109
@@ -1,4 +1,3 @@
|
||||
import { Dialog, DialogTitle, Menu, MenuButton, MenuItem, MenuItems, Transition } from "@headlessui/react";
|
||||
/**
|
||||
* Copyright 2023 sigma
|
||||
*
|
||||
@@ -33,6 +32,21 @@ import Settings from "../../Settings";
|
||||
import Toast from "../../components/Notification";
|
||||
import distros, { distroName } from '../../utils/distros';
|
||||
import { useTranslation } from "../../i18n/useTranslation";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
IArtifact,
|
||||
IEndpoint,
|
||||
@@ -46,7 +60,6 @@ import {
|
||||
IUserSelf,
|
||||
IVuln
|
||||
} from "../../interfaces";
|
||||
import { NamespaceRole, UserRole } from "../../interfaces/enums";
|
||||
import { Tooltip, trimHTTP } from "../../utils";
|
||||
|
||||
export default function Tag({ localServer }: { localServer: string }) {
|
||||
@@ -258,69 +271,36 @@ export default function Tag({ localServer }: { localServer: string }) {
|
||||
</div>
|
||||
</main>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<Transition.Root show={deleteTagModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setDeleteTagModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
|
||||
<DialogTitle as="h3" className="text-base font-semibold leading-6 text-gray-900">
|
||||
Delete tag
|
||||
</DialogTitle>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Are you sure you want to delete the tag <span className="text-black font-medium">{deleteTagModalTag.name}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto"
|
||||
onClick={e => { setDeleteTagModal(false); deleteTag(deleteTagModalTag.id); }}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
|
||||
onClick={() => setDeleteTagModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
<Dialog open={deleteTagModal} onOpenChange={setDeleteTagModal}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="mx-auto flex size-10 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0">
|
||||
<ExclamationTriangleIcon className="size-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="text-center sm:text-left">
|
||||
<DialogTitle>Delete tag</DialogTitle>
|
||||
<DialogDescription className="mt-2">
|
||||
Are you sure you want to delete the tag{' '}
|
||||
<span className="text-foreground font-medium">{deleteTagModalTag.name}</span>
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setDeleteTagModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => { setDeleteTagModal(false); deleteTag(deleteTagModalTag.id); }}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<div className="flex flex-col border-b border-gray-200">
|
||||
{
|
||||
tagList.items?.map((tag, index) => {
|
||||
@@ -369,43 +349,27 @@ export default function Tag({ localServer }: { localServer: string }) {
|
||||
</code>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center">
|
||||
<Menu as="div" className="relative flex-none" onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<MenuButton className="mx-auto my-auto block p-1 text-gray-500 hover:text-gray-900 margin">
|
||||
<span className="sr-only">Open options</span>
|
||||
<EllipsisVerticalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</MenuButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems className={(index > 10 ? "menu-action-top" : "mt-2") + " text-left absolute right-0 z-10 w-20 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none"} >
|
||||
<MenuItem>
|
||||
{({ active }) => (
|
||||
<div
|
||||
className={
|
||||
(active ? 'bg-gray-50' : '') +
|
||||
(((userObj.role == UserRole.Admin || userObj.role == UserRole.Root || (namespaceObj.role != undefined && (namespaceObj.role == NamespaceRole.Admin || namespaceObj.role == NamespaceRole.Manager)))) ? ' cursor-pointer' : ' cursor-not-allowed') +
|
||||
' block px-3 py-1 text-sm leading-6 text-gray-900 hover:text-white hover:bg-red-600 cursor-pointer'
|
||||
}
|
||||
onClick={e => {
|
||||
setDeleteTagModal(true);
|
||||
setDeleteTagModalTag(tag);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
)}
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="icon-sm" className="text-gray-500">
|
||||
<span className="sr-only">Open options</span>
|
||||
<EllipsisVerticalIcon className="size-5" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="w-20">
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setDeleteTagModal(true);
|
||||
setDeleteTagModalTag(tag);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
{/* first row end */}
|
||||
@@ -520,15 +484,15 @@ function DetailItem({ artifact }: { artifact: IArtifact }) {
|
||||
let imageConfigObj = JSON.parse(artifact.config_raw) as IImageConfig;
|
||||
return (
|
||||
<tr className="hover:bg-gray-50 cursor-pointer">
|
||||
<td className="text-left w-[180px]">
|
||||
<td className="text-left w-45">
|
||||
<code className="text-xs underline underline-offset-1 text-blue-600 hover:text-blue-500">
|
||||
{cutDigest(artifact.digest)}
|
||||
</code>
|
||||
</td>
|
||||
<td className="text-left text-xs w-[180px] capitalize">
|
||||
<td className="text-left text-xs w-45 capitalize">
|
||||
{artifact.type}
|
||||
</td>
|
||||
<td className="text-left text-xs w-[180px]">
|
||||
<td className="text-left text-xs w-45">
|
||||
<div className='flex gap-1'>
|
||||
{distros(sbomObj.distro?.name) === "" ? "" : (
|
||||
<img src={"/distros/" + distros(sbomObj.distro.name)} alt={sbomObj.distro.name} className="w-4 h-4 inline relative" />
|
||||
@@ -538,7 +502,7 @@ function DetailItem({ artifact }: { artifact: IArtifact }) {
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-left text-xs w-[180px]">
|
||||
<td className="text-left text-xs w-45">
|
||||
{
|
||||
imageConfigObj.os === undefined ||
|
||||
imageConfigObj.architecture === undefined ||
|
||||
@@ -548,22 +512,22 @@ function DetailItem({ artifact }: { artifact: IArtifact }) {
|
||||
)
|
||||
}
|
||||
</td>
|
||||
<td className="text-left text-xs w-[180px]">
|
||||
<td className="text-left text-xs w-45">
|
||||
Verified
|
||||
</td>
|
||||
<td className="text-left text-xs w-[180px]">
|
||||
<td className="text-left text-xs w-45">
|
||||
{(artifact.pull_times || 0) > 0 ? dayjs().to(dayjs(artifact.last_pull)) : "Never pulled"}
|
||||
</td>
|
||||
{/* <td className="text-right text-xs w-[180px]">
|
||||
{/* <td className="text-right text-xs w-45">
|
||||
{artifact.pull_times}
|
||||
</td> */}
|
||||
<td className="text-right text-xs w-[220px]">
|
||||
<td className="text-right text-xs w-55">
|
||||
<span className="bg-red-800 text-white text-xs font-medium mr-1 px-2 py-0.5 dark:bg-red-900 dark:text-red-300"><span>{vulnerabilityObj.critical || 0}</span> C</span>
|
||||
<span className="bg-red-300 text-gray-800 text-xs font-medium mr-1 px-2 py-0.5 dark:bg-red-900 dark:text-red-300">{vulnerabilityObj.high || 0} H</span>
|
||||
<span className="bg-amber-400 text-gray-800 text-xs font-medium mr-1 px-2 py-0.5 dark:bg-red-900 dark:text-red-300">{vulnerabilityObj.medium || 0} M</span>
|
||||
<span className="bg-amber-200 text-gray-800 text-xs font-medium px-2 py-0.5 dark:bg-red-900 dark:text-red-300">{vulnerabilityObj.low || 0} L</span>
|
||||
</td>
|
||||
<td className="text-right text-xs w-[180px]">
|
||||
<td className="text-right text-xs w-45">
|
||||
{humanFormat(artifact.blob_size || 0)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+412
-573
File diff suppressed because it is too large.
Load diff
+89
-241
@@ -1,4 +1,3 @@
|
||||
import { Dialog, Menu, Transition } from "@headlessui/react";
|
||||
/**
|
||||
* Copyright 2024 sigma
|
||||
*
|
||||
@@ -19,7 +18,8 @@ import axios from "axios";
|
||||
import dayjs from "dayjs";
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { Dialog as ShadDialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogTitle } from "@/components/ui/dialog";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { Link, useParams, useSearchParams, useLocation } from 'react-router-dom';
|
||||
|
||||
@@ -30,7 +30,6 @@ import Pagination from "../../components/Pagination";
|
||||
import Settings from "../../Settings";
|
||||
import { IHTTPError, INamespaceItem, IOrder, IUserSelf, IWebhookLogItem, IWebhookLogList } from "../../interfaces";
|
||||
import OrderHeader from "../../components/OrderHeader";
|
||||
import { EllipsisVertical, TriangleAlert } from "lucide-react";
|
||||
import { EllipsisVerticalIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
import { NamespaceRole, UserRole } from "../../interfaces/enums";
|
||||
|
||||
@@ -157,7 +156,7 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
<div
|
||||
id="tooltip-webhook-retry-duration"
|
||||
role="tooltip"
|
||||
className="absolute z-50 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700 w-[220px]">
|
||||
className="absolute z-50 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700 w-55">
|
||||
Less than 10, unit is second.
|
||||
</div>
|
||||
<div className="min-h-screen flex overflow-hidden bg-white">
|
||||
@@ -200,7 +199,7 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
</div>
|
||||
)
|
||||
} />
|
||||
<div className="pt-1 pb-1 flex justify-between items-center min-h-[60px]">
|
||||
<div className="pt-1 pb-1 flex justify-between items-center min-h-15">
|
||||
<div className="px-4">
|
||||
<div className="flex gap-4">
|
||||
<div className="relative mt-2 flex items-center">
|
||||
@@ -264,74 +263,35 @@ export default function ({ localServer }: { localServer: string }) {
|
||||
<Pagination limit={Settings.PageSize} page={page} setPage={setPage} total={total} />
|
||||
</div>
|
||||
</div>
|
||||
<Transition.Root show={webhookPingModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setWebhookPingModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6 min-w-[600px]">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
|
||||
<Dialog.Title as="h3" className="text-base font-semibold leading-6 text-gray-900">
|
||||
Send webhook ping event
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Are you sure you want to send the webhook ping event?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto"
|
||||
onClick={e => { setWebhookPingModal(false); webhookPing(); }}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
|
||||
onClick={() => setWebhookPingModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
<Dialog open={webhookPingModal} onOpenChange={setWebhookPingModal}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="mx-auto flex size-10 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0">
|
||||
<ExclamationTriangleIcon className="size-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="text-center sm:text-left">
|
||||
<DialogTitle>Send webhook ping event</DialogTitle>
|
||||
<DialogDescription className="mt-2">
|
||||
Are you sure you want to send the webhook ping event?
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setWebhookPingModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => { setWebhookPingModal(false); webhookPing(); }}>
|
||||
Send
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Fragment >
|
||||
);
|
||||
}
|
||||
|
||||
function TableItem({ localServer, index, userObj, namespaceObj, webhookLogObj, setRefresh }: { localServer: string, index: number, userObj: IUserSelf, namespaceObj: INamespaceItem, webhookLogObj: IWebhookLogItem, setRefresh: (param: any) => void }) {
|
||||
const canManageWebhookLog = userObj.role == UserRole.Admin || userObj.role == UserRole.Root || (namespaceObj.role != undefined && (namespaceObj.role == NamespaceRole.Admin || namespaceObj.role == NamespaceRole.Manager));
|
||||
const [webhookLogResendModal, setWebhookLogResendModal] = useState(false);
|
||||
const [webhookLogDeleteModal, setWebhookLogDeleteModal] = useState(false);
|
||||
|
||||
@@ -395,192 +355,80 @@ function TableItem({ localServer, index, userObj, namespaceObj, webhookLogObj, s
|
||||
<td className="pr-3 whitespace-nowrap text-center" onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<Menu as="div" className="relative flex-none" onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<Menu.Button className="mx-auto -m-2.5 block p-2.5 text-gray-500 hover:text-gray-900 margin">
|
||||
<span className="sr-only">Open options</span>
|
||||
<EllipsisVerticalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</Menu.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className={(index > 10 ? "menu-action-top" : "mt-2") + " text-left absolute right-0 z-10 w-20 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none"} >
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<div
|
||||
className={
|
||||
(active ? 'bg-gray-100' : '') +
|
||||
(((userObj.role == UserRole.Admin || userObj.role == UserRole.Root || (namespaceObj.role != undefined && (namespaceObj.role == NamespaceRole.Admin || namespaceObj.role == NamespaceRole.Manager)))) ? ' cursor-pointer' : ' cursor-not-allowed') +
|
||||
' block px-3 py-1 text-sm leading-6 text-gray-900'
|
||||
}
|
||||
onClick={e => {
|
||||
((userObj.role == UserRole.Admin || userObj.role == UserRole.Root || (namespaceObj.role != undefined && (namespaceObj.role == NamespaceRole.Admin || namespaceObj.role == NamespaceRole.Manager)))) && setWebhookLogResendModal(true);
|
||||
}}
|
||||
>
|
||||
Resend
|
||||
</div>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<div
|
||||
className={
|
||||
(active ? 'bg-gray-50' : '') +
|
||||
(((userObj.role == UserRole.Admin || userObj.role == UserRole.Root || (namespaceObj.role != undefined && (namespaceObj.role == NamespaceRole.Admin || namespaceObj.role == NamespaceRole.Manager)))) ? ' cursor-pointer' : ' cursor-not-allowed') +
|
||||
' block px-3 py-1 text-sm leading-6 text-gray-900 hover:text-white hover:bg-red-600 cursor-pointer'
|
||||
}
|
||||
onClick={e => {
|
||||
((userObj.role == UserRole.Admin || userObj.role == UserRole.Root || (namespaceObj.role != undefined && (namespaceObj.role == NamespaceRole.Admin || namespaceObj.role == NamespaceRole.Manager)))) && setWebhookLogDeleteModal(true);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="icon-sm" className="text-gray-500">
|
||||
<span className="sr-only">Open options</span>
|
||||
<EllipsisVerticalIcon className="size-5" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent align="end" className="w-28">
|
||||
<DropdownMenuItem disabled={!canManageWebhookLog} onClick={() => setWebhookLogResendModal(true)}>
|
||||
Resend
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem variant="destructive" disabled={!canManageWebhookLog} onClick={() => setWebhookLogDeleteModal(true)}>
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</td>
|
||||
<td className="absolute hidden" onClick={e => { e.preventDefault() }}>
|
||||
<Transition.Root show={webhookLogResendModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setWebhookLogResendModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
|
||||
<Dialog.Title as="h3" className="text-base font-semibold leading-6 text-gray-900">
|
||||
Resend webhook event
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Are you sure you want to resend the webhook event <span className="text-black font-medium capitalize">{webhookLogObj.resource_type}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto"
|
||||
onClick={e => { setWebhookLogResendModal(false); webhookLogResend() }}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
|
||||
onClick={() => setWebhookLogResendModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
<Dialog open={webhookLogResendModal} onOpenChange={setWebhookLogResendModal}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="mx-auto flex size-10 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0">
|
||||
<ExclamationTriangleIcon className="size-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="text-center sm:text-left">
|
||||
<DialogTitle>Resend webhook event</DialogTitle>
|
||||
<DialogDescription className="mt-2">
|
||||
Are you sure you want to resend the webhook event{" "}
|
||||
<span className="text-foreground font-medium capitalize">{webhookLogObj.resource_type}</span>
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setWebhookLogResendModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={() => { setWebhookLogResendModal(false); webhookLogResend() }}>
|
||||
Resend
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</td>
|
||||
<td className="absolute hidden" onClick={e => { e.preventDefault() }}>
|
||||
<Transition.Root show={webhookLogDeleteModal} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setWebhookLogDeleteModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
|
||||
<Dialog.Title as="h3" className="text-base font-semibold leading-6 text-gray-900">
|
||||
Delete webhook log
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Are you sure you want to delete the webhook event <span className="text-black font-medium capitalize">{webhookLogObj.resource_type}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto"
|
||||
onClick={e => { setWebhookLogDeleteModal(false); webhookLogDelete() }}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
|
||||
onClick={() => setWebhookLogDeleteModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
<Dialog open={webhookLogDeleteModal} onOpenChange={setWebhookLogDeleteModal}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="mx-auto flex size-10 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0">
|
||||
<ExclamationTriangleIcon className="size-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="text-center sm:text-left">
|
||||
<DialogTitle>Delete webhook log</DialogTitle>
|
||||
<DialogDescription className="mt-2">
|
||||
Are you sure you want to delete the webhook event{" "}
|
||||
<span className="text-foreground font-medium capitalize">{webhookLogObj.resource_type}</span>
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setWebhookLogDeleteModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={() => { setWebhookLogDeleteModal(false); webhookLogDelete() }}>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</td>
|
||||
<td className="absolute" onClick={e => { e.preventDefault() }}>
|
||||
<div className={`fixed inset-0 z-30 bg-black/20 transition-opacity ${drawerShow ? "opacity-100" : "pointer-events-none opacity-0"}`} onClick={() => setDrawerShow(false)} />
|
||||
<div id={`drawer-${index}`} className={`fixed top-0 right-0 z-40 h-screen p-4 overflow-y-auto bg-white w-[800px] shadow-xl transition-transform dark:bg-gray-800 ${drawerShow ? "translate-x-0" : "translate-x-full"}`} aria-labelledby="drawer-right-label">
|
||||
<div id={`drawer-${index}`} className={`fixed top-0 right-0 z-40 h-screen p-4 overflow-y-auto bg-white w-200 shadow-xl transition-transform dark:bg-gray-800 ${drawerShow ? "translate-x-0" : "translate-x-full"}`} aria-labelledby="drawer-right-label">
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-4 top-4 rounded-md text-gray-400 hover:text-gray-600"
|
||||
|
||||
Reference in new issue
Block a user