ok
This commit is contained in:
408
package-lock.json
generated
408
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "workspace",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@react-pdf/renderer": "^4.3.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@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": {
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz",
|
||||
@@ -3279,6 +3445,14 @@
|
||||
"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": {
|
||||
"version": "10.4.0",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "1.3.8",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||
@@ -4976,6 +5174,14 @@
|
||||
"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": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
@@ -5086,11 +5292,27 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
|
||||
"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": {
|
||||
"version": "4.25.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
|
||||
@@ -5426,6 +5648,14 @@
|
||||
"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": {
|
||||
"version": "2.1.1",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||
@@ -5778,6 +6017,11 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.2.2",
|
||||
"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": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
@@ -8694,6 +8959,19 @@
|
||||
"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": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
|
||||
@@ -8888,6 +9166,11 @@
|
||||
"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": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
@@ -9674,6 +9957,14 @@
|
||||
"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": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||
@@ -10784,6 +11075,23 @@
|
||||
"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": {
|
||||
"version": "1.2.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -11317,6 +11630,14 @@
|
||||
"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": {
|
||||
"version": "6.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
@@ -11692,6 +12018,11 @@
|
||||
"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": {
|
||||
"version": "6.0.1",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -14029,6 +14368,11 @@
|
||||
"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": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||
@@ -14675,6 +15019,19 @@
|
||||
"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": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
@@ -15328,6 +15685,11 @@
|
||||
"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": {
|
||||
"version": "2.0.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
@@ -16070,6 +16437,15 @@
|
||||
"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": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
|
||||
@@ -16078,6 +16454,20 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||
@@ -16260,6 +16650,19 @@
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@react-pdf/renderer": "^4.3.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@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">
|
||||
<head>
|
||||
<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="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<meta name="description" content="Website SOLID DATA" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
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`.
|
||||
-->
|
||||
|
||||
<title>React App</title>
|
||||
<title>SOLID DATA</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"short_name": "IKASAPTA",
|
||||
"name": "IKASAPTA HUB",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"src": "ikasapta.png",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"src": "ikasapta.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"src": "ikasapta.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ const Dashboard = () => {
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://bot.kediritechnopark.com/webhook/dashboard/psi",
|
||||
"https://bot.kediritechnopark.com/webhook/solid-data/dashboard",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -75,7 +75,7 @@ const Dashboard = () => {
|
||||
const token = localStorage.getItem("token");
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://bot.kediritechnopark.com/webhook/list-user/psi",
|
||||
"https://bot.kediritechnopark.com/webhook/solid-data/list-user",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -110,7 +110,7 @@ const Dashboard = () => {
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://bot.kediritechnopark.com/webhook/add-officer",
|
||||
"https://bot.kediritechnopark.com/webhook/solid-data/add-officer",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -163,7 +163,7 @@ const Dashboard = () => {
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://bot.kediritechnopark.com/webhook/psi/delete-officer`,
|
||||
`https://bot.kediritechnopark.com/webhook/solid-data/delete-officer`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
@@ -193,8 +193,11 @@ const Dashboard = () => {
|
||||
<div className={styles.dashboardContainer}>
|
||||
<div className={styles.dashboardHeader}>
|
||||
<div className={styles.logoAndTitle}>
|
||||
<img src="/PSI.png" alt="Bot Avatar" />
|
||||
<h1 className={styles.h1}>Kawal PSI Dashboard</h1>
|
||||
<img src="/ikasapta.png" alt="Bot Avatar" />
|
||||
<h1 className={styles.h1}>SOLID</h1>
|
||||
<h1 className={styles.h1} styles="color: #43a0a7;">
|
||||
DATA
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className={styles.dropdownContainer} ref={menuRef}>
|
||||
@@ -343,7 +346,7 @@ const Dashboard = () => {
|
||||
)}
|
||||
|
||||
<div className={styles.chartSection}>
|
||||
<h2>Grafik Pertumbuhan Anggota</h2>
|
||||
<h2>Grafik Upload Document</h2>
|
||||
{officerPerformanceData.length > 0 ? (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={officerPerformanceData}>
|
||||
@@ -355,7 +358,7 @@ const Dashboard = () => {
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<div className={styles.warning}>
|
||||
📋 Belum ada data performa untuk ditampilkan
|
||||
📋 Belum ada data upload untuk ditampilkan
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -370,7 +373,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
|
||||
<div className={styles.footer}>
|
||||
© 2025 Kediri Technopark • Dashboard PSI
|
||||
© 2025 Kediri Technopark • Dashboard SOLID DATA
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
--neutral-800: #262626;
|
||||
--neutral-900: #171717;
|
||||
--white: #ffffff;
|
||||
--success-green: #10b981;
|
||||
--success-green: #43a0a7;
|
||||
--warning-amber: #f59e0b;
|
||||
--error-red: #ef4444;
|
||||
--text-primary: #0f172a;
|
||||
@@ -59,7 +59,7 @@ body {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: var(--shadow-sm);
|
||||
border-bottom: 3px solid #ef4444;
|
||||
border-bottom: 3px solid #43a0a7;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
@@ -81,10 +81,18 @@ body {
|
||||
}
|
||||
|
||||
.dashboardHeader .h1 {
|
||||
margin: 2px;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #43a0a7;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.data {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #ed4344;
|
||||
color: #154666;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
@@ -207,7 +215,7 @@ body {
|
||||
.summaryCard p {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #ef4444;
|
||||
color: #43a0a7;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
@@ -270,7 +278,7 @@ body {
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
background-color: #ef4444;
|
||||
background-color: #43a0a7;
|
||||
color: var(--text-light);
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
@@ -285,7 +293,7 @@ body {
|
||||
}
|
||||
|
||||
.submitButton:hover {
|
||||
background-color: #d03b3b;
|
||||
background-color: #357734;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
@@ -296,9 +304,9 @@ body {
|
||||
|
||||
/* Messages */
|
||||
.success {
|
||||
background-color: rgb(16 185 129 / 0.1);
|
||||
background-color: rgb(67 160 167 / 0.1);
|
||||
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;
|
||||
border-radius: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
@@ -318,9 +326,9 @@ body {
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: #ef444417;
|
||||
color: #ef4444;
|
||||
border: 1px solid #ef444433;
|
||||
background-color: rgb(67 160 167 / 0.1);
|
||||
color: #43a0a7;
|
||||
border: 1px solid rgb(67 160 167 / 0.2);
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import styles from "./FileListComponent.module.css";
|
||||
import * as XLSX from "xlsx";
|
||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||
import KTPPDF from "./KTPPDF";
|
||||
|
||||
const FileListComponent = ({
|
||||
setTotalFilesSentToday,
|
||||
@@ -9,17 +11,18 @@ const FileListComponent = ({
|
||||
setOfficerPerformanceData,
|
||||
}) => {
|
||||
const [files, setFiles] = useState([]);
|
||||
const [filteredFiles, setFilteredFiles] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [selectedFile, setSelectedFile] = useState(null);
|
||||
const [successMessage, setSuccessMessage] = useState("");
|
||||
const [selectedDocumentType, setSelectedDocumentType] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFiles = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://bot.kediritechnopark.com/webhook/files",
|
||||
"https://bot.kediritechnopark.com/webhook/solid-data/files",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -29,35 +32,25 @@ const FileListComponent = ({
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!data.success || !Array.isArray(data.data)) {
|
||||
if (!data.success || !Array.isArray(data.data))
|
||||
throw new Error("Format respons tidak valid.");
|
||||
}
|
||||
|
||||
const fileData = data.data;
|
||||
|
||||
// 1. Set ke state
|
||||
setFiles(fileData);
|
||||
setFilteredFiles(fileData);
|
||||
|
||||
// 2. Hitung total file hari ini
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const totalToday = fileData.filter((f) =>
|
||||
f.created_at.startsWith(today)
|
||||
).length;
|
||||
setTotalFilesSentToday(totalToday);
|
||||
|
||||
// 3. Hitung total bulan ini
|
||||
const now = new Date();
|
||||
const currentMonth = now.getMonth();
|
||||
const currentYear = now.getFullYear();
|
||||
@@ -69,10 +62,8 @@ const FileListComponent = ({
|
||||
}).length;
|
||||
setTotalFilesSentMonth(totalThisMonth);
|
||||
|
||||
// 4. Total keseluruhan
|
||||
setTotalFilesSentOverall(fileData.length);
|
||||
|
||||
// 5. Grafik performa per bulan (dinamis)
|
||||
const dateObjects = fileData.map((item) => new Date(item.created_at));
|
||||
if (dateObjects.length > 0) {
|
||||
const minDate = new Date(Math.min(...dateObjects));
|
||||
@@ -95,19 +86,17 @@ const FileListComponent = ({
|
||||
const monthKey = `${d.getFullYear()}-${String(
|
||||
d.getMonth() + 1
|
||||
).padStart(2, "0")}`;
|
||||
if (monthlyDataMap[monthKey] !== undefined) {
|
||||
if (monthlyDataMap[monthKey] !== undefined)
|
||||
monthlyDataMap[monthKey]++;
|
||||
}
|
||||
});
|
||||
|
||||
const performanceArray = Object.entries(monthlyDataMap).map(
|
||||
([month, count]) => {
|
||||
const [year, monthNum] = month.split("-");
|
||||
const dateObj = new Date(`${month}-01`);
|
||||
const label = new Intl.DateTimeFormat("id-ID", {
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}).format(dateObj); // hasil: "Juli 2025"
|
||||
}).format(dateObj);
|
||||
return { month: label, count };
|
||||
}
|
||||
);
|
||||
@@ -124,11 +113,18 @@ const FileListComponent = ({
|
||||
fetchFiles();
|
||||
}, []);
|
||||
|
||||
const formatPhoneNumber = (phone) =>
|
||||
phone?.replace(/(\d{4})(\d{4})(\d{4})/, "$1-$2-$3");
|
||||
useEffect(() => {
|
||||
if (selectedDocumentType) {
|
||||
setFilteredFiles(
|
||||
files.filter((file) => file.document_type === selectedDocumentType)
|
||||
);
|
||||
} else {
|
||||
setFilteredFiles(files);
|
||||
}
|
||||
}, [selectedDocumentType, files]);
|
||||
|
||||
const handleRowClick = async (file) => {
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
if (!token) {
|
||||
alert("Token tidak ditemukan. Silakan login kembali.");
|
||||
return;
|
||||
@@ -136,60 +132,51 @@ const FileListComponent = ({
|
||||
|
||||
try {
|
||||
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",
|
||||
headers: {
|
||||
Authorization: token, // atau `Bearer ${token}` jika diperlukan
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
if (!text) {
|
||||
throw new Error("Respons kosong dari server.");
|
||||
}
|
||||
if (!text) throw new Error("Respons kosong dari server.");
|
||||
|
||||
const data = JSON.parse(text);
|
||||
|
||||
if (data.error) {
|
||||
alert(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = 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
|
||||
setSelectedFile(data[0]);
|
||||
} catch (error) {
|
||||
console.error("Gagal mengambil detail:", error.message || error);
|
||||
console.error("Gagal mengambil detail:", error.message);
|
||||
alert("Gagal mengambil detail. Pastikan data tersedia.");
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setSelectedFile(null);
|
||||
const getImageSrc = (base64) => {
|
||||
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) => ({
|
||||
ID: item.id,
|
||||
Petugas_ID: item.petugas_id,
|
||||
@@ -200,7 +187,8 @@ const FileListComponent = ({
|
||||
Tanggal_Lahir: new Date(item.tanggal_lahir),
|
||||
Jenis_Kelamin: item.jenis_kelamin,
|
||||
Alamat: item.alamat,
|
||||
RT_RW: item.rt_rw,
|
||||
RT: item.rt,
|
||||
RW: item.rw,
|
||||
Kel_Desa: item.kel_desa,
|
||||
Kecamatan: item.kecamatan,
|
||||
Agama: item.agama,
|
||||
@@ -213,61 +201,40 @@ const FileListComponent = ({
|
||||
Pembuatan: new Date(item.pembuatan),
|
||||
Kota_Pembuatan: item.kota_pembuatan,
|
||||
Created_At: new Date(item.created_at),
|
||||
ImageURL: `${domain}/${item.nik}`,
|
||||
}));
|
||||
|
||||
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();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Data");
|
||||
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 (
|
||||
<div className={styles.fileListSection}>
|
||||
<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" }}>
|
||||
<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
|
||||
onClick={() => {
|
||||
exportToExcel(files);
|
||||
exportToExcel(filteredFiles);
|
||||
}}
|
||||
className={styles.downloadButton}
|
||||
>
|
||||
⬇️ Unduh Excel
|
||||
</button>
|
||||
<span className={styles.fileCount}>{files.length} anggota</span>
|
||||
<span className={styles.fileCount}>
|
||||
{filteredFiles.length} document
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -279,11 +246,11 @@ const FileListComponent = ({
|
||||
)}
|
||||
|
||||
<div className={styles.tableContainer}>
|
||||
{files.length === 0 ? (
|
||||
{filteredFiles.length === 0 ? (
|
||||
<div className={styles.emptyState}>
|
||||
<div className={styles.emptyStateTitle}>Belum ada data</div>
|
||||
<p className={styles.emptyStateText}>
|
||||
Tidak ada data KTP yang tersedia saat ini.
|
||||
Tidak ada data KK yang tersedia saat ini.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -292,13 +259,12 @@ const FileListComponent = ({
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>NIK</th>
|
||||
<th>Jenis</th>
|
||||
<th className={styles.nameColumn}>Nama Lengkap</th>
|
||||
<th>No. HP</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{files.map((file, index) => (
|
||||
{filteredFiles.map((file, index) => (
|
||||
<tr
|
||||
key={file.id}
|
||||
onClick={() => handleRowClick(file)}
|
||||
@@ -306,27 +272,26 @@ const FileListComponent = ({
|
||||
>
|
||||
<td>{index + 1}</td>
|
||||
<td>{file.nik}</td>
|
||||
<td>{file.document_type}</td>
|
||||
<td className={styles.nameColumn}>{file.nama_lengkap}</td>
|
||||
<td>{formatPhoneNumber(file.no_hp)}</td>
|
||||
<td>{file.email}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modal Detail */}
|
||||
{/* Modal dan komponen lainnya tetap seperti sebelumnya */}
|
||||
{selectedFile && (
|
||||
<div className={styles.modalOverlay} onClick={closeModal}>
|
||||
{" "}
|
||||
<div
|
||||
className={styles.modalContent}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Foto KTP */}
|
||||
{" "}
|
||||
{selectedFile.data && (
|
||||
<img
|
||||
src={`data:image/jpeg;base64,${selectedFile.data}`}
|
||||
src={getImageSrc(selectedFile.data)}
|
||||
alt={`Foto KTP - ${selectedFile.nik}`}
|
||||
style={{
|
||||
width: "100%",
|
||||
@@ -337,89 +302,80 @@ const FileListComponent = ({
|
||||
boxShadow: "0 2px 6px rgba(0,0,0,0.2)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<h3>🪪 Detail Data Anggota</h3>
|
||||
)}{" "}
|
||||
<h3>🪪 Detail Data Document</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}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>NIK</td>
|
||||
<td>{selectedFile.nik}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nama Lengkap</td>
|
||||
<td>{selectedFile.nama_lengkap}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tempat Lahir</td>
|
||||
<td>{selectedFile.tempat_lahir}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tanggal Lahir</td>
|
||||
<td>{selectedFile.tanggal_lahir}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Jenis Kelamin</td>
|
||||
<td>{selectedFile.jenis_kelamin}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Alamat</td>
|
||||
<td>{selectedFile.alamat}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RT/RW</td>
|
||||
<td>{selectedFile.rt_rw}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kelurahan/Desa</td>
|
||||
<td>{selectedFile.kel_desa}</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>
|
||||
{[
|
||||
["NIK", selectedFile.nik],
|
||||
["No.Al", selectedFile.no_al],
|
||||
["Nomor Akta Kelahiran", selectedFile.akta_kelahiran_nomor],
|
||||
["Nama Lengkap", selectedFile.nama_lengkap],
|
||||
["Anak Ke", selectedFile.anak_ke],
|
||||
["Tempat Lahir", selectedFile.tempat_lahir],
|
||||
["Tanggal Lahir", selectedFile.tanggal_lahir],
|
||||
["Jenis Kelamin", selectedFile.jenis_kelamin],
|
||||
["Alamat", selectedFile.alamat],
|
||||
["Ayah", selectedFile.ayah],
|
||||
["ibu", selectedFile.ibu],
|
||||
["RT", selectedFile.rt],
|
||||
["RW", selectedFile.rw],
|
||||
["Kelurahan/Desa", selectedFile.kel_desa],
|
||||
["Kecamatan", selectedFile.kecamatan],
|
||||
["Agama", selectedFile.agama],
|
||||
["Status Perkawinan", selectedFile.status_perkawinan],
|
||||
["Pekerjaan", selectedFile.pekerjaan],
|
||||
["Kewarganegaraan", selectedFile.kewarganegaraan],
|
||||
["No HP", selectedFile.no_hp],
|
||||
["Email", selectedFile.email],
|
||||
["Berlaku Hingga", selectedFile.berlaku_hingga],
|
||||
["Tanggal Pembuatan", selectedFile.pembuatan],
|
||||
["Kota Pembuatan", selectedFile.kota_pembuatan],
|
||||
]
|
||||
.filter(([_, value]) => value !== null && value !== "")
|
||||
.map(([label, value]) => (
|
||||
<tr key={label}>
|
||||
<td>{label}</td>
|
||||
<td>{value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<button className={styles.closeButton} onClick={closeModal}>
|
||||
Tutup
|
||||
</button>
|
||||
</div>
|
||||
{" "}
|
||||
Tutup{" "}
|
||||
</button>{" "}
|
||||
</div>{" "}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
--neutral-800: #262626;
|
||||
--neutral-900: #171717;
|
||||
--white: #ffffff;
|
||||
--success-green: #10b981;
|
||||
--success-green: #43a0a7;
|
||||
--warning-amber: #f59e0b;
|
||||
--error-red: #ef4444;
|
||||
--text-primary: #0f172a;
|
||||
@@ -72,19 +72,19 @@
|
||||
}
|
||||
|
||||
.fileCount {
|
||||
font-size: 0.875rem;
|
||||
font-size: 0.6rem;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
background-color: #ef4444;
|
||||
background-color: #43a0a7;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.successMessage {
|
||||
background-color: rgb(16 185 129 / 0.1);
|
||||
background-color: rgb(67 160 167 / 0.1);
|
||||
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;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
@@ -116,7 +116,7 @@
|
||||
}
|
||||
|
||||
.fileTable th {
|
||||
background-color: #ef4444;
|
||||
background-color: #43a0a7;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
@@ -176,7 +176,7 @@
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 3px solid var(--neutral-300);
|
||||
border-top: 3px solid #ef4444;
|
||||
border-top: 3px solid #43a0a7;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 1rem;
|
||||
@@ -203,13 +203,13 @@
|
||||
}
|
||||
|
||||
.tableContainer::-webkit-scrollbar-thumb {
|
||||
background: #ef4444;
|
||||
background: #43a0a7;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.tableContainer::-webkit-scrollbar-thumb:hover {
|
||||
background: #dc2626;
|
||||
background: #306a2f;
|
||||
}
|
||||
|
||||
.tableContainer::-webkit-scrollbar-corner {
|
||||
@@ -218,7 +218,7 @@
|
||||
|
||||
.tableContainer {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #ef4444 var(--neutral-100);
|
||||
scrollbar-color: #43a0a7 var(--neutral-100);
|
||||
}
|
||||
|
||||
/* Modal Styles - Matching Dashboard Design */
|
||||
@@ -291,7 +291,7 @@
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
background-color: #ef4444;
|
||||
background-color: #43a0a7;
|
||||
color: var(--text-light);
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
@@ -399,7 +399,7 @@
|
||||
}
|
||||
|
||||
.fileCount {
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.6rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
@@ -482,14 +482,14 @@
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
background-color: #00adef;
|
||||
background-color: #164665;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 1rem;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.6rem;
|
||||
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 {
|
||||
position: absolute;
|
||||
border: 3px dashed red;
|
||||
border: 3px dashed #43a0a7;
|
||||
width: 80%; /* atau sesuaikan */
|
||||
aspect-ratio: 85.6 / 53.98;
|
||||
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 {
|
||||
const loginResponse = await fetch(
|
||||
"https://bot.kediritechnopark.com/webhook/login/psi",
|
||||
"https://bot.kediritechnopark.com/webhook/solid-data/login",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -48,8 +48,8 @@ const Login = () => {
|
||||
return (
|
||||
<div className={styles.loginContainer}>
|
||||
<div className={styles.loginBox}>
|
||||
<img src="/PSI.png" alt="Logo" className={styles.logo} />
|
||||
<h1 className={styles.h1}>Kawal PSI</h1>
|
||||
<img src="/ikasapta.png" alt="Logo" className={styles.logo} />
|
||||
<h1 className={styles.h1}>SOLID DATA</h1>
|
||||
<p className={styles.subtitle}>
|
||||
Silakan masuk untuk melanjutkan ke dashboard
|
||||
</p>
|
||||
|
||||
@@ -28,9 +28,8 @@
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
color: #ef4444; /* 🔴 Warna merah PSI */
|
||||
color: #43a0a7;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
@@ -56,7 +55,7 @@
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #ef4444; /* 🔴 Warna merah PSI */
|
||||
background-color: #43a0a7;
|
||||
color: #ffffff;
|
||||
padding: 12px 24px;
|
||||
border-radius: 24px;
|
||||
@@ -69,7 +68,7 @@
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #b71c1c; /* versi lebih gelap saat hover */
|
||||
background-color: #357734; /* darker shade of #43a0a7 */
|
||||
}
|
||||
|
||||
.error {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
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 menuRef = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [profile, setProfile] = useState({});
|
||||
const [profileTemp, setProfileTemp] = useState({});
|
||||
const [user, setUser] = useState({});
|
||||
const [userTemp, setUserTemp] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
@@ -27,34 +28,62 @@ const ProfileTab = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const dummyProfile = {
|
||||
username: "admin",
|
||||
const verifyTokenAndFetchData = async () => {
|
||||
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);
|
||||
setProfileTemp(dummyProfile);
|
||||
verifyTokenAndFetchData();
|
||||
}, []);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setProfile((prev) => ({ ...prev, [name]: value }));
|
||||
setUser((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
if (!profile.oldPassword || !profile.newPassword) {
|
||||
if (!user.oldPassword || !user.newPassword) {
|
||||
alert("Password lama dan baru tidak boleh kosong.");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
username: profile.username,
|
||||
oldPassword: profile.oldPassword,
|
||||
newPassword: profile.newPassword,
|
||||
username: user.username,
|
||||
oldPassword: user.oldPassword,
|
||||
newPassword: user.newPassword,
|
||||
};
|
||||
|
||||
const response = await fetch(
|
||||
"https://bot.kediritechnopark.com/webhook/reset-password/psi",
|
||||
"https://bot.kediritechnopark.com/webhook/solid-data/reset-password",
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
@@ -77,21 +106,26 @@ const ProfileTab = () => {
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsEditing(false);
|
||||
setProfile(profileTemp);
|
||||
setUser(userTemp);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.dashboardContainer}>
|
||||
<div className={styles.dashboardHeader}>
|
||||
<div className={styles.logoAndTitle}>
|
||||
<img src="/PSI.png" alt="Profile Avatar" />
|
||||
<h1 className={styles.h1}>Kawal PSI Profile</h1>
|
||||
<div className={dashboardStyles.dashboardContainer}>
|
||||
<div className={dashboardStyles.dashboardHeader}>
|
||||
<div className={dashboardStyles.logoAndTitle}>
|
||||
<img src="/ikasapta.png" alt="Bot Avatar" />
|
||||
<h1 className={dashboardStyles.h1}>SOLID</h1>
|
||||
<h1 className={dashboardStyles.h1} styles="color: #43a0a7;">
|
||||
DATA
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className={styles.dropdownContainer} ref={menuRef}>
|
||||
<div className={dashboardStyles.dropdownContainer} ref={menuRef}>
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className={styles.dropdownToggle}
|
||||
className={dashboardStyles.dropdownToggle}
|
||||
aria-expanded={isMenuOpen ? "true" : "false"}
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<svg
|
||||
width="15"
|
||||
@@ -108,24 +142,32 @@ const ProfileTab = () => {
|
||||
<line x1="3" y1="18" x2="21" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{isMenuOpen && (
|
||||
<div className={styles.dropdownMenu}>
|
||||
<div className={dashboardStyles.dropdownMenu}>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/dashboard");
|
||||
setIsMenuOpen(false);
|
||||
}}
|
||||
className={styles.dropdownItem}
|
||||
className={dashboardStyles.dropdownItem}
|
||||
>
|
||||
Dashboard
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/scan");
|
||||
setIsMenuOpen(false);
|
||||
}}
|
||||
className={dashboardStyles.dropdownItem}
|
||||
>
|
||||
Scan
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleLogout();
|
||||
setIsMenuOpen(false);
|
||||
}}
|
||||
className={styles.dropdownItem}
|
||||
className={dashboardStyles.dropdownItem}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
@@ -134,43 +176,43 @@ const ProfileTab = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.mainContent}>
|
||||
<div className={styles.profileSection}>
|
||||
<div className={styles.profileCard}>
|
||||
<div className={styles.profileHeader}>
|
||||
<div className={profileStyles.mainContent}>
|
||||
<div className={profileStyles.profileSection}>
|
||||
<div className={profileStyles.profileCard}>
|
||||
<div className={profileStyles.profileHeader}>
|
||||
<h2>Account</h2>
|
||||
{!isEditing ? (
|
||||
<button
|
||||
onClick={() => setIsEditing(true)}
|
||||
className={styles.editButton}
|
||||
className={profileStyles.editButton}
|
||||
>
|
||||
Change Password
|
||||
</button>
|
||||
) : (
|
||||
<div className={styles.actionButtons}>
|
||||
<div className={profileStyles.actionButtons}>
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
className={styles.cancelButton}
|
||||
className={profileStyles.cancelButton}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button onClick={handleSave} className={styles.saveButton}>
|
||||
<button onClick={handleSave} className={profileStyles.saveButton}>
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.profileForm}>
|
||||
<div className={profileStyles.profileForm}>
|
||||
{!isEditing && (
|
||||
<div className={styles.inputGroup}>
|
||||
<label className={styles.inputLabel}>Username</label>
|
||||
<div className={profileStyles.inputGroup}>
|
||||
<label className={profileStyles.inputLabel}>Username</label>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
value={profile.username}
|
||||
className={`${styles.input} ${
|
||||
!isEditing ? styles.readOnly : ""
|
||||
value={user.username}
|
||||
className={`${profileStyles.input} ${
|
||||
!isEditing ? profileStyles.readOnly : ""
|
||||
}`}
|
||||
disabled
|
||||
/>
|
||||
@@ -179,25 +221,25 @@ const ProfileTab = () => {
|
||||
|
||||
{isEditing && (
|
||||
<>
|
||||
<div className={styles.inputGroup}>
|
||||
<label className={styles.inputLabel}>
|
||||
<div className={profileStyles.inputGroup}>
|
||||
<label className={profileStyles.inputLabel}>
|
||||
Current Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="oldPassword"
|
||||
onChange={handleChange}
|
||||
className={styles.input}
|
||||
className={profileStyles.input}
|
||||
placeholder="Enter current password"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.inputGroup}>
|
||||
<label className={styles.inputLabel}>New Password</label>
|
||||
<div className={profileStyles.inputGroup}>
|
||||
<label className={profileStyles.inputLabel}>New Password</label>
|
||||
<input
|
||||
type="password"
|
||||
name="newPassword"
|
||||
onChange={handleChange}
|
||||
className={styles.input}
|
||||
className={profileStyles.input}
|
||||
placeholder="Enter new password"
|
||||
/>
|
||||
</div>
|
||||
@@ -208,8 +250,8 @@ const ProfileTab = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.footer}>
|
||||
© 2025 Kediri Technopark • Dermalounge AI Admin
|
||||
<div className={dashboardStyles.footer}>
|
||||
© 2025 Kediri Technopark • Dashboard SOLID DATA
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* ProfileTab.module.css - Modern Design */
|
||||
/* ProfileTab.module.css - Modern Design with Unified Header */
|
||||
|
||||
/* Modern Color Palette */
|
||||
:root {
|
||||
@@ -14,7 +14,7 @@
|
||||
--neutral-800: #262626;
|
||||
--neutral-900: #171717;
|
||||
--white: #ffffff;
|
||||
--success-green: #10b981;
|
||||
--success-green: #43a0a7;
|
||||
--warning-amber: #f59e0b;
|
||||
--error-red: #ef4444;
|
||||
--text-primary: #0f172a;
|
||||
@@ -50,6 +50,7 @@ body {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* --- UNIFIED HEADER (sama dengan Dashboard.css) --- */
|
||||
.dashboardHeader {
|
||||
background-color: var(--white);
|
||||
color: var(--text-primary);
|
||||
@@ -58,7 +59,7 @@ body {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: var(--shadow-sm);
|
||||
border-bottom: 3px solid #ef4444;
|
||||
border-bottom: 3px solid #43a0a7; /* Warna dari Dashboard.css */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
@@ -80,10 +81,18 @@ body {
|
||||
}
|
||||
|
||||
.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;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #ed4344;
|
||||
color: #154666;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
@@ -96,6 +105,12 @@ body {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.userDisplayName {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.dropdownToggle {
|
||||
background-color: var(--neutral-100);
|
||||
color: var(--text-primary);
|
||||
@@ -155,7 +170,7 @@ body {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
/* --- MAIN CONTENT --- */
|
||||
.mainContent {
|
||||
flex-grow: 1;
|
||||
padding: 2rem 1.5rem;
|
||||
@@ -205,7 +220,7 @@ body {
|
||||
}
|
||||
|
||||
.editButton {
|
||||
background-color: #ef4444;
|
||||
background-color: #43a0a7; /* Diseragamkan dengan warna header */
|
||||
color: var(--text-light);
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
@@ -218,7 +233,7 @@ body {
|
||||
}
|
||||
|
||||
.editButton:hover {
|
||||
background-color: var(--dark-blue);
|
||||
background-color: #357734;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
@@ -328,7 +343,11 @@ body {
|
||||
}
|
||||
|
||||
.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);
|
||||
padding: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
@@ -421,7 +440,7 @@ body {
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
/* --- RESPONSIVE DESIGN --- */
|
||||
@media (min-width: 768px) {
|
||||
.dashboardHeader {
|
||||
padding: 1rem 2rem;
|
||||
@@ -436,6 +455,10 @@ body {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.userDisplayName {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
padding: 2.5rem 2rem;
|
||||
gap: 2.5rem;
|
||||
|
||||
@@ -18,7 +18,7 @@ const ShowImage = () => {
|
||||
|
||||
try {
|
||||
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",
|
||||
headers: {
|
||||
|
||||
Reference in New Issue
Block a user