From 4b914e1852114c032009e57d4b8b17017b8fae5d Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 19 Nov 2025 15:26:58 +0800 Subject: [PATCH 1/4] upd: dashboard admin Deskripsi: - update modal surat - api surat - dowload surat No Issues --- bun.lock | 46 ++++++- package.json | 2 + src/components/ModalSurat.tsx | 108 ++++++++++++++++ src/components/surat/SKUsaha.tsx | 118 ++++++++++++++++++ src/index.tsx | 2 + src/lib/categoryPelayananSurat.ts | 4 +- src/lib/configurationDesa.ts | 2 +- .../pelayanan-surat/detail_pelayanan_page.tsx | 26 ++-- src/server/routes/pelayanan_surat_route.ts | 13 +- src/server/routes/surat_route.ts | 59 +++++++++ 10 files changed, 364 insertions(+), 16 deletions(-) create mode 100644 src/components/ModalSurat.tsx create mode 100644 src/components/surat/SKUsaha.tsx create mode 100644 src/server/routes/surat_route.ts diff --git a/bun.lock b/bun.lock index e8abdde..64afd89 100644 --- a/bun.lock +++ b/bun.lock @@ -22,6 +22,8 @@ "@types/uuid": "^11.0.0", "add": "^2.0.6", "elysia": "^1.4.15", + "html2canvas": "^1.4.1", + "jspdf": "^3.0.3", "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "react": "^19.2.0", @@ -139,10 +141,16 @@ "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="], + "@types/pako": ["@types/pako@2.0.4", "", {}, "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw=="], + + "@types/raf": ["@types/raf@3.4.3", "", {}, "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw=="], + "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + "@types/uuid": ["@types/uuid@11.0.0", "", { "dependencies": { "uuid": "*" } }, "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA=="], "@unhead/schema": ["@unhead/schema@1.11.20", "", { "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" } }, "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA=="], @@ -175,6 +183,8 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "base64-arraybuffer": ["base64-arraybuffer@1.0.2", "", {}, "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="], + "bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="], "biome": ["biome@0.3.3", "", { "dependencies": { "bluebird": "^3.4.1", "chalk": "^1.1.3", "commander": "^2.9.0", "editor": "^1.0.0", "fs-promise": "^0.5.0", "inquirer-promise": "0.0.3", "request-promise": "^3.0.0", "untildify": "^3.0.2", "user-home": "^2.0.0" }, "bin": { "biome": "./dist/index.js" } }, "sha512-4LXjrQYbn9iTXu9Y4SKT7ABzTV0WnLDHCVSd2fPUOKsy1gQ+E4xPFmlY1zcWexoi0j7fGHItlL6OWA2CZ/yYAQ=="], @@ -197,6 +207,8 @@ "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], + "canvg": ["canvg@3.0.11", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@types/raf": "^3.4.0", "core-js": "^3.8.3", "raf": "^3.4.1", "regenerator-runtime": "^0.13.7", "rgbcolor": "^1.0.1", "stackblur-canvas": "^2.0.0", "svg-pathdata": "^6.0.3" } }, "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA=="], + "caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="], "chalk": ["chalk@1.1.3", "", { "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } }, "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A=="], @@ -231,7 +243,7 @@ "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], - "core-js": ["core-js@2.6.12", "", {}, "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="], + "core-js": ["core-js@3.47.0", "", {}, "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg=="], "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], @@ -239,6 +251,8 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "css-line-break": ["css-line-break@2.1.0", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w=="], + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], @@ -265,6 +279,8 @@ "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + "dompurify": ["dompurify@3.3.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ=="], + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], @@ -323,6 +339,8 @@ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fast-png": ["fast-png@6.4.0", "", { "dependencies": { "@types/pako": "^2.0.3", "iobuffer": "^5.3.2", "pako": "^2.1.0" } }, "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], @@ -379,6 +397,8 @@ "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], + "html2canvas": ["html2canvas@1.4.1", "", { "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" } }, "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA=="], + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], "http-signature": ["http-signature@1.2.0", "", { "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } }, "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ=="], @@ -395,6 +415,8 @@ "inquirer-promise": ["inquirer-promise@0.0.3", "", { "dependencies": { "earlgrey-runtime": ">=0.0.11", "inquirer": "^0.11.3" } }, "sha512-82CQX586JAV9GAgU9yXZsMDs+NorjA0nLhkfFx9+PReyOnuoHRbHrC1Z90sS95bFJI1Tm1gzMObuE0HabzkJpg=="], + "iobuffer": ["iobuffer@5.4.0", "", {}, "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "is-fullwidth-code-point": ["is-fullwidth-code-point@1.0.0", "", { "dependencies": { "number-is-nan": "^1.0.0" } }, "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw=="], @@ -423,6 +445,8 @@ "jsonfile": ["jsonfile@2.4.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw=="], + "jspdf": ["jspdf@3.0.3", "", { "dependencies": { "@babel/runtime": "^7.26.9", "fast-png": "^6.2.0", "fflate": "^0.8.1" }, "optionalDependencies": { "canvg": "^3.0.11", "core-js": "^3.6.0", "dompurify": "^3.2.4", "html2canvas": "^1.0.0-rc.5" } }, "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ=="], + "jsprim": ["jsprim@1.4.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw=="], "jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="], @@ -487,6 +511,8 @@ "oxlint": ["oxlint@1.25.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.25.0", "@oxlint/darwin-x64": "1.25.0", "@oxlint/linux-arm64-gnu": "1.25.0", "@oxlint/linux-arm64-musl": "1.25.0", "@oxlint/linux-x64-gnu": "1.25.0", "@oxlint/linux-x64-musl": "1.25.0", "@oxlint/win32-arm64": "1.25.0", "@oxlint/win32-x64": "1.25.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.4.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint", "oxc_language_server": "bin/oxc_language_server" } }, "sha512-O6iJ9xeuy9eQCi8/EghvsNO6lzSaUPs0FR1uLy51Exp3RkVpjvJKyPPhd9qv65KLnfG/BNd2HE/rH0NbEfVVzA=="], + "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -539,6 +565,8 @@ "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "raf": ["raf@3.4.1", "", { "dependencies": { "performance-now": "^2.1.0" } }, "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA=="], + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], "raw-body": ["raw-body@3.0.1", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.7.0", "unpipe": "1.0.0" } }, "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA=="], @@ -571,7 +599,7 @@ "readline2": ["readline2@1.0.1", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "mute-stream": "0.0.5" } }, "sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g=="], - "regenerator-runtime": ["regenerator-runtime@0.9.6", "", {}, "sha512-D0Y/JJ4VhusyMOd/o25a3jdUqN/bC85EFsaoL9Oqmy/O4efCh+xhp7yj2EEOsj974qvMkcW8AwUzJ1jB/MbxCw=="], + "regenerator-runtime": ["regenerator-runtime@0.13.11", "", {}, "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="], "request": ["request@2.88.2", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" } }, "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw=="], @@ -581,6 +609,8 @@ "restore-cursor": ["restore-cursor@1.0.1", "", { "dependencies": { "exit-hook": "^1.0.0", "onetime": "^1.0.0" } }, "sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw=="], + "rgbcolor": ["rgbcolor@1.0.1", "", {}, "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw=="], + "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], @@ -619,6 +649,8 @@ "sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="], + "stackblur-canvas": ["stackblur-canvas@2.7.0", "", {}, "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], "string-width": ["string-width@1.0.2", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw=="], @@ -631,10 +663,14 @@ "supports-color": ["supports-color@2.0.0", "", {}, "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="], + "svg-pathdata": ["svg-pathdata@6.0.3", "", {}, "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw=="], + "swr": ["swr@2.3.6", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw=="], "tabbable": ["tabbable@6.3.0", "", {}, "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ=="], + "text-segmentation": ["text-segmentation@1.0.3", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], @@ -687,6 +723,8 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "utrie": ["utrie@1.0.2", "", { "dependencies": { "base64-arraybuffer": "^1.0.2" } }, "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw=="], + "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], "valtio": ["valtio@2.2.0", "", { "dependencies": { "proxy-compare": "^3.0.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0" }, "optionalPeers": ["@types/react", "react"] }, "sha512-l/zzQahUIm+dfUUP9fIecNVEWJLea9shMC1Bb1aK+v4XNOEzoq796Qax+yzMemmqpltuxfH7kPJy62FVGJDEtw=="], @@ -711,6 +749,10 @@ "c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "earlgrey-runtime/core-js": ["core-js@2.6.12", "", {}, "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="], + + "earlgrey-runtime/regenerator-runtime": ["regenerator-runtime@0.9.6", "", {}, "sha512-D0Y/JJ4VhusyMOd/o25a3jdUqN/bC85EFsaoL9Oqmy/O4efCh+xhp7yj2EEOsj974qvMkcW8AwUzJ1jB/MbxCw=="], + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], diff --git a/package.json b/package.json index 23c6db1..d283974 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "@types/uuid": "^11.0.0", "add": "^2.0.6", "elysia": "^1.4.15", + "html2canvas": "^1.4.1", + "jspdf": "^3.0.3", "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "react": "^19.2.0", diff --git a/src/components/ModalSurat.tsx b/src/components/ModalSurat.tsx new file mode 100644 index 0000000..4d6c913 --- /dev/null +++ b/src/components/ModalSurat.tsx @@ -0,0 +1,108 @@ +import apiFetch from "@/lib/apiFetch"; +import { ActionIcon, Flex, Modal } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconDownload, IconX } from "@tabler/icons-react"; +import html2canvas from "html2canvas"; +import jsPDF from "jspdf"; +import { useRef } from "react"; +import useSWR from "swr"; +import SKUsaha from "./surat/SKUsaha"; + +export default function ModalSurat({ open, onClose, surat }: { open: boolean, onClose: () => void, surat: string }) { + const A4Style = { + width: "210mm", + height: "297mm", + padding: "20mm", + background: "#fff", + color: "#000", + fontSize: "14px", + fontFamily: "Times New Roman", + }; + const hiddenRef = useRef(null); + const { data, mutate, isLoading } = useSWR("surat", () => + apiFetch.api.surat.detail.get({ + query: { + id: surat, + }, + }), + ); + + useShallowEffect(() => { + mutate(); + }, []); + + + const downloadPDF = async () => { + const element = hiddenRef.current; + + const canvas = await html2canvas(element, { + scale: 2, + useCORS: true, + allowTaint: true, + width: element.offsetWidth, + height: element.offsetHeight, + }); + + const imgData = canvas.toDataURL("image/jpeg", 1.0); + + const pdf = new jsPDF("p", "mm", "a4"); + const pageWidth = 210; // A4 width mm + const pageHeight = 297; // A4 height mm + + const imgWidth = pageWidth; + const imgHeight = (canvas.height * pageWidth) / canvas.width; + + pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight); + + pdf.save("surat-keterangan-usaha.pdf"); + }; + + return ( + <> + onClose()} + overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} + size="auto" + withCloseButton={false} + removeScrollProps={{ allowPinchZoom: true }} + styles={{ + header: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + padding: "12px 16px", + }, + title: { + width: "100%", + } + }} + title={ + +
+ Preview Surat +
+ + + + + + + + + + +
+ } + > +
+ { + data && data.data && data.data.surat.idCategory == "skusaha" + ? + : <> + } +
+
+ + ) +} \ No newline at end of file diff --git a/src/components/surat/SKUsaha.tsx b/src/components/surat/SKUsaha.tsx new file mode 100644 index 0000000..3abd065 --- /dev/null +++ b/src/components/surat/SKUsaha.tsx @@ -0,0 +1,118 @@ +import _ from "lodash"; + +export default function SKUsaha({ data }: { data: any }) { + const getValue = (jenis: string) => + _.upperFirst( + data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" + ); + + return ( +
+ {/* HEADER */} +
+ PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}
+ KECAMATAN {_.upperCase(data.setting.desaKecamatan)}
+ DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}
+ Alamat: {data.setting.desaAlamat}
+ Kode Pos: {data.setting.desaPos} +
+ + {/* JUDUL */} +
+ SURAT KETERANGAN USAHA
+ Nomor: {data.surat.noSurat} +
+ + {/* YANG BERTANDA TANGAN */} +
+ Yang bertanda tangan di bawah ini: + + + + + + + + + + + + + + + + + + + + + + + +
Nama:{data.setting.perbekelNama}
Jabatan:{data.setting.perbekelJabatan}
Kecamatan:{data.setting.desaKecamatan}
Kabupaten:{data.setting.desaKabupaten}
+
+ + {/* IDENTITAS ORANG YG MEMINTA SURAT */} +
+ Dengan ini menerangkan dengan sesungguhnya bahwa: + + + + + + + + + + + +
Nama:{getValue("nama")}
Jenis Kelamin:{getValue("jenis kelamin")}
Tempat / Tanggal Lahir:{getValue("tempat tanggal lahir")}
Warga Negara:{getValue("negara")}
Agama:{getValue("agama")}
Status:{getValue("status perkawinan")}
Pekerjaan:{getValue("pekerjaan")}
Alamat:{getValue("alamat")}
+
+ + {/* DOMISILI */} +
+ Bahwa orang tersebut di atas benar-benar penduduk: + + + + + + +
Desa / Kelurahan:{data.setting.desaNama}
Kecamatan:{data.setting.desaKecamatan}
Kabupaten:{data.setting.desaKabupaten}
+
+ + {/* USAHA */} +
+ Dan yang bersangkutan benar memiliki usaha: + + + + + +
Jenis Usaha:{getValue("jenis usaha")}
Alamat Usaha:{getValue("alamat usaha")}
+
+ +
+ Surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan sebagaimana mestinya. +
+ +
+ Dikeluarkan di {data.setting.desaNama}
+ Pada tanggal {data.surat.createdAt} +
+ + {/* TANDA TANGAN */} +
+
+ +

