mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b79c6615b4 | ||
|
|
5d4e491611 | ||
|
|
9b1f920863 | ||
|
|
7c7f0a0f10 | ||
|
|
76541c701c | ||
|
|
d2046fee21 | ||
|
|
8e2cbe1d2a | ||
|
|
8a08583225 |
No files matched your search
Generated
+74
-68
@@ -513,9 +513,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.28.tgz",
|
||||
"integrity": "sha512-PAmWhJfJQlP+kxZwCjrVd9QnR5x0R3u0mTXTiZDgSd4h5LdXmjxCCWbN9kq6hkZBOax8Rm3xDW5HagWyJuT37g==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.32.tgz",
|
||||
"integrity": "sha512-n9mQdigI6iZ/DF6pCTwMKeWgF2e8lg7qgt5M7HXMLtyhZYMnf/u905M18sSpPmHL9MKp9JHo56C6jrD2EvWxng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
@@ -529,9 +529,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -578,9 +578,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.28.tgz",
|
||||
"integrity": "sha512-kzGChl9setxYWpk3H6fTZXXPFFjg7urptLq5o5ZgYezCrqlemKttwMT5iFyx/p1e/JeglTwDFRtb923gTJ3R1w==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.32.tgz",
|
||||
"integrity": "sha512-osHXveM70zC+ilfuFa/2W6a1XQxJTvEhzEycnjUaVE8kpUS09lDpiDDX2YLdyFCzoUbvbo5r0X1Kp4MllIOShw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -594,9 +594,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.28.tgz",
|
||||
"integrity": "sha512-z6FXYHDJlFOzVEOiiJ/4NG8aLCeayZdcRSMjPDysW297Up6r22xw6Ea9AOwQqbNsth8JNgIK8EkWz2IDwaLQcw==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.32.tgz",
|
||||
"integrity": "sha512-P9NpCAJuOiaHHpqtrCNncjqtSBi1f6QUdHK/+dNabBIXB2RUFWL19TY1Hkhu74OvyNQEYEzzMJCMQk5agjw1Qg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -610,9 +610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.28.tgz",
|
||||
"integrity": "sha512-9ARHLEQXhAilNJ7rgQX8xs9aH3yJSj888ssSjJLeldiZKR4D7N08MfMqljk77fAwZsWwsrp8ohHsMvurvv9liQ==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.32.tgz",
|
||||
"integrity": "sha512-v7JaO0oXXt6d+cFjrrKqYnR2ubrD+JYP7nQVRZgeo5uNE5hkCpWnHmXm9vy3g6foMO8SPwL0P3MPw1c+BjbAzA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -626,9 +626,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.28.tgz",
|
||||
"integrity": "sha512-p6gvatI1nX41KCizEe6JkF0FS/cEEF0u23vKDpl+WhPe/fCTBeGkEBh7iW2cUM0rvquPVwPWdiUR6Ebr/kQWxQ==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.32.tgz",
|
||||
"integrity": "sha512-tA6sIKShXtSJBTH88i0DRd6I9n3ZTirmwpwAqH5zdJoQF7/wlJXR8DkPmKwYl5mFWhEKr5IIa3LfpMW9RRwKmQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -642,9 +642,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.28.tgz",
|
||||
"integrity": "sha512-nsiSnz2wO6GwMAX2o0iucONlVL7dNgKUqt/mDTATGO2NY59EO/ZKnKEr80BJFhuA5UC1KZOMblJHWZoqIJddpA==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.32.tgz",
|
||||
"integrity": "sha512-7S1GY4TdnlGVIdeXXKQdDkfDysoIVFMD0lJuVVMeb3eoVjrknQ0JNN7wFlhCvea0hEk0Sd4D1hedVChDKfV2jw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -658,9 +658,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.28.tgz",
|
||||
"integrity": "sha512-+IuGQKoI3abrXFqx7GtlvNOpeExUH1mTIqCrh1LGFf8DnlUcTmOOCApEnPJUSLrSbzOdsF2ho2KhnQoO0I1RDw==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.32.tgz",
|
||||
"integrity": "sha512-OHHC81P4tirVa6Awk6eCQ6RBfWl8HpFsZtfEkMpJ5GjPsJ3nhPe6wKAJUZ/piC8sszUkAgv3fLflgzPStIwfWg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -674,9 +674,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.28.tgz",
|
||||
"integrity": "sha512-l61WZ3nevt4BAnGksUVFKy2uJP5DPz2E0Ma/Oklvo3sGj9sw3q7vBWONFRgz+ICiHpW5mV+mBrkB3XEubMrKaA==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.32.tgz",
|
||||
"integrity": "sha512-rORQjXsAFeX6TLYJrCG5yoIDj+NKq31Rqwn8Wpn/bkPNy5rTHvOXkW8mLFonItS7QC6M+1JIIcLe+vOCTOYpvg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -690,9 +690,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.28.tgz",
|
||||
"integrity": "sha512-+Kcp1T3jHZnJ9v9VTJ/yf1t/xmtFAc/Sge4v7mVc1z+NYfYzisi8kJ9AsY8itbgq+WgEwMtOpiLLJsUy2qnXZw==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.32.tgz",
|
||||
"integrity": "sha512-jHUeDPVHrgFltqoAqDB6g6OStNnFxnc7Aks3p0KE0FbwAvRg6qWKYF5mSTdCTxA3axoSAUwxYdILzXJfUwlHhA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -706,9 +706,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.28.tgz",
|
||||
"integrity": "sha512-1gCmpvyhz7DkB1srRItJTnmR2UwQPAUXXIg9r0/56g3O8etGmwlX68skKXJOp9EejW3hhv7nSQUJ2raFiz4MoA==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.32.tgz",
|
||||
"integrity": "sha512-2N0lSoU4GjfLSO50wvKpMQgKd4HdI2UHEhQPPPnlgfBJlOgJxkjpkYBqzk08f1gItBB6xF/n+ykso2hgxuydsA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2809,9 +2809,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3396,9 +3396,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -4532,14 +4533,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
|
||||
"integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.2",
|
||||
"has-tostringtag": "^1.0.0",
|
||||
"hasown": "^2.0.0"
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -5310,14 +5313,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
@@ -5683,12 +5688,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.2"
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -6783,12 +6789,12 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "14.2.28",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.28.tgz",
|
||||
"integrity": "sha512-QLEIP/kYXynIxtcKB6vNjtWLVs3Y4Sb+EClTC/CSVzdLD1gIuItccpu/n1lhmduffI32iPGEK2cLLxxt28qgYA==",
|
||||
"version": "14.2.32",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.32.tgz",
|
||||
"integrity": "sha512-fg5g0GZ7/nFc09X8wLe6pNSU8cLWbLRG3TZzPJ1BJvi2s9m7eF991se67wliM9kR5yLHRkyGKU49MMx58s3LJg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "14.2.28",
|
||||
"@next/env": "14.2.32",
|
||||
"@swc/helpers": "0.5.5",
|
||||
"busboy": "1.6.0",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
@@ -6803,15 +6809,15 @@
|
||||
"node": ">=18.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "14.2.28",
|
||||
"@next/swc-darwin-x64": "14.2.28",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.28",
|
||||
"@next/swc-linux-arm64-musl": "14.2.28",
|
||||
"@next/swc-linux-x64-gnu": "14.2.28",
|
||||
"@next/swc-linux-x64-musl": "14.2.28",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.28",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.28",
|
||||
"@next/swc-win32-x64-msvc": "14.2.28"
|
||||
"@next/swc-darwin-arm64": "14.2.32",
|
||||
"@next/swc-darwin-x64": "14.2.32",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.32",
|
||||
"@next/swc-linux-arm64-musl": "14.2.32",
|
||||
"@next/swc-linux-x64-gnu": "14.2.32",
|
||||
"@next/swc-linux-x64-musl": "14.2.32",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.32",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.32",
|
||||
"@next/swc-win32-x64-msvc": "14.2.32"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
@@ -8565,9 +8571,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
|
||||
"integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
+250
-127
@@ -2,6 +2,7 @@
|
||||
|
||||
import Breadcrumbs from "@components/Breadcrumbs";
|
||||
import Button from "@components/Button";
|
||||
import { Callout } from "@components/Callout";
|
||||
import Card from "@components/Card";
|
||||
import FancyToggleSwitch from "@components/FancyToggleSwitch";
|
||||
import FullTooltip from "@components/FullTooltip";
|
||||
@@ -219,7 +220,7 @@ const PeerGeneralInformation = () => {
|
||||
<ModalTrigger>
|
||||
<div
|
||||
className={
|
||||
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
|
||||
"flex h-8 w-8 items-center justify-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 rounded-md cursor-pointer"
|
||||
}
|
||||
>
|
||||
<PencilIcon size={16} />
|
||||
@@ -324,7 +325,7 @@ const PeerGeneralInformation = () => {
|
||||
}
|
||||
interactive={false}
|
||||
className={"w-full block"}
|
||||
disabled={!permission.peers.update}
|
||||
disabled={permission.peers.update}
|
||||
>
|
||||
<FancyToggleSwitch
|
||||
value={ssh}
|
||||
@@ -415,158 +416,200 @@ const PeerOverviewTabs = () => {
|
||||
|
||||
function PeerInformationCard({ peer }: Readonly<{ peer: Peer }>) {
|
||||
const { isLoading, getRegionByPeer } = useCountries();
|
||||
const { update } = usePeer();
|
||||
const { mutate } = useSWRConfig();
|
||||
const [showEditIPModal, setShowEditIPModal] = useState(false);
|
||||
const { permission } = usePermissions();
|
||||
|
||||
const countryText = useMemo(() => {
|
||||
return getRegionByPeer(peer);
|
||||
}, [getRegionByPeer, peer]);
|
||||
|
||||
return (
|
||||
<Card className={"w-full xl:w-1/2"}>
|
||||
<Card.List>
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"NetBird IP-Address"}
|
||||
label={
|
||||
<>
|
||||
<MapPin size={16} />
|
||||
NetBird IP-Address
|
||||
</>
|
||||
}
|
||||
value={peer.ip}
|
||||
<>
|
||||
<Modal open={showEditIPModal} onOpenChange={setShowEditIPModal}>
|
||||
<EditIPModal
|
||||
onSuccess={(newIP) => {
|
||||
notify({
|
||||
title: peer.name,
|
||||
description: "Peer IP was successfully updated",
|
||||
promise: update({ ip: newIP }).then(() => {
|
||||
mutate("/peers/" + peer.id);
|
||||
setShowEditIPModal(false);
|
||||
}),
|
||||
loadingMessage: "Updating peer IP...",
|
||||
});
|
||||
}}
|
||||
peer={peer}
|
||||
key={showEditIPModal ? 1 : 0}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"Public IP-Address"}
|
||||
label={
|
||||
<>
|
||||
<NetworkIcon size={16} />
|
||||
Public IP-Address
|
||||
</>
|
||||
}
|
||||
value={peer.connection_ip}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"DNS label"}
|
||||
label={
|
||||
<>
|
||||
<Globe size={16} />
|
||||
Domain Name
|
||||
</>
|
||||
}
|
||||
className={
|
||||
peer?.extra_dns_labels && peer.extra_dns_labels.length > 0
|
||||
? "items-start"
|
||||
: ""
|
||||
}
|
||||
value={peer.dns_label}
|
||||
extraText={peer?.extra_dns_labels}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"Hostname"}
|
||||
label={
|
||||
<>
|
||||
<MonitorSmartphoneIcon size={16} />
|
||||
Hostname
|
||||
</>
|
||||
}
|
||||
value={peer.hostname}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<FlagIcon size={16} />
|
||||
Region
|
||||
</>
|
||||
}
|
||||
tooltip={false}
|
||||
value={
|
||||
isEmpty(peer.country_code) ? (
|
||||
"Unknown"
|
||||
) : (
|
||||
</Modal>
|
||||
<Card className={"w-full xl:w-1/2"}>
|
||||
<Card.List>
|
||||
<Card.ListItem
|
||||
copy
|
||||
tooltip={false}
|
||||
copyText={"NetBird IP Address"}
|
||||
label={
|
||||
<>
|
||||
{isLoading ? (
|
||||
<Skeleton width={140} />
|
||||
) : (
|
||||
<div className={"flex gap-2 items-center"}>
|
||||
<div className={"border-0 border-nb-gray-800 rounded-full"}>
|
||||
<RoundedFlag country={peer.country_code} size={12} />
|
||||
</div>
|
||||
{countryText}
|
||||
</div>
|
||||
)}
|
||||
<MapPin size={16} />
|
||||
NetBird IP Address
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
valueToCopy={peer.ip}
|
||||
value={
|
||||
<div className="flex items-center gap-2 justify-between w-full">
|
||||
<span>{peer.ip}</span>
|
||||
{permission.peers.update && (
|
||||
<button
|
||||
className="flex w-7 h-7 items-center justify-center gap-2 text-nb-gray-400 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 rounded-md cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowEditIPModal(true);
|
||||
}}
|
||||
>
|
||||
<PencilIcon size={14} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<Cpu size={16} />
|
||||
Operating System
|
||||
</>
|
||||
}
|
||||
value={peer.os}
|
||||
/>
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"Public IP Address"}
|
||||
label={
|
||||
<>
|
||||
<NetworkIcon size={16} />
|
||||
Public IP Address
|
||||
</>
|
||||
}
|
||||
value={peer.connection_ip}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"DNS label"}
|
||||
label={
|
||||
<>
|
||||
<Globe size={16} />
|
||||
Domain Name
|
||||
</>
|
||||
}
|
||||
className={
|
||||
peer?.extra_dns_labels && peer.extra_dns_labels.length > 0
|
||||
? "items-start"
|
||||
: ""
|
||||
}
|
||||
value={peer.dns_label}
|
||||
extraText={peer?.extra_dns_labels}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
copy
|
||||
copyText={"Hostname"}
|
||||
label={
|
||||
<>
|
||||
<MonitorSmartphoneIcon size={16} />
|
||||
Hostname
|
||||
</>
|
||||
}
|
||||
value={peer.hostname}
|
||||
/>
|
||||
|
||||
{peer.serial_number && peer.serial_number !== "" && (
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<Barcode size={16} />
|
||||
Serial Number
|
||||
<FlagIcon size={16} />
|
||||
Region
|
||||
</>
|
||||
}
|
||||
value={peer.serial_number}
|
||||
tooltip={false}
|
||||
value={
|
||||
isEmpty(peer.country_code) ? (
|
||||
"Unknown"
|
||||
) : (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<Skeleton width={140} />
|
||||
) : (
|
||||
<div className={"flex gap-2 items-center"}>
|
||||
<div
|
||||
className={"border-0 border-nb-gray-800 rounded-full"}
|
||||
>
|
||||
<RoundedFlag country={peer.country_code} size={12} />
|
||||
</div>
|
||||
{countryText}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<History size={16} />
|
||||
Last seen
|
||||
</>
|
||||
}
|
||||
value={
|
||||
peer.connected
|
||||
? "just now"
|
||||
: dayjs(peer.last_seen).format("D MMMM, YYYY [at] h:mm A") +
|
||||
" (" +
|
||||
dayjs().to(peer.last_seen) +
|
||||
")"
|
||||
}
|
||||
/>
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<Cpu size={16} />
|
||||
Operating System
|
||||
</>
|
||||
}
|
||||
value={peer.os}
|
||||
/>
|
||||
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<NetBirdIcon size={16} />
|
||||
Agent Version
|
||||
</>
|
||||
}
|
||||
value={peer.version}
|
||||
/>
|
||||
{peer.serial_number && peer.serial_number !== "" && (
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<Barcode size={16} />
|
||||
Serial Number
|
||||
</>
|
||||
}
|
||||
value={peer.serial_number}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<History size={16} />
|
||||
Last seen
|
||||
</>
|
||||
}
|
||||
value={
|
||||
peer.connected
|
||||
? "just now"
|
||||
: dayjs(peer.last_seen).format("D MMMM, YYYY [at] h:mm A") +
|
||||
" (" +
|
||||
dayjs().to(peer.last_seen) +
|
||||
")"
|
||||
}
|
||||
/>
|
||||
|
||||
{peer.ui_version && (
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<NetBirdIcon size={16} />
|
||||
UI Version
|
||||
Agent Version
|
||||
</>
|
||||
}
|
||||
value={peer.ui_version?.replace("netbird-desktop-ui/", "")}
|
||||
value={peer.version}
|
||||
/>
|
||||
)}
|
||||
</Card.List>
|
||||
</Card>
|
||||
|
||||
{peer.ui_version && (
|
||||
<Card.ListItem
|
||||
label={
|
||||
<>
|
||||
<NetBirdIcon size={16} />
|
||||
UI Version
|
||||
</>
|
||||
}
|
||||
value={peer.ui_version?.replace("netbird-desktop-ui/", "")}
|
||||
/>
|
||||
)}
|
||||
</Card.List>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -654,3 +697,83 @@ function EditNameModal({ onSuccess, peer, initialName }: Readonly<ModalProps>) {
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
interface EditIPModalProps {
|
||||
onSuccess: (ip: string) => void;
|
||||
peer: Peer;
|
||||
}
|
||||
|
||||
function EditIPModal({ onSuccess, peer }: Readonly<EditIPModalProps>) {
|
||||
const [ip, setIP] = useState(peer.ip);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const validateIP = (ipAddress: string) => {
|
||||
const ipRegex =
|
||||
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
return ipRegex.test(ipAddress);
|
||||
};
|
||||
|
||||
const isDisabled = useMemo(() => {
|
||||
if (ip === peer.ip) return true;
|
||||
const trimmedIP = trim(ip);
|
||||
return trimmedIP.length === 0 || !validateIP(ip);
|
||||
}, [ip, peer.ip]);
|
||||
|
||||
React.useEffect(() => {
|
||||
switch (true) {
|
||||
case ip === peer.ip:
|
||||
setError("");
|
||||
break;
|
||||
case !validateIP(ip):
|
||||
setError("Please enter a valid IP, e.g., 100.64.0.15");
|
||||
break;
|
||||
default:
|
||||
setError("");
|
||||
break;
|
||||
}
|
||||
}, [ip, peer.ip]);
|
||||
|
||||
return (
|
||||
<ModalContent maxWidthClass={"max-w-md"}>
|
||||
<form>
|
||||
<ModalHeader
|
||||
title={"Edit Peer IP Address"}
|
||||
description={"Update the NetBird IP address for this peer."}
|
||||
color={"blue"}
|
||||
/>
|
||||
|
||||
<div className={"p-default flex flex-col gap-4"}>
|
||||
<div>
|
||||
<Input
|
||||
placeholder={"e.g., 100.64.0.15"}
|
||||
value={ip}
|
||||
onChange={(e) => setIP(e.target.value)}
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Callout>Changes take effect when the peer reconnects.</Callout>
|
||||
</div>
|
||||
|
||||
<ModalFooter className={"items-center"} separator={false}>
|
||||
<div className={"flex gap-3 w-full justify-end"}>
|
||||
<ModalClose asChild={true}>
|
||||
<Button variant={"secondary"} className={"w-full"}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
|
||||
<Button
|
||||
variant={"primary"}
|
||||
className={"w-full"}
|
||||
onClick={() => onSuccess(ip)}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ function PeersBlockedView() {
|
||||
<div className={"px-3 pt-1 pb-8 max-w-3xl w-full"}>
|
||||
<div
|
||||
className={
|
||||
"rounded-md border border-nb-gray-900/70 grid w-full bg-nb-gray-930/40 stepper-bg-variant"
|
||||
"rounded-md border border-nb-gray-900/70 grid w-full bg-nb-gray-930/40"
|
||||
}
|
||||
>
|
||||
<SetupModalContent header={false} footer={false} />
|
||||
|
||||
@@ -289,6 +289,7 @@ function UserInformationCard({ user }: Readonly<{ user: User }>) {
|
||||
const neverLoggedIn = dayjs(user.last_login).isBefore(
|
||||
dayjs().subtract(1000, "years"),
|
||||
);
|
||||
const isPendingApproval = user?.pending_approval;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
@@ -328,18 +329,20 @@ function UserInformationCard({ user }: Readonly<{ user: User }>) {
|
||||
|
||||
{!isServiceUser && (
|
||||
<>
|
||||
{!user.is_current && user.role != Role.Owner && (
|
||||
<Card.ListItem
|
||||
tooltip={false}
|
||||
label={
|
||||
<>
|
||||
<Ban size={16} />
|
||||
Block User
|
||||
</>
|
||||
}
|
||||
value={<UserBlockCell user={user} isUserPage={true} />}
|
||||
/>
|
||||
)}
|
||||
{!user.is_current &&
|
||||
user.role != Role.Owner &&
|
||||
!isPendingApproval && (
|
||||
<Card.ListItem
|
||||
tooltip={false}
|
||||
label={
|
||||
<>
|
||||
<Ban size={16} />
|
||||
Block User
|
||||
</>
|
||||
}
|
||||
value={<UserBlockCell user={user} isUserPage={true} />}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card.ListItem
|
||||
label={
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import { useOidc } from "@axa-fr/react-oidc";
|
||||
import Button from "@components/Button";
|
||||
import Paragraph from "@components/Paragraph";
|
||||
import loadConfig from "@utils/config";
|
||||
import { ArrowRightIcon, RefreshCw } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import NetBirdIcon from "@/assets/icons/NetBirdIcon";
|
||||
|
||||
const config = loadConfig();
|
||||
|
||||
export default function ErrorPage() {
|
||||
const { logout, isAuthenticated } = useOidc();
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [error, setError] = useState<{
|
||||
code: number;
|
||||
message: string;
|
||||
type: string;
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Get error details from URL params
|
||||
const code = searchParams.get("code");
|
||||
const message = searchParams.get("message");
|
||||
const type = searchParams.get("type");
|
||||
|
||||
if (code && message) {
|
||||
setError({
|
||||
code: parseInt(code),
|
||||
message: decodeURIComponent(message),
|
||||
type: type || "error",
|
||||
});
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
const handleLogout = () => {
|
||||
// Use the same logout pattern as OIDCError
|
||||
logout("/", { client_id: config.clientId });
|
||||
};
|
||||
|
||||
const handleRetry = () => {
|
||||
router.push("/");
|
||||
};
|
||||
|
||||
if (!isAuthenticated) {
|
||||
// If not authenticated, redirect to home
|
||||
router.push("/");
|
||||
return null;
|
||||
}
|
||||
|
||||
const isBlockedUser =
|
||||
error?.code === 403 && error?.message?.toLowerCase().includes("blocked");
|
||||
const isPendingApproval =
|
||||
error?.code === 403 &&
|
||||
error?.message?.toLowerCase().includes("pending approval");
|
||||
|
||||
const getTitle = () => {
|
||||
if (isBlockedUser) return "User Account Blocked";
|
||||
if (isPendingApproval) return "User Approval Pending";
|
||||
return "Access Error";
|
||||
};
|
||||
|
||||
const getDescription = () => {
|
||||
if (isBlockedUser) {
|
||||
return "Your access has been blocked by the NetBird account administrator, possibly due to new user approval requirements or security policies. Please contact your administrator to regain access.";
|
||||
}
|
||||
if (isPendingApproval) {
|
||||
return "Your account is pending approval from an administrator. Please wait for approval before accessing the dashboard.";
|
||||
}
|
||||
return "An error occurred while trying to access the dashboard. Please try again or contact your administrator.";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center flex-col h-screen max-w-xl mx-auto">
|
||||
<div className="bg-nb-gray-930 mb-3 border border-nb-gray-900 h-12 w-12 rounded-md flex items-center justify-center">
|
||||
<NetBirdIcon size={23} />
|
||||
</div>
|
||||
|
||||
<h1 className="text-center mt-2">{getTitle()}</h1>
|
||||
|
||||
<Paragraph className="text-center mt-2 block">
|
||||
{getDescription()}
|
||||
</Paragraph>
|
||||
|
||||
{error && (
|
||||
<div className="bg-nb-gray-930 border border-nb-gray-800 rounded-md p-4 mt-4 max-w-md font-mono mb-2">
|
||||
<div className="text-center text-sm text-netbird">
|
||||
<div>response_message: {error.message}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Paragraph className="text-center mt-2 text-sm">
|
||||
If you believe this is an error, please contact your administrator.
|
||||
</Paragraph>
|
||||
|
||||
<div className="mt-5 space-y-3">
|
||||
{!isBlockedUser && !isPendingApproval && (
|
||||
<Button variant="default-outline" size="sm" onClick={handleRetry}>
|
||||
<RefreshCw size={16} className="mr-2" />
|
||||
Try Again
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button variant="primary" size="sm" onClick={handleLogout}>
|
||||
{isBlockedUser || isPendingApproval ? "Sign Out" : "Logout"}
|
||||
<ArrowRightIcon size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+41
-2
@@ -67,7 +67,7 @@ p {
|
||||
}
|
||||
|
||||
.stepper-bg-variant .step-circle {
|
||||
@apply !border-[#1d2024];
|
||||
@apply !border-nb-gray-940;
|
||||
}
|
||||
|
||||
.webkit-scroll{
|
||||
@@ -117,4 +117,43 @@ p {
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.animate-slow-ping {
|
||||
animation: ping 1.6s cubic-bezier(0, 0, 0.2, 1) infinite
|
||||
}
|
||||
|
||||
@keyframes ping {
|
||||
75%, 100% {
|
||||
transform: scale(2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slow-pulse {
|
||||
animation: pulse 2.5s cubic-bezier(0.4, 0, 0.6, 1) infinite
|
||||
}
|
||||
|
||||
|
||||
@keyframes pulse {
|
||||
60% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bg-scroll {
|
||||
0% {
|
||||
background-position: 0% 100%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-bg-scroll {
|
||||
animation: bg-scroll 4s linear infinite;
|
||||
}
|
||||
.animate-bg-scroll-faster {
|
||||
animation: bg-scroll 1.8s linear infinite;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg width="573" height="148" viewBox="0 0 573 148" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4_4)">
|
||||
<path d="M0.739014 125.602V33.09H28.239C34.491 33.09 39.919 34.274 44.524 36.638C49.128 39.004 52.698 42.341 55.233 46.65C57.767 50.958 59.034 56.071 59.034 61.984V96.58C59.034 102.41 57.767 107.5 55.233 111.85C52.698 116.203 49.128 119.581 44.523 121.99C39.918 124.397 34.491 125.601 28.239 125.601L0.739014 125.602ZM16.58 111.028H28.24C32.801 111.028 36.433 109.741 39.138 107.163C41.841 104.587 43.193 101.06 43.193 96.581V61.984C43.193 57.592 41.841 54.107 39.138 51.529C36.433 48.952 32.801 47.663 28.239 47.663H16.58V111.028ZM76.396 125.602V33.09H95.786L121.512 107.86C121.216 104.673 120.942 101.483 120.688 98.292C120.386 94.5366 120.154 90.776 119.991 87.012C119.821 83.169 119.738 79.812 119.738 76.938V33.09H134.185V125.602H114.795L89.323 50.832C89.491 53.283 89.703 56.239 89.956 59.702C90.2137 63.2478 90.4251 66.7967 90.59 70.348C90.758 73.982 90.844 77.318 90.844 80.36V125.602H76.396ZM181.582 126.869C175.245 126.869 169.752 125.812 165.107 123.701C160.46 121.591 156.889 118.569 154.398 114.64C151.905 110.711 150.616 106.086 150.533 100.763H166.374C166.374 104.565 167.747 107.543 170.494 109.698C173.238 111.852 176.976 112.929 181.709 112.929C186.271 112.929 189.839 111.874 192.417 109.761C194.993 107.65 196.282 104.735 196.282 101.017C196.282 97.892 195.374 95.167 193.558 92.842C191.74 90.52 189.142 88.936 185.764 88.09L175.119 85.175C167.852 83.318 162.256 79.979 158.327 75.164C154.398 70.348 152.434 64.518 152.434 57.675C152.434 52.438 153.616 47.875 155.982 43.988C158.347 40.103 161.705 37.103 166.058 34.99C170.408 32.88 175.54 31.822 181.455 31.822C190.409 31.822 197.506 34.125 202.745 38.729C207.983 43.335 210.645 49.523 210.73 57.295H194.888C194.888 53.663 193.704 50.812 191.34 48.741C188.974 46.671 185.637 45.636 181.328 45.636C177.188 45.636 173.978 46.608 171.697 48.551C169.416 50.495 168.275 53.24 168.275 56.788C168.275 60 169.141 62.724 170.873 64.962C172.603 67.202 175.119 68.786 178.413 69.714L189.439 72.756C196.789 74.616 202.407 77.932 206.294 82.Line truncated
|
||||
<path d="M332.69 126.999C329.057 126.999 326.164 125.941 324.01 123.831C321.855 121.72 320.778 118.847 320.778 115.213C320.778 111.581 321.855 108.686 324.01 106.532C326.164 104.378 329.057 103.3 332.69 103.3C336.322 103.3 339.217 104.378 341.372 106.532C343.526 108.686 344.603 111.582 344.603 115.213C344.603 118.847 343.526 121.72 341.372 123.831C339.217 125.941 336.322 126.999 332.691 126.999H332.69ZM381.862 125.732V33.219H437.369V47.032H397.449V71.112H432.934V84.798H397.45V111.918H437.37V125.732H381.862ZM484.766 126.999C475.725 126.999 468.65 124.528 463.539 119.585C458.426 114.643 455.872 107.906 455.872 99.372V33.219H471.84V99.245C471.84 103.639 472.937 107.061 475.135 109.51C477.331 111.962 480.54 113.185 484.766 113.185C488.905 113.185 492.095 111.962 494.334 109.51C496.572 107.06 497.693 103.639 497.693 99.245V33.219H513.66V99.372C513.66 107.906 511.126 114.642 506.057 119.585C500.987 124.528 493.891 126.999 484.767 126.999H484.766Z" fill="#686868"/>
|
||||
<path d="M549 0H573V148H549V0Z" fill="#FFCC03"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4_4">
|
||||
<rect width="573" height="148" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg width="573" height="148" viewBox="0 0 573 148" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4_4)">
|
||||
<path d="M0.739014 125.602V33.09H28.239C34.491 33.09 39.919 34.274 44.524 36.638C49.128 39.004 52.698 42.341 55.233 46.65C57.767 50.958 59.034 56.071 59.034 61.984V96.58C59.034 102.41 57.767 107.5 55.233 111.85C52.698 116.203 49.128 119.581 44.523 121.99C39.918 124.397 34.491 125.601 28.239 125.601L0.739014 125.602ZM16.58 111.028H28.24C32.801 111.028 36.433 109.741 39.138 107.163C41.841 104.587 43.193 101.06 43.193 96.581V61.984C43.193 57.592 41.841 54.107 39.138 51.529C36.433 48.952 32.801 47.663 28.239 47.663H16.58V111.028ZM76.396 125.602V33.09H95.786L121.512 107.86C121.216 104.673 120.942 101.483 120.688 98.292C120.386 94.5366 120.154 90.776 119.991 87.012C119.821 83.169 119.738 79.812 119.738 76.938V33.09H134.185V125.602H114.795L89.323 50.832C89.491 53.283 89.703 56.239 89.956 59.702C90.2137 63.2478 90.4251 66.7967 90.59 70.348C90.758 73.982 90.844 77.318 90.844 80.36V125.602H76.396ZM181.582 126.869C175.245 126.869 169.752 125.812 165.107 123.701C160.46 121.591 156.889 118.569 154.398 114.64C151.905 110.711 150.616 106.086 150.533 100.763H166.374C166.374 104.565 167.747 107.543 170.494 109.698C173.238 111.852 176.976 112.929 181.709 112.929C186.271 112.929 189.839 111.874 192.417 109.761C194.993 107.65 196.282 104.735 196.282 101.017C196.282 97.892 195.374 95.167 193.558 92.842C191.74 90.52 189.142 88.936 185.764 88.09L175.119 85.175C167.852 83.318 162.256 79.979 158.327 75.164C154.398 70.348 152.434 64.518 152.434 57.675C152.434 52.438 153.616 47.875 155.982 43.988C158.347 40.103 161.705 37.103 166.058 34.99C170.408 32.88 175.54 31.822 181.455 31.822C190.409 31.822 197.506 34.125 202.745 38.729C207.983 43.335 210.645 49.523 210.73 57.295H194.888C194.888 53.663 193.704 50.812 191.34 48.741C188.974 46.671 185.637 45.636 181.328 45.636C177.188 45.636 173.978 46.608 171.697 48.551C169.416 50.495 168.275 53.24 168.275 56.788C168.275 60 169.141 62.724 170.873 64.962C172.603 67.202 175.119 68.786 178.413 69.714L189.439 72.756C196.789 74.616 202.407 77.932 206.294 82.Line truncated
|
||||
<path d="M332.69 126.999C329.057 126.999 326.164 125.941 324.01 123.831C321.855 121.72 320.778 118.847 320.778 115.213C320.778 111.581 321.855 108.686 324.01 106.532C326.164 104.378 329.057 103.3 332.69 103.3C336.322 103.3 339.217 104.378 341.372 106.532C343.526 108.686 344.603 111.582 344.603 115.213C344.603 118.847 343.526 121.72 341.372 123.831C339.217 125.941 336.322 126.999 332.691 126.999H332.69ZM381.862 125.732V33.219H437.369V47.032H397.449V71.112H432.934V84.798H397.45V111.918H437.37V125.732H381.862ZM484.766 126.999C475.725 126.999 468.65 124.528 463.539 119.585C458.426 114.643 455.872 107.906 455.872 99.372V33.219H471.84V99.245C471.84 103.639 472.937 107.061 475.135 109.51C477.331 111.962 480.54 113.185 484.766 113.185C488.905 113.185 492.095 111.962 494.334 109.51C496.572 107.06 497.693 103.639 497.693 99.245V33.219H513.66V99.372C513.66 107.906 511.126 114.642 506.057 119.585C500.987 124.528 493.891 126.999 484.767 126.999H484.766Z" fill="#359CEF"/>
|
||||
<path d="M549 0H573V148H549V0Z" fill="#FFCC03"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4_4">
|
||||
<rect width="573" height="148" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -61,9 +61,9 @@ export default function Badge({
|
||||
<div
|
||||
className={cn(
|
||||
"relative z-10 cursor-inherit whitespace-nowrap rounded-md text-[12px] py-1.5 px-3 font-normal flex gap-1.5 items-center justify-center transition-all",
|
||||
className,
|
||||
variants({ variant, hover: useHover ? variant : "none" }),
|
||||
disabled && "cursor-not-allowed opacity-50 select-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cva, VariantProps } from "class-variance-authority";
|
||||
import classNames from "classnames";
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
type ButtonVariants = VariantProps<typeof buttonVariants>;
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>;
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
@@ -28,7 +28,7 @@ export const buttonVariants = cva(
|
||||
"dark:focus:ring-zinc-800/50 dark:bg-nb-gray dark:text-gray-400 dark:border-gray-700/30 dark:hover:text-white dark:hover:bg-zinc-800/50",
|
||||
],
|
||||
primary: [
|
||||
"dark:focus:ring-netbird-600/50 dark:ring-offset-neutral-950/50 enabled:dark:bg-netbird disabled:dark:bg-nb-gray-920 dark:text-gray-100 enabled:dark:hover:text-white enabled:dark:hover:bg-netbird-500/80",
|
||||
"dark:focus:ring-netbird-600/50 dark:ring-offset-neutral-950/50 enabled:dark:bg-netbird disabled:dark:bg-nb-gray-910 dark:text-gray-100 enabled:dark:hover:text-white enabled:dark:hover:bg-netbird-500/80",
|
||||
"enabled:bg-netbird enabled:text-white enabled:focus:ring-netbird-400/50 enabled:hover:bg-netbird-500",
|
||||
],
|
||||
secondary: [
|
||||
@@ -49,7 +49,7 @@ export const buttonVariants = cva(
|
||||
dropdown: [
|
||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-neutral-200 text-gray-900",
|
||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20 ",
|
||||
"dark:bg-nb-gray-900/40 dark:text-gray-400 dark:border-nb-gray-800 dark:hover:bg-nb-gray-900/50",
|
||||
"dark:bg-nb-gray-900/40 dark:text-gray-400 dark:border-nb-gray-900 dark:hover:bg-nb-gray-900/50",
|
||||
],
|
||||
dotted: [
|
||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900 border-dashed",
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
import { cn } from "@utils/helpers";
|
||||
import { cva, VariantProps } from "class-variance-authority";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
type CalloutVariants = VariantProps<typeof calloutVariants>;
|
||||
|
||||
type Props = {
|
||||
icon?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
} & CalloutVariants;
|
||||
|
||||
export const calloutVariants = cva(
|
||||
["px-4 py-3.5 rounded-md border text-sm font-normal flex gap-3 font-light"],
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-nb-gray-900/60 border-nb-gray-800/80 text-nb-gray-300",
|
||||
warning: "bg-netbird-500/10 border-netbird-400/20 text-netbird-150",
|
||||
info: "bg-sky-400/10 border-sky-400/20 text-sky-100",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const Callout = ({
|
||||
children,
|
||||
icon = <InfoIcon size={14} className={"shrink-0 relative top-[1px]"} />,
|
||||
icon = <InfoIcon size={14} className={"shrink-0 relative top-[2px]"} />,
|
||||
className,
|
||||
variant = "default",
|
||||
}: Props) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"px-4 py-3 rounded-md border text-sm font-normal flex gap-3",
|
||||
"bg-nb-gray-900/60 border-nb-gray-800/80 text-nb-gray-300",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className={cn(calloutVariants({ variant }), className)}>
|
||||
{icon}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
|
||||
@@ -29,6 +29,7 @@ function CardList({ children }: Props) {
|
||||
type CardListItemProps = {
|
||||
label: React.ReactNode;
|
||||
value: React.ReactNode;
|
||||
valueToCopy?: string;
|
||||
className?: string;
|
||||
copy?: boolean;
|
||||
copyText?: string;
|
||||
@@ -39,6 +40,7 @@ type CardListItemProps = {
|
||||
function CardListItem({
|
||||
label,
|
||||
value,
|
||||
valueToCopy,
|
||||
className,
|
||||
copy = false,
|
||||
copyText,
|
||||
@@ -57,6 +59,7 @@ function CardListItem({
|
||||
<CardTextItem
|
||||
label={label}
|
||||
value={value}
|
||||
valueToCopy={valueToCopy}
|
||||
copy={copy}
|
||||
copyText={copyText}
|
||||
tooltip={tooltip}
|
||||
@@ -79,6 +82,7 @@ function CardListItem({
|
||||
type CardTextItemProps = {
|
||||
label: React.ReactNode;
|
||||
value: React.ReactNode;
|
||||
valueToCopy?: string;
|
||||
copy?: boolean;
|
||||
copyText?: string;
|
||||
tooltip?: boolean;
|
||||
@@ -88,10 +92,11 @@ const CardTextItem = ({
|
||||
label,
|
||||
value,
|
||||
copy = false,
|
||||
valueToCopy,
|
||||
copyText,
|
||||
tooltip = true,
|
||||
}: CardTextItemProps) => {
|
||||
const [, copyToClipBoard] = useCopyToClipboard(value as string);
|
||||
const [, copyToClipBoard] = useCopyToClipboard(valueToCopy ?? `${value}`);
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -34,6 +34,10 @@ const defaultRanges = {
|
||||
from: dayjs().subtract(2, "day").startOf("day").toDate(),
|
||||
to: dayjs().endOf("day").toDate(),
|
||||
},
|
||||
last7Days: {
|
||||
from: dayjs().subtract(7, "day").startOf("day").toDate(),
|
||||
to: dayjs().endOf("day").toDate(),
|
||||
},
|
||||
lastMonth: {
|
||||
from: dayjs().subtract(1, "month").startOf("day").toDate(),
|
||||
to: dayjs().endOf("day").toDate(),
|
||||
@@ -64,6 +68,7 @@ export function DatePickerWithRange({
|
||||
yesterday: isEqualDateRange(value, defaultRanges.yesterday),
|
||||
last14Days: isEqualDateRange(value, defaultRanges.last14Days),
|
||||
last2Days: isEqualDateRange(value, defaultRanges.last2Days),
|
||||
last7Days: isEqualDateRange(value, defaultRanges.last7Days),
|
||||
lastMonth: isEqualDateRange(value, defaultRanges.lastMonth),
|
||||
allTime: isEqualDateRange(value, defaultRanges.allTime),
|
||||
};
|
||||
@@ -76,6 +81,7 @@ export function DatePickerWithRange({
|
||||
if (isActive.lastMonth) return "Last Month";
|
||||
if (isActive.last14Days) return "Last 14 Days";
|
||||
if (isActive.last2Days) return "Last 2 Days";
|
||||
if (isActive.last7Days) return "Last 7 Days";
|
||||
if (isActive.yesterday) return "Yesterday";
|
||||
if (isActive.today) return "Today";
|
||||
|
||||
@@ -88,12 +94,11 @@ export function DatePickerWithRange({
|
||||
const [calendarOpen, setCalendarOpen] = useState(false);
|
||||
|
||||
const updateRangeAndClose = (range: DateRange) => {
|
||||
setCalendarOpen(false);
|
||||
onChange?.(range);
|
||||
};
|
||||
|
||||
const debouncedOnChange = useMemo(() => {
|
||||
return onChange ? debounce(onChange, 300) : undefined;
|
||||
return onChange ? debounce(onChange, 500) : undefined;
|
||||
}, [onChange]);
|
||||
|
||||
const handleOnSelect = (range?: DateRange) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
TooltipVariants,
|
||||
} from "@components/Tooltip";
|
||||
import { TooltipProps } from "@radix-ui/react-tooltip";
|
||||
import { cn } from "@utils/helpers";
|
||||
@@ -24,7 +25,9 @@ type Props = {
|
||||
customOnOpenChange?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
delayDuration?: number;
|
||||
skipDelayDuration?: number;
|
||||
} & TooltipProps;
|
||||
} & TooltipProps &
|
||||
TooltipVariants;
|
||||
|
||||
export default function FullTooltip({
|
||||
children,
|
||||
content,
|
||||
@@ -41,6 +44,7 @@ export default function FullTooltip({
|
||||
customOnOpenChange,
|
||||
delayDuration = 1,
|
||||
skipDelayDuration = 300,
|
||||
variant = "default",
|
||||
}: Props) {
|
||||
const [open, setOpen] = useState(!!keepOpen);
|
||||
|
||||
@@ -66,7 +70,7 @@ export default function FullTooltip({
|
||||
<div
|
||||
className={cn(
|
||||
isAction ? "cursor-pointer" : "cursor-default",
|
||||
"inline-flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md",
|
||||
"inline-flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -82,6 +86,7 @@ export default function FullTooltip({
|
||||
alignOffset={20}
|
||||
forceMount={true}
|
||||
className={contentClassName}
|
||||
variant={variant}
|
||||
align={align}
|
||||
side={side}
|
||||
>
|
||||
|
||||
@@ -12,6 +12,7 @@ const variants = cva("", {
|
||||
variants: {
|
||||
variant: {
|
||||
default: ["bg-nb-gray-800 border-nb-gray-700 text-nb-gray-300 "],
|
||||
darker: ["bg-nb-gray-930 border-nb-gray-900 text-nb-gray-250 "],
|
||||
netbird: ["bg-netbird-100 text-netbird border-netbird "],
|
||||
},
|
||||
size: {
|
||||
@@ -30,7 +31,7 @@ export default function Kbd({
|
||||
size = "default",
|
||||
disabled = false,
|
||||
className,
|
||||
}: Props) {
|
||||
}: Readonly<Props>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { cn } from "@utils/helpers";
|
||||
import Image from "next/image";
|
||||
import * as React from "react";
|
||||
import NetBirdLogoMark from "@/assets/netbird.svg";
|
||||
import NetBirdLogoFull from "@/assets/netbird-full.svg";
|
||||
|
||||
type Props = {
|
||||
size?: "default" | "large";
|
||||
mobile?: boolean;
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
default: {
|
||||
desktop: 22,
|
||||
mobile: 30,
|
||||
},
|
||||
large: {
|
||||
desktop: 24,
|
||||
mobile: 40,
|
||||
},
|
||||
};
|
||||
|
||||
export const NetBirdLogo = ({ size = "default", mobile = true }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src={NetBirdLogoFull}
|
||||
height={sizes[size].desktop}
|
||||
alt={"NetBird Logo"}
|
||||
className={cn(mobile && "hidden md:block")}
|
||||
/>
|
||||
{mobile && (
|
||||
<Image
|
||||
src={NetBirdLogoMark}
|
||||
width={sizes[size].mobile}
|
||||
alt={"NetBird Logo"}
|
||||
className={cn(mobile && "md:hidden ml-4")}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -172,6 +172,7 @@ export function PeerSelector({
|
||||
{filteredItems.length > 0 && (
|
||||
<VirtualScrollAreaList
|
||||
items={filteredItems}
|
||||
estimatedItemHeight={37}
|
||||
onSelect={(item) => {
|
||||
const isSupported = isRoutingPeerSupported(
|
||||
item.version,
|
||||
|
||||
+279
-212
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,67 @@
|
||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||
import { ReactNode } from "react"; // or replace with clsx or similar
|
||||
import { cn } from "@/utils/helpers";
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
title: ReactNode;
|
||||
description: ReactNode;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const RadioCard = ({
|
||||
value,
|
||||
title,
|
||||
description,
|
||||
className,
|
||||
icon,
|
||||
}: Props) => {
|
||||
return (
|
||||
<RadioGroup.Item
|
||||
value={value}
|
||||
className={cn(
|
||||
"peer relative block cursor-pointer rounded-lg border border-nb-gray-900 bg-nb-gray-930/60 px-5 py-3 transition-all focus:outline-none",
|
||||
"data-[state=checked]:border-nb-gray-400 data-[state=checked]:bg-nb-gray-920",
|
||||
"outline-none focus:ring-0 focus:bg-nb-gray-930 focus:border-nb-gray-920",
|
||||
"hover:bg-nb-gray-930",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="text-nb-gray-100 font-normal text-sm text-left gap-2 flex items-center">
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
<div className="text-nb-gray-300 text-[0.8rem] text-left">
|
||||
{description}
|
||||
</div>
|
||||
</RadioGroup.Item>
|
||||
);
|
||||
};
|
||||
|
||||
type RadioCardGroupProps = {
|
||||
value: string;
|
||||
onValueChange: (val: string) => void;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
"aria-label"?: string;
|
||||
};
|
||||
|
||||
export const RadioCardGroup = ({
|
||||
value,
|
||||
onValueChange,
|
||||
children,
|
||||
className,
|
||||
"aria-label": ariaLabel = "Options",
|
||||
}: RadioCardGroupProps) => {
|
||||
return (
|
||||
<RadioGroup.Root
|
||||
className={cn("flex flex-col gap-2", className)}
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
{children}
|
||||
</RadioGroup.Root>
|
||||
);
|
||||
};
|
||||
Loaded 20 of 93 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user