From 34a37dc63b9335e162b3fe7fd069688a4cc258b3 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 9 Apr 2026 11:33:19 +0800 Subject: [PATCH 1/4] chore(dev-dependency): add playwright-mcp package for testing - Add playwright-mcp v0.0.19 as dev dependency - Update bun.lock with new dependency tree - Add .qwen configuration files Co-authored-by: Qwen-Coder --- .qwen/settings.json | 13 ++++ .qwen/settings.json.orig | 9 +++ bun.lock | 133 ++++++++++++++++++++++++++++++++++++++- package.json | 1 + 4 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 .qwen/settings.json create mode 100644 .qwen/settings.json.orig diff --git a/.qwen/settings.json b/.qwen/settings.json new file mode 100644 index 00000000..08fad9b2 --- /dev/null +++ b/.qwen/settings.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "playwright-mcp": { + "command": "npx", + "args": [ + "-y", + "playwright-mcp@latest" + ], + "timeout": 60000 + } + }, + "$version": 3 +} \ No newline at end of file diff --git a/.qwen/settings.json.orig b/.qwen/settings.json.orig new file mode 100644 index 00000000..63d02981 --- /dev/null +++ b/.qwen/settings.json.orig @@ -0,0 +1,9 @@ +{ + "mcpServers": { + "playwright-mcp": { + "command": "npx", + "args": ["-y", "playwright-mcp@latest"], + "timeout": 60000 + } + } +} diff --git a/bun.lock b/bun.lock index 6c87f21a..09ab344c 100644 --- a/bun.lock +++ b/bun.lock @@ -111,10 +111,11 @@ "@types/react-dom": "^19", "@vitest/ui": "^4.0.18", "eslint": "^9", - "eslint-config-next": "15.1.6", + "eslint-config-next": "15.5.12", "jsdom": "^28.0.0", "msw": "^2.12.9", "parcel": "^2.6.2", + "playwright-mcp": "^0.0.19", "postcss": "^8.5.1", "postcss-preset-mantine": "^1.17.0", "postcss-simple-vars": "^7.0.1", @@ -246,6 +247,8 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + "@hono/node-server": ["@hono/node-server@1.19.13", "", { "peerDependencies": { "hono": "^4" } }, "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], @@ -360,6 +363,8 @@ "@mischnic/json-sourcemap": ["@mischnic/json-sourcemap@0.1.1", "", { "dependencies": { "@lezer/common": "^1.0.0", "@lezer/lr": "^1.0.0", "json5": "^2.2.1" } }, "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], @@ -378,7 +383,7 @@ "@next/env": ["@next/env@15.5.14", "", {}, "sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA=="], - "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.1.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw=="], + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.5.12", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-+ZRSDFTv4aC96aMb5E41rMjysx8ApkryevnvEYZvPZO52KvkqP5rNExLUXJFr9P4s0f3oqNQR6vopCZsPWKDcQ=="], "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg=="], @@ -600,6 +605,8 @@ "@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], + "@posthog/core": ["@posthog/core@1.25.1", "", {}, "sha512-76enEGwLVtcCTbfAr1wJ6zi4tYzVkGsqJx+H9JiX0K8/VxuKdbOtVShsOJhTD9uvFfTeW3DX+PSCelcqWrRRkw=="], + "@prisma/client": ["@prisma/client@6.3.1", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-ARAJaPs+eBkemdky/XU3cvGRl+mIPHCN2lCXsl5Vlb0E2gV+R6IN7aCI8CisRGszEZondwIsW9Iz8EJkTdykyA=="], "@prisma/debug": ["@prisma/debug@6.3.1", "", {}, "sha512-RrEBkd+HLZx+ydfmYT0jUj7wjLiS95wfTOSQ+8FQbvb6vHh5AeKfEPt/XUQ5+Buljj8hltEfOslEW57/wQIVeA=="], @@ -662,6 +669,8 @@ "@sinclair/typebox": ["@sinclair/typebox@0.34.49", "", {}, "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A=="], + "@skorotkiewicz/snowflake-id": ["@skorotkiewicz/snowflake-id@1.0.1", "", {}, "sha512-leZkiZoBgg6zpVfg+cMkehr/bdKXGa6pOoX0rNc1C2jVdGTWa87+/BBgIy8C4uVtI404cuOMBu2IzIXI1BE3TQ=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], @@ -926,6 +935,8 @@ "@vitest/utils": ["@vitest/utils@4.1.2", "", { "dependencies": { "@vitest/pretty-format": "4.1.2", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -938,6 +949,8 @@ "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "animate.css": ["animate.css@4.1.1", "", {}, "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -994,6 +1007,8 @@ "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + "brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -1008,6 +1023,8 @@ "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -1054,6 +1071,10 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], @@ -1062,6 +1083,8 @@ "core-js": ["core-js@3.49.0", "", {}, "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg=="], + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="], "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], @@ -1132,6 +1155,8 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], @@ -1154,6 +1179,8 @@ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "electron-to-chromium": ["electron-to-chromium@1.5.331", "", {}, "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q=="], "elysia": ["elysia@1.4.28", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-Vrx8sBnvq8squS/3yNBzR1jBXI+SgmnmvwawPjNuEHndUe5l1jV2Gp6JJ4ulDkEB8On6bWmmuyPpA+bq4t+WYg=="], @@ -1168,6 +1195,8 @@ "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -1196,11 +1225,13 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="], - "eslint-config-next": ["eslint-config-next@15.1.6", "", { "dependencies": { "@next/eslint-plugin-next": "15.1.6", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-Wd1uy6y7nBbXUSg9QAuQ+xYEKli5CgUhLjz1QHW11jLDis5vK5XB3PemL6jEmy7HrdhaRFDz+GTZ/3FoH+EUjg=="], + "eslint-config-next": ["eslint-config-next@15.5.12", "", { "dependencies": { "@next/eslint-plugin-next": "15.5.12", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-ktW3XLfd+ztEltY5scJNjxjHwtKWk6vU2iwzZqSN09UsbBmMeE/cVlJ1yESg6Yx5LW7p/Z8WzUAgYXGLEmGIpg=="], "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], @@ -1232,8 +1263,14 @@ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="], "exif-js": ["exif-js@2.3.0", "", {}, "sha512-1Og9pAzG2FZRVlaavH8bB8BTeHcjMdJhKmeQITkX+uLRCD0xPtKAdZ2clZmQdJ56p9adXtJ8+jwrGp/4505lYg=="], @@ -1242,6 +1279,10 @@ "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.3.2", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg=="], + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], @@ -1254,6 +1295,8 @@ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], @@ -1272,6 +1315,8 @@ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="], "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], @@ -1286,8 +1331,12 @@ "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + "framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="], + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -1324,6 +1373,8 @@ "graphql": ["graphql@16.13.2", "", {}, "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig=="], + "happy-dom": ["happy-dom@17.6.3", "", { "dependencies": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" } }, "sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug=="], + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -1342,14 +1393,20 @@ "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + "hono": ["hono@4.12.12", "", {}, "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q=="], + "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -1362,10 +1419,16 @@ "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "iron-session": ["iron-session@8.0.4", "", { "dependencies": { "cookie": "^0.7.2", "iron-webcrypto": "^1.2.1", "uncrypto": "^0.1.3" } }, "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA=="], "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], @@ -1412,6 +1475,8 @@ "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], @@ -1454,6 +1519,8 @@ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], @@ -1546,8 +1613,12 @@ "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="], + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], @@ -1586,6 +1657,8 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "next": ["next@15.5.14", "", { "dependencies": { "@next/env": "15.5.14", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.14", "@next/swc-darwin-x64": "15.5.14", "@next/swc-linux-arm64-gnu": "15.5.14", "@next/swc-linux-arm64-musl": "15.5.14", "@next/swc-linux-x64-gnu": "15.5.14", "@next/swc-linux-x64-musl": "15.5.14", "@next/swc-win32-arm64-msvc": "15.5.14", "@next/swc-win32-x64-msvc": "15.5.14", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ=="], "next-view-transitions": ["next-view-transitions@0.3.5", "", { "peerDependencies": { "next": ">=14.0.0", "react": ">=18.2.0 || ^19.0.0", "react-dom": ">=18.2.0 || ^19.0.0" } }, "sha512-yP8OPNydLmKpmE94hLurLGEzPsUy1uyl9iSv8oxaC2JwhSXTD86SVwk1NMMQT7Ado4kMENDJ7fNBIXHy3GU/Lg=="], @@ -1624,6 +1697,8 @@ "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], @@ -1650,6 +1725,8 @@ "parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -1668,10 +1745,14 @@ "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + "playwright": ["playwright@1.59.1", "", { "dependencies": { "playwright-core": "1.59.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw=="], "playwright-core": ["playwright-core@1.59.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg=="], + "playwright-mcp": ["playwright-mcp@0.0.19", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.25.1", "@skorotkiewicz/snowflake-id": "^1.0.1", "happy-dom": "^17.4.4", "lodash": "^4.17.21", "playwright": "^1.51.0", "posthog-node": "^5.11.1", "zod": "^3.24.2" }, "bin": { "playwright-mcp": "dist/server.js" } }, "sha512-KzqO66bShWvXQU7pGrb/NkQ3pQKRAxOK5H+tHVUCBv/0X+2fcV7AJrsgr97BOAo5dv+/7ZrNK6o7aMkMJ/MMmA=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], @@ -1690,6 +1771,8 @@ "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + "posthog-node": ["posthog-node@5.29.1", "", { "dependencies": { "@posthog/core": "1.25.1" }, "peerDependencies": { "rxjs": "^7.0.0" }, "optionalPeers": ["rxjs"] }, "sha512-XA1OGrE8SAmO3JkfWjeVg7XxEN/6M6ZXzYmBJKl3uXv/xxk7ru0oeoqi/rcWJ6z5+nFP9wUFVoS37//qaPxwFg=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "primeicons": ["primeicons@7.0.0", "", {}, "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw=="], @@ -1736,6 +1819,8 @@ "prosemirror-view": ["prosemirror-view@1.41.8", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "proxy-compare": ["proxy-compare@3.0.1", "", {}, "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q=="], "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], @@ -1744,10 +1829,16 @@ "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "ramda": ["ramda@0.27.2", "", {}, "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA=="], + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], @@ -1820,6 +1911,8 @@ "rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="], + "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=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], @@ -1830,18 +1923,26 @@ "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -1940,6 +2041,8 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], @@ -1958,6 +2061,8 @@ "type-fest": ["type-fest@5.5.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], @@ -1980,6 +2085,8 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], "until-async": ["until-async@3.0.2", "", {}, "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw=="], @@ -2010,6 +2117,8 @@ "valtio": ["valtio@2.3.1", "", { "dependencies": { "proxy-compare": "^3.0.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0" }, "optionalPeers": ["@types/react", "react"] }, "sha512-ZsOaOEn0U9IJ96cAj3CZ3GjwpN3EJdjsi1PT4PREuB+Pcqfsczu16isT5DT1UrmHbk4PtLjk8kwNEHuR2CX56w=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], "vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="], @@ -2072,6 +2181,8 @@ "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + "@cubejs-client/core/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], "@elysiajs/cookie/cookie": ["cookie@0.5.0", "", {}, "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="], @@ -2084,6 +2195,8 @@ "@mantine/core/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "@parcel/core/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "@parcel/packager-js/globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], @@ -2102,6 +2215,8 @@ "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + "ajv-formats/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "babel-plugin-macros/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -2120,10 +2235,16 @@ "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "happy-dom/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + + "happy-dom/whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], + "iron-session/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "lmdb/node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], @@ -2146,10 +2267,14 @@ "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + "string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@parcel/packager-js/globals/type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], "@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="], @@ -2158,6 +2283,8 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "cross-fetch/node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], diff --git a/package.json b/package.json index 0d116269..1b5c51e0 100644 --- a/package.json +++ b/package.json @@ -125,6 +125,7 @@ "jsdom": "^28.0.0", "msw": "^2.12.9", "parcel": "^2.6.2", + "playwright-mcp": "^0.0.19", "postcss": "^8.5.1", "postcss-preset-mantine": "^1.17.0", "postcss-simple-vars": "^7.0.1", From 5e822f0b052823ea1d91d471aad1e7bdb1a90ab5 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 9 Apr 2026 17:10:29 +0800 Subject: [PATCH 2/4] feat: implement Kependudukan menu with CRUD admin pages - Add Distribusi Umur admin pages (list, create, edit) - Add Data Banjar admin pages (list, create, edit) - Add Migrasi Penduduk admin pages (list, create, edit) - Update state management with full CRUD operations for all modules - Add Kependudukan menu to admin sidebar (devBar, navBar, role1) - Add public pages for Distribusi Umur with age range sorting - Update Dinamika Penduduk to use real-time birth/death data - Add Biome configuration for code linting - Create API routes for all Kependudukan modules Features: - Pagination and search for all admin list pages - Responsive design (table for desktop, cards for mobile) - Delete confirmation modal - Toast notifications for user feedback - Zod validation for all forms - Age range auto-sorting in public Distribusi Umur chart Co-authored-by: Qwen-Coder --- biome.json | 49 +++ .../_state/kependudukan/dashboard.ts | 27 ++ .../_state/kependudukan/data-banjar.ts | 205 +++++++++++ .../_state/kependudukan/distribusi-agama.ts | 197 ++++++++++ .../_state/kependudukan/distribusi-umur.ts | 197 ++++++++++ .../_state/kependudukan/migrasi-penduduk.ts | 209 +++++++++++ .../kependudukan/data-banjar/[id]/page.tsx | 249 +++++++++++++ .../kependudukan/data-banjar/create/page.tsx | 189 ++++++++++ .../kependudukan/data-banjar/page.tsx | 304 ++++++++++++++++ .../distribusi-agama/[id]/page.tsx | 232 ++++++++++++ .../distribusi-agama/create/page.tsx | 174 +++++++++ .../kependudukan/distribusi-agama/page.tsx | 283 +++++++++++++++ .../distribusi-umur/[id]/page.tsx | 232 ++++++++++++ .../distribusi-umur/create/page.tsx | 174 +++++++++ .../kependudukan/distribusi-umur/page.tsx | 284 +++++++++++++++ .../migrasi-penduduk/[id]/page.tsx | 267 ++++++++++++++ .../migrasi-penduduk/create/page.tsx | 199 ++++++++++ .../kependudukan/migrasi-penduduk/page.tsx | 339 ++++++++++++++++++ src/app/admin/_com/list_PageAdmin.tsx | 81 +++++ .../_lib/kependudukan/dashboard/index.ts | 10 + .../_lib/kependudukan/dashboard/summary.ts | 147 ++++++++ .../_lib/kependudukan/data-banjar/create.ts | 40 +++ .../_lib/kependudukan/data-banjar/del.ts | 36 ++ .../_lib/kependudukan/data-banjar/findMany.ts | 49 +++ .../kependudukan/data-banjar/findUnique.ts | 46 +++ .../_lib/kependudukan/data-banjar/index.ts | 43 +++ .../_lib/kependudukan/data-banjar/updt.ts | 51 +++ .../kependudukan/distribusi-agama/create.ts | 34 ++ .../_lib/kependudukan/distribusi-agama/del.ts | 36 ++ .../kependudukan/distribusi-agama/findMany.ts | 50 +++ .../distribusi-agama/findUnique.ts | 46 +++ .../kependudukan/distribusi-agama/index.ts | 39 ++ .../kependudukan/distribusi-agama/updt.ts | 47 +++ .../kependudukan/distribusi-umur/create.ts | 34 ++ .../_lib/kependudukan/distribusi-umur/del.ts | 36 ++ .../kependudukan/distribusi-umur/findMany.ts | 42 +++ .../distribusi-umur/findUnique.ts | 46 +++ .../kependudukan/distribusi-umur/index.ts | 39 ++ .../_lib/kependudukan/distribusi-umur/updt.ts | 47 +++ .../[[...slugs]]/_lib/kependudukan/index.ts | 18 + .../kependudukan/migrasi-penduduk/create.ts | 43 +++ .../_lib/kependudukan/migrasi-penduduk/del.ts | 36 ++ .../kependudukan/migrasi-penduduk/findMany.ts | 62 ++++ .../migrasi-penduduk/findUnique.ts | 46 +++ .../kependudukan/migrasi-penduduk/index.ts | 45 +++ .../kependudukan/migrasi-penduduk/updt.ts | 53 +++ .../(pages)/kependudukan/dashboard/page.tsx | 196 ++++++++++ .../kependudukan/data-per-banjar/page.tsx | 147 ++++++++ .../kependudukan/dinamika-penduduk/page.tsx | 189 ++++++++++ .../kependudukan/distribusi-agama/page.tsx | 170 +++++++++ .../kependudukan/distribusi-umur/page.tsx | 150 ++++++++ 51 files changed, 5964 insertions(+) create mode 100644 biome.json create mode 100644 src/app/admin/(dashboard)/_state/kependudukan/dashboard.ts create mode 100644 src/app/admin/(dashboard)/_state/kependudukan/data-banjar.ts create mode 100644 src/app/admin/(dashboard)/_state/kependudukan/distribusi-agama.ts create mode 100644 src/app/admin/(dashboard)/_state/kependudukan/distribusi-umur.ts create mode 100644 src/app/admin/(dashboard)/_state/kependudukan/migrasi-penduduk.ts create mode 100644 src/app/admin/(dashboard)/kependudukan/data-banjar/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/data-banjar/create/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/data-banjar/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/distribusi-agama/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/distribusi-agama/create/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/distribusi-agama/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/distribusi-umur/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/distribusi-umur/create/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/distribusi-umur/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/migrasi-penduduk/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/migrasi-penduduk/create/page.tsx create mode 100644 src/app/admin/(dashboard)/kependudukan/migrasi-penduduk/page.tsx create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/dashboard/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/dashboard/summary.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/data-banjar/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/data-banjar/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/data-banjar/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/data-banjar/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/data-banjar/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/data-banjar/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-agama/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-agama/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-agama/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-agama/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-agama/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-agama/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-umur/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-umur/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-umur/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-umur/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-umur/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/distribusi-umur/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/migrasi-penduduk/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/migrasi-penduduk/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/migrasi-penduduk/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/migrasi-penduduk/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/migrasi-penduduk/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/kependudukan/migrasi-penduduk/updt.ts create mode 100644 src/app/darmasaba/(pages)/kependudukan/dashboard/page.tsx create mode 100644 src/app/darmasaba/(pages)/kependudukan/data-per-banjar/page.tsx create mode 100644 src/app/darmasaba/(pages)/kependudukan/dinamika-penduduk/page.tsx create mode 100644 src/app/darmasaba/(pages)/kependudukan/distribusi-agama/page.tsx create mode 100644 src/app/darmasaba/(pages)/kependudukan/distribusi-umur/page.tsx diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..95a1463b --- /dev/null +++ b/biome.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "experimentalScannerIgnores": [ + "node_modules", + ".next", + "out", + "public" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "warn", + "noUnusedImports": "warn" + }, + "suspicious": { + "noExplicitAny": "warn" + }, + "style": { + "noNonNullAssertion": "warn" + }, + "complexity": { + "noForEach": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "all", + "semicolons": "always" + } + } +} diff --git a/src/app/admin/(dashboard)/_state/kependudukan/dashboard.ts b/src/app/admin/(dashboard)/_state/kependudukan/dashboard.ts new file mode 100644 index 00000000..894ccbee --- /dev/null +++ b/src/app/admin/(dashboard)/_state/kependudukan/dashboard.ts @@ -0,0 +1,27 @@ +import ApiFetch from "@/lib/api-fetch"; +import { proxy } from "valtio"; + +const kependudukanDashboard = proxy({ + summary: { + data: null as any, + loading: false, + async load() { + kependudukanDashboard.summary.loading = true; + try { + const res = await ApiFetch.api.kependudukan.dashboard.summary.get(); + if (res.status === 200 && res.data?.success) { + kependudukanDashboard.summary.data = res.data.data; + } else { + kependudukanDashboard.summary.data = null; + } + } catch (err) { + console.error("Gagal fetch dashboard summary:", err); + kependudukanDashboard.summary.data = null; + } finally { + kependudukanDashboard.summary.loading = false; + } + }, + }, +}); + +export default kependudukanDashboard; diff --git a/src/app/admin/(dashboard)/_state/kependudukan/data-banjar.ts b/src/app/admin/(dashboard)/_state/kependudukan/data-banjar.ts new file mode 100644 index 00000000..7267863c --- /dev/null +++ b/src/app/admin/(dashboard)/_state/kependudukan/data-banjar.ts @@ -0,0 +1,205 @@ +import ApiFetch from "@/lib/api-fetch"; +import { proxy } from "valtio"; +import { toast } from "react-toastify"; +import { z } from "zod"; + +const templateDataBanjar = z.object({ + nama: z.string().min(1, "Nama banjar harus diisi"), + penduduk: z.number().min(0, "Jumlah penduduk harus diisi"), + kk: z.number().min(0, "Jumlah KK harus diisi"), + miskin: z.number().min(0, "Jumlah penduduk miskin harus diisi"), + tahun: z.number().min(2000, "Tahun harus diisi"), +}); + +const dataBanjar = proxy({ + create: { + form: { + nama: "", + penduduk: 0, + kk: 0, + miskin: 0, + tahun: new Date().getFullYear(), + }, + loading: false, + async create() { + const cek = templateDataBanjar.safeParse(dataBanjar.create.form); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + try { + dataBanjar.create.loading = true; + const res = await ApiFetch.api.kependudukan.databanjar["create"].post(dataBanjar.create.form); + + if (res.status === 200) { + const id = res.data?.data?.id; + if (id) { + toast.success("Sukses menambahkan data banjar"); + dataBanjar.create.form = { nama: "", penduduk: 0, kk: 0, miskin: 0, tahun: new Date().getFullYear() }; + dataBanjar.findMany.load(); + return id; + } + } + toast.error("Gagal menambahkan data"); + return null; + } catch (error) { + console.log((error as Error).message); + return null; + } finally { + dataBanjar.create.loading = false; + } + }, + }, + + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => { + dataBanjar.findMany.loading = true; + dataBanjar.findMany.page = page; + dataBanjar.findMany.search = search; + + try { + const query: any = { page, limit, tahun }; + if (search) query.search = search; + + const res = await ApiFetch.api.kependudukan.databanjar["find-many"].get({ query }); + + if (res.status === 200 && res.data?.success) { + dataBanjar.findMany.data = res.data.data ?? []; + dataBanjar.findMany.totalPages = res.data.totalPages ?? 1; + } else { + dataBanjar.findMany.data = []; + dataBanjar.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch data banjar paginated:", err); + dataBanjar.findMany.data = []; + dataBanjar.findMany.totalPages = 1; + } finally { + dataBanjar.findMany.loading = false; + } + }, + }, + + findUnique: { + data: null as any | null, + async load(id: string) { + try { + const res = await fetch(`/api/kependudukan/databanjar/${id}`); + if (res.ok) { + const data = await res.json(); + dataBanjar.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data banjar:", res.statusText); + dataBanjar.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data banjar:", error); + dataBanjar.findUnique.data = null; + } + }, + }, + + update: { + id: "", + form: { + nama: "", + penduduk: 0, + kk: 0, + miskin: 0, + tahun: new Date().getFullYear(), + }, + loading: false, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + const formData = { + nama: this.form.nama, + penduduk: this.form.penduduk, + kk: this.form.kk, + miskin: this.form.miskin, + tahun: this.form.tahun, + }; + + const cek = templateDataBanjar.safeParse(formData); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + + try { + this.loading = true; + const res = await fetch(`/api/kependudukan/databanjar/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const result = await res.json(); + + if (!res.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + + toast.success("Berhasil update data!"); + await dataBanjar.findMany.load(); + return result.data; + } catch (error) { + console.error("Update error:", error); + toast.error("Gagal update data banjar"); + throw error; + } finally { + this.loading = false; + } + }, + }, + + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + dataBanjar.delete.loading = true; + + const response = await fetch( + `/api/kependudukan/databanjar/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Data banjar berhasil dihapus"); + await dataBanjar.findMany.load(); + } else { + toast.error(result?.message || "Gagal menghapus data banjar"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus data banjar"); + } finally { + dataBanjar.delete.loading = false; + } + }, + }, +}); + +export default dataBanjar; diff --git a/src/app/admin/(dashboard)/_state/kependudukan/distribusi-agama.ts b/src/app/admin/(dashboard)/_state/kependudukan/distribusi-agama.ts new file mode 100644 index 00000000..0ba69e07 --- /dev/null +++ b/src/app/admin/(dashboard)/_state/kependudukan/distribusi-agama.ts @@ -0,0 +1,197 @@ +import ApiFetch from "@/lib/api-fetch"; +import { proxy } from "valtio"; +import { toast } from "react-toastify"; +import { z } from "zod"; + +const templateDistribusiAgama = z.object({ + agama: z.string().min(1, "Agama harus diisi"), + jumlah: z.number().min(0, "Jumlah harus diisi"), + tahun: z.number().min(2000, "Tahun harus diisi"), +}); + +const distribusiAgama = proxy({ + create: { + form: { + agama: "", + jumlah: 0, + tahun: new Date().getFullYear(), + }, + loading: false, + async create() { + const cek = templateDistribusiAgama.safeParse(distribusiAgama.create.form); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + try { + distribusiAgama.create.loading = true; + const res = await ApiFetch.api.kependudukan.distribusiagama["create"].post(distribusiAgama.create.form); + + if (res.status === 200) { + const id = res.data?.data?.id; + if (id) { + toast.success("Sukses menambahkan distribusi agama"); + distribusiAgama.create.form = { agama: "", jumlah: 0, tahun: new Date().getFullYear() }; + distribusiAgama.findMany.load(); + return id; + } + } + toast.error("Gagal menambahkan data"); + return null; + } catch (error) { + console.log((error as Error).message); + return null; + } finally { + distribusiAgama.create.loading = false; + } + }, + }, + + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => { + distribusiAgama.findMany.loading = true; + distribusiAgama.findMany.page = page; + distribusiAgama.findMany.search = search; + + try { + const query: any = { page, limit, tahun }; + if (search) query.search = search; + + const res = await ApiFetch.api.kependudukan.distribusiagama["find-many"].get({ query }); + + if (res.status === 200 && res.data?.success) { + distribusiAgama.findMany.data = res.data.data ?? []; + distribusiAgama.findMany.totalPages = res.data.totalPages ?? 1; + } else { + distribusiAgama.findMany.data = []; + distribusiAgama.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch distribusi agama paginated:", err); + distribusiAgama.findMany.data = []; + distribusiAgama.findMany.totalPages = 1; + } finally { + distribusiAgama.findMany.loading = false; + } + }, + }, + + findUnique: { + data: null as any | null, + async load(id: string) { + try { + const res = await fetch(`/api/kependudukan/distribusiagama/${id}`); + if (res.ok) { + const data = await res.json(); + distribusiAgama.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch distribusiAgama:", res.statusText); + distribusiAgama.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching distribusiAgama:", error); + distribusiAgama.findUnique.data = null; + } + }, + }, + + update: { + id: "", + form: { + agama: "", + jumlah: 0, + tahun: new Date().getFullYear(), + }, + loading: false, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + const formData = { + agama: this.form.agama, + jumlah: this.form.jumlah, + tahun: this.form.tahun, + }; + + const cek = templateDistribusiAgama.safeParse(formData); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + + try { + this.loading = true; + const res = await fetch(`/api/kependudukan/distribusiagama/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const result = await res.json(); + + if (!res.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + + toast.success("Berhasil update data!"); + await distribusiAgama.findMany.load(); + return result.data; + } catch (error) { + console.error("Update error:", error); + toast.error("Gagal update data distribusi agama"); + throw error; + } finally { + this.loading = false; + } + }, + }, + + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + distribusiAgama.delete.loading = true; + + const response = await fetch( + `/api/kependudukan/distribusiagama/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Distribusi agama berhasil dihapus"); + await distribusiAgama.findMany.load(); + } else { + toast.error(result?.message || "Gagal menghapus distribusi agama"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus distribusi agama"); + } finally { + distribusiAgama.delete.loading = false; + } + }, + }, +}); + +export default distribusiAgama; diff --git a/src/app/admin/(dashboard)/_state/kependudukan/distribusi-umur.ts b/src/app/admin/(dashboard)/_state/kependudukan/distribusi-umur.ts new file mode 100644 index 00000000..98752a3b --- /dev/null +++ b/src/app/admin/(dashboard)/_state/kependudukan/distribusi-umur.ts @@ -0,0 +1,197 @@ +import ApiFetch from "@/lib/api-fetch"; +import { proxy } from "valtio"; +import { toast } from "react-toastify"; +import { z } from "zod"; + +const templateDistribusiUmur = z.object({ + rentangUmur: z.string().min(1, "Rentang umur harus diisi"), + jumlah: z.number().min(0, "Jumlah harus diisi"), + tahun: z.number().min(2000, "Tahun harus diisi"), +}); + +const distribusiUmur = proxy({ + create: { + form: { + rentangUmur: "", + jumlah: 0, + tahun: new Date().getFullYear(), + }, + loading: false, + async create() { + const cek = templateDistribusiUmur.safeParse(distribusiUmur.create.form); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + try { + distribusiUmur.create.loading = true; + const res = await ApiFetch.api.kependudukan.distribusiumur["create"].post(distribusiUmur.create.form); + + if (res.status === 200) { + const id = res.data?.data?.id; + if (id) { + toast.success("Sukses menambahkan distribusi umur"); + distribusiUmur.create.form = { rentangUmur: "", jumlah: 0, tahun: new Date().getFullYear() }; + distribusiUmur.findMany.load(); + return id; + } + } + toast.error("Gagal menambahkan data"); + return null; + } catch (error) { + console.log((error as Error).message); + return null; + } finally { + distribusiUmur.create.loading = false; + } + }, + }, + + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => { + distribusiUmur.findMany.loading = true; + distribusiUmur.findMany.page = page; + distribusiUmur.findMany.search = search; + + try { + const query: any = { page, limit, tahun }; + if (search) query.search = search; + + const res = await ApiFetch.api.kependudukan.distribusiumur["find-many"].get({ query }); + + if (res.status === 200 && res.data?.success) { + distribusiUmur.findMany.data = res.data.data ?? []; + distribusiUmur.findMany.totalPages = res.data.totalPages ?? 1; + } else { + distribusiUmur.findMany.data = []; + distribusiUmur.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch distribusi umur paginated:", err); + distribusiUmur.findMany.data = []; + distribusiUmur.findMany.totalPages = 1; + } finally { + distribusiUmur.findMany.loading = false; + } + }, + }, + + findUnique: { + data: null as any | null, + async load(id: string) { + try { + const res = await fetch(`/api/kependudukan/distribusiumur/${id}`); + if (res.ok) { + const data = await res.json(); + distribusiUmur.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch distribusi umur:", res.statusText); + distribusiUmur.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching distribusi umur:", error); + distribusiUmur.findUnique.data = null; + } + }, + }, + + update: { + id: "", + form: { + rentangUmur: "", + jumlah: 0, + tahun: new Date().getFullYear(), + }, + loading: false, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + const formData = { + rentangUmur: this.form.rentangUmur, + jumlah: this.form.jumlah, + tahun: this.form.tahun, + }; + + const cek = templateDistribusiUmur.safeParse(formData); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + + try { + this.loading = true; + const res = await fetch(`/api/kependudukan/distribusiumur/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const result = await res.json(); + + if (!res.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + + toast.success("Berhasil update data!"); + await distribusiUmur.findMany.load(); + return result.data; + } catch (error) { + console.error("Update error:", error); + toast.error("Gagal update data distribusi umur"); + throw error; + } finally { + this.loading = false; + } + }, + }, + + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + distribusiUmur.delete.loading = true; + + const response = await fetch( + `/api/kependudukan/distribusiumur/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Distribusi umur berhasil dihapus"); + await distribusiUmur.findMany.load(); + } else { + toast.error(result?.message || "Gagal menghapus distribusi umur"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus distribusi umur"); + } finally { + distribusiUmur.delete.loading = false; + } + }, + }, +}); + +export default distribusiUmur; diff --git a/src/app/admin/(dashboard)/_state/kependudukan/migrasi-penduduk.ts b/src/app/admin/(dashboard)/_state/kependudukan/migrasi-penduduk.ts new file mode 100644 index 00000000..30d8bc91 --- /dev/null +++ b/src/app/admin/(dashboard)/_state/kependudukan/migrasi-penduduk.ts @@ -0,0 +1,209 @@ +import ApiFetch from "@/lib/api-fetch"; +import { proxy } from "valtio"; +import { toast } from "react-toastify"; +import { z } from "zod"; + +const templateMigrasiPenduduk = z.object({ + jenis: z.string().min(1, "Jenis migrasi harus diisi"), + nama: z.string().min(1, "Nama harus diisi"), + tanggal: z.string().min(1, "Tanggal harus diisi"), + asalTujuan: z.string().min(1, "Asal/Tujuan harus diisi"), + alasan: z.string().optional(), + jenisKelamin: z.string().optional(), +}); + +const migrasiPenduduk = proxy({ + create: { + form: { + jenis: "", + nama: "", + tanggal: "", + asalTujuan: "", + alasan: "", + jenisKelamin: "", + }, + loading: false, + async create() { + const cek = templateMigrasiPenduduk.safeParse(migrasiPenduduk.create.form); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + try { + migrasiPenduduk.create.loading = true; + const res = await ApiFetch.api.kependudukan.migrasipenduduk["create"].post(migrasiPenduduk.create.form); + + if (res.status === 200) { + const id = res.data?.data?.id; + if (id) { + toast.success("Sukses menambahkan data migrasi penduduk"); + migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "", jenisKelamin: "" }; + migrasiPenduduk.findMany.load(); + return id; + } + } + toast.error("Gagal menambahkan data"); + return null; + } catch (error) { + console.log((error as Error).message); + return null; + } finally { + migrasiPenduduk.create.loading = false; + } + }, + }, + + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "", tahun = new Date().getFullYear()) => { + migrasiPenduduk.findMany.loading = true; + migrasiPenduduk.findMany.page = page; + migrasiPenduduk.findMany.search = search; + + try { + const query: any = { page, limit, tahun }; + if (search) query.search = search; + + const res = await ApiFetch.api.kependudukan.migrasipenduduk["find-many"].get({ query }); + + if (res.status === 200 && res.data?.success) { + migrasiPenduduk.findMany.data = res.data.data ?? []; + migrasiPenduduk.findMany.totalPages = res.data.totalPages ?? 1; + } else { + migrasiPenduduk.findMany.data = []; + migrasiPenduduk.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch migrasi penduduk paginated:", err); + migrasiPenduduk.findMany.data = []; + migrasiPenduduk.findMany.totalPages = 1; + } finally { + migrasiPenduduk.findMany.loading = false; + } + }, + }, + + findUnique: { + data: null as any | null, + async load(id: string) { + try { + const res = await fetch(`/api/kependudukan/migrasipenduduk/${id}`); + if (res.ok) { + const data = await res.json(); + migrasiPenduduk.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch migrasi penduduk:", res.statusText); + migrasiPenduduk.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching migrasi penduduk:", error); + migrasiPenduduk.findUnique.data = null; + } + }, + }, + + update: { + id: "", + form: { + jenis: "", + nama: "", + tanggal: "", + asalTujuan: "", + alasan: "", + jenisKelamin: "", + }, + loading: false, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + const formData = { + jenis: this.form.jenis, + nama: this.form.nama, + tanggal: this.form.tanggal, + asalTujuan: this.form.asalTujuan, + alasan: this.form.alasan, + jenisKelamin: this.form.jenisKelamin, + }; + + const cek = templateMigrasiPenduduk.safeParse(formData); + if (!cek.success) { + const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + toast.error(err); + return null; + } + + try { + this.loading = true; + const res = await fetch(`/api/kependudukan/migrasipenduduk/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const result = await res.json(); + + if (!res.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + + toast.success("Berhasil update data!"); + await migrasiPenduduk.findMany.load(); + return result.data; + } catch (error) { + console.error("Update error:", error); + toast.error("Gagal update data migrasi penduduk"); + throw error; + } finally { + this.loading = false; + } + }, + }, + + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + migrasiPenduduk.delete.loading = true; + + const response = await fetch( + `/api/kependudukan/migrasipenduduk/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Data migrasi penduduk berhasil dihapus"); + await migrasiPenduduk.findMany.load(); + } else { + toast.error(result?.message || "Gagal menghapus data migrasi penduduk"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus data migrasi penduduk"); + } finally { + migrasiPenduduk.delete.loading = false; + } + }, + }, +}); + +export default migrasiPenduduk; diff --git a/src/app/admin/(dashboard)/kependudukan/data-banjar/[id]/page.tsx b/src/app/admin/(dashboard)/kependudukan/data-banjar/[id]/page.tsx new file mode 100644 index 00000000..2bfbffa1 --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/data-banjar/[id]/page.tsx @@ -0,0 +1,249 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + Title, + NumberInput, + TextInput +} from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useCallback, useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import dataBanjar from '../../../_state/kependudukan/data-banjar'; + +interface FormData { + nama: string; + penduduk: number; + kk: number; + miskin: number; + tahun: number; +} + +export default function EditDataBanjar() { + const router = useRouter(); + const { id } = useParams() as { id: string }; + const stateDataBanjar = useProxy(dataBanjar); + const [isSubmitting, setIsSubmitting] = useState(false); + const [formData, setFormData] = useState({ + nama: '', + penduduk: 0, + kk: 0, + miskin: 0, + tahun: new Date().getFullYear(), + }); + const [originalData, setOriginalData] = useState({ + nama: '', + penduduk: 0, + kk: 0, + miskin: 0, + tahun: new Date().getFullYear(), + }); + + const currentYear = new Date().getFullYear(); + + const isFormValid = () => { + return ( + formData.nama?.trim() !== '' && + formData.penduduk !== null && + formData.penduduk >= 0 && + formData.kk !== null && + formData.kk >= 0 && + formData.miskin !== null && + formData.miskin >= 0 && + formData.tahun !== null + ); + }; + + useEffect(() => { + if (!id) return; + + const loadData = async () => { + try { + setIsSubmitting(true); + stateDataBanjar.update.id = id; + await stateDataBanjar.findUnique.load(id); + + const data = stateDataBanjar.findUnique.data; + if (data) { + setFormData({ + nama: data.nama ?? '', + penduduk: Number(data.penduduk ?? 0), + kk: Number(data.kk ?? 0), + miskin: Number(data.miskin ?? 0), + tahun: Number(data.tahun ?? currentYear), + }); + setOriginalData({ + nama: data.nama ?? '', + penduduk: Number(data.penduduk ?? 0), + kk: Number(data.kk ?? 0), + miskin: Number(data.miskin ?? 0), + tahun: Number(data.tahun ?? currentYear), + }); + } + } catch (error) { + console.error('Error loading data:', error); + toast.error('Gagal memuat data'); + } finally { + setIsSubmitting(false); + } + }; + + loadData(); + }, [id]); + + const handleChange = useCallback( + (field: keyof FormData) => + (value: any) => { + const val = + field === 'penduduk' || field === 'kk' || field === 'miskin' || field === 'tahun' + ? Number(value || 0) + : value; + + setFormData((prev) => ({ ...prev, [field]: val })); + }, + [] + ); + + const handleResetForm = () => { + setFormData({ + nama: originalData.nama, + penduduk: Number(originalData.penduduk), + kk: Number(originalData.kk), + miskin: Number(originalData.miskin), + tahun: Number(originalData.tahun), + }); + toast.info("Form dikembalikan ke data awal"); + }; + + const handleSubmit = async () => { + try { + setIsSubmitting(true); + stateDataBanjar.update.id = id; + stateDataBanjar.update.form = { ...formData }; + + await stateDataBanjar.update.submit(); + + toast.success('Data berhasil diperbarui'); + router.push('/admin/kependudukan/data-banjar'); + } catch (error) { + console.error('Error updating data:', error); + toast.error('Gagal memperbarui data'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Edit Data Banjar + + + + {/* Form Card */} + + + + + + + + + + + + + + + + {/* Tombol Simpan */} + + + + + + ); +} diff --git a/src/app/admin/(dashboard)/kependudukan/data-banjar/create/page.tsx b/src/app/admin/(dashboard)/kependudukan/data-banjar/create/page.tsx new file mode 100644 index 00000000..64e8584f --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/data-banjar/create/page.tsx @@ -0,0 +1,189 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + Title, + NumberInput, + TextInput +} from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import dataBanjar from '../../../_state/kependudukan/data-banjar'; +import { toast } from 'react-toastify'; + +function CreateDataBanjar() { + const stateDataBanjar = useProxy(dataBanjar); + const router = useRouter(); + const [isSubmitting, setIsSubmitting] = useState(false); + + const currentYear = new Date().getFullYear(); + const yearOptions = Array.from({ length: 10 }, (_, i) => ({ + value: String(currentYear - i), + label: String(currentYear - i), + })); + + const isFormValid = () => { + return ( + stateDataBanjar.create.form.nama?.trim() !== '' && + stateDataBanjar.create.form.penduduk !== null && + stateDataBanjar.create.form.penduduk >= 0 && + stateDataBanjar.create.form.kk !== null && + stateDataBanjar.create.form.kk >= 0 && + stateDataBanjar.create.form.miskin !== null && + stateDataBanjar.create.form.miskin >= 0 && + stateDataBanjar.create.form.tahun !== null + ); + }; + + const resetForm = () => { + stateDataBanjar.create.form = { + nama: '', + penduduk: 0, + kk: 0, + miskin: 0, + tahun: currentYear, + }; + }; + + const handleSubmit = async () => { + try { + const id = await stateDataBanjar.create.create(); + if (id) { + resetForm(); + router.push('/admin/kependudukan/data-banjar'); + } + } catch (error) { + console.error('Error creating data banjar:', error); + toast.error('Terjadi kesalahan saat menambah data banjar'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Tambah Data Banjar + + + + {/* Form */} + + + { + stateDataBanjar.create.form.nama = e.currentTarget.value; + }} + required + /> + + { + stateDataBanjar.create.form.penduduk = Number(val || 0); + }} + min={0} + required + /> + + { + stateDataBanjar.create.form.kk = Number(val || 0); + }} + min={0} + required + /> + + { + stateDataBanjar.create.form.miskin = Number(val || 0); + }} + min={0} + required + /> + + { + stateDataBanjar.create.form.tahun = Number(val || currentYear); + }} + min={2000} + max={currentYear + 1} + required + /> + + + + + {/* Tombol Simpan */} + + + + + + ); +} + +export default CreateDataBanjar; diff --git a/src/app/admin/(dashboard)/kependudukan/data-banjar/page.tsx b/src/app/admin/(dashboard)/kependudukan/data-banjar/page.tsx new file mode 100644 index 00000000..f25f6c62 --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/data-banjar/page.tsx @@ -0,0 +1,304 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client' +import colors from '@/con/colors'; +import { + Box, + Button, + Center, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title +} from '@mantine/core'; +import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'; +import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../_com/header'; +import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus'; +import dataBanjar from '../../_state/kependudukan/data-banjar'; + +function DataBanjarAdmin() { + const [search, setSearch] = useState(''); + return ( + + } + value={search} + onChange={(e: React.ChangeEvent) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListDataBanjar({ search }: { search: string }) { + type DataBanjarType = { + id: string; + nama: string; + penduduk: number; + kk: number; + miskin: number; + tahun: number; + }; + + const router = useRouter(); + const stateDataBanjar = useProxy(dataBanjar); + const [modalHapus, setModalHapus] = useState(false); + const [debouncedSearch] = useDebouncedValue(search, 1000); + const [selectedId, setSelectedId] = useState(null); + + const { + data, + page, + totalPages, + loading, + load, + } = stateDataBanjar.findMany; + + const handleDelete = () => { + if (selectedId) { + stateDataBanjar.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + } + }; + + useShallowEffect(() => { + load(page, 10, debouncedSearch); + }, [page, debouncedSearch]); + + const filteredData = data || []; + + if (loading || !data) { + return ( + + + + ); + } + + return ( + + + + + List Data Banjar + + + + + {/* Desktop Table */} + + + + + Nama Banjar + Penduduk + KK + Miskin + Tahun + Edit + Hapus + + + + {filteredData.length > 0 ? ( + filteredData.map((item: DataBanjarType) => ( + + {item.nama} + {item.penduduk.toLocaleString('id-ID')} + {item.kk.toLocaleString('id-ID')} + {item.miskin.toLocaleString('id-ID')} + {item.tahun} + + + + + + + + )) + ) : ( + + +
+ + Tidak ada data banjar yang cocok + +
+
+
+ )} +
+
+
+ + {/* Mobile Card */} + + + {filteredData.length > 0 ? ( + filteredData.map((item: DataBanjarType) => ( + + + + + Nama Banjar + + + {item.nama} + + + + + Jumlah Penduduk + + + {item.penduduk.toLocaleString('id-ID')} + + + + + KK + + + {item.kk.toLocaleString('id-ID')} + + + + + Penduduk Miskin + + + {item.miskin.toLocaleString('id-ID')} + + + + + Tahun + + + {item.tahun} + + + + + + + + + )) + ) : ( +
+ + Tidak ada data banjar yang cocok + +
+ )} +
+
+
+ + {/* Pagination */} +
+ { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+ + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleDelete} + text="Apakah anda yakin ingin menghapus data banjar ini?" + /> +
+ ); +} + +export default DataBanjarAdmin; diff --git a/src/app/admin/(dashboard)/kependudukan/distribusi-agama/[id]/page.tsx b/src/app/admin/(dashboard)/kependudukan/distribusi-agama/[id]/page.tsx new file mode 100644 index 00000000..f382d689 --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/distribusi-agama/[id]/page.tsx @@ -0,0 +1,232 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + TextInput, + Title, + NumberInput, + Select +} from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useCallback, useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import distribusiAgama from '../../../_state/kependudukan/distribusi-agama'; + +interface FormData { + agama: string; + jumlah: number; + tahun: number; +} + +export default function EditDistribusiAgama() { + const router = useRouter(); + const { id } = useParams() as { id: string }; + const stateDistribusiAgama = useProxy(distribusiAgama); + const [isSubmitting, setIsSubmitting] = useState(false); + const [formData, setFormData] = useState({ + agama: '', + jumlah: 0, + tahun: new Date().getFullYear(), + }); + const [originalData, setOriginalData] = useState({ + agama: '', + jumlah: 0, + tahun: new Date().getFullYear(), + }); + + const currentYear = new Date().getFullYear(); + const yearOptions = Array.from({ length: 10 }, (_, i) => ({ + value: String(currentYear - i), + label: String(currentYear - i), + })); + + const agamaOptions = [ + { value: 'HINDU', label: 'Hindu' }, + { value: 'ISLAM', label: 'Islam' }, + { value: 'KRISTEN', label: 'Kristen' }, + { value: 'KRISTEN_PROTESTAN', label: 'Kristen Protestan' }, + { value: 'KRISTEN_KATOLIK', label: 'Kristen Katolik' }, + { value: 'BUDDHA', label: 'Buddha' }, + { value: 'KONGHUCU', label: 'Konghucu' }, + { value: 'LAINNYA', label: 'Lainnya' }, + ]; + + const isFormValid = () => { + return ( + formData.agama?.trim() !== '' && + formData.jumlah !== null && + formData.jumlah >= 0 && + formData.tahun !== null + ); + }; + + useEffect(() => { + if (!id) return; + + const loadData = async () => { + try { + setIsSubmitting(true); + stateDistribusiAgama.update.id = id; + await stateDistribusiAgama.findUnique.load(id); + + const data = stateDistribusiAgama.findUnique.data; + if (data) { + setFormData({ + agama: data.agama ?? '', + jumlah: Number(data.jumlah ?? 0), + tahun: Number(data.tahun ?? currentYear), + }); + setOriginalData({ + agama: data.agama ?? '', + jumlah: Number(data.jumlah ?? 0), + tahun: Number(data.tahun ?? currentYear), + }); + } + } catch (error) { + console.error('Error loading data:', error); + toast.error('Gagal memuat data'); + } finally { + setIsSubmitting(false); + } + }; + + loadData(); + }, [id]); + + const handleChange = useCallback( + (field: keyof FormData) => + (value: any) => { + const val = + field === 'jumlah' || field === 'tahun' + ? Number(value || 0) + : value; + + setFormData((prev) => ({ ...prev, [field]: val })); + }, + [] + ); + + const handleResetForm = () => { + setFormData({ + agama: originalData.agama, + jumlah: Number(originalData.jumlah), + tahun: Number(originalData.tahun), + }); + toast.info("Form dikembalikan ke data awal"); + }; + + const handleSubmit = async () => { + try { + setIsSubmitting(true); + stateDistribusiAgama.update.id = id; + stateDistribusiAgama.update.form = { ...formData }; + + await stateDistribusiAgama.update.submit(); + + toast.success('Data berhasil diperbarui'); + router.push('/admin/kependudukan/distribusi-agama'); + } catch (error) { + console.error('Error updating data:', error); + toast.error('Gagal memperbarui data'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Edit Distribusi Agama + + + + {/* Form Card */} + + + + + + + + {/* Tombol Simpan */} + + + + + + ); +} \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kependudukan/distribusi-agama/create/page.tsx b/src/app/admin/(dashboard)/kependudukan/distribusi-agama/create/page.tsx new file mode 100644 index 00000000..928d281d --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/distribusi-agama/create/page.tsx @@ -0,0 +1,174 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + TextInput, + Title, + NumberInput, + Select +} from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import distribusiAgama from '../../../_state/kependudukan/distribusi-agama'; +import { toast } from 'react-toastify'; + +function CreateDistribusiAgama() { + const stateDistribusiAgama = useProxy(distribusiAgama); + const router = useRouter(); + const [isSubmitting, setIsSubmitting] = useState(false); + + const agamaOptions = [ + { value: 'HINDU', label: 'Hindu' }, + { value: 'ISLAM', label: 'Islam' }, + { value: 'KRISTEN', label: 'Kristen' }, + { value: 'KRISTEN_PROTESTAN', label: 'Kristen Protestan' }, + { value: 'KRISTEN_KATOLIK', label: 'Kristen Katolik' }, + { value: 'BUDDHA', label: 'Buddha' }, + { value: 'KONGHUCU', label: 'Konghucu' }, + { value: 'LAINNYA', label: 'Lainnya' }, + ]; + + const currentYear = new Date().getFullYear(); + const yearOptions = Array.from({ length: 10 }, (_, i) => ({ + value: String(currentYear - i), + label: String(currentYear - i), + })); + + const isFormValid = () => { + return ( + stateDistribusiAgama.create.form.agama?.trim() !== '' && + stateDistribusiAgama.create.form.jumlah !== null && + stateDistribusiAgama.create.form.jumlah >= 0 && + stateDistribusiAgama.create.form.tahun !== null + ); + }; + + const resetForm = () => { + stateDistribusiAgama.create.form = { + agama: '', + jumlah: 0, + tahun: currentYear, + }; + }; + + const handleSubmit = async () => { + try { + const id = await stateDistribusiAgama.create.create(); + if (id) { + resetForm(); + router.push('/admin/kependudukan/distribusi-agama'); + } + } catch (error) { + console.error('Error creating distribusi agama:', error); + toast.error('Terjadi kesalahan saat menambah distribusi agama'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Tambah Distribusi Agama + + + + {/* Form */} + + + { + stateDistribusiAgama.create.form.tahun = Number(val || currentYear); + }} + required + /> + + + + + {/* Tombol Simpan */} + + + + + + ); +} + +export default CreateDistribusiAgama; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kependudukan/distribusi-agama/page.tsx b/src/app/admin/(dashboard)/kependudukan/distribusi-agama/page.tsx new file mode 100644 index 00000000..d9037cf1 --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/distribusi-agama/page.tsx @@ -0,0 +1,283 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client' +import colors from '@/con/colors'; +import { + Box, + Button, + Center, + Flex, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title +} from '@mantine/core'; +import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'; +import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../_com/header'; +import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus'; +import distribusiAgama from '../../_state/kependudukan/distribusi-agama'; + +function DistribusiAgamaAdmin() { + const [search, setSearch] = useState(''); + return ( + + } + value={search} + onChange={(e: React.ChangeEvent) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListDistribusiAgama({ search }: { search: string }) { + type DistribusiAgamaType = { + id: string; + agama: string; + jumlah: number; + tahun: number; + }; + + const router = useRouter(); + const stateDistribusiAgama = useProxy(distribusiAgama); + const [modalHapus, setModalHapus] = useState(false); + const [debouncedSearch] = useDebouncedValue(search, 1000); + const [selectedId, setSelectedId] = useState(null); + + const { + data, + page, + totalPages, + loading, + load, + } = stateDistribusiAgama.findMany; + + const handleDelete = () => { + if (selectedId) { + stateDistribusiAgama.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + } + }; + + useShallowEffect(() => { + load(page, 10, debouncedSearch); + }, [page, debouncedSearch]); + + const filteredData = data || []; + + if (loading || !data) { + return ( + + + + ); + } + + return ( + + + + + List Distribusi Agama + + + + + {/* Desktop Table */} + + + + + Agama + Jumlah + Tahun + Edit + Hapus + + + + {filteredData.length > 0 ? ( + filteredData.map((item: DistribusiAgamaType) => ( + + {item.agama} + {item.jumlah.toLocaleString('id-ID')} + {item.tahun} + + + + + + + + )) + ) : ( + + +
+ + Tidak ada data distribusi agama yang cocok + +
+
+
+ )} +
+
+
+ + {/* Mobile Card */} + + + {filteredData.length > 0 ? ( + filteredData.map((item: DistribusiAgamaType) => ( + + + + + Agama + + + {item.agama} + + + + + Jumlah + + + {item.jumlah.toLocaleString('id-ID')} + + + + + Tahun + + + {item.tahun} + + + + + + + + + )) + ) : ( +
+ + Tidak ada data distribusi agama yang cocok + +
+ )} +
+
+
+ + {/* Pagination */} +
+ { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+ + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleDelete} + text="Apakah anda yakin ingin menghapus data distribusi agama ini?" + /> +
+ ); +} + +export default DistribusiAgamaAdmin; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kependudukan/distribusi-umur/[id]/page.tsx b/src/app/admin/(dashboard)/kependudukan/distribusi-umur/[id]/page.tsx new file mode 100644 index 00000000..26f32af3 --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/distribusi-umur/[id]/page.tsx @@ -0,0 +1,232 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + Title, + NumberInput, + Select +} from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useCallback, useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import distribusiUmur from '../../../_state/kependudukan/distribusi-umur'; + +interface FormData { + rentangUmur: string; + jumlah: number; + tahun: number; +} + +export default function EditDistribusiUmur() { + const router = useRouter(); + const { id } = useParams() as { id: string }; + const stateDistribusiUmur = useProxy(distribusiUmur); + const [isSubmitting, setIsSubmitting] = useState(false); + const [formData, setFormData] = useState({ + rentangUmur: '', + jumlah: 0, + tahun: new Date().getFullYear(), + }); + const [originalData, setOriginalData] = useState({ + rentangUmur: '', + jumlah: 0, + tahun: new Date().getFullYear(), + }); + + const currentYear = new Date().getFullYear(); + const yearOptions = Array.from({ length: 10 }, (_, i) => ({ + value: String(currentYear - i), + label: String(currentYear - i), + })); + + const rentangUmurOptions = [ + { value: '0-5', label: '0-5 Tahun' }, + { value: '6-12', label: '6-12 Tahun' }, + { value: '13-17', label: '13-17 Tahun' }, + { value: '18-25', label: '18-25 Tahun' }, + { value: '26-35', label: '26-35 Tahun' }, + { value: '36-45', label: '36-45 Tahun' }, + { value: '46-55', label: '46-55 Tahun' }, + { value: '56-65', label: '56-65 Tahun' }, + { value: '65+', label: '65+ Tahun' }, + ]; + + const isFormValid = () => { + return ( + formData.rentangUmur?.trim() !== '' && + formData.jumlah !== null && + formData.jumlah >= 0 && + formData.tahun !== null + ); + }; + + useEffect(() => { + if (!id) return; + + const loadData = async () => { + try { + setIsSubmitting(true); + stateDistribusiUmur.update.id = id; + await stateDistribusiUmur.findUnique.load(id); + + const data = stateDistribusiUmur.findUnique.data; + if (data) { + setFormData({ + rentangUmur: data.rentangUmur ?? '', + jumlah: Number(data.jumlah ?? 0), + tahun: Number(data.tahun ?? currentYear), + }); + setOriginalData({ + rentangUmur: data.rentangUmur ?? '', + jumlah: Number(data.jumlah ?? 0), + tahun: Number(data.tahun ?? currentYear), + }); + } + } catch (error) { + console.error('Error loading data:', error); + toast.error('Gagal memuat data'); + } finally { + setIsSubmitting(false); + } + }; + + loadData(); + }, [id]); + + const handleChange = useCallback( + (field: keyof FormData) => + (value: any) => { + const val = + field === 'jumlah' || field === 'tahun' + ? Number(value || 0) + : value; + + setFormData((prev) => ({ ...prev, [field]: val })); + }, + [] + ); + + const handleResetForm = () => { + setFormData({ + rentangUmur: originalData.rentangUmur, + jumlah: Number(originalData.jumlah), + tahun: Number(originalData.tahun), + }); + toast.info("Form dikembalikan ke data awal"); + }; + + const handleSubmit = async () => { + try { + setIsSubmitting(true); + stateDistribusiUmur.update.id = id; + stateDistribusiUmur.update.form = { ...formData }; + + await stateDistribusiUmur.update.submit(); + + toast.success('Data berhasil diperbarui'); + router.push('/admin/kependudukan/distribusi-umur'); + } catch (error) { + console.error('Error updating data:', error); + toast.error('Gagal memperbarui data'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Edit Distribusi Umur + + + + {/* Form Card */} + + + + + + + + {/* Tombol Simpan */} + + + + + + ); +} diff --git a/src/app/admin/(dashboard)/kependudukan/distribusi-umur/create/page.tsx b/src/app/admin/(dashboard)/kependudukan/distribusi-umur/create/page.tsx new file mode 100644 index 00000000..cfa3abd2 --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/distribusi-umur/create/page.tsx @@ -0,0 +1,174 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + Title, + NumberInput, + Select +} from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import distribusiUmur from '../../../_state/kependudukan/distribusi-umur'; +import { toast } from 'react-toastify'; + +function CreateDistribusiUmur() { + const stateDistribusiUmur = useProxy(distribusiUmur); + const router = useRouter(); + const [isSubmitting, setIsSubmitting] = useState(false); + + const rentangUmurOptions = [ + { value: '0-5', label: '0-5 Tahun' }, + { value: '6-12', label: '6-12 Tahun' }, + { value: '13-17', label: '13-17 Tahun' }, + { value: '18-25', label: '18-25 Tahun' }, + { value: '26-35', label: '26-35 Tahun' }, + { value: '36-45', label: '36-45 Tahun' }, + { value: '46-55', label: '46-55 Tahun' }, + { value: '56-65', label: '56-65 Tahun' }, + { value: '65+', label: '65+ Tahun' }, + ]; + + const currentYear = new Date().getFullYear(); + const yearOptions = Array.from({ length: 10 }, (_, i) => ({ + value: String(currentYear - i), + label: String(currentYear - i), + })); + + const isFormValid = () => { + return ( + stateDistribusiUmur.create.form.rentangUmur?.trim() !== '' && + stateDistribusiUmur.create.form.jumlah !== null && + stateDistribusiUmur.create.form.jumlah >= 0 && + stateDistribusiUmur.create.form.tahun !== null + ); + }; + + const resetForm = () => { + stateDistribusiUmur.create.form = { + rentangUmur: '', + jumlah: 0, + tahun: currentYear, + }; + }; + + const handleSubmit = async () => { + try { + const id = await stateDistribusiUmur.create.create(); + if (id) { + resetForm(); + router.push('/admin/kependudukan/distribusi-umur'); + } + } catch (error) { + console.error('Error creating distribusi umur:', error); + toast.error('Terjadi kesalahan saat menambah distribusi umur'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Tambah Distribusi Umur + + + + {/* Form */} + + + { + stateDistribusiUmur.create.form.tahun = Number(val || currentYear); + }} + required + /> + + + + + {/* Tombol Simpan */} + + + + + + ); +} + +export default CreateDistribusiUmur; diff --git a/src/app/admin/(dashboard)/kependudukan/distribusi-umur/page.tsx b/src/app/admin/(dashboard)/kependudukan/distribusi-umur/page.tsx new file mode 100644 index 00000000..dad6483e --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/distribusi-umur/page.tsx @@ -0,0 +1,284 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client' +import colors from '@/con/colors'; +import { + Box, + Button, + Center, + Flex, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title +} from '@mantine/core'; +import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'; +import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../_com/header'; +import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus'; +import distribusiUmur from '../../_state/kependudukan/distribusi-umur'; + +function DistribusiUmurAdmin() { + const [search, setSearch] = useState(''); + return ( + + } + value={search} + onChange={(e: React.ChangeEvent) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListDistribusiUmur({ search }: { search: string }) { + type DistribusiUmurType = { + id: string; + rentangUmur: string; + jumlah: number; + tahun: number; + }; + + const router = useRouter(); + const stateDistribusiUmur = useProxy(distribusiUmur); + const [modalHapus, setModalHapus] = useState(false); + const [debouncedSearch] = useDebouncedValue(search, 1000); + const [selectedId, setSelectedId] = useState(null); + + + const { + data, + page, + totalPages, + loading, + load, + } = stateDistribusiUmur.findMany; + + const handleDelete = () => { + if (selectedId) { + stateDistribusiUmur.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + } + }; + + useShallowEffect(() => { + load(page, 10, debouncedSearch); + }, [page, debouncedSearch]); + + const filteredData = data || []; + + if (loading || !data) { + return ( + + + + ); + } + + return ( + + + + + List Distribusi Umur + + + + + {/* Desktop Table */} + + + + + Rentang Umur + Jumlah + Tahun + Edit + Hapus + + + + {filteredData.length > 0 ? ( + filteredData.map((item: DistribusiUmurType) => ( + + {item.rentangUmur} + {item.jumlah.toLocaleString('id-ID')} + {item.tahun} + + + + + + + + )) + ) : ( + + +
+ + Tidak ada data distribusi umur yang cocok + +
+
+
+ )} +
+
+
+ + {/* Mobile Card */} + + + {filteredData.length > 0 ? ( + filteredData.map((item: DistribusiUmurType) => ( + + + + + Rentang Umur + + + {item.rentangUmur} + + + + + Jumlah + + + {item.jumlah.toLocaleString('id-ID')} + + + + + Tahun + + + {item.tahun} + + + + + + + + + )) + ) : ( +
+ + Tidak ada data distribusi umur yang cocok + +
+ )} +
+
+
+ + {/* Pagination */} +
+ { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+ + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleDelete} + text="Apakah anda yakin ingin menghapus data distribusi umur ini?" + /> +
+ ); +} + +export default DistribusiUmurAdmin; diff --git a/src/app/admin/(dashboard)/kependudukan/migrasi-penduduk/[id]/page.tsx b/src/app/admin/(dashboard)/kependudukan/migrasi-penduduk/[id]/page.tsx new file mode 100644 index 00000000..2618bfcb --- /dev/null +++ b/src/app/admin/(dashboard)/kependudukan/migrasi-penduduk/[id]/page.tsx @@ -0,0 +1,267 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Loader, + Paper, + Stack, + Title, + TextInput, + Select, + Textarea +} from '@mantine/core'; +import { DatePickerInput } from '@mantine/dates'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useCallback, useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import migrasiPenduduk from '../../../_state/kependudukan/migrasi-penduduk'; + +interface FormData { + jenis: string; + nama: string; + tanggal: string; + asalTujuan: string; + alasan: string; + jenisKelamin: string; +} + +export default function EditMigrasiPenduduk() { + const router = useRouter(); + const { id } = useParams() as { id: string }; + const stateMigrasiPenduduk = useProxy(migrasiPenduduk); + const [isSubmitting, setIsSubmitting] = useState(false); + const [formData, setFormData] = useState({ + jenis: '', + nama: '', + tanggal: '', + asalTujuan: '', + alasan: '', + jenisKelamin: '', + }); + const [originalData, setOriginalData] = useState({ + jenis: '', + nama: '', + tanggal: '', + asalTujuan: '', + alasan: '', + jenisKelamin: '', + }); + + const jenisOptions = [ + { value: 'MASUK', label: 'Masuk' }, + { value: 'KELUAR', label: 'Keluar' }, + ]; + + const jenisKelaminOptions = [ + { value: 'L', label: 'Laki-laki' }, + { value: 'P', label: 'Perempuan' }, + ]; + + const isFormValid = () => { + return ( + formData.jenis?.trim() !== '' && + formData.nama?.trim() !== '' && + formData.tanggal?.trim() !== '' && + formData.asalTujuan?.trim() !== '' + ); + }; + + useEffect(() => { + if (!id) return; + + const loadData = async () => { + try { + setIsSubmitting(true); + stateMigrasiPenduduk.update.id = id; + await stateMigrasiPenduduk.findUnique.load(id); + + const data = stateMigrasiPenduduk.findUnique.data; + if (data) { + setFormData({ + jenis: data.jenis ?? '', + nama: data.nama ?? '', + tanggal: data.tanggal ?? '', + asalTujuan: data.asalTujuan ?? '', + alasan: data.alasan ?? '', + jenisKelamin: data.jenisKelamin ?? '', + }); + setOriginalData({ + jenis: data.jenis ?? '', + nama: data.nama ?? '', + tanggal: data.tanggal ?? '', + asalTujuan: data.asalTujuan ?? '', + alasan: data.alasan ?? '', + jenisKelamin: data.jenisKelamin ?? '', + }); + } + } catch (error) { + console.error('Error loading data:', error); + toast.error('Gagal memuat data'); + } finally { + setIsSubmitting(false); + } + }; + + loadData(); + }, [id]); + + const handleChange = useCallback( + (field: keyof FormData) => + (value: any) => { + const val = value || ''; + setFormData((prev) => ({ ...prev, [field]: val })); + }, + [] + ); + + const handleResetForm = () => { + setFormData({ + jenis: originalData.jenis, + nama: originalData.nama, + tanggal: originalData.tanggal, + asalTujuan: originalData.asalTujuan, + alasan: originalData.alasan, + jenisKelamin: originalData.jenisKelamin, + }); + toast.info("Form dikembalikan ke data awal"); + }; + + const handleSubmit = async () => { + try { + setIsSubmitting(true); + stateMigrasiPenduduk.update.id = id; + stateMigrasiPenduduk.update.form = { ...formData }; + + await stateMigrasiPenduduk.update.submit(); + + toast.success('Data berhasil diperbarui'); + router.push('/admin/kependudukan/migrasi-penduduk'); + } catch (error) { + console.error('Error updating data:', error); + toast.error('Gagal memperbarui data'); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {/* Header */} + + + + Edit Migrasi Penduduk + + + + {/* Form Card */} + + +