@robsun/create-keystone-app 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-keystone-app.js +71 -0
- package/package.json +17 -0
- package/template/README.md +33 -0
- package/template/apps/server/go.mod +83 -0
- package/template/apps/server/go.sum +207 -0
- package/template/apps/server/internal/demo/demo.go +187 -0
- package/template/apps/server/main.go +28 -0
- package/template/apps/web/index.html +13 -0
- package/template/apps/web/package.json +28 -0
- package/template/apps/web/src/app.config.ts +17 -0
- package/template/apps/web/src/main.tsx +18 -0
- package/template/apps/web/src/modules/demo/index.ts +7 -0
- package/template/apps/web/src/modules/demo/pages/DemoTasksPage.tsx +185 -0
- package/template/apps/web/src/modules/demo/routes.tsx +41 -0
- package/template/apps/web/src/modules/demo/services/demoTasks.ts +28 -0
- package/template/apps/web/src/modules/demo/types.ts +9 -0
- package/template/apps/web/src/vite-env.d.ts +1 -0
- package/template/apps/web/tsconfig.app.json +23 -0
- package/template/apps/web/tsconfig.json +4 -0
- package/template/apps/web/tsconfig.node.json +21 -0
- package/template/apps/web/vite.config.ts +14 -0
- package/template/config.yaml +13 -0
- package/template/package.json +11 -0
- package/template/pnpm-workspace.yaml +2 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const usage = 'Usage: create-keystone-app <dir>';
|
|
6
|
+
const rawTarget = process.argv[2];
|
|
7
|
+
|
|
8
|
+
if (!rawTarget) {
|
|
9
|
+
console.error(usage);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const targetDir = path.resolve(process.cwd(), rawTarget);
|
|
14
|
+
const targetName = rawTarget === '.'
|
|
15
|
+
? path.basename(process.cwd())
|
|
16
|
+
: path.basename(targetDir);
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(targetDir)) {
|
|
19
|
+
const entries = fs.readdirSync(targetDir);
|
|
20
|
+
if (entries.length > 0) {
|
|
21
|
+
console.error(`Target directory is not empty: ${targetDir}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const templateDir = path.resolve(__dirname, '..', 'template');
|
|
29
|
+
copyDir(templateDir, targetDir, {
|
|
30
|
+
'__APP_NAME__': normalizePackageName(targetName),
|
|
31
|
+
'__RAW_NAME__': targetName,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log(`Created ${targetName}`);
|
|
35
|
+
console.log('Next steps:');
|
|
36
|
+
console.log(` cd ${rawTarget}`);
|
|
37
|
+
console.log(' pnpm install');
|
|
38
|
+
console.log(' pnpm server:dev');
|
|
39
|
+
console.log(' pnpm dev');
|
|
40
|
+
|
|
41
|
+
function normalizePackageName(name) {
|
|
42
|
+
const cleaned = String(name || '')
|
|
43
|
+
.trim()
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
46
|
+
.replace(/-+/g, '-')
|
|
47
|
+
.replace(/^-+|-+$/g, '');
|
|
48
|
+
return cleaned || 'keystone-app';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function copyDir(src, dest, replacements) {
|
|
52
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
53
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const srcPath = path.join(src, entry.name);
|
|
56
|
+
const destPath = path.join(dest, entry.name);
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
copyDir(srcPath, destPath, replacements);
|
|
59
|
+
} else {
|
|
60
|
+
copyFile(srcPath, destPath, replacements);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function copyFile(src, dest, replacements) {
|
|
66
|
+
let content = fs.readFileSync(src, 'utf8');
|
|
67
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
68
|
+
content = content.split(key).join(value);
|
|
69
|
+
}
|
|
70
|
+
fs.writeFileSync(dest, content, 'utf8');
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@robsun/create-keystone-app",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-keystone-app": "bin/create-keystone-app.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"template"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# __RAW_NAME__
|
|
2
|
+
Minimal Keystone platform shell (web + server) with a demo module.
|
|
3
|
+
|
|
4
|
+
## Prereqs
|
|
5
|
+
- Node 18+ and pnpm
|
|
6
|
+
- Go 1.23+
|
|
7
|
+
|
|
8
|
+
## Setup
|
|
9
|
+
1) Install dependencies:
|
|
10
|
+
```bash
|
|
11
|
+
pnpm install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
2) Start the Keystone server:
|
|
15
|
+
```bash
|
|
16
|
+
pnpm server:dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
3) Start the web shell:
|
|
20
|
+
```bash
|
|
21
|
+
pnpm dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Demo module:
|
|
25
|
+
|
|
26
|
+
- Menu: Demo Tasks
|
|
27
|
+
- API: `/api/v1/demo/tasks`
|
|
28
|
+
|
|
29
|
+
Default login:
|
|
30
|
+
|
|
31
|
+
- Tenant code: `default`
|
|
32
|
+
- Identifier: `admin`
|
|
33
|
+
- Password: `Admin123!`
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module __APP_NAME__/apps/server
|
|
2
|
+
|
|
3
|
+
go 1.24.3
|
|
4
|
+
|
|
5
|
+
require (
|
|
6
|
+
github.com/gin-gonic/gin v1.11.0
|
|
7
|
+
github.com/robsuncn/keystone v0.1.1
|
|
8
|
+
gorm.io/gorm v1.31.1
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
require (
|
|
12
|
+
filippo.io/edwards25519 v1.1.0 // indirect
|
|
13
|
+
github.com/bytedance/sonic v1.14.0 // indirect
|
|
14
|
+
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
|
15
|
+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
|
16
|
+
github.com/cloudwego/base64x v0.1.6 // indirect
|
|
17
|
+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
|
18
|
+
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
19
|
+
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
|
20
|
+
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
|
21
|
+
github.com/gin-contrib/sse v1.1.0 // indirect
|
|
22
|
+
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
|
23
|
+
github.com/glebarez/sqlite v1.11.0 // indirect
|
|
24
|
+
github.com/go-playground/locales v0.14.1 // indirect
|
|
25
|
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
|
26
|
+
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
|
27
|
+
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
|
28
|
+
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
|
29
|
+
github.com/goccy/go-json v0.10.2 // indirect
|
|
30
|
+
github.com/goccy/go-yaml v1.18.0 // indirect
|
|
31
|
+
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
|
32
|
+
github.com/google/uuid v1.6.0 // indirect
|
|
33
|
+
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
34
|
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
|
35
|
+
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
|
36
|
+
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
|
37
|
+
github.com/jinzhu/inflection v1.0.0 // indirect
|
|
38
|
+
github.com/jinzhu/now v1.1.5 // indirect
|
|
39
|
+
github.com/json-iterator/go v1.1.12 // indirect
|
|
40
|
+
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
|
41
|
+
github.com/leodido/go-urn v1.4.0 // indirect
|
|
42
|
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
43
|
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
|
44
|
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
|
45
|
+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
|
46
|
+
github.com/quic-go/qpack v0.5.1 // indirect
|
|
47
|
+
github.com/quic-go/quic-go v0.54.0 // indirect
|
|
48
|
+
github.com/redis/go-redis/v9 v9.17.2 // indirect
|
|
49
|
+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
50
|
+
github.com/richardlehane/mscfb v1.0.4 // indirect
|
|
51
|
+
github.com/richardlehane/msoleps v1.0.4 // indirect
|
|
52
|
+
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
|
53
|
+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
|
54
|
+
github.com/spf13/afero v1.15.0 // indirect
|
|
55
|
+
github.com/spf13/cast v1.10.0 // indirect
|
|
56
|
+
github.com/spf13/pflag v1.0.10 // indirect
|
|
57
|
+
github.com/spf13/viper v1.21.0 // indirect
|
|
58
|
+
github.com/subosito/gotenv v1.6.0 // indirect
|
|
59
|
+
github.com/tiendc/go-deepcopy v1.7.1 // indirect
|
|
60
|
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
|
61
|
+
github.com/ugorji/go/codec v1.3.0 // indirect
|
|
62
|
+
github.com/xuri/efp v0.0.1 // indirect
|
|
63
|
+
github.com/xuri/excelize/v2 v2.10.0 // indirect
|
|
64
|
+
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
|
65
|
+
go.uber.org/mock v0.5.0 // indirect
|
|
66
|
+
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
|
67
|
+
golang.org/x/arch v0.20.0 // indirect
|
|
68
|
+
golang.org/x/crypto v0.46.0 // indirect
|
|
69
|
+
golang.org/x/mod v0.30.0 // indirect
|
|
70
|
+
golang.org/x/net v0.47.0 // indirect
|
|
71
|
+
golang.org/x/sync v0.19.0 // indirect
|
|
72
|
+
golang.org/x/sys v0.39.0 // indirect
|
|
73
|
+
golang.org/x/text v0.32.0 // indirect
|
|
74
|
+
golang.org/x/tools v0.39.0 // indirect
|
|
75
|
+
google.golang.org/protobuf v1.36.9 // indirect
|
|
76
|
+
gorm.io/datatypes v1.2.7 // indirect
|
|
77
|
+
gorm.io/driver/mysql v1.5.6 // indirect
|
|
78
|
+
gorm.io/driver/postgres v1.6.0 // indirect
|
|
79
|
+
modernc.org/libc v1.22.5 // indirect
|
|
80
|
+
modernc.org/mathutil v1.5.0 // indirect
|
|
81
|
+
modernc.org/memory v1.5.0 // indirect
|
|
82
|
+
modernc.org/sqlite v1.23.1 // indirect
|
|
83
|
+
)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
|
2
|
+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
|
3
|
+
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
|
4
|
+
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
|
5
|
+
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
|
6
|
+
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
|
7
|
+
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
|
8
|
+
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
|
9
|
+
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
|
10
|
+
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
|
11
|
+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
|
12
|
+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
13
|
+
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
|
14
|
+
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
|
15
|
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
16
|
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
17
|
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
18
|
+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
|
19
|
+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
|
20
|
+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
|
21
|
+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
|
22
|
+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
|
23
|
+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
|
24
|
+
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
|
25
|
+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
|
26
|
+
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
|
27
|
+
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
|
28
|
+
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
|
29
|
+
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
|
30
|
+
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
|
31
|
+
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
|
32
|
+
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
|
33
|
+
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
|
34
|
+
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
|
35
|
+
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
|
36
|
+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
|
37
|
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
38
|
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
|
39
|
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
|
40
|
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
|
41
|
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
|
42
|
+
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
|
43
|
+
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
|
44
|
+
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
|
45
|
+
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
|
46
|
+
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
|
47
|
+
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
|
48
|
+
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
|
49
|
+
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
|
50
|
+
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
|
51
|
+
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
|
52
|
+
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
|
53
|
+
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
|
54
|
+
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
|
55
|
+
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
|
56
|
+
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
|
57
|
+
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
|
58
|
+
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
|
59
|
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
60
|
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
61
|
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
62
|
+
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
|
63
|
+
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
|
64
|
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
65
|
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
66
|
+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
|
67
|
+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
68
|
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
|
69
|
+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
|
70
|
+
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
|
71
|
+
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
|
72
|
+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
|
73
|
+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
|
74
|
+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
|
75
|
+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
|
76
|
+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|
77
|
+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
|
78
|
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
|
79
|
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
|
80
|
+
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
|
81
|
+
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
82
|
+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
83
|
+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
84
|
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
85
|
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
86
|
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
|
87
|
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
|
88
|
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
89
|
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
90
|
+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
|
91
|
+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
|
92
|
+
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
|
93
|
+
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
|
94
|
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
95
|
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
96
|
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
|
97
|
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
98
|
+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
|
99
|
+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
|
100
|
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
101
|
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
102
|
+
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
|
103
|
+
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
|
104
|
+
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
|
105
|
+
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
|
106
|
+
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
|
107
|
+
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
|
108
|
+
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
109
|
+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
|
110
|
+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
111
|
+
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
|
112
|
+
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
|
113
|
+
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
|
114
|
+
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
|
|
115
|
+
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
|
116
|
+
github.com/robsuncn/keystone v0.1.1 h1:0BK2lL9wGjp9/0ZWnwoNnEHqGvAkGYLQbkwlVemDSzQ=
|
|
117
|
+
github.com/robsuncn/keystone v0.1.1/go.mod h1:VPNHWG9pZi00SRC8hqy47EvfxnI795/ZC1vSkLm6x1c=
|
|
118
|
+
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
|
119
|
+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
120
|
+
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
|
121
|
+
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
|
122
|
+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
|
123
|
+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
|
124
|
+
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
|
125
|
+
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
|
126
|
+
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
|
127
|
+
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
|
128
|
+
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
|
129
|
+
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
130
|
+
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
|
131
|
+
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
|
132
|
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
133
|
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
134
|
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
135
|
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
136
|
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
137
|
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
138
|
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
139
|
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
140
|
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
|
141
|
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
|
142
|
+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
|
143
|
+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
|
144
|
+
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
|
|
145
|
+
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
|
|
146
|
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
|
147
|
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
|
148
|
+
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
|
149
|
+
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
|
150
|
+
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
|
|
151
|
+
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
|
152
|
+
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
|
|
153
|
+
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
|
|
154
|
+
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
|
|
155
|
+
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
|
156
|
+
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
|
157
|
+
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
|
158
|
+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
|
159
|
+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
|
160
|
+
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
|
161
|
+
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
|
162
|
+
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
|
163
|
+
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
|
164
|
+
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
|
|
165
|
+
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
|
|
166
|
+
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
|
167
|
+
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
|
168
|
+
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
|
169
|
+
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
|
170
|
+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
|
171
|
+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
172
|
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
173
|
+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
|
174
|
+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
175
|
+
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
|
176
|
+
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
|
177
|
+
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
|
178
|
+
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
|
179
|
+
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
|
180
|
+
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
|
181
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
182
|
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
|
183
|
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
184
|
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
185
|
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
186
|
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
187
|
+
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
|
|
188
|
+
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
|
189
|
+
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
|
190
|
+
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
|
191
|
+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
|
192
|
+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
|
193
|
+
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
|
194
|
+
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
|
195
|
+
gorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc=
|
|
196
|
+
gorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOzehntWw=
|
|
197
|
+
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
|
198
|
+
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
|
199
|
+
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
|
200
|
+
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
|
201
|
+
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
|
202
|
+
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
|
203
|
+
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
|
204
|
+
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
|
205
|
+
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
|
206
|
+
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
|
207
|
+
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
package demo
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strconv"
|
|
5
|
+
"strings"
|
|
6
|
+
"sync"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"github.com/gin-gonic/gin"
|
|
10
|
+
|
|
11
|
+
"github.com/robsuncn/keystone/api/response"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
type TaskStatus string
|
|
15
|
+
|
|
16
|
+
const (
|
|
17
|
+
StatusTodo TaskStatus = "todo"
|
|
18
|
+
StatusInProgress TaskStatus = "in_progress"
|
|
19
|
+
StatusDone TaskStatus = "done"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
type Task struct {
|
|
23
|
+
ID int `json:"id"`
|
|
24
|
+
Title string `json:"title"`
|
|
25
|
+
Status TaskStatus `json:"status"`
|
|
26
|
+
CreatedAt time.Time `json:"created_at"`
|
|
27
|
+
UpdatedAt time.Time `json:"updated_at"`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type taskStore struct {
|
|
31
|
+
mu sync.Mutex
|
|
32
|
+
nextID int
|
|
33
|
+
tasks []Task
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var store = newTaskStore()
|
|
37
|
+
|
|
38
|
+
func newTaskStore() *taskStore {
|
|
39
|
+
now := time.Now().UTC()
|
|
40
|
+
return &taskStore{
|
|
41
|
+
nextID: 4,
|
|
42
|
+
tasks: []Task{
|
|
43
|
+
{ID: 1, Title: "Set up project", Status: StatusTodo, CreatedAt: now.Add(-4 * time.Hour), UpdatedAt: now.Add(-4 * time.Hour)},
|
|
44
|
+
{ID: 2, Title: "Wire demo API", Status: StatusInProgress, CreatedAt: now.Add(-2 * time.Hour), UpdatedAt: now.Add(-2 * time.Hour)},
|
|
45
|
+
{ID: 3, Title: "Review UI states", Status: StatusDone, CreatedAt: now.Add(-1 * time.Hour), UpdatedAt: now.Add(-1 * time.Hour)},
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func RegisterRoutes(router *gin.RouterGroup) {
|
|
51
|
+
group := router.Group("/demo")
|
|
52
|
+
group.GET("/tasks", listTasks)
|
|
53
|
+
group.POST("/tasks", createTask)
|
|
54
|
+
group.PATCH("/tasks/:id", updateTask)
|
|
55
|
+
group.DELETE("/tasks/:id", deleteTask)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type createTaskInput struct {
|
|
59
|
+
Title string `json:"title"`
|
|
60
|
+
Status TaskStatus `json:"status"`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type updateTaskInput struct {
|
|
64
|
+
Title string `json:"title"`
|
|
65
|
+
Status TaskStatus `json:"status"`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func listTasks(c *gin.Context) {
|
|
69
|
+
store.mu.Lock()
|
|
70
|
+
items := make([]Task, len(store.tasks))
|
|
71
|
+
copy(items, store.tasks)
|
|
72
|
+
store.mu.Unlock()
|
|
73
|
+
|
|
74
|
+
response.Success(c, gin.H{"items": items})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func createTask(c *gin.Context) {
|
|
78
|
+
var input createTaskInput
|
|
79
|
+
if err := c.ShouldBindJSON(&input); err != nil {
|
|
80
|
+
response.BadRequest(c, "invalid payload")
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
title := strings.TrimSpace(input.Title)
|
|
85
|
+
if title == "" {
|
|
86
|
+
response.BadRequest(c, "title is required")
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
status, ok := parseStatus(input.Status, true)
|
|
91
|
+
if !ok {
|
|
92
|
+
response.BadRequest(c, "invalid status")
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
now := time.Now().UTC()
|
|
97
|
+
store.mu.Lock()
|
|
98
|
+
task := Task{
|
|
99
|
+
ID: store.nextID,
|
|
100
|
+
Title: title,
|
|
101
|
+
Status: status,
|
|
102
|
+
CreatedAt: now,
|
|
103
|
+
UpdatedAt: now,
|
|
104
|
+
}
|
|
105
|
+
store.nextID++
|
|
106
|
+
store.tasks = append(store.tasks, task)
|
|
107
|
+
store.mu.Unlock()
|
|
108
|
+
|
|
109
|
+
response.Created(c, task)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func updateTask(c *gin.Context) {
|
|
113
|
+
id, err := strconv.Atoi(c.Param("id"))
|
|
114
|
+
if err != nil || id <= 0 {
|
|
115
|
+
response.BadRequest(c, "invalid id")
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var input updateTaskInput
|
|
120
|
+
if err := c.ShouldBindJSON(&input); err != nil {
|
|
121
|
+
response.BadRequest(c, "invalid payload")
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
updateTitle := strings.TrimSpace(input.Title)
|
|
126
|
+
status, ok := parseStatus(input.Status, false)
|
|
127
|
+
if !ok {
|
|
128
|
+
response.BadRequest(c, "invalid status")
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
store.mu.Lock()
|
|
133
|
+
defer store.mu.Unlock()
|
|
134
|
+
for idx, task := range store.tasks {
|
|
135
|
+
if task.ID != id {
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
if updateTitle != "" {
|
|
139
|
+
task.Title = updateTitle
|
|
140
|
+
}
|
|
141
|
+
if status != "" {
|
|
142
|
+
task.Status = status
|
|
143
|
+
}
|
|
144
|
+
task.UpdatedAt = time.Now().UTC()
|
|
145
|
+
store.tasks[idx] = task
|
|
146
|
+
response.SuccessWithMessage(c, "updated", task)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
response.NotFound(c, "task not found")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
func deleteTask(c *gin.Context) {
|
|
154
|
+
id, err := strconv.Atoi(c.Param("id"))
|
|
155
|
+
if err != nil || id <= 0 {
|
|
156
|
+
response.BadRequest(c, "invalid id")
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
store.mu.Lock()
|
|
161
|
+
defer store.mu.Unlock()
|
|
162
|
+
for idx, task := range store.tasks {
|
|
163
|
+
if task.ID != id {
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
store.tasks = append(store.tasks[:idx], store.tasks[idx+1:]...)
|
|
167
|
+
response.SuccessWithMessage(c, "deleted", gin.H{"id": task.ID})
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
response.NotFound(c, "task not found")
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
func parseStatus(status TaskStatus, allowDefault bool) (TaskStatus, bool) {
|
|
175
|
+
if status == "" {
|
|
176
|
+
if allowDefault {
|
|
177
|
+
return StatusTodo, true
|
|
178
|
+
}
|
|
179
|
+
return "", true
|
|
180
|
+
}
|
|
181
|
+
switch status {
|
|
182
|
+
case StatusTodo, StatusInProgress, StatusDone:
|
|
183
|
+
return status, true
|
|
184
|
+
default:
|
|
185
|
+
return "", false
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"log"
|
|
5
|
+
|
|
6
|
+
"github.com/gin-gonic/gin"
|
|
7
|
+
"gorm.io/gorm"
|
|
8
|
+
|
|
9
|
+
"github.com/robsuncn/keystone/bootstrap/config"
|
|
10
|
+
"github.com/robsuncn/keystone/server"
|
|
11
|
+
|
|
12
|
+
"__APP_NAME__/apps/server/internal/demo"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
func registerDemoRoutes(router *gin.Engine, _ *config.Config, _ *gorm.DB) {
|
|
16
|
+
v1 := router.Group("/api/v1")
|
|
17
|
+
demo.RegisterRoutes(v1)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func main() {
|
|
21
|
+
app, err := server.New(server.WithRouterHook(registerDemoRoutes))
|
|
22
|
+
if err != nil {
|
|
23
|
+
log.Fatalf("failed to initialize server: %v", err)
|
|
24
|
+
}
|
|
25
|
+
if err := app.Run(); err != nil {
|
|
26
|
+
log.Fatalf("server stopped: %v", err)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="description" content="Keystone platform shell" />
|
|
7
|
+
<title>Keystone</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@ant-design/icons": "^6.1.0",
|
|
13
|
+
"@robsun/keystone-web-core": "0.1.1",
|
|
14
|
+
"antd": "^6.0.1",
|
|
15
|
+
"dayjs": "^1.11.19",
|
|
16
|
+
"react": "^19.2.0",
|
|
17
|
+
"react-dom": "^19.2.0",
|
|
18
|
+
"react-router-dom": "^7.10.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^24.10.1",
|
|
22
|
+
"@types/react": "^19.2.5",
|
|
23
|
+
"@types/react-dom": "^19.2.3",
|
|
24
|
+
"typescript": "~5.9.3",
|
|
25
|
+
"vite": "^7.2.4"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { KeystoneWebConfig } from '@robsun/keystone-web-core'
|
|
2
|
+
|
|
3
|
+
export const appConfig: Partial<KeystoneWebConfig> = {
|
|
4
|
+
brand: {
|
|
5
|
+
name: 'Keystone',
|
|
6
|
+
shortName: 'KS',
|
|
7
|
+
appName: 'Keystone',
|
|
8
|
+
platformName: 'Keystone Platform',
|
|
9
|
+
},
|
|
10
|
+
modules: {
|
|
11
|
+
enabled: ['keystone', 'demo'],
|
|
12
|
+
},
|
|
13
|
+
approval: {
|
|
14
|
+
businessTypes: [{ value: 'general', label: 'General Flow' }],
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StrictMode } from 'react'
|
|
2
|
+
import { createRoot } from 'react-dom/client'
|
|
3
|
+
import dayjs from 'dayjs'
|
|
4
|
+
import { KeystoneApp, getKeystoneConfig, setKeystoneConfig } from '@robsun/keystone-web-core'
|
|
5
|
+
import { appConfig } from './app.config'
|
|
6
|
+
import '@robsun/keystone-web-core/styles/keystone.css'
|
|
7
|
+
import './modules/demo'
|
|
8
|
+
|
|
9
|
+
setKeystoneConfig(appConfig)
|
|
10
|
+
const dayjsLocale = getKeystoneConfig().ui?.locale?.dayjs ?? 'en'
|
|
11
|
+
dayjs.locale(dayjsLocale)
|
|
12
|
+
|
|
13
|
+
createRoot(document.getElementById('root')!).render(
|
|
14
|
+
<StrictMode>
|
|
15
|
+
<KeystoneApp />
|
|
16
|
+
</StrictMode>
|
|
17
|
+
)
|
|
18
|
+
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import { App, Button, Card, Form, Input, Popconfirm, Select, Space, Table, Tag } from 'antd'
|
|
3
|
+
import type { ColumnsType } from 'antd/es/table'
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
import { createDemoTask, deleteDemoTask, listDemoTasks, updateDemoTask } from '../services/demoTasks'
|
|
6
|
+
import type { DemoTask, DemoTaskStatus } from '../types'
|
|
7
|
+
|
|
8
|
+
type CreateValues = {
|
|
9
|
+
title: string
|
|
10
|
+
status?: DemoTaskStatus
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const statusMeta: Record<DemoTaskStatus, { label: string; color: string }> = {
|
|
14
|
+
todo: { label: 'Todo', color: 'default' },
|
|
15
|
+
in_progress: { label: 'In Progress', color: 'processing' },
|
|
16
|
+
done: { label: 'Done', color: 'success' },
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const statusOptions = [
|
|
20
|
+
{ value: 'todo', label: 'Todo' },
|
|
21
|
+
{ value: 'in_progress', label: 'In Progress' },
|
|
22
|
+
{ value: 'done', label: 'Done' },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
export function DemoTasksPage() {
|
|
26
|
+
const { message } = App.useApp()
|
|
27
|
+
const [items, setItems] = useState<DemoTask[]>([])
|
|
28
|
+
const [loading, setLoading] = useState(false)
|
|
29
|
+
const [submitting, setSubmitting] = useState(false)
|
|
30
|
+
const [form] = Form.useForm<CreateValues>()
|
|
31
|
+
|
|
32
|
+
const fetchTasks = useCallback(async () => {
|
|
33
|
+
setLoading(true)
|
|
34
|
+
try {
|
|
35
|
+
const data = await listDemoTasks()
|
|
36
|
+
setItems(data)
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const detail = err instanceof Error ? err.message : 'Failed to load tasks'
|
|
39
|
+
message.error(detail)
|
|
40
|
+
} finally {
|
|
41
|
+
setLoading(false)
|
|
42
|
+
}
|
|
43
|
+
}, [message])
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
void fetchTasks()
|
|
47
|
+
}, [fetchTasks])
|
|
48
|
+
|
|
49
|
+
const handleCreate = useCallback(
|
|
50
|
+
async (values: CreateValues) => {
|
|
51
|
+
const title = values.title?.trim()
|
|
52
|
+
if (!title) {
|
|
53
|
+
message.error('Title is required')
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
setSubmitting(true)
|
|
57
|
+
try {
|
|
58
|
+
await createDemoTask({ title, status: values.status })
|
|
59
|
+
form.resetFields()
|
|
60
|
+
await fetchTasks()
|
|
61
|
+
message.success('Task created')
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const detail = err instanceof Error ? err.message : 'Failed to create task'
|
|
64
|
+
message.error(detail)
|
|
65
|
+
} finally {
|
|
66
|
+
setSubmitting(false)
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[fetchTasks, form, message]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
const handleStatusChange = useCallback(
|
|
73
|
+
async (id: number, status: DemoTaskStatus) => {
|
|
74
|
+
try {
|
|
75
|
+
await updateDemoTask(id, { status })
|
|
76
|
+
await fetchTasks()
|
|
77
|
+
message.success('Task updated')
|
|
78
|
+
} catch (err) {
|
|
79
|
+
const detail = err instanceof Error ? err.message : 'Failed to update task'
|
|
80
|
+
message.error(detail)
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
[fetchTasks, message]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const handleDelete = useCallback(
|
|
87
|
+
async (id: number) => {
|
|
88
|
+
try {
|
|
89
|
+
await deleteDemoTask(id)
|
|
90
|
+
await fetchTasks()
|
|
91
|
+
message.success('Task deleted')
|
|
92
|
+
} catch (err) {
|
|
93
|
+
const detail = err instanceof Error ? err.message : 'Failed to delete task'
|
|
94
|
+
message.error(detail)
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
[fetchTasks, message]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const columns: ColumnsType<DemoTask> = useMemo(
|
|
101
|
+
() => [
|
|
102
|
+
{ title: 'Title', dataIndex: 'title', key: 'title' },
|
|
103
|
+
{
|
|
104
|
+
title: 'Status',
|
|
105
|
+
dataIndex: 'status',
|
|
106
|
+
key: 'status',
|
|
107
|
+
render: (value: DemoTaskStatus) => {
|
|
108
|
+
const meta = statusMeta[value]
|
|
109
|
+
return <Tag color={meta.color}>{meta.label}</Tag>
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
title: 'Updated',
|
|
114
|
+
dataIndex: 'updated_at',
|
|
115
|
+
key: 'updated_at',
|
|
116
|
+
render: (value: string) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-'),
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
title: 'Actions',
|
|
120
|
+
key: 'actions',
|
|
121
|
+
render: (_, record) => (
|
|
122
|
+
<Space>
|
|
123
|
+
<Select
|
|
124
|
+
size="small"
|
|
125
|
+
value={record.status}
|
|
126
|
+
options={statusOptions}
|
|
127
|
+
style={{ width: 140 }}
|
|
128
|
+
onChange={(value) => handleStatusChange(record.id, value as DemoTaskStatus)}
|
|
129
|
+
/>
|
|
130
|
+
<Popconfirm
|
|
131
|
+
title="Delete this task?"
|
|
132
|
+
onConfirm={() => handleDelete(record.id)}
|
|
133
|
+
okText="Delete"
|
|
134
|
+
>
|
|
135
|
+
<Button size="small" danger>
|
|
136
|
+
Delete
|
|
137
|
+
</Button>
|
|
138
|
+
</Popconfirm>
|
|
139
|
+
</Space>
|
|
140
|
+
),
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
[handleDelete, handleStatusChange]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<Card
|
|
148
|
+
title="Demo Tasks"
|
|
149
|
+
extra={
|
|
150
|
+
<Button onClick={fetchTasks} loading={loading}>
|
|
151
|
+
Refresh
|
|
152
|
+
</Button>
|
|
153
|
+
}
|
|
154
|
+
>
|
|
155
|
+
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
|
156
|
+
<Form
|
|
157
|
+
form={form}
|
|
158
|
+
layout="inline"
|
|
159
|
+
onFinish={handleCreate}
|
|
160
|
+
initialValues={{ status: 'todo' }}
|
|
161
|
+
>
|
|
162
|
+
<Form.Item name="title" rules={[{ required: true, message: 'Title is required' }]}>
|
|
163
|
+
<Input placeholder="Task title" allowClear style={{ width: 240 }} />
|
|
164
|
+
</Form.Item>
|
|
165
|
+
<Form.Item name="status">
|
|
166
|
+
<Select options={statusOptions} style={{ width: 160 }} />
|
|
167
|
+
</Form.Item>
|
|
168
|
+
<Form.Item>
|
|
169
|
+
<Button type="primary" htmlType="submit" loading={submitting}>
|
|
170
|
+
Add Task
|
|
171
|
+
</Button>
|
|
172
|
+
</Form.Item>
|
|
173
|
+
</Form>
|
|
174
|
+
|
|
175
|
+
<Table<DemoTask>
|
|
176
|
+
rowKey="id"
|
|
177
|
+
loading={loading}
|
|
178
|
+
columns={columns}
|
|
179
|
+
dataSource={items}
|
|
180
|
+
pagination={false}
|
|
181
|
+
/>
|
|
182
|
+
</Space>
|
|
183
|
+
</Card>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { lazy, Suspense, type ComponentType, type ReactElement } from 'react'
|
|
2
|
+
import type { RouteObject } from 'react-router-dom'
|
|
3
|
+
import { AppstoreOutlined } from '@ant-design/icons'
|
|
4
|
+
import { Spin } from 'antd'
|
|
5
|
+
|
|
6
|
+
const lazyNamed = <T extends Record<string, ComponentType<any>>, K extends keyof T>(
|
|
7
|
+
factory: () => Promise<T>,
|
|
8
|
+
name: K
|
|
9
|
+
) =>
|
|
10
|
+
lazy(async () => {
|
|
11
|
+
const module = await factory()
|
|
12
|
+
return { default: module[name] }
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const withSuspense = (element: ReactElement) => (
|
|
16
|
+
<Suspense
|
|
17
|
+
fallback={
|
|
18
|
+
<div style={{ padding: 24, display: 'flex', justifyContent: 'center' }}>
|
|
19
|
+
<Spin />
|
|
20
|
+
</div>
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
{element}
|
|
24
|
+
</Suspense>
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const DemoTasksPage = lazyNamed(() => import('./pages/DemoTasksPage'), 'DemoTasksPage')
|
|
28
|
+
|
|
29
|
+
export const demoRoutes: RouteObject[] = [
|
|
30
|
+
{
|
|
31
|
+
path: 'demo',
|
|
32
|
+
element: <DemoTasksPage />,
|
|
33
|
+
handle: {
|
|
34
|
+
menu: { label: 'Demo Tasks', icon: <AppstoreOutlined /> },
|
|
35
|
+
breadcrumb: 'Demo Tasks',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
].map((route) => ({
|
|
39
|
+
...route,
|
|
40
|
+
element: route.element ? withSuspense(route.element) : route.element,
|
|
41
|
+
}))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { api, type ApiResponse } from '@robsun/keystone-web-core'
|
|
2
|
+
import type { DemoTask, DemoTaskStatus } from '../types'
|
|
3
|
+
|
|
4
|
+
type TaskListResponse = {
|
|
5
|
+
items: DemoTask[]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const listDemoTasks = async () => {
|
|
9
|
+
const { data } = await api.get<ApiResponse<TaskListResponse>>('/demo/tasks')
|
|
10
|
+
return data.data.items
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const createDemoTask = async (payload: { title: string; status?: DemoTaskStatus }) => {
|
|
14
|
+
const { data } = await api.post<ApiResponse<DemoTask>>('/demo/tasks', payload)
|
|
15
|
+
return data.data
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const updateDemoTask = async (
|
|
19
|
+
id: number,
|
|
20
|
+
payload: { title?: string; status?: DemoTaskStatus }
|
|
21
|
+
) => {
|
|
22
|
+
const { data } = await api.patch<ApiResponse<DemoTask>>(`/demo/tasks/${id}`, payload)
|
|
23
|
+
return data.data
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const deleteDemoTask = async (id: number) => {
|
|
27
|
+
await api.delete<ApiResponse<{ id: number }>>(`/demo/tasks/${id}`)
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"types": ["vite/client"],
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
"strict": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true,
|
|
20
|
+
"noUncheckedSideEffectImports": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"verbatimModuleSyntax": true,
|
|
12
|
+
"moduleDetection": "force",
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedSideEffectImports": true
|
|
19
|
+
},
|
|
20
|
+
"include": ["vite.config.ts"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createKeystoneViteConfig } from '@robsun/keystone-web-core/vite'
|
|
2
|
+
|
|
3
|
+
export default createKeystoneViteConfig({
|
|
4
|
+
server: {
|
|
5
|
+
port: 3000,
|
|
6
|
+
proxy: {
|
|
7
|
+
'/api': {
|
|
8
|
+
target: 'http://localhost:8080',
|
|
9
|
+
changeOrigin: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|