diff --git a/.github/workflows/git-sync.yml b/.github/workflows/git-sync.yml index 3d5e68a..c4d6d3d 100644 --- a/.github/workflows/git-sync.yml +++ b/.github/workflows/git-sync.yml @@ -20,7 +20,7 @@ jobs: docker run --rm \ -v ${{ github.workspace }}:${{ github.workspace }} \ -w ${{ github.workspace }} \ - -e PLUGIN_TARGET_URL="https://cnb.cool/kevisual/astro-template.git" \ + -e PLUGIN_TARGET_URL="https://cnb.cool/kevisual/assistant-shop.git" \ -e PLUGIN_AUTH_TYPE="https" \ -e PLUGIN_USERNAME="cnb" \ -e PLUGIN_PASSWORD=${{ secrets.GIT_PASSWORD }} \ diff --git a/astro.config.mjs b/astro.config.mjs index 727f60f..118abb8 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -7,12 +7,13 @@ import tailwindcss from '@tailwindcss/vite'; import basicSsl from '@vitejs/plugin-basic-ssl'; const isDev = process.env.NODE_ENV === 'development'; -const plugins = [tailwindcss()] +const plugins = [tailwindcss()]; const isCNB = process.env.CNB === 'true'; if (isDev && !isCNB) { plugins.push(basicSsl()); } -let target = process.env.VITE_API_URL || 'http://localhost:3000'; +let target = process.env.VITE_API_URL || 'https://localhost:51015'; +const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' }; let proxy = { '/root/center/': { target: `${target}/root/center/`, @@ -20,13 +21,8 @@ let proxy = { '/user/login/': { target: `${target}/user/login/`, }, - '/api': { - target: target, - changeOrigin: true, - ws: true, - rewriteWsOrigin: true, - cookieDomainRewrite: 'localhost', - }, + '/api': apiProxy, + '/client': apiProxy, }; export default defineConfig({ diff --git a/package.json b/package.json index fe524ab..efe4aab 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { - "name": "@kevisual/astro-template", + "name": "@kevisual/assistant-shop", "version": "0.0.1", "description": "", "main": "index.js", - "basename": "root/astro-template", + "basename": "/root/assistant-shop", "scripts": { "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "pub": "envision deploy ./dist -k vite-react -v 0.0.1", + "pub": "envision deploy ./dist -k assistant-shop -v 0.0.1 -u", "git:submodule": "git submodule update --init --recursive", "sn": "pnpm dlx shadcn@latest add " }, @@ -23,6 +23,8 @@ "@kevisual/query": "^0.0.18", "@kevisual/query-login": "^0.0.5", "@kevisual/registry": "^0.0.1", + "@radix-ui/react-alert-dialog": "^1.1.13", + "@radix-ui/react-slot": "^1.2.2", "@tailwindcss/vite": "^4.1.7", "astro": "^5.7.13", "class-variance-authority": "^0.7.1", @@ -42,6 +44,7 @@ }, "devDependencies": { "@kevisual/query-awesome": "^0.0.2", + "@kevisual/router": "^0.0.20", "@kevisual/types": "^0.0.10", "@types/react": "^19.1.4", "@types/react-dom": "^19.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9283b4b..9f84278 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,12 @@ importers: '@kevisual/registry': specifier: ^0.0.1 version: 0.0.1(typescript@5.8.3) + '@radix-ui/react-alert-dialog': + specifier: ^1.1.13 + version: 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': + specifier: ^1.2.2 + version: 1.2.2(@types/react@19.1.4)(react@19.1.0) '@tailwindcss/vite': specifier: ^4.1.7 version: 4.1.7(vite@6.3.5(@types/node@18.19.87)(jiti@2.4.2)(lightningcss@1.30.1)) @@ -69,6 +75,9 @@ importers: '@kevisual/query-awesome': specifier: ^0.0.2 version: 0.0.2 + '@kevisual/router': + specifier: ^0.0.20 + version: 0.0.20 '@kevisual/types': specifier: ^0.0.10 version: 0.0.10 @@ -525,6 +534,9 @@ packages: '@kevisual/registry@0.0.1': resolution: {integrity: sha512-//OHu9m4JDrMjgP8o8dcjZd3D3IAUkRVlkTSviouZEH7r5m7mccA3Hvzw0XJ/lelx6exC6LWsyv6c4uV0Dp+gw==} + '@kevisual/router@0.0.20': + resolution: {integrity: sha512-uSwDYWh+kvAu6i0m0SJVgcLR/CYz7WvIWGz0nSF8Vg6smJuAgI+laHR4ESO8Fbz+Xn8bPHuSwmM//HHLMLx2FA==} + '@kevisual/types@0.0.10': resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==} @@ -534,6 +546,190 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + + '@radix-ui/react-alert-dialog@1.1.13': + resolution: {integrity: sha512-/uPs78OwxGxslYOG5TKeUsv9fZC0vo376cXSADdKirTmsLJU2au6L3n34c3p6W26rFDDDze/hwy4fYeNd0qdGA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.13': + resolution: {integrity: sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.9': + resolution: {integrity: sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.6': + resolution: {integrity: sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-portal@1.1.8': + resolution: {integrity: sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.2': + resolution: {integrity: sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.2': + resolution: {integrity: sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rollup/plugin-commonjs@28.0.3': resolution: {integrity: sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -830,6 +1026,9 @@ packages: '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + '@types/node-forge@1.3.11': + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} @@ -914,6 +1113,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.5: + resolution: {integrity: sha512-N+u63/2br5AG+OSrk0rIgFOYPSEYlkD7Yk5fq3LDole6QDIAbAFpbchE5fhBiUdRV2Fa8pWAaXvy+VK/maBeTA==} + engines: {node: '>=10'} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -972,6 +1175,9 @@ packages: caniuse-lite@1.0.30001715: resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==} + caniuse-lite@1.0.30001718: + resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1120,6 +1326,9 @@ packages: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + deterministic-object-hash@2.0.2: resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} engines: {node: '>=18'} @@ -1293,6 +1502,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -1807,6 +2020,10 @@ packages: encoding: optional: true + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + node-mock-http@1.0.0: resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==} @@ -1871,6 +2088,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1928,6 +2149,36 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.0: + resolution: {integrity: sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-toastify@11.0.5: resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==} peerDependencies: @@ -2038,6 +2289,10 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2289,6 +2544,26 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -2887,6 +3162,11 @@ snapshots: - react-native - typescript + '@kevisual/router@0.0.20': + dependencies: + path-to-regexp: 8.2.0 + selfsigned: 2.4.1 + '@kevisual/types@0.0.10': {} '@mdx-js/mdx@3.1.0(acorn@8.14.1)': @@ -2921,6 +3201,163 @@ snapshots: '@oslojs/encoding@1.1.0': {} + '@radix-ui/primitive@1.1.2': {} + + '@radix-ui/react-alert-dialog@1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.4)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-context@1.1.2(@types/react@19.1.4)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-dialog@1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + aria-hidden: 1.2.5 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.0(@types/react@19.1.4)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.4)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-focus-scope@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-id@1.1.1(@types/react@19.1.4)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-portal@1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + + '@radix-ui/react-slot@1.2.2(@types/react@19.1.4)(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.4)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.4)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.4)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.4)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.4)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + '@rollup/plugin-commonjs@28.0.3(rollup@4.40.1)': dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.40.1) @@ -3184,6 +3621,10 @@ snapshots: '@types/node': 18.19.87 form-data: 4.0.2 + '@types/node-forge@1.3.11': + dependencies: + '@types/node': 18.19.87 + '@types/node@17.0.45': {} '@types/node@18.19.87': @@ -3258,6 +3699,11 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.5: + dependencies: + caniuse-lite: 1.0.30001718 + tslib: 2.8.1 + aria-query@5.3.2: {} array-iterate@2.0.1: {} @@ -3406,6 +3852,8 @@ snapshots: caniuse-lite@1.0.30001715: {} + caniuse-lite@1.0.30001718: {} + ccount@2.0.1: {} chalk@5.4.1: {} @@ -3523,6 +3971,8 @@ snapshots: detect-libc@2.0.4: {} + detect-node-es@1.1.0: {} + deterministic-object-hash@2.0.2: dependencies: base-64: 1.0.0 @@ -3727,6 +4177,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -4534,6 +4986,8 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-forge@1.3.1: {} + node-mock-http@1.0.0: {} node-releases@2.0.19: {} @@ -4610,6 +5064,8 @@ snapshots: path-parse@1.0.7: {} + path-to-regexp@8.2.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -4652,6 +5108,33 @@ snapshots: react-refresh@0.17.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + react-style-singleton: 2.2.3(@types/react@19.1.4)(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.4 + + react-remove-scroll@2.7.0(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.1.4)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.4)(react@19.1.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.1.4)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.1.4)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + + react-style-singleton@2.2.3(@types/react@19.1.4)(react@19.1.0): + dependencies: + get-nonce: 1.0.1 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.4 + react-toastify@11.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: clsx: 2.1.1 @@ -4856,6 +5339,11 @@ snapshots: scheduler@0.26.0: {} + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + semver@6.3.1: {} semver@7.7.1: {} @@ -5097,6 +5585,21 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + use-callback-ref@1.3.3(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.4 + + use-sidecar@1.1.3(@types/react@19.1.4)(react@19.1.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.4 + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7ce5606..f59b198 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,8 @@ packages: - packages/* - apps/* - - submodules/* \ No newline at end of file + - submodules/* +onlyBuiltDependencies: + - '@tailwindcss/oxide' + - esbuild + - sharp diff --git a/src/apps/config/store.ts b/src/apps/config/store.ts new file mode 100644 index 0000000..5a741f2 --- /dev/null +++ b/src/apps/config/store.ts @@ -0,0 +1,51 @@ +import { create } from 'zustand'; +import { clientQuery as client } from '@/modules/query'; +import { toast } from 'react-toastify'; + +type ConfigStore = { + config: any; + setConfig: (config: any) => void; + getConfig: () => Promise; + saveConfig: (config: any) => Promise; + pageApi: string; + setPageApi: (pageApi: string) => void; + pageStoreApi: string; + setPageStoreApi: (pageStoreApi: string) => void; +}; + +export const useConfigStore = create((set) => ({ + config: {}, + setConfig: (config) => set({ config }), + getConfig: async () => { + const res = await client.post({ + path: 'config', + }); + if (res.code === 200) { + console.log(res.data); + set({ config: res.data, pageApi: res.data?.pageApi || '', pageStoreApi: res.data?.pageStoreApi || '' }); + } else { + toast.error(res.message || '获取配置失败'); + } + }, + pageApi: '', + setPageApi: (pageApi) => set({ pageApi }), + pageStoreApi: '', + setPageStoreApi: (pageStoreApi) => set({ pageStoreApi }), + saveConfig: async ({ pageApi, loadURL }) => { + console.log(pageApi, loadURL); + if (!pageApi) { + toast.error('配置不能为空'); + return; + } + const res = await client.post({ + path: 'config', + key: 'set', + data: { pageApi, loadURL }, + }); + if (res.code === 200) { + toast.success('保存配置成功'); + } else { + toast.error(res.message || '保存配置失败'); + } + }, +})); diff --git a/src/apps/shop-list/Confirm.tsx b/src/apps/shop-list/Confirm.tsx new file mode 100644 index 0000000..4c6a413 --- /dev/null +++ b/src/apps/shop-list/Confirm.tsx @@ -0,0 +1,100 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { useEffect, useMemo, useState } from 'react'; + +type useConfirmOptions = { + confrimProps: ConfirmProps; +}; +type Fn = () => void; +export const useConfirm = (opts?: useConfirmOptions) => { + const [open, setOpen] = useState(false); + type ConfirmOptions = { + onOk?: Fn; + onCancel?: Fn; + }; + const confirm = (opts?: ConfirmOptions) => { + setOpen(true); + }; + const module = useMemo(() => { + return ; + }, [open]); + return { + module: module, + open, + setOpen, + confirm, + }; +}; + +type ConfirmProps = { + children?: React.ReactNode; + tip?: React.ReactNode; + title?: string; + description?: string; + onOkText?: string; + onCancelText?: string; + onOk?: Fn; + onCancle?: Fn; + hasTrigger?: boolean; + open?: boolean; + setOpen?: (open: boolean) => void; + footer?: React.ReactNode; +}; +export const Confirm = (props: ConfirmProps) => { + const [isOpen, setIsOpen] = useState(props.open); + const hasTrigger = props.hasTrigger ?? true; + useEffect(() => { + setIsOpen(props.open); + }, [props.open]); + return ( + { + setIsOpen(v); + props?.setOpen?.(v); + }}> + {hasTrigger && ( + <> + {props?.children && {props?.children ?? props?.tip ?? '提示'}} + {!props?.children && {props?.tip ?? '提示'}} + + )} + + + {props?.title ?? '是否确认删除?'} + {props?.description ?? '此操作无法撤销,是否继续。'} + + + {props?.footer &&
{props?.footer}
} + {!props?.footer && ( + <> + { + props?.onCancle?.(); + e.stopPropagation(); + }}> + {props?.onCancelText ?? '取消'} + + { + props?.onOk?.(); + e.stopPropagation(); + }}> + {props?.onOkText ?? '确定'} + {' '} + + )} +
+
+
+ ); +}; diff --git a/src/apps/shop-list/PackageManager.tsx b/src/apps/shop-list/PackageManager.tsx new file mode 100644 index 0000000..fb54cd7 --- /dev/null +++ b/src/apps/shop-list/PackageManager.tsx @@ -0,0 +1,173 @@ +import { useEffect, useState } from 'react'; +import './style.css'; +import { usePackageStore, Package } from './store.ts'; +import { Link2, SquareArrowOutUpRight } from 'lucide-react'; +import { useConfigStore } from '@/apps/config/store'; +import { useConfirm } from './Confirm.tsx'; +import { AlertDialogAction } from '@/components/ui/alert-dialog.tsx'; +const BASE_REGISTRY = 'https://kevisual.cn'; +export const PackageManager = () => { + const { shopPackages, installedPackages, getInstalledPackages, getShopPackages, uninstallPackage, installPackage } = usePackageStore(); + const { pageApi, pageStoreApi } = useConfigStore(); + const [confirmId, setConfirmId] = useState(''); + + const { confirm, module } = useConfirm({ + confrimProps: { + title: '应用类型', + description: '确认应用类型,web 或者 app, web 为前端的访问界面。', + footer: ( + <> + { + handleInstall(confirmId, 'web'); + }}> + Web + + handleInstall(confirmId, 'app')}> + App + + + ), + }, + }); + useEffect(() => { + getInstalledPackages(); + getShopPackages(); + }, []); + + const getPackageStatus = (pkg: Package) => { + const installed = installedPackages.find((p) => p.id === pkg.id || (p.user === pkg.user && p.key === pkg.key)); + + if (!installed) return { status: 'not-installed' }; + if (installed.version !== pkg.version) return { status: 'update-available' }; + console.log('installed', installed); + return { status: 'installed', appType: installed.appType }; + }; + + const handleInstall = (id: string, type?: 'web' | 'app') => { + if (!type) { + setConfirmId(id); + confirm(); + return; + } + + const pkg = shopPackages.find((p) => p.id === id); + if (pkg) { + installPackage({ + id: `${pkg.user}/${pkg.key}`, + force: false, + type: pkg.appType || 'web', + }); + } + }; + + const handleUpdate = (id: string) => { + const pkg = shopPackages.find((p) => p.id === id); + if (pkg) { + installPackage({ + id: `${pkg.user}/${pkg.key}`, + force: false, + type: pkg.appType || 'web', + }); + } + }; + + const handleReinstall = (id: string) => { + const pkg = installedPackages.find((p) => p.id === id); + if (pkg) { + installPackage({ + id: `${pkg.user}/${pkg.key}`, + force: false, + type: pkg.appType || 'web', + }); + } + }; + + const handleUninstall = (id: string) => { + const pkg = installedPackages.find((p) => p.id === id); + if (pkg) { + uninstallPackage({ id: `${pkg.user}/${pkg.key}`, type: pkg.appType }); + } + }; + + const getActionButton = (status: string, pkg: Package) => { + switch (status) { + case 'not-installed': + return ( + + ); + case 'update-available': + return ( + + ); + case 'installed': + return ( + + ); + } + }; + const handleOpenWindow = (pkg: Package) => { + let baseUrl = pageStoreApi; + if (!baseUrl) { + baseUrl = new URL(window.location.href).origin; + } + const path = `/${pkg.user}/${pkg.key}`; + window.open(`${baseUrl}${path}`, '_blank'); + }; + const handleOpenClientWindow = (pkg: Package) => { + const baseUrl = new URL(window.location.href).origin; + const path = `/${pkg.user}/${pkg.key}`; + window.open(`${baseUrl}${path}`, '_blank'); + }; + return ( +
+

Package Manager

+
+ {shopPackages.map((pkg) => { + const pkgStatus = getPackageStatus(pkg); + const status = pkgStatus.status; + const isInstalled = status !== 'not-installed'; + return ( +
+

{pkg.title}

+

{pkg.description}

+
+ Version: {pkg.version} + User: {pkg.user} +
+
+ {getActionButton(status, pkg)} + + {status !== 'not-installed' && ( + + )} +
+
+ handleOpenWindow(pkg)} /> +
+ {isInstalled && pkgStatus?.appType === 'web' && ( +
+ handleOpenClientWindow(pkg)} /> +
+ )} +
+
+
+ ); + })} +
+ {module} +
+ ); +}; + +export default PackageManager; diff --git a/src/apps/shop-list/index.tsx b/src/apps/shop-list/index.tsx new file mode 100644 index 0000000..b902b52 --- /dev/null +++ b/src/apps/shop-list/index.tsx @@ -0,0 +1,10 @@ +import { PackageManager } from './PackageManager.tsx'; +import { ToastContainer } from 'react-toastify'; +export const App = () => { + return ( +
+ + +
+ ); +}; diff --git a/src/apps/shop-list/query.ts b/src/apps/shop-list/query.ts new file mode 100644 index 0000000..c0791ca --- /dev/null +++ b/src/apps/shop-list/query.ts @@ -0,0 +1,8 @@ +import { clientQuery, query } from '@/modules/query'; +import { QueryApp as QueryShop } from '@kevisual/query-awesome/query-shop.js'; + +import { QueryApp } from '@kevisual/query-awesome/query-app.js'; +export const qs = new QueryShop({ query: clientQuery }); +export const qa = new QueryApp({ query: query }); + +console.log('qs', qa.query.url); \ No newline at end of file diff --git a/src/apps/shop-list/store.ts b/src/apps/shop-list/store.ts new file mode 100644 index 0000000..dcede2c --- /dev/null +++ b/src/apps/shop-list/store.ts @@ -0,0 +1,135 @@ +import { create } from 'zustand'; +import { clientQuery as client, query } from '@/modules/query'; +import { toast } from 'react-toastify'; +import { qa, qs } from './query.ts'; +export type Package = { + id: string; + name?: string; + version?: string; + description?: string; + title?: string; + user?: string; + key?: string; + appType?: any; +}; +export type InstallOptions = { + id: string; + type?: 'web' | 'app'; + /** + * 会自动清理之前安装的内容 + */ + force?: boolean; + yes?: boolean; +}; +export type UninstallOptions = { + id: string; + type?: 'web' | 'app'; + yes?: boolean; +}; +type PackageStore = { + installedPackages: Package[]; + shopPackages: Package[]; + setInstalledPackages: (packages: Package[]) => void; + setShopPackages: (packages: Package[]) => void; + getInstalledPackages: () => Promise; + getShopPackages: () => Promise; + uninstallPackage: (data: UninstallOptions) => Promise; + installPackage: (data: InstallOptions) => Promise; +}; +export const usePackageStore = create((set, get) => ({ + installedPackages: [], + shopPackages: [], + setInstalledPackages: (packages) => set({ installedPackages: packages }), + setShopPackages: (packages) => set({ shopPackages: packages }), + getInstalledPackages: async () => { + type ListPackage = { + appInfo: Package; + id?: string; + [key: string]: any; + }; + const res = await qs.appDefine.queryChain('listInstalled').get({}); + if (res.code === 200) { + const listPackages = res.data || []; + const installed = listPackages.map((item) => { + return { appType: item.appType, ...item.appInfo }; + }); + + set({ installedPackages: installed }); + } + return res; + }, + getShopPackages: async () => { + const res = await qa.post({ + path: 'app', + key: 'public-list', + }); + if (res.code === 200) { + set({ shopPackages: res.data }); + } + return res.data; + }, + uninstallPackage: async (data: UninstallOptions) => { + if (typeof data.yes === 'undefined') data.yes = true; + const res = await qs.post({ + path: 'shop', + key: 'uninstall', + data, + }); + if (res.code === 200) { + get().getInstalledPackages(); + toast.success('Package uninstalled successfully'); + } else { + toast.error(res.message || 'Failed to uninstall package'); + } + console.log('uninstallPackage', res); + }, + installPackage: async (data: InstallOptions) => { + const toastId = toast.loading('Installing package...'); + if (typeof data.yes === 'undefined') data.yes = true; + const res = await client.post({ + path: 'shop', + key: 'install', + data, + }); + toast.dismiss(toastId); + if (res.code === 200) { + get().getInstalledPackages(); + toast.success('Package installed successfully'); + } else { + toast.error(res.message || 'Failed to install package'); + } + console.log('installPackage', res); + }, +})); + +const installedPackages: Package[] = [ + { user: 'test', key: 'test-key', version: '1.0.0', id: '1', title: '', description: '' }, + { user: 'demo', key: 'demo-package', version: '1.2.0', id: '2', title: '', description: '' }, +]; + +const mockPackages: Package[] = [ + { + id: '1', + title: 'Demo Package 1', + description: 'A test package for demonstration', + version: '1.0.0', + user: 'test', + key: 'test-key', + }, + { + id: '2', + title: 'Demo Package 2', + description: 'Another test package with updates', + version: '2.0.0', + user: 'demo', + key: 'demo-package', + }, + { + id: '3', + title: 'New Package', + description: "A package that hasn't been installed yet", + version: '1.0.0', + user: 'demo', + key: 'new-package', + }, +]; diff --git a/src/apps/shop-list/style.css b/src/apps/shop-list/style.css new file mode 100644 index 0000000..c908878 --- /dev/null +++ b/src/apps/shop-list/style.css @@ -0,0 +1,120 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + color-scheme: light dark; + background-color: #fff8e1; + color: #213547; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; +} + +h1 { + text-align: center; + color: #ff8f00; +} + +.package-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1rem; + padding: 1rem; +} + +.package-card { + background: white; + border-radius: 8px; + padding: 1.5rem; + box-shadow: 0 2px 4px rgba(255, 143, 0, 0.1); + border: 1px solid #ffe0b2; +} + +.package-card h2 { + margin: 0 0 0.5rem 0; + color: #f57c00; +} + +.package-card .description { + color: #666; + margin-bottom: 1rem; + font-size: 0.9rem; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.5; + max-height: 6em; +} + +.package-info { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + font-size: 0.9rem; + color: #666; +} + +.actions { + display: flex; + gap: 0.5rem; +} + +.button { + padding: 0.5rem 1rem; + border-radius: 4px; + border: none; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s; +} + +.button-install { + background-color: #ffa000; + color: white; +} + +.button-update { + background-color: #ff8f00; + color: white; +} + +.button-reinstall { + background-color: #ffb300; + color: white; +} + +.button-uninstall { + background-color: #ff6f00; + color: white; +} + +.button:hover { + opacity: 0.9; +} + +.button:disabled { + background-color: #ffe0b2; + cursor: not-allowed; +} + +.error-message { + text-align: center; + color: #ff6f00; + padding: 2rem; + background: white; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(255, 143, 0, 0.1); + grid-column: 1 / -1; +} \ No newline at end of file diff --git a/src/components/a/auto-complate.tsx b/src/components/a/auto-complate.tsx new file mode 100644 index 0000000..4df152c --- /dev/null +++ b/src/components/a/auto-complate.tsx @@ -0,0 +1,80 @@ +'use client'; + +import * as React from 'react'; +import { Check, ChevronsUpDown } from 'lucide-react'; + +import { cn } from '@/lib/utils'; +import { Button } from '@/components/a/button'; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; + +type Option = { + value?: string; + label: string; +}; + +type AutoComplateProps = { + options: Option[]; + placeholder?: string; + value?: string; + onChange?: (value: string) => void; + width?: string; +}; +export function AutoComplate(props: AutoComplateProps) { + const [open, setOpen] = React.useState(false); + const [value, _setValue] = React.useState(''); + const setValue = (value: string) => { + props?.onChange?.(value); + _setValue(value); + }; + const showLabel = React.useMemo(() => { + const option = props.options.find((option) => option.value === value); + if (option) { + return option?.label; + } + if (props.value) return props.value; + if (value) return value; + return 'Select ...'; + }, [value, props.value]); + return ( + + + + + + + { + if (e.key === 'Enter') { + setOpen(false); + const value = e.target?.value || ''; + setValue(value.trim()); + } + }} + /> + + No options found. + + {props.options.map((framework) => ( + { + setValue(currentValue === value ? '' : currentValue); + setOpen(false); + }}> + + {framework.label} + + ))} + + + + + + ); +} diff --git a/src/components/a/button.tsx b/src/components/a/button.tsx new file mode 100644 index 0000000..92c6989 --- /dev/null +++ b/src/components/a/button.tsx @@ -0,0 +1,18 @@ +import { Button as UiButton, ButtonProps } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; +export const IconButton: typeof UiButton = (props) => { + return ; +}; + +export const Button: typeof UiButton = (props) => { + return ; +}; + +export const ButtonTextIcon = (props: ButtonProps & { icon: React.ReactNode }) => { + return ( + + {props.icon} + {props.children} + + ); +}; diff --git a/src/components/a/confirm.tsx b/src/components/a/confirm.tsx new file mode 100644 index 0000000..4c6a413 --- /dev/null +++ b/src/components/a/confirm.tsx @@ -0,0 +1,100 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { useEffect, useMemo, useState } from 'react'; + +type useConfirmOptions = { + confrimProps: ConfirmProps; +}; +type Fn = () => void; +export const useConfirm = (opts?: useConfirmOptions) => { + const [open, setOpen] = useState(false); + type ConfirmOptions = { + onOk?: Fn; + onCancel?: Fn; + }; + const confirm = (opts?: ConfirmOptions) => { + setOpen(true); + }; + const module = useMemo(() => { + return ; + }, [open]); + return { + module: module, + open, + setOpen, + confirm, + }; +}; + +type ConfirmProps = { + children?: React.ReactNode; + tip?: React.ReactNode; + title?: string; + description?: string; + onOkText?: string; + onCancelText?: string; + onOk?: Fn; + onCancle?: Fn; + hasTrigger?: boolean; + open?: boolean; + setOpen?: (open: boolean) => void; + footer?: React.ReactNode; +}; +export const Confirm = (props: ConfirmProps) => { + const [isOpen, setIsOpen] = useState(props.open); + const hasTrigger = props.hasTrigger ?? true; + useEffect(() => { + setIsOpen(props.open); + }, [props.open]); + return ( + { + setIsOpen(v); + props?.setOpen?.(v); + }}> + {hasTrigger && ( + <> + {props?.children && {props?.children ?? props?.tip ?? '提示'}} + {!props?.children && {props?.tip ?? '提示'}} + + )} + + + {props?.title ?? '是否确认删除?'} + {props?.description ?? '此操作无法撤销,是否继续。'} + + + {props?.footer &&
{props?.footer}
} + {!props?.footer && ( + <> + { + props?.onCancle?.(); + e.stopPropagation(); + }}> + {props?.onCancelText ?? '取消'} + + { + props?.onOk?.(); + e.stopPropagation(); + }}> + {props?.onOkText ?? '确定'} + {' '} + + )} +
+
+
+ ); +}; diff --git a/src/components/a/select.tsx b/src/components/a/select.tsx new file mode 100644 index 0000000..6b968bd --- /dev/null +++ b/src/components/a/select.tsx @@ -0,0 +1,31 @@ +import { Select as UISelect, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; + +type Option = { + value: string; + label?: string; +}; +type SelectProps = { + options?: Option[]; + value?: string; + placeholder?: string; + onChange?: (value: string) => any; +}; +export const Select = (props: SelectProps) => { + const options = props.options || []; + return ( + + + + + + {options.map((item, index) => { + return ( + + {item.label} + + ); + })} + + + ); +}; diff --git a/src/components/a/tooltip.tsx b/src/components/a/tooltip.tsx new file mode 100644 index 0000000..d07a4d9 --- /dev/null +++ b/src/components/a/tooltip.tsx @@ -0,0 +1,14 @@ +import { Tooltip as UITooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; + +export const Tooltip = (props: { children?: React.ReactNode; title?: string }) => { + return ( + + + {props.children} + +

{props.title}

+
+
+
+ ); +}; diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..935eecf --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,155 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx new file mode 100644 index 0000000..1421354 --- /dev/null +++ b/src/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/src/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return