+ Kepala Desa / Lurah {data.setting.desaNama} +



+ {data.setting.perbekelNama}
+ NIP. {data.setting.perbekelNIP} +
+
+ +
+ ); +} diff --git a/src/index.tsx b/src/index.tsx index 5164620..211dc38 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -17,6 +17,7 @@ import PengaduanRoute from "./server/routes/pengaduan_route"; import TestPengaduanRoute from "./server/routes/test_pengaduan"; import UserRoute from "./server/routes/user_route"; import WargaRoute from "./server/routes/warga_route"; +import SuratRoute from "./server/routes/surat_route"; const Docs = new Elysia({ tags: ["docs"], @@ -34,6 +35,7 @@ const Api = new Elysia({ .use(PelayananRoute) .use(ConfigurationDesaRoute) .use(WargaRoute) + .use(SuratRoute) .use(TestPengaduanRoute) .use(apiAuth) .use(ApiKeyRoute) diff --git a/src/lib/categoryPelayananSurat.ts b/src/lib/categoryPelayananSurat.ts index 0c502fa..e4a27e3 100644 --- a/src/lib/categoryPelayananSurat.ts +++ b/src/lib/categoryPelayananSurat.ts @@ -25,7 +25,7 @@ export const categoryPelayananSurat = [ syaratDokumen: [ { name: "pengantar kelian", desc: "Surat Pengantar Kelian Banjar Dinas" }, { name: "skt organisasi", desc: "Fotokopi Surat Keterangan Terdaftar (SKT) Organisasi atau Pengukuhan Kelompok" }, - {name: "susunan pengurus", desc: "Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap denganKop Organisasi"} + { name: "susunan pengurus", desc: "Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap denganKop Organisasi" } ], dataText: ["nama organisasi", "alamat organisasi", "nama pemohon", "jabatan pemohon", "kontak", "penanggung jawab", "tanggal berdiri"] }, @@ -95,7 +95,7 @@ export const categoryPelayananSurat = [ { name: "ktp/kk", desc: "Fotokopi KTP atau Kartu Keluarga" }, { name: "foto lokasi", desc: "Foto lokasi usaha dicetak dalam selembar kertas, diparaf dan distempel oleh Kelian" } ], - dataText: ["jenis usaha", "alamat usaha"] + dataText: ["nama", "jenis kelamin", "tempat tanggal lahir", "negara", "agama", "status perkawinan", "alamat", "pekerjaan", "jenis usaha", "alamat usaha"] }, { id: "skyatimpiatu", diff --git a/src/lib/configurationDesa.ts b/src/lib/configurationDesa.ts index fe8dd0e..7bf450a 100644 --- a/src/lib/configurationDesa.ts +++ b/src/lib/configurationDesa.ts @@ -47,7 +47,7 @@ export const confDesa = [ { id: "perbekelNIP", name: "NIP", - value: "" + value: "1122334455" }, { id: "perbekelTTD", diff --git a/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx b/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx index b49e0dd..f1eb2a3 100644 --- a/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx +++ b/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx @@ -1,6 +1,8 @@ +import ModalSurat from "@/components/ModalSurat"; import notification from "@/components/notificationGlobal"; import apiFetch from "@/lib/apiFetch"; import { + Anchor, Badge, Button, Card, @@ -73,6 +75,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data const [keterangan, setKeterangan] = useState(""); const [host, setHost] = useState(null); const [noSurat, setNoSurat] = useState(""); + const [openedPreview, setOpenedPreview] = useState(false); useEffect(() => { async function fetchHost() { @@ -169,6 +172,11 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data )} + { + data?.status == "selesai" && + ( setOpenedPreview(false)} surat={data?.idSurat} />) + } + + } > {syaratDokumen?.map((v: any) => ( - {v.jenis} + + + {v.jenis} + + ))} @@ -307,15 +319,9 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data - ) diff --git a/src/server/routes/pelayanan_surat_route.ts b/src/server/routes/pelayanan_surat_route.ts index b0b95ab..4dbfbd3 100644 --- a/src/server/routes/pelayanan_surat_route.ts +++ b/src/server/routes/pelayanan_surat_route.ts @@ -170,6 +170,17 @@ const PelayananRoute = new Elysia({ } }) + const dataSurat = await prisma.suratPelayanan.findFirst({ + where: { + idPengajuanLayanan: data?.id, + isActive: true + }, + select: { + id: true, + idCategory: true, + } + }) + const dataSyarat = await prisma.syaratDokumenPelayanan.findMany({ where: { idPengajuanLayanan: data?.id, @@ -266,6 +277,7 @@ const PelayananRoute = new Elysia({ status: data?.status, createdAt: data?.createdAt, updatedAt: data?.updatedAt, + idSurat: dataSurat?.id, } const datafix = { @@ -275,7 +287,6 @@ const PelayananRoute = new Elysia({ syaratDokumen: dataSyaratFix, dataText: dataTextFix, } - return datafix }, { query: t.Object({ diff --git a/src/server/routes/surat_route.ts b/src/server/routes/surat_route.ts new file mode 100644 index 0000000..21eacb3 --- /dev/null +++ b/src/server/routes/surat_route.ts @@ -0,0 +1,59 @@ +import Elysia, { t } from "elysia"; +import { prisma } from "../lib/prisma"; + +const SuratRoute = new Elysia({ + prefix: "surat", + tags: ["surat"], +}) + .get("/detail", async ({ query }) => { + const { id } = query + + const dataSurat = await prisma.suratPelayanan.findUnique({ + where: { + id + }, + select: { + id: true, + noSurat: true, + idCategory: true, + createdAt: true, + PelayananAjuan: { + select: { + DataTextPelayanan: true, + } + } + } + }) + + const dataSetting = await prisma.configuration.findMany() + + const toObject = (arr: any[]) => + dataSetting.reduce((acc: any, item: any) => { + acc[item.id] = item.value; + return acc; + }, {}); + + return { + surat: { + id: dataSurat?.id, + idCategory: dataSurat?.idCategory, + noSurat: dataSurat?.noSurat, + dataText: dataSurat?.PelayananAjuan?.DataTextPelayanan, + createdAt: dataSurat?.createdAt.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" }), + }, + setting: toObject(dataSetting) + } + + }, { + query: t.Object({ + id: t.String({ minLength: 1, error: "id harus diisi" }) + }), + detail: { + summary: "Detail Surat", + description: `tool untuk mendapatkan detail surat`, + } + + }) + ; + +export default SuratRoute From 73e247c87f62dc96769e8be6fc3ad09bcfdeaa3c Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 19 Nov 2025 15:45:44 +0800 Subject: [PATCH 2/4] upd: dashboard admin Deskripsi: - sk tidak mampu - sk yatim piatu No Issues --- src/components/ModalSurat.tsx | 4 +- src/components/surat/SKTidakMampu.tsx | 81 +++++++++++++ src/components/surat/SKYatimPiatu.tsx | 163 ++++++++++++++++++++++++++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 src/components/surat/SKTidakMampu.tsx create mode 100644 src/components/surat/SKYatimPiatu.tsx diff --git a/src/components/ModalSurat.tsx b/src/components/ModalSurat.tsx index 4d6c913..20cc099 100644 --- a/src/components/ModalSurat.tsx +++ b/src/components/ModalSurat.tsx @@ -7,6 +7,8 @@ import jsPDF from "jspdf"; import { useRef } from "react"; import useSWR from "swr"; import SKUsaha from "./surat/SKUsaha"; +import SKYatim from "./surat/SKYatimPiatu"; +import SKTidakMampu from "./surat/SKTidakMampu"; export default function ModalSurat({ open, onClose, surat }: { open: boolean, onClose: () => void, surat: string }) { const A4Style = { @@ -98,7 +100,7 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
{ data && data.data && data.data.surat.idCategory == "skusaha" - ? + ? : <> }
diff --git a/src/components/surat/SKTidakMampu.tsx b/src/components/surat/SKTidakMampu.tsx new file mode 100644 index 0000000..2ef68e3 --- /dev/null +++ b/src/components/surat/SKTidakMampu.tsx @@ -0,0 +1,81 @@ +import _ from "lodash"; + +export default function SKTidakMampu({ data }: { data: any }) { + const getValue = (key: string) => + _.upperFirst(data.surat.dataText.find((i: any) => i.jenis === key)?.value || ""); + + return ( +
+ {/* TITLE */} +
+ SURAT KETERANGAN TIDAK MAMPU
+ Nomor: {data.surat.noSurat} +
+ + {/* ISI */} +
+
+ Yang bertanda tangan dibawah ini, saya +
+ + {/* DATA PEJABAT */} +
+ + + + + +
+ +
+ +
Dengan ini menerangkan bahwa:
+ + {/* DATA WARGA */} +
+ + + + + + +
+ +
+ +

+ Orang tersebut benar-benar penduduk desa {data.setting.desaNama} dan termasuk keluarga tidak mampu. + Surat keterangan ini dipergunakan untuk + {getValue("alasan permohonan")}. +

+ +

+ Demikian surat keterangan ini kami buat dengan sebenar-benarnya untuk dapat dipergunakan + sebagaimana mestinya. +

+ +

+ + {/* TANDA TANGAN */} +
+
+ {data.setting.desaNama}, {data.surat.createdAt}


+ + {data.setting.perbekelNama}
+ {data.setting.perbekelJabatan + " " + data.setting.desaNama} +
+
+
+
+ ); +} + +function Row({ label, value }: { label: string, value: string }) { + return ( +
+
{label}
+
:
+
{value}
+
+ ); +} diff --git a/src/components/surat/SKYatimPiatu.tsx b/src/components/surat/SKYatimPiatu.tsx new file mode 100644 index 0000000..c80763b --- /dev/null +++ b/src/components/surat/SKYatimPiatu.tsx @@ -0,0 +1,163 @@ +import _ from "lodash"; + +export default function SKYatim({ data }: { data: any }) { + + const getValue = (key: string) => + _.upperFirst(data.surat.dataText.find((i: any) => i.jenis === key)?.value || ""); + + return ( +
+ + {/* HEADER */} +
+ PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}
+ KECAMATAN {_.upperCase(data.setting.desaKecamatan)}
+ DESA {_.upperCase(data.setting.desaNama)}
+ Alamat: {data.setting.desaAlamat}. Kode Pos: {data.setting.desaPos} +
+ +
+ SURAT KETERANGAN YATIM / PIATU / YATIM PIATU
+ Nomor: {data.surat.noSurat} +
+ +
+ + {/* BAGIAN PENANDATANGAN */} +
Yang bertanda tangan di bawah ini:
+ + + + + + + + + + + + + + + +
Nama: {data.setting.perbekelNama}
Jabatan: {data.setting.perbekelJabatan}
Alamat Kantor: {data.setting.desaAlamat}
+ +
+ + {/* BAGIAN IDENTITAS ANAK */} +
Dengan ini menerangkan bahwa:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nama: {getValue("nama")}
Tempat/Tanggal Lahir: {getValue("tempat tanggal lahir")}
Jenis Kelamin: {getValue("jenis kelamin")}
Alamat: {getValue("alamat")}
NIK: {getValue("nik")}
Pekerjaan: {getValue("pekerjaan")}
+ +
+ + {/* KETERANGAN ORANG TUA */} +
+ Benar bahwa yang bersangkutan adalah anak (Yatim / Piatu / Yatim Piatu), + dengan keterangan sebagai berikut: +
+ +
+ +
1. Nama Ayah
+ + + + + + + + + + + +
Nama Ayah: {getValue("nama ayah")}
Status: {getValue("status ayah")}
+ +
+ +
2. Nama Ibu
+ + + + + + + + + + + +
Nama Ibu: {getValue("nama ibu")}
Status: {getValue("status ibu")}
+ +
+ +
+ Dengan demikian, berdasarkan keterangan pihak keluarga dan data di Kantor Desa, + maka benar bahwa yang bersangkutan adalah + anak (Yatim / Piatu / Yatim Piatu). +
+ +
+ +
+ Surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan sebagaimana mestinya. +
+ +
+ + {/* TANGGAL & TEMPAT */} + + + + + + + + + + + +
Dikeluarkan di: {data.setting.desaNama}
Pada tanggal: {data.surat.createdAt}
+ +

+ + {/* TTD */} +
+
+ Kepala Desa {data.setting.desaNama} +



+ {data.setting.perbekelNama}
+ NIP. {data.setting.perbekelNIP} +
+
+ +
+ ); +} From 99247b7a4461ac66cc251f70d98f74586f22c507 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 19 Nov 2025 16:58:19 +0800 Subject: [PATCH 3/4] upd: dashboard admin Deskripsi: - update sk penghasilan - update sk kelakuan baik - update sk kelahiran NO Issues --- src/components/ModalSurat.tsx | 22 ++++- src/components/surat/SKKelahiran.tsx | 113 +++++++++++++++++++++++ src/components/surat/SKKelakuanBaik.tsx | 114 ++++++++++++++++++++++++ src/components/surat/SKPenghasilan.tsx | 112 +++++++++++++++++++++++ src/lib/categoryPelayananSurat.ts | 6 +- 5 files changed, 361 insertions(+), 6 deletions(-) create mode 100644 src/components/surat/SKKelahiran.tsx create mode 100644 src/components/surat/SKKelakuanBaik.tsx create mode 100644 src/components/surat/SKPenghasilan.tsx diff --git a/src/components/ModalSurat.tsx b/src/components/ModalSurat.tsx index 20cc099..33fb181 100644 --- a/src/components/ModalSurat.tsx +++ b/src/components/ModalSurat.tsx @@ -6,9 +6,12 @@ import html2canvas from "html2canvas"; import jsPDF from "jspdf"; import { useRef } from "react"; import useSWR from "swr"; +import SKKelahiran from "./surat/SKKelahiran"; +import SKKelakuanBaik from "./surat/SKKelakuanBaik"; +import SKPenghasilan from "./surat/SKPenghasilan"; +import SKTidakMampu from "./surat/SKTidakMampu"; import SKUsaha from "./surat/SKUsaha"; import SKYatim from "./surat/SKYatimPiatu"; -import SKTidakMampu from "./surat/SKTidakMampu"; export default function ModalSurat({ open, onClose, surat }: { open: boolean, onClose: () => void, surat: string }) { const A4Style = { @@ -99,8 +102,21 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on >
{ - data && data.data && data.data.surat.idCategory == "skusaha" - ? + data && data.data + ? data.data.surat.idCategory == "skusaha" + ? + : data.data.surat.idCategory == "skkelahiran" + ? + : data.data.surat.idCategory == "skkelakuanbaik" + ? + : data.data.surat.idCategory == "skpenghasilan" + ? + : data.data.surat.idCategory == "sktidakmampu" + ? + : data.data.surat.idCategory == "skyatimpiatu" + ? + : <> + : <> }
diff --git a/src/components/surat/SKKelahiran.tsx b/src/components/surat/SKKelahiran.tsx new file mode 100644 index 0000000..08b58d3 --- /dev/null +++ b/src/components/surat/SKKelahiran.tsx @@ -0,0 +1,113 @@ +import _ from "lodash"; + +export default function SKKelahiran({ data }: { data: any }) { + const getValue = (jenis: string) => + _.upperFirst( + data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" + ); + + return ( +
+ + {/* HEADER */} +
+ PEMERINTAH KABUPATEN/KOTA {_.upperCase(data.setting.desaKabupaten)}
+ KECAMATAN {_.upperCase(data.setting.desaKecamatan)}
+ DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}
+ Alamat: {data.setting.desaAlamat} +
+ + {/* JUDUL */} +
+ SURAT KETERANGAN KELAHIRAN
+ Nomor : {data.surat.noSurat} +
+ + {/* PEMBUKA */} +
+ Yang bertanda tangan di bawah ini, {data.setting.perbekelJabatan} + {` ${data.setting.desaNama}, Kecamatan ${data.setting.desaKecamatan}, Kabupaten/Kota ${data.setting.desaKabupaten}`} + , dengan ini menerangkan bahwa: +
+ + {/* DATA KELAHIRAN ANAK */} +
+ Telah lahir seorang anak pada: + + + + + + + + + +
Tanggal Lahir:{getValue("tanggal lahir anak")}
Pukul:{getValue("pukul lahir anak")}
Tempat Kelahiran:{getValue("tempat lahir anak")}
Jenis Kelamin:{getValue("jenis kelamin anak")}
Anak ke:{getValue("anak ke")}
Nama Anak:{getValue("nama anak")}
+
+ + {/* DATA IBU */} +
+ Dari seorang ibu bernama: + + + + + + + + +
Nama Lengkap Ibu:{getValue("nama ibu")}
NIK:{getValue("nik ibu")}
Tempat & Tanggal Lahir:{getValue("tempat tanggal lahir ibu")}
Pekerjaan:{getValue("pekerjaan ibu")}
Alamat:{getValue("alamat ibu")}
+
+ + {/* DATA AYAH */} +
+ Dan seorang ayah bernama: + + + + + + + + +
Nama Lengkap Ayah:{getValue("nama ayah")}
NIK:{getValue("nik ayah")}
Tempat & Tanggal Lahir:{getValue("tempat tanggal lahir ayah")}
Pekerjaan:{getValue("pekerjaan ayah")}
Alamat:{getValue("alamat ayah")}
+
+ + {/* DATA PELAPOR */} +
+ Berdasarkan laporan dari: + + + + + + +
Nama Pelapor:{getValue("nama pelapor")}
Hubungan dengan Anak:{getValue("hubungan pelapor")}
Alamat:{getValue("alamat pelapor")}
+
+ + {/* PENUTUP */} +
+ Demikian Surat Keterangan Kelahiran ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya. +
+ + {/* TEMPAT TANGGAL */} + + + + + +
Dikeluarkan di:{data.setting.desaNama}
Pada tanggal:{data.surat.createdAt}
+ + {/* TANDA TANGAN */} +
+
+ Kepala Desa / Lurah {data.setting.desaNama} +



+ {data.setting.perbekelNama}
+ NIP. {data.setting.perbekelNIP} +
+
+ +
+ ); +} diff --git a/src/components/surat/SKKelakuanBaik.tsx b/src/components/surat/SKKelakuanBaik.tsx new file mode 100644 index 0000000..876e55c --- /dev/null +++ b/src/components/surat/SKKelakuanBaik.tsx @@ -0,0 +1,114 @@ +import _ from "lodash"; + +export default function SKKelakuanBaik({ data }: { data: any }) { + const getValue = (jenis: string) => + _.upperFirst( + data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" + ); + + return ( +
+ + {/* HEADER */} +
+ SURAT KETERANGAN KELAKUAN BAIK
+ (PENGANTAR SKCK)
+ Nomor: {data.surat.noSurat} +
+ + {/* PEMBUKA */} +
+ Yang bertanda tangan di bawah ini menerangkan dengan sebenarnya bahwa: +
+ + {/* IDENTITAS PENDUDUK */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nama lengkap:{getValue("nama")}
NIK:{getValue("nik")}
Tempat/Tgl Lahir:{getValue("tempat tanggal lahir")}
Jenis Kelamin:{getValue("jenis kelamin")}
Agama:{getValue("agama")}
Pekerjaan:{getValue("pekerjaan")}
Alamat:{getValue("alamat")}
+ + {/* ISI */} +
+ Adalah benar penduduk yang berdomisili di wilayah kami dan selama tinggal di lingkungan + Desa {data.setting.desaNama}, berkelakuan baik, tidak pernah terlibat perbuatan melanggar hukum, + serta dikenal sopan dan aktif dalam kegiatan kemasyarakatan. +
+ +
+ Surat keterangan ini diberikan sebagai pengantar permohonan penerbitan Surat Keterangan + Catatan Kepolisian (SKCK) ke Polsek/Polres {getValue("polsek")}. +
+ +
+ Surat ini berlaku selama 6 (enam) bulan sejak tanggal diterbitkan, kecuali terdapat perubahan + data yang mendasar. +
+ +
+ Demikian surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan sebagaimana mestinya. +
+ + {/* TANGGAL */} + + + + + + + + + + + + + +
Dikeluarkan di:{data.setting.desaNama}
Pada tanggal:{data.surat.createdAt}
+ + {/* TANDA TANGAN */} +
+
+ Kepala Desa {data.setting.desaNama} +



+ {data.setting.perbekelNama}
+ NIP. {data.setting.perbekelNIP} +
+
+ +
+ ); +} diff --git a/src/components/surat/SKPenghasilan.tsx b/src/components/surat/SKPenghasilan.tsx new file mode 100644 index 0000000..d42eb6c --- /dev/null +++ b/src/components/surat/SKPenghasilan.tsx @@ -0,0 +1,112 @@ +import _ from "lodash"; + +export default function SKPenghasilan({ data }: { data: any }) { + const getValue = (jenis: string) => + _.upperFirst( + data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" + ); + + return ( +
+ {/* HEADER */} +
+ PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}
+ KECAMATAN {_.upperCase(data.setting.desaKecamatan)}
+ DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}
+ Alamat: {data.setting.desaAlamat}
+ Kode Pos: {data.setting.desaPos} +
+ + {/* JUDUL */} +
+ SURAT KETERANGAN PENGHASILAN
+ Nomor: {data.surat.noSurat} +
+ + {/* YANG BERTANDA TANGAN */} +
+ Yang bertanda tangan di bawah ini: + + + + + + + + + + + + + + + + + + + + + + + +
Nama:{data.setting.perbekelNama}
Jabatan:{data.setting.perbekelJabatan}
Kecamatan:{data.setting.desaKecamatan}
Kabupaten:{data.setting.desaKabupaten}
+
+ + {/* IDENTITAS */} +
+ Dengan ini menerangkan bahwa: + + + + + + + + +
Nama:{getValue("nama")}
Jenis Kelamin:{getValue("jenis kelamin")}
Tempat / Tanggal Lahir:{getValue("tempat tanggal lahir")}
Pekerjaan:{getValue("pekerjaan")}
Alamat:{getValue("alamat")}
+
+ + {/* PENGHASILAN */} +
+ Berdasarkan keterangan yang bersangkutan, orang tersebut memiliki penghasilan rata-rata: + + + + + + + + +
Penghasilan: + Rp {getValue("penghasilan")} + {" "} + ({getValue("penghasilan terbilang")}) per bulan +
+
+ + {/* KEPERLUAN */} +
+ Surat keterangan ini dibuat untuk keperluan: {getValue("alasan permohonan")}. +
+ +
+ Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat dipergunakan sebagaimana mestinya. +
+ + {/* TANGGAL & TANDA TANGAN */} +
+ Dikeluarkan di {data.setting.desaNama}
+ Pada tanggal {data.surat.createdAt} +
+ +
+
+ Kepala Desa / Lurah {data.setting.desaNama} +



+ {data.setting.perbekelNama}
+ NIP. {data.setting.perbekelNIP} +
+
+
+ ); +} diff --git a/src/lib/categoryPelayananSurat.ts b/src/lib/categoryPelayananSurat.ts index e4a27e3..0db02cb 100644 --- a/src/lib/categoryPelayananSurat.ts +++ b/src/lib/categoryPelayananSurat.ts @@ -36,7 +36,7 @@ export const categoryPelayananSurat = [ { name: "pengantar kelian", desc: "Surat Pengantar Kelian Banjar Dinas" }, { name: "surat lahir", desc: "Fotokopi Surat Keterangan Lahir dari Bidan/Dokter (jika ada)" } ], - dataText: ["nama ayah", "nama ibu", "nama anak", "tanggal lahir", "tempat lahir", "jenis kelamin", "nama pelapor"] + dataText: ["nama ayah", "nama ibu", "nama anak", "tanggal lahir anak", "pukul lahir anak", "tempat lahir anak", "jenis kelamin anak", "anak ke", "nik ibu", "tempat tanggal lahir ibu", "pekerjaan ibu", "alamat ibu", "nik ayah", "tempat tanggal lahir ayah", "pekerjaan ayah", "alamat ayah", "nama pelapor", "hubungan pelapor", "alamat pelapor"] }, { id: "skkelakuanbaik", @@ -45,7 +45,7 @@ export const categoryPelayananSurat = [ { name: "pengantar kelian", desc: "Surat Pengantar Kelian Banjar Dinas" }, { name: "ktp/kk", desc: "Fotokopi KTP atau Kartu Keluarga" } ], - dataText: ["nik", "nama", "tempat tanggal lahir", "jenis kelamin", "alamat", "keperluan"] + dataText: ["nik", "nama", "tempat tanggal lahir", "jenis kelamin", "alamat", "polsek"] }, { id: "skkematian", @@ -65,7 +65,7 @@ export const categoryPelayananSurat = [ { name: "ktp ortu/kk", desc: "Fotokopi KTP orang tua atau Kartu Keluarga" }, { name: "surat pernyataan", desc: "Surat Pernyataan Penghasilan bermaterai" } ], - dataText: ["nama", "nik", "alamat", "pekerjaan", "jenis usaha", "penghasilan"] + dataText: ["nama", "nik", "alamat", "pekerjaan", "jenis usaha", "penghasilan", "alasan permohonan"] }, { id: "sktempatusaha", From fe52fb52c63b557027cd6046afb65156165cfcd4 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 19 Nov 2025 17:10:00 +0800 Subject: [PATCH 4/4] fix: dashboard admin Deskripsi: - fix error pada list pengajuan surat - menghilangkan status dikerjakan pada list pengajuan surat No Issues --- .../pelayanan-surat/list_pelayanan_page.tsx | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/pages/scr/dashboard/pelayanan-surat/list_pelayanan_page.tsx b/src/pages/scr/dashboard/pelayanan-surat/list_pelayanan_page.tsx index f7f538e..4128259 100644 --- a/src/pages/scr/dashboard/pelayanan-surat/list_pelayanan_page.tsx +++ b/src/pages/scr/dashboard/pelayanan-surat/list_pelayanan_page.tsx @@ -47,10 +47,14 @@ export default function PelayananSuratListPage() { function TabListPelayananSurat({ status }: { status: string }) { const navigate = useNavigate(); - const dataCount = useSwr("/pelayanan-surat/count", () => + const { data, mutate, isLoading } = useSwr("/pelayanan-surat/count", () => apiFetch.api.pelayanan.count.get().then((res) => res.data), ); + useShallowEffect(() => { + mutate(); + }, []); + return ( @@ -60,7 +64,7 @@ function TabListPelayananSurat({ status }: { status: string }) { navigate("?status=semua"); }} > - Semua ({dataCount?.data?.semua || 0}) + Semua ({data?.semua || 0}) - Antrian ({dataCount?.data?.antrian || 0}) + Antrian ({data?.antrian || 0}) - Diterima ({dataCount?.data?.diterima || 0}) - - { - navigate("?status=dikerjakan"); - }} - > - Dikerjakan ({dataCount?.data?.dikerjakan || 0}) + Diterima ({data?.diterima || 0}) - Selesai ({dataCount?.data?.selesai || 0}) + Selesai ({data?.selesai || 0}) - Ditolak ({dataCount?.data?.ditolak || 0}) + Ditolak ({data?.ditolak || 0}) @@ -189,7 +185,7 @@ function ListPelayananSurat({ status }: { status: StatusKey }) { ) : ( - list?.map((v: any) => ( + Array.isArray(list) && list?.map((v: any) => (