ok
This commit is contained in:
408
package-lock.json
generated
408
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "workspace",
|
"name": "workspace",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-pdf/renderer": "^4.3.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
@@ -2912,6 +2913,171 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-pdf/fns": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g=="
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/font": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-/dAWu7Y2RD1RxarDZ9SkYPHgBYOhmcDnet4W/qN/m8k+A2Hr3ja54GymSR7GGxWBtxjKtNauVKrTa9LS1n8WUw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-pdf/pdfkit": "^4.0.3",
|
||||||
|
"@react-pdf/types": "^2.9.0",
|
||||||
|
"fontkit": "^2.0.2",
|
||||||
|
"is-url": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/image": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-lvP5ryzYM3wpbO9bvqLZYwEr5XBDX9jcaRICvtnoRqdJOo7PRrMnmB4MMScyb+Xw10mGeIubZAAomNAG5ONQZQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-pdf/png-js": "^3.0.0",
|
||||||
|
"jay-peg": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/layout": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-Aq+Cc6JYausWLoks2FvHe3PwK9cTuvksB2uJ0AnkKJEUtQbvCq8eCRb1bjbbwIji9OzFRTTzZij7LzkpKHjIeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-pdf/fns": "3.1.2",
|
||||||
|
"@react-pdf/image": "^3.0.3",
|
||||||
|
"@react-pdf/primitives": "^4.1.1",
|
||||||
|
"@react-pdf/stylesheet": "^6.1.0",
|
||||||
|
"@react-pdf/textkit": "^6.0.0",
|
||||||
|
"@react-pdf/types": "^2.9.0",
|
||||||
|
"emoji-regex": "^10.3.0",
|
||||||
|
"queue": "^6.0.1",
|
||||||
|
"yoga-layout": "^3.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/layout/node_modules/emoji-regex": {
|
||||||
|
"version": "10.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||||
|
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/pdfkit": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-k+Lsuq8vTwWsCqTp+CCB4+2N+sOTFrzwGA7aw3H9ix/PDWR9QksbmNg0YkzGbLAPI6CeawmiLHcf4trZ5ecLPQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.20.13",
|
||||||
|
"@react-pdf/png-js": "^3.0.0",
|
||||||
|
"browserify-zlib": "^0.2.0",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"fontkit": "^2.0.2",
|
||||||
|
"jay-peg": "^1.1.1",
|
||||||
|
"linebreak": "^1.1.0",
|
||||||
|
"vite-compatible-readable-stream": "^3.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/png-js": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==",
|
||||||
|
"dependencies": {
|
||||||
|
"browserify-zlib": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/primitives": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/reconciler": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"scheduler": "0.25.0-rc-603e6108-20241029"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/reconciler/node_modules/scheduler": {
|
||||||
|
"version": "0.25.0-rc-603e6108-20241029",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz",
|
||||||
|
"integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA=="
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/render": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-MdWfWaqO6d7SZD75TZ2z5L35V+cHpyA43YNRlJNG0RJ7/MeVGDQv12y/BXOJgonZKkeEGdzM3EpAt9/g4E22WA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.20.13",
|
||||||
|
"@react-pdf/fns": "3.1.2",
|
||||||
|
"@react-pdf/primitives": "^4.1.1",
|
||||||
|
"@react-pdf/textkit": "^6.0.0",
|
||||||
|
"@react-pdf/types": "^2.9.0",
|
||||||
|
"abs-svg-path": "^0.1.1",
|
||||||
|
"color-string": "^1.9.1",
|
||||||
|
"normalize-svg-path": "^1.1.0",
|
||||||
|
"parse-svg-path": "^0.1.2",
|
||||||
|
"svg-arc-to-cubic-bezier": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/renderer": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-28gpA69fU9ZQrDzmd5xMJa1bDf8t0PT3ApUKBl2PUpoE/x4JlvCB5X66nMXrfFrgF2EZrA72zWQAkvbg7TE8zw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.20.13",
|
||||||
|
"@react-pdf/fns": "3.1.2",
|
||||||
|
"@react-pdf/font": "^4.0.2",
|
||||||
|
"@react-pdf/layout": "^4.4.0",
|
||||||
|
"@react-pdf/pdfkit": "^4.0.3",
|
||||||
|
"@react-pdf/primitives": "^4.1.1",
|
||||||
|
"@react-pdf/reconciler": "^1.1.4",
|
||||||
|
"@react-pdf/render": "^4.3.0",
|
||||||
|
"@react-pdf/types": "^2.9.0",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"queue": "^6.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/stylesheet": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-BGZ2sYNUp38VJUegjva/jsri3iiRGnVNjWI+G9dTwAvLNOmwFvSJzqaCsEnqQ/DW5mrTBk/577FhDY7pv6AidA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-pdf/fns": "3.1.2",
|
||||||
|
"@react-pdf/types": "^2.9.0",
|
||||||
|
"color-string": "^1.9.1",
|
||||||
|
"hsl-to-hex": "^1.0.0",
|
||||||
|
"media-engine": "^1.0.3",
|
||||||
|
"postcss-value-parser": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/textkit": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-fDt19KWaJRK/n2AaFoVm31hgGmpygmTV7LsHGJNGZkgzXcFyLsx+XUl63DTDPH3iqxj3xUX128t104GtOz8tTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-pdf/fns": "3.1.2",
|
||||||
|
"bidi-js": "^1.0.2",
|
||||||
|
"hyphen": "^1.6.4",
|
||||||
|
"unicode-properties": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-pdf/types": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-ckj80vZLlvl9oYrQ4tovEaqKWP3O06Eb1D48/jQWbdwz1Yh7Y9v1cEmwlP8ET+a1Whp8xfdM0xduMexkuPANCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-pdf/font": "^4.0.2",
|
||||||
|
"@react-pdf/primitives": "^4.1.1",
|
||||||
|
"@react-pdf/stylesheet": "^6.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reduxjs/toolkit": {
|
"node_modules/@reduxjs/toolkit": {
|
||||||
"version": "2.8.2",
|
"version": "2.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz",
|
||||||
@@ -3279,6 +3445,14 @@
|
|||||||
"url": "https://github.com/sponsors/gregberge"
|
"url": "https://github.com/sponsors/gregberge"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.5.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||||
|
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||||
@@ -4152,6 +4326,11 @@
|
|||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/abs-svg-path": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA=="
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
@@ -4956,6 +5135,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/batch": {
|
"node_modules/batch": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||||
@@ -4976,6 +5174,14 @@
|
|||||||
"node": ">= 8.0.0"
|
"node": ">= 8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bidi-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||||
|
"dependencies": {
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/big.js": {
|
"node_modules/big.js": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
@@ -5086,11 +5292,27 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/brotli": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browser-process-hrtime": {
|
"node_modules/browser-process-hrtime": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
|
||||||
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
|
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
|
||||||
},
|
},
|
||||||
|
"node_modules/browserify-zlib": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "~1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.25.0",
|
"version": "4.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
|
||||||
@@ -5426,6 +5648,14 @@
|
|||||||
"wrap-ansi": "^7.0.0"
|
"wrap-ansi": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
@@ -5549,6 +5779,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/color-string": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-support": {
|
"node_modules/color-support": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||||
@@ -5778,6 +6017,11 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto-js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||||
|
},
|
||||||
"node_modules/crypto-random-string": {
|
"node_modules/crypto-random-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||||
@@ -6509,6 +6753,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/dfa": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="
|
||||||
|
},
|
||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
@@ -7996,6 +8245,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fontkit": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.12",
|
||||||
|
"brotli": "^1.3.2",
|
||||||
|
"clone": "^2.1.2",
|
||||||
|
"dfa": "^1.2.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"restructure": "^3.0.0",
|
||||||
|
"tiny-inflate": "^1.0.3",
|
||||||
|
"unicode-properties": "^1.4.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
@@ -8694,6 +8959,19 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hsl-to-hex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==",
|
||||||
|
"dependencies": {
|
||||||
|
"hsl-to-rgb-for-reals": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hsl-to-rgb-for-reals": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg=="
|
||||||
|
},
|
||||||
"node_modules/html-encoding-sniffer": {
|
"node_modules/html-encoding-sniffer": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
|
||||||
@@ -8888,6 +9166,11 @@
|
|||||||
"node": ">=10.17.0"
|
"node": ">=10.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hyphen": {
|
||||||
|
"version": "1.10.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz",
|
||||||
|
"integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw=="
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
@@ -9674,6 +9957,14 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jay-peg": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==",
|
||||||
|
"dependencies": {
|
||||||
|
"restructure": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest": {
|
"node_modules/jest": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||||
@@ -10784,6 +11075,23 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/linebreak": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "0.0.8",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/linebreak/node_modules/base64-js": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
@@ -10937,6 +11245,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
|
||||||
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA=="
|
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/media-engine": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg=="
|
||||||
|
},
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
@@ -11317,6 +11630,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/normalize-svg-path": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg-arc-to-cubic-bezier": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-url": {
|
"node_modules/normalize-url": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||||
@@ -11655,6 +11976,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
|
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
|
},
|
||||||
"node_modules/param-case": {
|
"node_modules/param-case": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||||
@@ -11692,6 +12018,11 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/parse-svg-path": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ=="
|
||||||
|
},
|
||||||
"node_modules/parse5": {
|
"node_modules/parse5": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
@@ -13293,6 +13624,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/queue": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "~2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
@@ -14029,6 +14368,11 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/restructure": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw=="
|
||||||
|
},
|
||||||
"node_modules/retry": {
|
"node_modules/retry": {
|
||||||
"version": "0.13.1",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||||
@@ -14675,6 +15019,19 @@
|
|||||||
"simple-concat": "^1.0.0"
|
"simple-concat": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||||
|
},
|
||||||
"node_modules/sisteransi": {
|
"node_modules/sisteransi": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||||
@@ -15328,6 +15685,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg-arc-to-cubic-bezier": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
|
||||||
|
},
|
||||||
"node_modules/svg-parser": {
|
"node_modules/svg-parser": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
||||||
@@ -15756,6 +16118,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||||
|
},
|
||||||
"node_modules/tiny-invariant": {
|
"node_modules/tiny-invariant": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
@@ -16070,6 +16437,15 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-properties": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unicode-property-aliases-ecmascript": {
|
"node_modules/unicode-property-aliases-ecmascript": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
|
||||||
@@ -16078,6 +16454,20 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-trie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^0.2.5",
|
||||||
|
"tiny-inflate": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie/node_modules/pako": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
|
||||||
|
},
|
||||||
"node_modules/unique-string": {
|
"node_modules/unique-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||||
@@ -16260,6 +16650,19 @@
|
|||||||
"d3-timer": "^3.0.1"
|
"d3-timer": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-compatible-readable-stream": {
|
||||||
|
"version": "3.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
|
||||||
|
"integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/w3c-hr-time": {
|
"node_modules/w3c-hr-time": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||||
@@ -17178,6 +17581,11 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yoga-layout": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="
|
||||||
|
},
|
||||||
"node_modules/zlibjs": {
|
"node_modules/zlibjs": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-pdf/renderer": "^4.3.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
|
|||||||
BIN
public/PSI.png
BIN
public/PSI.png
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB |
BIN
public/asfasf.png
Normal file
BIN
public/asfasf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 297 KiB |
BIN
public/ikasapta.png
Normal file
BIN
public/ikasapta.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
@@ -2,13 +2,10 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/ikasapta.png" type="image/png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta name="description" content="Website SOLID DATA" />
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
@@ -25,7 +22,7 @@
|
|||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<title>React App</title>
|
<title>SOLID DATA</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"short_name": "React App",
|
"short_name": "IKASAPTA",
|
||||||
"name": "Create React App Sample",
|
"name": "IKASAPTA HUB",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "ikasapta.png",
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
"type": "image/x-icon"
|
"type": "image/x-icon"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "logo192.png",
|
"src": "ikasapta.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "192x192"
|
"sizes": "192x192"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "logo512.png",
|
"src": "ikasapta.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "512x512"
|
"sizes": "512x512"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://bot.kediritechnopark.com/webhook/dashboard/psi",
|
"https://bot.kediritechnopark.com/webhook/solid-data/dashboard",
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -75,7 +75,7 @@ const Dashboard = () => {
|
|||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://bot.kediritechnopark.com/webhook/list-user/psi",
|
"https://bot.kediritechnopark.com/webhook/solid-data/list-user",
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -110,7 +110,7 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://bot.kediritechnopark.com/webhook/add-officer",
|
"https://bot.kediritechnopark.com/webhook/solid-data/add-officer",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -163,7 +163,7 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://bot.kediritechnopark.com/webhook/psi/delete-officer`,
|
`https://bot.kediritechnopark.com/webhook/solid-data/delete-officer`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -193,8 +193,11 @@ const Dashboard = () => {
|
|||||||
<div className={styles.dashboardContainer}>
|
<div className={styles.dashboardContainer}>
|
||||||
<div className={styles.dashboardHeader}>
|
<div className={styles.dashboardHeader}>
|
||||||
<div className={styles.logoAndTitle}>
|
<div className={styles.logoAndTitle}>
|
||||||
<img src="/PSI.png" alt="Bot Avatar" />
|
<img src="/ikasapta.png" alt="Bot Avatar" />
|
||||||
<h1 className={styles.h1}>Kawal PSI Dashboard</h1>
|
<h1 className={styles.h1}>SOLID</h1>
|
||||||
|
<h1 className={styles.h1} styles="color: #43a0a7;">
|
||||||
|
DATA
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.dropdownContainer} ref={menuRef}>
|
<div className={styles.dropdownContainer} ref={menuRef}>
|
||||||
@@ -343,7 +346,7 @@ const Dashboard = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.chartSection}>
|
<div className={styles.chartSection}>
|
||||||
<h2>Grafik Pertumbuhan Anggota</h2>
|
<h2>Grafik Upload Document</h2>
|
||||||
{officerPerformanceData.length > 0 ? (
|
{officerPerformanceData.length > 0 ? (
|
||||||
<ResponsiveContainer width="100%" height={300}>
|
<ResponsiveContainer width="100%" height={300}>
|
||||||
<BarChart data={officerPerformanceData}>
|
<BarChart data={officerPerformanceData}>
|
||||||
@@ -355,7 +358,7 @@ const Dashboard = () => {
|
|||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.warning}>
|
<div className={styles.warning}>
|
||||||
📋 Belum ada data performa untuk ditampilkan
|
📋 Belum ada data upload untuk ditampilkan
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -370,7 +373,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
© 2025 Kediri Technopark • Dashboard PSI
|
© 2025 Kediri Technopark • Dashboard SOLID DATA
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
--neutral-800: #262626;
|
--neutral-800: #262626;
|
||||||
--neutral-900: #171717;
|
--neutral-900: #171717;
|
||||||
--white: #ffffff;
|
--white: #ffffff;
|
||||||
--success-green: #10b981;
|
--success-green: #43a0a7;
|
||||||
--warning-amber: #f59e0b;
|
--warning-amber: #f59e0b;
|
||||||
--error-red: #ef4444;
|
--error-red: #ef4444;
|
||||||
--text-primary: #0f172a;
|
--text-primary: #0f172a;
|
||||||
@@ -59,7 +59,7 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
border-bottom: 3px solid #ef4444;
|
border-bottom: 3px solid #43a0a7;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
@@ -81,10 +81,18 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dashboardHeader .h1 {
|
.dashboardHeader .h1 {
|
||||||
|
margin: 2px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #43a0a7;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #ed4344;
|
color: #154666;
|
||||||
letter-spacing: -0.025em;
|
letter-spacing: -0.025em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +215,7 @@ body {
|
|||||||
.summaryCard p {
|
.summaryCard p {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #ef4444;
|
color: #43a0a7;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
@@ -270,7 +278,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.submitButton {
|
.submitButton {
|
||||||
background-color: #ef4444;
|
background-color: #43a0a7;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.75rem 1.5rem;
|
||||||
@@ -285,7 +293,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.submitButton:hover {
|
.submitButton:hover {
|
||||||
background-color: #d03b3b;
|
background-color: #357734;
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
@@ -296,9 +304,9 @@ body {
|
|||||||
|
|
||||||
/* Messages */
|
/* Messages */
|
||||||
.success {
|
.success {
|
||||||
background-color: rgb(16 185 129 / 0.1);
|
background-color: rgb(67 160 167 / 0.1);
|
||||||
color: var(--success-green);
|
color: var(--success-green);
|
||||||
border: 1px solid rgb(16 185 129 / 0.2);
|
border: 1px solid rgb(67 160 167 / 0.2);
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
@@ -318,9 +326,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
background-color: #ef444417;
|
background-color: rgb(67 160 167 / 0.1);
|
||||||
color: #ef4444;
|
color: #43a0a7;
|
||||||
border: 1px solid #ef444433;
|
border: 1px solid rgb(67 160 167 / 0.2);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import styles from "./FileListComponent.module.css";
|
import styles from "./FileListComponent.module.css";
|
||||||
import * as XLSX from "xlsx";
|
import * as XLSX from "xlsx";
|
||||||
|
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||||
|
import KTPPDF from "./KTPPDF";
|
||||||
|
|
||||||
const FileListComponent = ({
|
const FileListComponent = ({
|
||||||
setTotalFilesSentToday,
|
setTotalFilesSentToday,
|
||||||
@@ -9,17 +11,18 @@ const FileListComponent = ({
|
|||||||
setOfficerPerformanceData,
|
setOfficerPerformanceData,
|
||||||
}) => {
|
}) => {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
|
const [filteredFiles, setFilteredFiles] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [selectedFile, setSelectedFile] = useState(null);
|
const [selectedFile, setSelectedFile] = useState(null);
|
||||||
const [successMessage, setSuccessMessage] = useState("");
|
const [successMessage, setSuccessMessage] = useState("");
|
||||||
|
const [selectedDocumentType, setSelectedDocumentType] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchFiles = async () => {
|
const fetchFiles = async () => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://bot.kediritechnopark.com/webhook/files",
|
"https://bot.kediritechnopark.com/webhook/solid-data/files",
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -29,35 +32,25 @@ const FileListComponent = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok)
|
||||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
}
|
|
||||||
|
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
|
if (!text) throw new Error("Server membalas kosong.");
|
||||||
if (!text) {
|
|
||||||
throw new Error("Server membalas kosong.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = JSON.parse(text);
|
const data = JSON.parse(text);
|
||||||
|
if (!data.success || !Array.isArray(data.data))
|
||||||
if (!data.success || !Array.isArray(data.data)) {
|
|
||||||
throw new Error("Format respons tidak valid.");
|
throw new Error("Format respons tidak valid.");
|
||||||
}
|
|
||||||
|
|
||||||
const fileData = data.data;
|
const fileData = data.data;
|
||||||
|
|
||||||
// 1. Set ke state
|
|
||||||
setFiles(fileData);
|
setFiles(fileData);
|
||||||
|
setFilteredFiles(fileData);
|
||||||
|
|
||||||
// 2. Hitung total file hari ini
|
|
||||||
const today = new Date().toISOString().slice(0, 10);
|
const today = new Date().toISOString().slice(0, 10);
|
||||||
const totalToday = fileData.filter((f) =>
|
const totalToday = fileData.filter((f) =>
|
||||||
f.created_at.startsWith(today)
|
f.created_at.startsWith(today)
|
||||||
).length;
|
).length;
|
||||||
setTotalFilesSentToday(totalToday);
|
setTotalFilesSentToday(totalToday);
|
||||||
|
|
||||||
// 3. Hitung total bulan ini
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const currentMonth = now.getMonth();
|
const currentMonth = now.getMonth();
|
||||||
const currentYear = now.getFullYear();
|
const currentYear = now.getFullYear();
|
||||||
@@ -69,10 +62,8 @@ const FileListComponent = ({
|
|||||||
}).length;
|
}).length;
|
||||||
setTotalFilesSentMonth(totalThisMonth);
|
setTotalFilesSentMonth(totalThisMonth);
|
||||||
|
|
||||||
// 4. Total keseluruhan
|
|
||||||
setTotalFilesSentOverall(fileData.length);
|
setTotalFilesSentOverall(fileData.length);
|
||||||
|
|
||||||
// 5. Grafik performa per bulan (dinamis)
|
|
||||||
const dateObjects = fileData.map((item) => new Date(item.created_at));
|
const dateObjects = fileData.map((item) => new Date(item.created_at));
|
||||||
if (dateObjects.length > 0) {
|
if (dateObjects.length > 0) {
|
||||||
const minDate = new Date(Math.min(...dateObjects));
|
const minDate = new Date(Math.min(...dateObjects));
|
||||||
@@ -95,19 +86,17 @@ const FileListComponent = ({
|
|||||||
const monthKey = `${d.getFullYear()}-${String(
|
const monthKey = `${d.getFullYear()}-${String(
|
||||||
d.getMonth() + 1
|
d.getMonth() + 1
|
||||||
).padStart(2, "0")}`;
|
).padStart(2, "0")}`;
|
||||||
if (monthlyDataMap[monthKey] !== undefined) {
|
if (monthlyDataMap[monthKey] !== undefined)
|
||||||
monthlyDataMap[monthKey]++;
|
monthlyDataMap[monthKey]++;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const performanceArray = Object.entries(monthlyDataMap).map(
|
const performanceArray = Object.entries(monthlyDataMap).map(
|
||||||
([month, count]) => {
|
([month, count]) => {
|
||||||
const [year, monthNum] = month.split("-");
|
|
||||||
const dateObj = new Date(`${month}-01`);
|
const dateObj = new Date(`${month}-01`);
|
||||||
const label = new Intl.DateTimeFormat("id-ID", {
|
const label = new Intl.DateTimeFormat("id-ID", {
|
||||||
month: "long",
|
month: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
}).format(dateObj); // hasil: "Juli 2025"
|
}).format(dateObj);
|
||||||
return { month: label, count };
|
return { month: label, count };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -124,11 +113,18 @@ const FileListComponent = ({
|
|||||||
fetchFiles();
|
fetchFiles();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const formatPhoneNumber = (phone) =>
|
useEffect(() => {
|
||||||
phone?.replace(/(\d{4})(\d{4})(\d{4})/, "$1-$2-$3");
|
if (selectedDocumentType) {
|
||||||
|
setFilteredFiles(
|
||||||
|
files.filter((file) => file.document_type === selectedDocumentType)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setFilteredFiles(files);
|
||||||
|
}
|
||||||
|
}, [selectedDocumentType, files]);
|
||||||
|
|
||||||
const handleRowClick = async (file) => {
|
const handleRowClick = async (file) => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
alert("Token tidak ditemukan. Silakan login kembali.");
|
alert("Token tidak ditemukan. Silakan login kembali.");
|
||||||
return;
|
return;
|
||||||
@@ -136,60 +132,51 @@ const FileListComponent = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://bot.kediritechnopark.com/webhook/6915ea36-e1f4-49ad-a7f1-a27ce0bf2279/ktp/${file.nik}`,
|
`https://bot.kediritechnopark.com/webhook/solid-data/merged?nama_lengkap=${encodeURIComponent(
|
||||||
|
file.nama_lengkap
|
||||||
|
)}`,
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: token, // atau `Bearer ${token}` jika diperlukan
|
Authorization: `Bearer ${token}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok)
|
||||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
}
|
|
||||||
|
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
if (!text) {
|
if (!text) throw new Error("Respons kosong dari server.");
|
||||||
throw new Error("Respons kosong dari server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = JSON.parse(text);
|
const data = JSON.parse(text);
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = data[0];
|
setSelectedFile(data[0]);
|
||||||
|
|
||||||
if (!item) {
|
|
||||||
alert("Data tidak ditemukan.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validasi jika ada image URL
|
|
||||||
if (item.foto_url && !item.foto_url.match(/\.(jpg|jpeg|png|webp)$/i)) {
|
|
||||||
console.warn(
|
|
||||||
"URL foto bukan format gambar yang didukung:",
|
|
||||||
item.foto_url
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedFile(item); // tampilkan di modal misalnya
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Gagal mengambil detail:", error.message || error);
|
console.error("Gagal mengambil detail:", error.message);
|
||||||
alert("Gagal mengambil detail. Pastikan data tersedia.");
|
alert("Gagal mengambil detail. Pastikan data tersedia.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const getImageSrc = (base64) => {
|
||||||
setSelectedFile(null);
|
if (!base64) return null;
|
||||||
|
const cleaned = base64.replace(/\s/g, "");
|
||||||
|
if (cleaned.startsWith("iVBOR")) return `data:image/png;base64,${cleaned}`;
|
||||||
|
if (cleaned.startsWith("/9j/")) return `data:image/jpeg;base64,${cleaned}`;
|
||||||
|
if (cleaned.startsWith("UklGR")) return `data:image/webp;base64,${cleaned}`;
|
||||||
|
return `data:image/*;base64,${cleaned}`;
|
||||||
};
|
};
|
||||||
const exportToExcel = (data) => {
|
|
||||||
const domain = window.location.origin;
|
|
||||||
|
|
||||||
|
const closeModal = () => setSelectedFile(null);
|
||||||
|
|
||||||
|
const formatPhoneNumber = (phone) =>
|
||||||
|
phone?.replace(/(\d{4})(\d{4})(\d{4})/, "$1-$2-$3");
|
||||||
|
|
||||||
|
const exportToExcel = (data) => {
|
||||||
const modifiedData = data.map((item) => ({
|
const modifiedData = data.map((item) => ({
|
||||||
ID: item.id,
|
ID: item.id,
|
||||||
Petugas_ID: item.petugas_id,
|
Petugas_ID: item.petugas_id,
|
||||||
@@ -200,7 +187,8 @@ const FileListComponent = ({
|
|||||||
Tanggal_Lahir: new Date(item.tanggal_lahir),
|
Tanggal_Lahir: new Date(item.tanggal_lahir),
|
||||||
Jenis_Kelamin: item.jenis_kelamin,
|
Jenis_Kelamin: item.jenis_kelamin,
|
||||||
Alamat: item.alamat,
|
Alamat: item.alamat,
|
||||||
RT_RW: item.rt_rw,
|
RT: item.rt,
|
||||||
|
RW: item.rw,
|
||||||
Kel_Desa: item.kel_desa,
|
Kel_Desa: item.kel_desa,
|
||||||
Kecamatan: item.kecamatan,
|
Kecamatan: item.kecamatan,
|
||||||
Agama: item.agama,
|
Agama: item.agama,
|
||||||
@@ -213,61 +201,40 @@ const FileListComponent = ({
|
|||||||
Pembuatan: new Date(item.pembuatan),
|
Pembuatan: new Date(item.pembuatan),
|
||||||
Kota_Pembuatan: item.kota_pembuatan,
|
Kota_Pembuatan: item.kota_pembuatan,
|
||||||
Created_At: new Date(item.created_at),
|
Created_At: new Date(item.created_at),
|
||||||
ImageURL: `${domain}/${item.nik}`,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const worksheet = XLSX.utils.json_to_sheet(modifiedData);
|
const worksheet = XLSX.utils.json_to_sheet(modifiedData);
|
||||||
|
|
||||||
// Add hyperlink to ImageURL column (last column)
|
|
||||||
modifiedData.forEach((item, index) => {
|
|
||||||
const cellAddress = `W${index + 2}`; // Column W (ImageURL), starts at row 2
|
|
||||||
if (worksheet[cellAddress]) {
|
|
||||||
worksheet[cellAddress].l = {
|
|
||||||
Target: item.ImageURL,
|
|
||||||
Tooltip: "Lihat Gambar",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Optional: Auto column widths (you can fine-tune)
|
|
||||||
worksheet["!cols"] = new Array(Object.keys(modifiedData[0]).length).fill({
|
|
||||||
wch: 20,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add autofilter
|
|
||||||
worksheet["!autofilter"] = { ref: `A1:W1` }; // Covers all columns (A to W)
|
|
||||||
|
|
||||||
// Export
|
|
||||||
const workbook = XLSX.utils.book_new();
|
const workbook = XLSX.utils.book_new();
|
||||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Data");
|
XLSX.utils.book_append_sheet(workbook, worksheet, "Data");
|
||||||
XLSX.writeFile(workbook, "data-export.xlsx");
|
XLSX.writeFile(workbook, "data-export.xlsx");
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className={styles.fileListSection}>
|
|
||||||
<div className={styles.emptyState}>
|
|
||||||
<div className={styles.spinner}></div>
|
|
||||||
<div className={styles.emptyStateTitle}>Memuat file...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.fileListSection}>
|
<div className={styles.fileListSection}>
|
||||||
<div className={styles.fileListHeader}>
|
<div className={styles.fileListHeader}>
|
||||||
<h2 className={styles.fileListTitle}>📁 Daftar Anggota</h2>
|
<h2 className={styles.fileListTitle}>📁 Daftar Document</h2>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
||||||
|
<select
|
||||||
|
value={selectedDocumentType}
|
||||||
|
onChange={(e) => setSelectedDocumentType(e.target.value)}
|
||||||
|
className={styles.fileCount}
|
||||||
|
>
|
||||||
|
<option value="">Semua</option>
|
||||||
|
<option value="ktp">KTP</option>
|
||||||
|
<option value="kk">KK</option>
|
||||||
|
<option value="akta_kelahiran">Akta Kelahiran</option>
|
||||||
|
</select>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
exportToExcel(files);
|
exportToExcel(filteredFiles);
|
||||||
}}
|
}}
|
||||||
className={styles.downloadButton}
|
className={styles.downloadButton}
|
||||||
>
|
>
|
||||||
⬇️ Unduh Excel
|
⬇️ Unduh Excel
|
||||||
</button>
|
</button>
|
||||||
<span className={styles.fileCount}>{files.length} anggota</span>
|
<span className={styles.fileCount}>
|
||||||
|
{filteredFiles.length} document
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -279,11 +246,11 @@ const FileListComponent = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.tableContainer}>
|
<div className={styles.tableContainer}>
|
||||||
{files.length === 0 ? (
|
{filteredFiles.length === 0 ? (
|
||||||
<div className={styles.emptyState}>
|
<div className={styles.emptyState}>
|
||||||
<div className={styles.emptyStateTitle}>Belum ada data</div>
|
<div className={styles.emptyStateTitle}>Belum ada data</div>
|
||||||
<p className={styles.emptyStateText}>
|
<p className={styles.emptyStateText}>
|
||||||
Tidak ada data KTP yang tersedia saat ini.
|
Tidak ada data KK yang tersedia saat ini.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -292,13 +259,12 @@ const FileListComponent = ({
|
|||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>NIK</th>
|
<th>NIK</th>
|
||||||
|
<th>Jenis</th>
|
||||||
<th className={styles.nameColumn}>Nama Lengkap</th>
|
<th className={styles.nameColumn}>Nama Lengkap</th>
|
||||||
<th>No. HP</th>
|
|
||||||
<th>Email</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{files.map((file, index) => (
|
{filteredFiles.map((file, index) => (
|
||||||
<tr
|
<tr
|
||||||
key={file.id}
|
key={file.id}
|
||||||
onClick={() => handleRowClick(file)}
|
onClick={() => handleRowClick(file)}
|
||||||
@@ -306,27 +272,26 @@ const FileListComponent = ({
|
|||||||
>
|
>
|
||||||
<td>{index + 1}</td>
|
<td>{index + 1}</td>
|
||||||
<td>{file.nik}</td>
|
<td>{file.nik}</td>
|
||||||
|
<td>{file.document_type}</td>
|
||||||
<td className={styles.nameColumn}>{file.nama_lengkap}</td>
|
<td className={styles.nameColumn}>{file.nama_lengkap}</td>
|
||||||
<td>{formatPhoneNumber(file.no_hp)}</td>
|
|
||||||
<td>{file.email}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* Modal dan komponen lainnya tetap seperti sebelumnya */}
|
||||||
{/* Modal Detail */}
|
|
||||||
{selectedFile && (
|
{selectedFile && (
|
||||||
<div className={styles.modalOverlay} onClick={closeModal}>
|
<div className={styles.modalOverlay} onClick={closeModal}>
|
||||||
|
{" "}
|
||||||
<div
|
<div
|
||||||
className={styles.modalContent}
|
className={styles.modalContent}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* Foto KTP */}
|
{" "}
|
||||||
{selectedFile.data && (
|
{selectedFile.data && (
|
||||||
<img
|
<img
|
||||||
src={`data:image/jpeg;base64,${selectedFile.data}`}
|
src={getImageSrc(selectedFile.data)}
|
||||||
alt={`Foto KTP - ${selectedFile.nik}`}
|
alt={`Foto KTP - ${selectedFile.nik}`}
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -337,89 +302,80 @@ const FileListComponent = ({
|
|||||||
boxShadow: "0 2px 6px rgba(0,0,0,0.2)",
|
boxShadow: "0 2px 6px rgba(0,0,0,0.2)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}{" "}
|
||||||
|
<h3>🪪 Detail Data Document</h3>
|
||||||
<h3>🪪 Detail Data Anggota</h3>
|
<div style={{ marginBottom: "1rem" }}>
|
||||||
|
<PDFDownloadLink
|
||||||
|
document={
|
||||||
|
<KTPPDF
|
||||||
|
data={{
|
||||||
|
...selectedFile,
|
||||||
|
data:
|
||||||
|
selectedFile.data?.startsWith("/") ||
|
||||||
|
selectedFile.data?.length < 50
|
||||||
|
? null
|
||||||
|
: selectedFile.data.replace(/\s/g, ""),
|
||||||
|
fallbackImage: selectedFile.foto_url,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
fileName={`KTP_${selectedFile.nik}.pdf`}
|
||||||
|
style={{
|
||||||
|
textDecoration: "none",
|
||||||
|
padding: "8px 16px",
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "#00adef",
|
||||||
|
borderRadius: "6px",
|
||||||
|
display: "inline-block",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading }) =>
|
||||||
|
loading ? "Menyiapkan PDF..." : "⬇️ Unduh PDF"
|
||||||
|
}
|
||||||
|
</PDFDownloadLink>
|
||||||
|
</div>
|
||||||
<table className={styles.detailTable}>
|
<table className={styles.detailTable}>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
{[
|
||||||
<td>NIK</td>
|
["NIK", selectedFile.nik],
|
||||||
<td>{selectedFile.nik}</td>
|
["No.Al", selectedFile.no_al],
|
||||||
</tr>
|
["Nomor Akta Kelahiran", selectedFile.akta_kelahiran_nomor],
|
||||||
<tr>
|
["Nama Lengkap", selectedFile.nama_lengkap],
|
||||||
<td>Nama Lengkap</td>
|
["Anak Ke", selectedFile.anak_ke],
|
||||||
<td>{selectedFile.nama_lengkap}</td>
|
["Tempat Lahir", selectedFile.tempat_lahir],
|
||||||
</tr>
|
["Tanggal Lahir", selectedFile.tanggal_lahir],
|
||||||
<tr>
|
["Jenis Kelamin", selectedFile.jenis_kelamin],
|
||||||
<td>Tempat Lahir</td>
|
["Alamat", selectedFile.alamat],
|
||||||
<td>{selectedFile.tempat_lahir}</td>
|
["Ayah", selectedFile.ayah],
|
||||||
</tr>
|
["ibu", selectedFile.ibu],
|
||||||
<tr>
|
["RT", selectedFile.rt],
|
||||||
<td>Tanggal Lahir</td>
|
["RW", selectedFile.rw],
|
||||||
<td>{selectedFile.tanggal_lahir}</td>
|
["Kelurahan/Desa", selectedFile.kel_desa],
|
||||||
</tr>
|
["Kecamatan", selectedFile.kecamatan],
|
||||||
<tr>
|
["Agama", selectedFile.agama],
|
||||||
<td>Jenis Kelamin</td>
|
["Status Perkawinan", selectedFile.status_perkawinan],
|
||||||
<td>{selectedFile.jenis_kelamin}</td>
|
["Pekerjaan", selectedFile.pekerjaan],
|
||||||
</tr>
|
["Kewarganegaraan", selectedFile.kewarganegaraan],
|
||||||
<tr>
|
["No HP", selectedFile.no_hp],
|
||||||
<td>Alamat</td>
|
["Email", selectedFile.email],
|
||||||
<td>{selectedFile.alamat}</td>
|
["Berlaku Hingga", selectedFile.berlaku_hingga],
|
||||||
</tr>
|
["Tanggal Pembuatan", selectedFile.pembuatan],
|
||||||
<tr>
|
["Kota Pembuatan", selectedFile.kota_pembuatan],
|
||||||
<td>RT/RW</td>
|
]
|
||||||
<td>{selectedFile.rt_rw}</td>
|
.filter(([_, value]) => value !== null && value !== "")
|
||||||
</tr>
|
.map(([label, value]) => (
|
||||||
<tr>
|
<tr key={label}>
|
||||||
<td>Kelurahan/Desa</td>
|
<td>{label}</td>
|
||||||
<td>{selectedFile.kel_desa}</td>
|
<td>{value}</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Kecamatan</td>
|
|
||||||
<td>{selectedFile.kecamatan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Agama</td>
|
|
||||||
<td>{selectedFile.agama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Status Perkawinan</td>
|
|
||||||
<td>{selectedFile.status_perkawinan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pekerjaan</td>
|
|
||||||
<td>{selectedFile.pekerjaan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Kewarganegaraan</td>
|
|
||||||
<td>{selectedFile.kewarganegaraan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>No HP</td>
|
|
||||||
<td>{selectedFile.no_hp}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Email</td>
|
|
||||||
<td>{selectedFile.email}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Berlaku Hingga</td>
|
|
||||||
<td>{selectedFile.berlaku_hingga}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Tanggal Pembuatan</td>
|
|
||||||
<td>{selectedFile.pembuatan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Kota Pembuatan</td>
|
|
||||||
<td>{selectedFile.kota_pembuatan}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<button className={styles.closeButton} onClick={closeModal}>
|
<button className={styles.closeButton} onClick={closeModal}>
|
||||||
Tutup
|
{" "}
|
||||||
</button>
|
Tutup{" "}
|
||||||
</div>
|
</button>{" "}
|
||||||
|
</div>{" "}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
--neutral-800: #262626;
|
--neutral-800: #262626;
|
||||||
--neutral-900: #171717;
|
--neutral-900: #171717;
|
||||||
--white: #ffffff;
|
--white: #ffffff;
|
||||||
--success-green: #10b981;
|
--success-green: #43a0a7;
|
||||||
--warning-amber: #f59e0b;
|
--warning-amber: #f59e0b;
|
||||||
--error-red: #ef4444;
|
--error-red: #ef4444;
|
||||||
--text-primary: #0f172a;
|
--text-primary: #0f172a;
|
||||||
@@ -72,19 +72,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fileCount {
|
.fileCount {
|
||||||
font-size: 0.875rem;
|
font-size: 0.6rem;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
background-color: #ef4444;
|
background-color: #43a0a7;
|
||||||
padding: 0.25rem 0.75rem;
|
padding: 0.25rem 0.75rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
border: 1px solid var(--border-light);
|
border: 1px solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.successMessage {
|
.successMessage {
|
||||||
background-color: rgb(16 185 129 / 0.1);
|
background-color: rgb(67 160 167 / 0.1);
|
||||||
color: var(--success-green);
|
color: var(--success-green);
|
||||||
border: 1px solid rgb(16 185 129 / 0.2);
|
border: 1px solid rgb(67 160 167 / 0.2);
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fileTable th {
|
.fileTable th {
|
||||||
background-color: #ef4444;
|
background-color: #43a0a7;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
border: 3px solid var(--neutral-300);
|
border: 3px solid var(--neutral-300);
|
||||||
border-top: 3px solid #ef4444;
|
border-top: 3px solid #43a0a7;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
margin: 0 auto 1rem;
|
margin: 0 auto 1rem;
|
||||||
@@ -203,13 +203,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tableContainer::-webkit-scrollbar-thumb {
|
.tableContainer::-webkit-scrollbar-thumb {
|
||||||
background: #ef4444;
|
background: #43a0a7;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableContainer::-webkit-scrollbar-thumb:hover {
|
.tableContainer::-webkit-scrollbar-thumb:hover {
|
||||||
background: #dc2626;
|
background: #306a2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableContainer::-webkit-scrollbar-corner {
|
.tableContainer::-webkit-scrollbar-corner {
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
|
|
||||||
.tableContainer {
|
.tableContainer {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #ef4444 var(--neutral-100);
|
scrollbar-color: #43a0a7 var(--neutral-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modal Styles - Matching Dashboard Design */
|
/* Modal Styles - Matching Dashboard Design */
|
||||||
@@ -291,7 +291,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.closeButton {
|
.closeButton {
|
||||||
background-color: #ef4444;
|
background-color: #43a0a7;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.75rem 1.5rem;
|
||||||
@@ -399,7 +399,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fileCount {
|
.fileCount {
|
||||||
font-size: 0.75rem;
|
font-size: 0.6rem;
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,14 +482,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.downloadButton {
|
.downloadButton {
|
||||||
background-color: #00adef;
|
background-color: #164665;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 6px 12px;
|
padding: 0.25rem 0.5rem;
|
||||||
border-radius: 8px;
|
border-radius: 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 0.9rem;
|
font-size: 0.6rem;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
123
src/KTPPDF.js
Normal file
123
src/KTPPDF.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// components/KTPPDF.js
|
||||||
|
import React from "react";
|
||||||
|
import FileListComponent from "./FileListComponent";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Page,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
Document,
|
||||||
|
StyleSheet,
|
||||||
|
View,
|
||||||
|
} from "@react-pdf/renderer";
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
page: { padding: 30, fontSize: 12 },
|
||||||
|
section: { marginBottom: 10 },
|
||||||
|
title: { fontSize: 18, marginBottom: 10 },
|
||||||
|
image: {
|
||||||
|
width: 180,
|
||||||
|
height: 120,
|
||||||
|
marginBottom: 10,
|
||||||
|
objectFit: "contain",
|
||||||
|
border: "1 solid #000",
|
||||||
|
},
|
||||||
|
label: { fontWeight: "bold" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const getImageSrc = (base64) => {
|
||||||
|
if (!base64) return null;
|
||||||
|
const cleaned = base64.replace(/\s/g, "");
|
||||||
|
|
||||||
|
if (cleaned.startsWith("iVBOR")) {
|
||||||
|
return `data:image/png;base64,${cleaned}`;
|
||||||
|
} else if (cleaned.startsWith("/9j/")) {
|
||||||
|
return `data:image/jpeg;base64,${cleaned}`;
|
||||||
|
} else if (cleaned.startsWith("UklGR")) {
|
||||||
|
return `data:image/webp;base64,${cleaned}`;
|
||||||
|
} else {
|
||||||
|
return `data:image/*;base64,${cleaned}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const KTPPDF = ({ data }) => (
|
||||||
|
<Document>
|
||||||
|
<Page size="A4" style={styles.page}>
|
||||||
|
<Text style={styles.title}>Biodata Anggota</Text>
|
||||||
|
{data.data ? (
|
||||||
|
<Image style={styles.image} src={getImageSrc(data.data)} />
|
||||||
|
) : data.fallbackImage ? (
|
||||||
|
<Image style={styles.image} src={data.fallbackImage} />
|
||||||
|
) : (
|
||||||
|
<Text>Tidak ada foto KTP tersedia</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>NIK:</Text> {data.nik}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Nama Lengkap:</Text> {data.nama_lengkap}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Tempat Lahir:</Text>{" "}
|
||||||
|
{data.tempat_lahir || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Tanggal Lahir:</Text>{" "}
|
||||||
|
{data.tanggal_lahir || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Jenis Kelamin:</Text>{" "}
|
||||||
|
{data.jenis_kelamin || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Alamat:</Text> {data.alamat || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>RT/RW:</Text> {data.rt_rw || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Kel/Desa:</Text> {data.kel_desa || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Kecamatan:</Text> {data.kecamatan || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Agama:</Text> {data.agama || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Status Perkawinan:</Text>{" "}
|
||||||
|
{data.status_perkawinan || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Pekerjaan:</Text> {data.pekerjaan || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Kewarganegaraan:</Text>{" "}
|
||||||
|
{data.kewarganegaraan || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>No HP:</Text> {data.no_hp || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Email:</Text> {data.email || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Berlaku Hingga:</Text>{" "}
|
||||||
|
{data.berlaku_hingga || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Tanggal Pembuatan:</Text>{" "}
|
||||||
|
{data.pembuatan || "-"}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text style={styles.label}>Kota Pembuatan:</Text>{" "}
|
||||||
|
{data.kota_pembuatan || "-"}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</Page>
|
||||||
|
</Document>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default KTPPDF;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
.overlay-box {
|
.overlay-box {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 3px dashed red;
|
border: 3px dashed #43a0a7;
|
||||||
width: 80%; /* atau sesuaikan */
|
width: 80%; /* atau sesuaikan */
|
||||||
aspect-ratio: 85.6 / 53.98;
|
aspect-ratio: 85.6 / 53.98;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|||||||
1168
src/KTPScanner.js
1168
src/KTPScanner.js
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ const Login = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const loginResponse = await fetch(
|
const loginResponse = await fetch(
|
||||||
"https://bot.kediritechnopark.com/webhook/login/psi",
|
"https://bot.kediritechnopark.com/webhook/solid-data/login",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -48,8 +48,8 @@ const Login = () => {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.loginContainer}>
|
<div className={styles.loginContainer}>
|
||||||
<div className={styles.loginBox}>
|
<div className={styles.loginBox}>
|
||||||
<img src="/PSI.png" alt="Logo" className={styles.logo} />
|
<img src="/ikasapta.png" alt="Logo" className={styles.logo} />
|
||||||
<h1 className={styles.h1}>Kawal PSI</h1>
|
<h1 className={styles.h1}>SOLID DATA</h1>
|
||||||
<p className={styles.subtitle}>
|
<p className={styles.subtitle}>
|
||||||
Silakan masuk untuk melanjutkan ke dashboard
|
Silakan masuk untuk melanjutkan ke dashboard
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -28,9 +28,8 @@
|
|||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
color: #ef4444; /* 🔴 Warna merah PSI */
|
color: #43a0a7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
@@ -56,7 +55,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
background-color: #ef4444; /* 🔴 Warna merah PSI */
|
background-color: #43a0a7;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
@@ -69,7 +68,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
background-color: #b71c1c; /* versi lebih gelap saat hover */
|
background-color: #357734; /* darker shade of #43a0a7 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import styles from "./ProfileTab.module.css";
|
import dashboardStyles from "./Dashboard.module.css";
|
||||||
|
import profileStyles from "./ProfileTab.module.css";
|
||||||
|
|
||||||
const ProfileTab = () => {
|
const ProfileTab = () => {
|
||||||
const menuRef = useRef(null);
|
const menuRef = useRef(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
const [profile, setProfile] = useState({});
|
const [user, setUser] = useState({});
|
||||||
const [profileTemp, setProfileTemp] = useState({});
|
const [userTemp, setUserTemp] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
@@ -27,34 +28,62 @@ const ProfileTab = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const dummyProfile = {
|
const verifyTokenAndFetchData = async () => {
|
||||||
username: "admin",
|
const token = localStorage.getItem("token");
|
||||||
|
if (!token) {
|
||||||
|
window.location.href = "/login";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
"https://bot.kediritechnopark.com/webhook/solid-data/dashboard",
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok || !data[0].username) {
|
||||||
|
throw new Error("Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(data[0]);
|
||||||
|
setUserTemp(data[0]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Token tidak valid:", error.message);
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setProfile(dummyProfile);
|
verifyTokenAndFetchData();
|
||||||
setProfileTemp(dummyProfile);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
setProfile((prev) => ({ ...prev, [name]: value }));
|
setUser((prev) => ({ ...prev, [name]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
if (!profile.oldPassword || !profile.newPassword) {
|
if (!user.oldPassword || !user.newPassword) {
|
||||||
alert("Password lama dan baru tidak boleh kosong.");
|
alert("Password lama dan baru tidak boleh kosong.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
username: profile.username,
|
username: user.username,
|
||||||
oldPassword: profile.oldPassword,
|
oldPassword: user.oldPassword,
|
||||||
newPassword: profile.newPassword,
|
newPassword: user.newPassword,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://bot.kediritechnopark.com/webhook/reset-password/psi",
|
"https://bot.kediritechnopark.com/webhook/solid-data/reset-password",
|
||||||
{
|
{
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -77,21 +106,26 @@ const ProfileTab = () => {
|
|||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
setProfile(profileTemp);
|
setUser(userTemp);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.dashboardContainer}>
|
<div className={dashboardStyles.dashboardContainer}>
|
||||||
<div className={styles.dashboardHeader}>
|
<div className={dashboardStyles.dashboardHeader}>
|
||||||
<div className={styles.logoAndTitle}>
|
<div className={dashboardStyles.logoAndTitle}>
|
||||||
<img src="/PSI.png" alt="Profile Avatar" />
|
<img src="/ikasapta.png" alt="Bot Avatar" />
|
||||||
<h1 className={styles.h1}>Kawal PSI Profile</h1>
|
<h1 className={dashboardStyles.h1}>SOLID</h1>
|
||||||
|
<h1 className={dashboardStyles.h1} styles="color: #43a0a7;">
|
||||||
|
DATA
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.dropdownContainer} ref={menuRef}>
|
<div className={dashboardStyles.dropdownContainer} ref={menuRef}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
className={styles.dropdownToggle}
|
className={dashboardStyles.dropdownToggle}
|
||||||
|
aria-expanded={isMenuOpen ? "true" : "false"}
|
||||||
|
aria-haspopup="true"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width="15"
|
width="15"
|
||||||
@@ -108,24 +142,32 @@ const ProfileTab = () => {
|
|||||||
<line x1="3" y1="18" x2="21" y2="18" />
|
<line x1="3" y1="18" x2="21" y2="18" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{isMenuOpen && (
|
{isMenuOpen && (
|
||||||
<div className={styles.dropdownMenu}>
|
<div className={dashboardStyles.dropdownMenu}>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/dashboard");
|
navigate("/dashboard");
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
}}
|
}}
|
||||||
className={styles.dropdownItem}
|
className={dashboardStyles.dropdownItem}
|
||||||
>
|
>
|
||||||
Dashboard
|
Dashboard
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/scan");
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
className={dashboardStyles.dropdownItem}
|
||||||
|
>
|
||||||
|
Scan
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleLogout();
|
handleLogout();
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
}}
|
}}
|
||||||
className={styles.dropdownItem}
|
className={dashboardStyles.dropdownItem}
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
@@ -134,43 +176,43 @@ const ProfileTab = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.mainContent}>
|
<div className={profileStyles.mainContent}>
|
||||||
<div className={styles.profileSection}>
|
<div className={profileStyles.profileSection}>
|
||||||
<div className={styles.profileCard}>
|
<div className={profileStyles.profileCard}>
|
||||||
<div className={styles.profileHeader}>
|
<div className={profileStyles.profileHeader}>
|
||||||
<h2>Account</h2>
|
<h2>Account</h2>
|
||||||
{!isEditing ? (
|
{!isEditing ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsEditing(true)}
|
onClick={() => setIsEditing(true)}
|
||||||
className={styles.editButton}
|
className={profileStyles.editButton}
|
||||||
>
|
>
|
||||||
Change Password
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.actionButtons}>
|
<div className={profileStyles.actionButtons}>
|
||||||
<button
|
<button
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
className={styles.cancelButton}
|
className={profileStyles.cancelButton}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button onClick={handleSave} className={styles.saveButton}>
|
<button onClick={handleSave} className={profileStyles.saveButton}>
|
||||||
Save Changes
|
Save Changes
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.profileForm}>
|
<div className={profileStyles.profileForm}>
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
<div className={styles.inputGroup}>
|
<div className={profileStyles.inputGroup}>
|
||||||
<label className={styles.inputLabel}>Username</label>
|
<label className={profileStyles.inputLabel}>Username</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="username"
|
name="username"
|
||||||
value={profile.username}
|
value={user.username}
|
||||||
className={`${styles.input} ${
|
className={`${profileStyles.input} ${
|
||||||
!isEditing ? styles.readOnly : ""
|
!isEditing ? profileStyles.readOnly : ""
|
||||||
}`}
|
}`}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
@@ -179,25 +221,25 @@ const ProfileTab = () => {
|
|||||||
|
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.inputGroup}>
|
<div className={profileStyles.inputGroup}>
|
||||||
<label className={styles.inputLabel}>
|
<label className={profileStyles.inputLabel}>
|
||||||
Current Password
|
Current Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
name="oldPassword"
|
name="oldPassword"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={styles.input}
|
className={profileStyles.input}
|
||||||
placeholder="Enter current password"
|
placeholder="Enter current password"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.inputGroup}>
|
<div className={profileStyles.inputGroup}>
|
||||||
<label className={styles.inputLabel}>New Password</label>
|
<label className={profileStyles.inputLabel}>New Password</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
name="newPassword"
|
name="newPassword"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={styles.input}
|
className={profileStyles.input}
|
||||||
placeholder="Enter new password"
|
placeholder="Enter new password"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -208,8 +250,8 @@ const ProfileTab = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.footer}>
|
<div className={dashboardStyles.footer}>
|
||||||
© 2025 Kediri Technopark • Dermalounge AI Admin
|
© 2025 Kediri Technopark • Dashboard SOLID DATA
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* ProfileTab.module.css - Modern Design */
|
/* ProfileTab.module.css - Modern Design with Unified Header */
|
||||||
|
|
||||||
/* Modern Color Palette */
|
/* Modern Color Palette */
|
||||||
:root {
|
:root {
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
--neutral-800: #262626;
|
--neutral-800: #262626;
|
||||||
--neutral-900: #171717;
|
--neutral-900: #171717;
|
||||||
--white: #ffffff;
|
--white: #ffffff;
|
||||||
--success-green: #10b981;
|
--success-green: #43a0a7;
|
||||||
--warning-amber: #f59e0b;
|
--warning-amber: #f59e0b;
|
||||||
--error-red: #ef4444;
|
--error-red: #ef4444;
|
||||||
--text-primary: #0f172a;
|
--text-primary: #0f172a;
|
||||||
@@ -50,6 +50,7 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- UNIFIED HEADER (sama dengan Dashboard.css) --- */
|
||||||
.dashboardHeader {
|
.dashboardHeader {
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
@@ -58,7 +59,7 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
border-bottom: 3px solid #ef4444;
|
border-bottom: 3px solid #43a0a7; /* Warna dari Dashboard.css */
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
@@ -80,10 +81,18 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dashboardHeader .h1 {
|
.dashboardHeader .h1 {
|
||||||
|
margin: 2px; /* Sama dengan Dashboard.css */
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #43a0a7; /* Warna dari Dashboard.css */
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #ed4344;
|
color: #154666;
|
||||||
letter-spacing: -0.025em;
|
letter-spacing: -0.025em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +105,12 @@ body {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userDisplayName {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdownToggle {
|
.dropdownToggle {
|
||||||
background-color: var(--neutral-100);
|
background-color: var(--neutral-100);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
@@ -155,7 +170,7 @@ body {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main Content */
|
/* --- MAIN CONTENT --- */
|
||||||
.mainContent {
|
.mainContent {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 2rem 1.5rem;
|
padding: 2rem 1.5rem;
|
||||||
@@ -205,7 +220,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editButton {
|
.editButton {
|
||||||
background-color: #ef4444;
|
background-color: #43a0a7; /* Diseragamkan dengan warna header */
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.75rem 1.5rem;
|
||||||
@@ -218,7 +233,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editButton:hover {
|
.editButton:hover {
|
||||||
background-color: var(--dark-blue);
|
background-color: #357734;
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
@@ -328,7 +343,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.licenseCard {
|
.licenseCard {
|
||||||
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
#43a0a7 0%,
|
||||||
|
#357734 100%
|
||||||
|
); /* Diseragamkan dengan warna header */
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
@@ -421,7 +440,7 @@ body {
|
|||||||
border-top: 1px solid var(--border-light);
|
border-top: 1px solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* --- RESPONSIVE DESIGN --- */
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.dashboardHeader {
|
.dashboardHeader {
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
@@ -436,6 +455,10 @@ body {
|
|||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userDisplayName {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
padding: 2.5rem 2rem;
|
padding: 2.5rem 2rem;
|
||||||
gap: 2.5rem;
|
gap: 2.5rem;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const ShowImage = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://bot.kediritechnopark.com/webhook/0f4420a8-8517-49ba-8ec5-75adde117813/ktp/img/${nik}`,
|
`https://bot.kediritechnopark.com/webhook/ed467164-05c0-4692-bb81-a8f13116bb1b/ktp/img/ikasapta/:nik/${nik}`,
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
Reference in New Issue
Block a user