add a page
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.env
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
||||
1
env.example
Normal file
1
env.example
Normal file
@@ -0,0 +1 @@
|
||||
VITE_API_SERVER=
|
||||
257
package-lock.json
generated
257
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"antd": "^5.25.2",
|
||||
"axios": "^1.13.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
@@ -1672,6 +1673,21 @@
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1720,6 +1736,18 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -1788,6 +1816,17 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/compute-scroll-into-view": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
|
||||
@@ -1860,12 +1899,74 @@
|
||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.155",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
|
||||
@@ -2174,6 +2275,40 @@
|
||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"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": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -2188,6 +2323,14 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -2197,6 +2340,41 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
@@ -2221,6 +2399,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -2230,6 +2419,42 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -2411,6 +2636,33 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -2591,6 +2843,11 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"antd": "^5.25.2",
|
||||
"axios": "^1.13.2",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
|
||||
50
src/App.jsx
50
src/App.jsx
@@ -2,6 +2,8 @@ import React, { useState } from 'react';
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
import { Menu } from 'antd';
|
||||
import { Radio, Tabs } from 'antd';
|
||||
import TheChild from './pages/TheChild';
|
||||
import Example from './pages/Example';
|
||||
const items = [
|
||||
{
|
||||
key: 'Dashboard',
|
||||
@@ -17,6 +19,7 @@ const items = [
|
||||
{ key: 'Devices', label: 'Devices' },
|
||||
{ key: 'Tags', label: 'Tags' },
|
||||
{ key: 'Products', label: 'Products' },
|
||||
{ key: 'Example', label: 'Example' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -74,7 +77,8 @@ const getLevelKeys = items1 => {
|
||||
return key;
|
||||
};
|
||||
const levelKeys = getLevelKeys(items);
|
||||
const TheChild = () => {return(<div>Hahalo</div>)}
|
||||
// const TheChild = () => {return(<div>Hahalo</div>)}
|
||||
const size = 'small';
|
||||
const App = () => {
|
||||
// const [stateOpenKeys, setStateOpenKeys] = useState(['2', '23']);
|
||||
const [stateOpenKeys, setStateOpenKeys] = useState(['0']);
|
||||
@@ -97,7 +101,7 @@ const App = () => {
|
||||
// children: <TheChild />,
|
||||
// },
|
||||
]);
|
||||
const [size, setSize] = useState('small');
|
||||
// const [size, setSize] = useState('small');
|
||||
const [activeKey, setActiveKey] = useState('Dashboard');
|
||||
|
||||
const onOpenChange = openKeys => {
|
||||
@@ -132,17 +136,29 @@ const App = () => {
|
||||
}
|
||||
setItemsTab(newItems);
|
||||
};
|
||||
|
||||
const add = (key) => {
|
||||
// const newKey = String((items || []).length + 1);
|
||||
// const newKey = String((itemsTab || []).length + 1);
|
||||
// console.log("newKey", newKey)
|
||||
// console.log("items", items)
|
||||
// console.log("itemsTab before", itemsTab)
|
||||
|
||||
// console.log("key", key)
|
||||
|
||||
let page;
|
||||
if(key[0] === "Example") {
|
||||
page = <Example />
|
||||
} else {
|
||||
page = `Content of editable tab ${key[0]}`;
|
||||
}
|
||||
|
||||
let newtab = {
|
||||
// label: `Tab ${newKey}`,
|
||||
label: `${key[0]}`,
|
||||
key: key[0],
|
||||
children: `Content of editable tab ${key[0]}`,
|
||||
// children: `Content of editable tab ${key[0]}`,
|
||||
children: page,
|
||||
}
|
||||
|
||||
if (!itemsTab.find(item => item.key === newtab.key)) {
|
||||
@@ -159,7 +175,7 @@ const App = () => {
|
||||
};
|
||||
const onAddTab = (targetKey, action) => {
|
||||
if (action === 'add') {
|
||||
add(key);
|
||||
// add(key);
|
||||
// console.log("Plus plus")
|
||||
} else {
|
||||
// console.log("targetKey", targetKey)
|
||||
@@ -170,19 +186,19 @@ const App = () => {
|
||||
// setSize(e.target.value);
|
||||
// };
|
||||
|
||||
const renderTabBar = (props, DefaultTabBar) => {
|
||||
return (
|
||||
<DefaultTabBar {...props}>
|
||||
{(node) => {
|
||||
// Filter out the add button node
|
||||
if (node.key && node.key.toString().startsWith('rc-tabs-add')) {
|
||||
return null;
|
||||
}
|
||||
return node;
|
||||
}}
|
||||
</DefaultTabBar>
|
||||
);
|
||||
};
|
||||
// const renderTabBar = (props, DefaultTabBar) => {
|
||||
// return (
|
||||
// <DefaultTabBar {...props}>
|
||||
// {(node) => {
|
||||
// // Filter out the add button node
|
||||
// if (node.key && node.key.toString().startsWith('rc-tabs-add')) {
|
||||
// return null;
|
||||
// }
|
||||
// return node;
|
||||
// }}
|
||||
// </DefaultTabBar>
|
||||
// );
|
||||
// };
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
0
src/Utils/Auth/Logout.jsx
Normal file
0
src/Utils/Auth/Logout.jsx
Normal file
0
src/Utils/Auth/Signin.jsx
Normal file
0
src/Utils/Auth/Signin.jsx
Normal file
171
src/components/Global/ApiRequest.jsx
Normal file
171
src/components/Global/ApiRequest.jsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import axios from 'axios';
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
const baseURL = import.meta.env.VITE_API_SERVER;
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
// axios khusus refresh
|
||||
const refreshApi = axios.create({
|
||||
baseURL,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
console.error('🚨 Response Error Interceptor:', {
|
||||
status: error.response?.status,
|
||||
url: originalRequest.url,
|
||||
message: error.response?.data?.message,
|
||||
hasRetried: originalRequest._retry,
|
||||
});
|
||||
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true;
|
||||
|
||||
try {
|
||||
console.log('🔄 Refresh token dipanggil...');
|
||||
const refreshRes = await refreshApi.post('/auth/refresh-token');
|
||||
|
||||
const newAccessToken = refreshRes.data.data.accessToken;
|
||||
localStorage.setItem('token', newAccessToken);
|
||||
console.log('✅ Token refreshed successfully');
|
||||
|
||||
// update token di header
|
||||
instance.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
|
||||
originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
|
||||
|
||||
console.log('🔁 Retrying original request...');
|
||||
return instance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
console.error(
|
||||
'❌ Refresh token gagal:',
|
||||
refreshError.response?.data || refreshError.message
|
||||
);
|
||||
localStorage.clear();
|
||||
window.location.href = '/signin';
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
async function ApiRequest({ method = 'GET', params = {}, prefix = '/', token = true } = {}) {
|
||||
const isFormData = params instanceof FormData;
|
||||
|
||||
const request = {
|
||||
method,
|
||||
url: prefix,
|
||||
data: params,
|
||||
headers: {
|
||||
'Accept-Language': 'en_US',
|
||||
...(isFormData ? {} : { 'Content-Type': 'application/json' }),
|
||||
},
|
||||
};
|
||||
|
||||
const rawToken = localStorage.getItem('token');
|
||||
if (token && rawToken) {
|
||||
const cleanToken = rawToken.replace(/"/g, '');
|
||||
request.headers['Authorization'] = `Bearer ${cleanToken}`;
|
||||
console.log('🔐 Sending request with token:', cleanToken.substring(0, 20) + '...');
|
||||
} else {
|
||||
console.warn('⚠️ No token found in localStorage');
|
||||
}
|
||||
|
||||
console.log('📤 API Request:', { method, url: prefix, hasToken: !!rawToken });
|
||||
|
||||
try {
|
||||
const response = await instance(request);
|
||||
console.log('✅ API Response:', {
|
||||
url: prefix,
|
||||
status: response.status,
|
||||
statusCode: response.data?.statusCode,
|
||||
});
|
||||
return { ...response, error: false };
|
||||
} catch (error) {
|
||||
const status = error?.response?.status || 500;
|
||||
const message = error?.response?.data?.message || error.message || 'Something Wrong';
|
||||
console.error('❌ API Error:', {
|
||||
url: prefix,
|
||||
status,
|
||||
message,
|
||||
fullError: error?.response?.data,
|
||||
});
|
||||
|
||||
if (status !== 401) {
|
||||
await cekError(status, message);
|
||||
}
|
||||
|
||||
return { ...error.response, error: true };
|
||||
}
|
||||
}
|
||||
|
||||
async function cekError(status, message = '') {
|
||||
if (status === 403) {
|
||||
await Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Forbidden',
|
||||
text: message,
|
||||
});
|
||||
} else if (status >= 500) {
|
||||
await Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Server Error',
|
||||
text: message,
|
||||
});
|
||||
} else {
|
||||
await Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Peringatan',
|
||||
text: message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const SendRequest = async (queryParams) => {
|
||||
try {
|
||||
const response = await ApiRequest(queryParams);
|
||||
console.log('📦 SendRequest response:', {
|
||||
hasError: response.error,
|
||||
status: response.status,
|
||||
statusCode: response.data?.statusCode,
|
||||
data: response.data,
|
||||
});
|
||||
|
||||
// If ApiRequest returned error flag, return error structure
|
||||
if (response.error) {
|
||||
const errorMsg = response.data?.message || response.statusText || 'Request failed';
|
||||
console.error('❌ SendRequest error response:', errorMsg);
|
||||
|
||||
// Return consistent error structure instead of empty array
|
||||
return {
|
||||
statusCode: response.status || 500,
|
||||
message: errorMsg,
|
||||
data: null,
|
||||
error: true,
|
||||
};
|
||||
}
|
||||
|
||||
return response || { statusCode: 200, data: [], message: 'Success' };
|
||||
} catch (error) {
|
||||
console.error('❌ SendRequest catch error:', error);
|
||||
|
||||
// Don't show Swal here, let the calling code handle it
|
||||
// This allows better error handling in each API call
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: error.message || 'Something went wrong',
|
||||
data: null,
|
||||
error: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export { ApiRequest, SendRequest };
|
||||
107
src/pages/Example.jsx
Normal file
107
src/pages/Example.jsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
const Example = () => {
|
||||
|
||||
// State to store the fetched data
|
||||
const [posts, setPosts] = React.useState([]);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [error, setError] = React.useState(null);
|
||||
|
||||
// Function to fetch data from API
|
||||
const fetchPosts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
// Make GET request to JSONPlaceholder API
|
||||
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
|
||||
setPosts(response.data);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
console.error('Error fetching data:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Use useEffect to fetch data when component mounts
|
||||
React.useEffect(() => {
|
||||
fetchPosts();
|
||||
}, []); // Empty dependency array means this runs once on mount
|
||||
|
||||
// Function to fetch a single post (example of another API call)
|
||||
const fetchSinglePost = async (postId) => {
|
||||
try {
|
||||
const response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${postId}`);
|
||||
console.log('Single post:', response.data);
|
||||
// Handle the single post data as needed
|
||||
} catch (err) {
|
||||
console.error('Error fetching single post:', err);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to create a new post (example of POST request)
|
||||
const createPost = async () => {
|
||||
try {
|
||||
const newPost = {
|
||||
title: 'New Post',
|
||||
body: 'This is a new post created via API',
|
||||
userId: 1,
|
||||
};
|
||||
const response = await axios.post('https://jsonplaceholder.typicode.com/posts', newPost);
|
||||
console.log('Created post:', response.data);
|
||||
// Optionally refetch posts after creation
|
||||
fetchPosts();
|
||||
} catch (err) {
|
||||
console.error('Error creating post:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading posts...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error}</div>;
|
||||
}
|
||||
|
||||
// return(<div>Example</div>)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Example Posts from JSONPlaceholder</h1>
|
||||
|
||||
<button onClick={fetchPosts} style={{ marginBottom: '20px' }}>
|
||||
Refresh Posts
|
||||
</button>
|
||||
|
||||
<button onClick={createPost} style={{ margin: '0 10px 20px 10px' }}>
|
||||
Create New Post
|
||||
</button>
|
||||
|
||||
<div style={{ display: 'grid', gap: '20px' }}>
|
||||
{posts.slice(0, 5).map((post) => (
|
||||
<div
|
||||
key={post.id}
|
||||
style={{
|
||||
border: '1px solid #ccc',
|
||||
padding: '15px',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={() => fetchSinglePost(post.id)}
|
||||
>
|
||||
<h3>{post.title}</h3>
|
||||
<p>{post.body}</p>
|
||||
<small>Post ID: {post.id} | User ID: {post.userId}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p>Showing {Math.min(posts.length, 5)} of {posts.length} posts</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default Example;
|
||||
7
src/pages/TheChild.jsx
Normal file
7
src/pages/TheChild.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
const TheChild = () => {
|
||||
return(<div>Hahalo</div>)
|
||||
}
|
||||
|
||||
export default TheChild;
|
||||
Reference in New Issue
Block a user