@simplysm/sd-cli 13.0.71 → 13.0.74
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/README.md +62 -14
- package/dist/commands/init.d.ts +4 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +26 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/publish.js +1 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +0 -20
- package/dist/sd-cli-entry.js.map +1 -1
- package/package.json +4 -4
- package/src/commands/init.ts +40 -21
- package/src/commands/publish.ts +1 -1
- package/src/sd-cli-entry.ts +0 -24
- package/src/utils/replace-deps.ts +361 -361
- package/src/utils/sd-config.ts +44 -44
- package/src/utils/tailwind-config-deps.ts +98 -98
- package/src/utils/template.ts +56 -56
- package/src/utils/tsconfig.ts +127 -127
- package/src/utils/typecheck-serialization.ts +86 -86
- package/templates/init/{.prettierrc.yaml.hbs → .prettierrc.yaml} +1 -1
- package/templates/init/eslint.config.ts +15 -0
- package/templates/init/mise.toml +3 -0
- package/templates/init/package.json.hbs +8 -7
- package/templates/init/packages/client-admin/index.html.hbs +144 -0
- package/templates/init/packages/client-admin/package.json.hbs +26 -0
- package/templates/init/packages/client-admin/public/assets/logo-landscape.png +0 -0
- package/templates/init/packages/client-admin/public/assets/logo.png +0 -0
- package/templates/init/packages/client-admin/src/App.tsx +42 -0
- package/templates/init/packages/client-admin/src/dev/DevDialog.tsx +34 -0
- package/templates/{add-client/__CLIENT__/src/main.css.hbs → init/packages/client-admin/src/main.css} +1 -1
- package/templates/init/packages/client-admin/src/main.tsx.hbs +146 -0
- package/templates/init/packages/client-admin/src/providers/AppServiceProvider.tsx.hbs +103 -0
- package/templates/init/packages/client-admin/src/providers/AppStructureProvider.tsx +84 -0
- package/templates/init/packages/client-admin/src/providers/AuthProvider.tsx.hbs +71 -0
- package/templates/init/packages/client-admin/src/providers/configureSharedData.ts.hbs +67 -0
- package/templates/init/packages/client-admin/src/views/auth/LoginView.tsx +132 -0
- package/templates/init/packages/client-admin/src/views/home/HomeView.tsx +108 -0
- package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeDetail.tsx.hbs +262 -0
- package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeSheet.tsx.hbs +271 -0
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleDetail.tsx.hbs +154 -0
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionDetail.tsx.hbs +123 -0
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionView.tsx +52 -0
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleSheet.tsx.hbs +125 -0
- package/templates/init/packages/client-admin/src/views/home/main/MainView.tsx.hbs +13 -0
- package/templates/init/packages/client-admin/src/views/home/my-info/MyInfoDetail.tsx.hbs +248 -0
- package/templates/init/packages/client-admin/src/views/home/system/system-log/SystemLogSheet.tsx.hbs +169 -0
- package/templates/init/packages/client-admin/src/views/not-found/NotFoundView.tsx +15 -0
- package/templates/init/packages/client-admin/tailwind.config.ts +10 -0
- package/templates/init/packages/db-main/package.json.hbs +13 -0
- package/templates/init/packages/db-main/src/MainDbContext.ts +20 -0
- package/templates/init/packages/db-main/src/dataLogExt.ts +127 -0
- package/templates/init/packages/db-main/src/index.ts +10 -0
- package/templates/init/packages/db-main/src/tables/Employee.ts +24 -0
- package/templates/init/packages/db-main/src/tables/EmployeeConfig.ts +13 -0
- package/templates/init/packages/db-main/src/tables/Role.ts +9 -0
- package/templates/init/packages/db-main/src/tables/RolePermission.ts +13 -0
- package/templates/init/packages/db-main/src/tables/_DataLog.ts +19 -0
- package/templates/init/packages/db-main/src/tables/_Log.ts +16 -0
- package/templates/init/packages/server/package.json.hbs +20 -0
- package/templates/init/packages/server/public-dev/dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
- package/templates/init/packages/server/src/index.ts +4 -0
- package/templates/init/packages/server/src/main.ts.hbs +34 -0
- package/templates/init/packages/server/src/services/AuthService.ts.hbs +171 -0
- package/templates/init/packages/server/src/services/DevService.ts.hbs +94 -0
- package/templates/init/packages/server/src/services/EmployeeService.ts.hbs +122 -0
- package/templates/init/packages/server/src/services/RoleService.ts.hbs +59 -0
- package/templates/init/{pnpm-workspace.yaml.hbs → pnpm-workspace.yaml} +3 -1
- package/templates/init/sd.config.ts.hbs +30 -1
- package/templates/init/tests/e2e/package.json.hbs +16 -0
- package/templates/init/tests/e2e/src/e2e.spec.ts +36 -0
- package/templates/init/tests/e2e/src/employee-crud.ts +204 -0
- package/templates/init/tests/e2e/src/login.ts +61 -0
- package/templates/init/tests/e2e/vitest.setup.ts.hbs +220 -0
- package/templates/init/tsconfig.json.hbs +0 -11
- package/templates/init/{vitest.config.ts.hbs → vitest.config.ts} +16 -12
- package/dist/commands/add-client.d.ts +0 -18
- package/dist/commands/add-client.d.ts.map +0 -1
- package/dist/commands/add-client.js +0 -79
- package/dist/commands/add-client.js.map +0 -6
- package/dist/commands/add-server.d.ts +0 -18
- package/dist/commands/add-server.d.ts.map +0 -1
- package/dist/commands/add-server.js +0 -83
- package/dist/commands/add-server.js.map +0 -6
- package/dist/utils/config-editor.d.ts +0 -17
- package/dist/utils/config-editor.d.ts.map +0 -1
- package/dist/utils/config-editor.js +0 -79
- package/dist/utils/config-editor.js.map +0 -6
- package/src/commands/add-client.ts +0 -126
- package/src/commands/add-server.ts +0 -138
- package/src/utils/config-editor.ts +0 -141
- package/templates/add-client/__CLIENT__/index.html.hbs +0 -13
- package/templates/add-client/__CLIENT__/package.json.hbs +0 -16
- package/templates/add-client/__CLIENT__/src/App.tsx.hbs +0 -65
- package/templates/add-client/__CLIENT__/src/appStructure.ts.hbs +0 -20
- package/templates/add-client/__CLIENT__/src/main.tsx.hbs +0 -24
- package/templates/add-client/__CLIENT__/src/pages/HomePage.tsx.hbs +0 -9
- package/templates/add-client/__CLIENT__/tailwind.config.ts.hbs +0 -15
- package/templates/add-server/__SERVER__/package.json.hbs +0 -10
- package/templates/add-server/__SERVER__/src/main.ts.hbs +0 -14
- package/templates/init/.gitignore.hbs +0 -26
- package/templates/init/.npmrc.hbs +0 -1
- package/templates/init/eslint.config.ts.hbs +0 -5
- package/templates/init/mise.toml.hbs +0 -3
- package/tests/config-editor.spec.ts +0 -160
- /package/templates/init/{.prettierignore.hbs → .prettierignore} +0 -0
- /package/templates/{add-client/__CLIENT__ → init/packages/client-admin}/public/favicon.ico +0 -0
- /package/templates/init/{stylelint.config.ts.hbs → stylelint.config.ts} +0 -0
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
import { fsExistsSync, fsReadSync } from "@simplysm/core-node";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Serialized Diagnostic that can be passed to Worker
|
|
6
|
-
*/
|
|
7
|
-
export interface SerializedDiagnostic {
|
|
8
|
-
category: number;
|
|
9
|
-
code: number;
|
|
10
|
-
messageText: string;
|
|
11
|
-
file?: {
|
|
12
|
-
fileName: string;
|
|
13
|
-
};
|
|
14
|
-
start?: number;
|
|
15
|
-
length?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Convert Diagnostic to serializable form
|
|
20
|
-
* (remove circular references/functions for structured clone communication between Worker threads)
|
|
21
|
-
*/
|
|
22
|
-
export function serializeDiagnostic(diagnostic: ts.Diagnostic): SerializedDiagnostic {
|
|
23
|
-
// If DiagnosticMessageChain, flatten entire chain to preserve all context info
|
|
24
|
-
const messageText = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
category: diagnostic.category,
|
|
28
|
-
code: diagnostic.code,
|
|
29
|
-
messageText,
|
|
30
|
-
file: diagnostic.file
|
|
31
|
-
? {
|
|
32
|
-
fileName: diagnostic.file.fileName,
|
|
33
|
-
}
|
|
34
|
-
: undefined,
|
|
35
|
-
start: diagnostic.start,
|
|
36
|
-
length: diagnostic.length,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Determine TypeScript ScriptKind from filename
|
|
42
|
-
*/
|
|
43
|
-
function getScriptKind(fileName: string): ts.ScriptKind {
|
|
44
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
45
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
46
|
-
if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs"))
|
|
47
|
-
return ts.ScriptKind.JS;
|
|
48
|
-
return ts.ScriptKind.TS;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Restore SerializedDiagnostic to ts.Diagnostic
|
|
53
|
-
* Reads actual file contents so source code context is displayed in formatDiagnosticsWithColorAndContext
|
|
54
|
-
* @param serialized - Serialized diagnostic information
|
|
55
|
-
* @param fileCache - File content cache (prevent duplicate reads of same file)
|
|
56
|
-
* @returns Restored ts.Diagnostic object
|
|
57
|
-
*/
|
|
58
|
-
export function deserializeDiagnostic(
|
|
59
|
-
serialized: SerializedDiagnostic,
|
|
60
|
-
fileCache: Map<string, string>,
|
|
61
|
-
): ts.Diagnostic {
|
|
62
|
-
let file: ts.SourceFile | undefined;
|
|
63
|
-
if (serialized.file != null) {
|
|
64
|
-
const fileName = serialized.file.fileName;
|
|
65
|
-
|
|
66
|
-
// Get cached file content (read and cache if not present)
|
|
67
|
-
// If file was deleted or inaccessible, treat as empty content
|
|
68
|
-
// (source code context won't be displayed but diagnostic message is shown normally)
|
|
69
|
-
if (!fileCache.has(fileName)) {
|
|
70
|
-
fileCache.set(fileName, fsExistsSync(fileName) ? fsReadSync(fileName) : "");
|
|
71
|
-
}
|
|
72
|
-
const content = fileCache.get(fileName)!;
|
|
73
|
-
|
|
74
|
-
const scriptKind = getScriptKind(fileName);
|
|
75
|
-
file = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, false, scriptKind);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
category: serialized.category,
|
|
80
|
-
code: serialized.code,
|
|
81
|
-
messageText: serialized.messageText,
|
|
82
|
-
file,
|
|
83
|
-
start: serialized.start,
|
|
84
|
-
length: serialized.length,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { fsExistsSync, fsReadSync } from "@simplysm/core-node";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Serialized Diagnostic that can be passed to Worker
|
|
6
|
+
*/
|
|
7
|
+
export interface SerializedDiagnostic {
|
|
8
|
+
category: number;
|
|
9
|
+
code: number;
|
|
10
|
+
messageText: string;
|
|
11
|
+
file?: {
|
|
12
|
+
fileName: string;
|
|
13
|
+
};
|
|
14
|
+
start?: number;
|
|
15
|
+
length?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convert Diagnostic to serializable form
|
|
20
|
+
* (remove circular references/functions for structured clone communication between Worker threads)
|
|
21
|
+
*/
|
|
22
|
+
export function serializeDiagnostic(diagnostic: ts.Diagnostic): SerializedDiagnostic {
|
|
23
|
+
// If DiagnosticMessageChain, flatten entire chain to preserve all context info
|
|
24
|
+
const messageText = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
category: diagnostic.category,
|
|
28
|
+
code: diagnostic.code,
|
|
29
|
+
messageText,
|
|
30
|
+
file: diagnostic.file
|
|
31
|
+
? {
|
|
32
|
+
fileName: diagnostic.file.fileName,
|
|
33
|
+
}
|
|
34
|
+
: undefined,
|
|
35
|
+
start: diagnostic.start,
|
|
36
|
+
length: diagnostic.length,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Determine TypeScript ScriptKind from filename
|
|
42
|
+
*/
|
|
43
|
+
function getScriptKind(fileName: string): ts.ScriptKind {
|
|
44
|
+
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
45
|
+
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
46
|
+
if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs"))
|
|
47
|
+
return ts.ScriptKind.JS;
|
|
48
|
+
return ts.ScriptKind.TS;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Restore SerializedDiagnostic to ts.Diagnostic
|
|
53
|
+
* Reads actual file contents so source code context is displayed in formatDiagnosticsWithColorAndContext
|
|
54
|
+
* @param serialized - Serialized diagnostic information
|
|
55
|
+
* @param fileCache - File content cache (prevent duplicate reads of same file)
|
|
56
|
+
* @returns Restored ts.Diagnostic object
|
|
57
|
+
*/
|
|
58
|
+
export function deserializeDiagnostic(
|
|
59
|
+
serialized: SerializedDiagnostic,
|
|
60
|
+
fileCache: Map<string, string>,
|
|
61
|
+
): ts.Diagnostic {
|
|
62
|
+
let file: ts.SourceFile | undefined;
|
|
63
|
+
if (serialized.file != null) {
|
|
64
|
+
const fileName = serialized.file.fileName;
|
|
65
|
+
|
|
66
|
+
// Get cached file content (read and cache if not present)
|
|
67
|
+
// If file was deleted or inaccessible, treat as empty content
|
|
68
|
+
// (source code context won't be displayed but diagnostic message is shown normally)
|
|
69
|
+
if (!fileCache.has(fileName)) {
|
|
70
|
+
fileCache.set(fileName, fsExistsSync(fileName) ? fsReadSync(fileName) : "");
|
|
71
|
+
}
|
|
72
|
+
const content = fileCache.get(fileName)!;
|
|
73
|
+
|
|
74
|
+
const scriptKind = getScriptKind(fileName);
|
|
75
|
+
file = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, false, scriptKind);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
category: serialized.category,
|
|
80
|
+
code: serialized.code,
|
|
81
|
+
messageText: serialized.messageText,
|
|
82
|
+
file,
|
|
83
|
+
start: serialized.start,
|
|
84
|
+
length: serialized.length,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import simplysmRecommended from "@simplysm/lint/eslint-recommended";
|
|
2
|
+
import { globalIgnores } from "eslint/config";
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
globalIgnores([".legacy/**"]),
|
|
6
|
+
...simplysmRecommended,
|
|
7
|
+
{
|
|
8
|
+
files: ["**/*.{ts,tsx}"],
|
|
9
|
+
settings: {
|
|
10
|
+
tailwindcss: {
|
|
11
|
+
config: "packages/client-admin/tailwind.config.ts",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "{{projectName}}",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
+
"description": "{{description}}",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"private": true,
|
|
6
7
|
"scripts": {
|
|
@@ -12,18 +13,18 @@
|
|
|
12
13
|
"typecheck": "sd-cli typecheck",
|
|
13
14
|
"lint": "sd-cli lint",
|
|
14
15
|
"lint:fix": "sd-cli lint --fix",
|
|
16
|
+
"check": "sd-cli check",
|
|
15
17
|
"vitest": "vitest"
|
|
16
18
|
},
|
|
17
19
|
"devDependencies": {
|
|
18
|
-
"@simplysm/
|
|
19
|
-
"@simplysm/sd-
|
|
20
|
-
"@
|
|
21
|
-
"
|
|
22
|
-
"eslint": "^9.39.2",
|
|
20
|
+
"@simplysm/lint": "~13.0.74",
|
|
21
|
+
"@simplysm/sd-cli": "~13.0.74",
|
|
22
|
+
"@types/node": "^20.19.35",
|
|
23
|
+
"eslint": "^9.39.3",
|
|
23
24
|
"prettier": "^3.8.1",
|
|
24
|
-
"stylelint": "^16.
|
|
25
|
+
"stylelint": "^16.26.1",
|
|
25
26
|
"typescript": "^5.9.3",
|
|
26
|
-
"vite-tsconfig-paths": "^6.1.
|
|
27
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
27
28
|
"vitest": "^4.0.18"
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ko">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="icon" href="favicon.ico" />
|
|
7
|
+
<title>{{projectName}}</title>
|
|
8
|
+
<script>
|
|
9
|
+
(function () {
|
|
10
|
+
var theme = localStorage.getItem("client-admin.theme");
|
|
11
|
+
if (theme === "dark") {
|
|
12
|
+
document.documentElement.classList.add("dark");
|
|
13
|
+
} else if (theme === "light") {
|
|
14
|
+
document.documentElement.classList.remove("dark");
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
17
|
+
</script>
|
|
18
|
+
<style>
|
|
19
|
+
* {
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
html,
|
|
24
|
+
body,
|
|
25
|
+
#root {
|
|
26
|
+
padding: 0;
|
|
27
|
+
margin: 0;
|
|
28
|
+
width: 100%;
|
|
29
|
+
height: 100%;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.app-loading {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
height: 100%;
|
|
38
|
+
width: 100%;
|
|
39
|
+
gap: 24px;
|
|
40
|
+
background: #f9fafb;
|
|
41
|
+
padding-bottom: 128px;
|
|
42
|
+
position: relative;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.dark .app-loading {
|
|
47
|
+
background: #18181b;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@media (prefers-color-scheme: dark) {
|
|
51
|
+
.app-loading {
|
|
52
|
+
background: #18181b;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.app-loading-spinner::before {
|
|
56
|
+
border-top-color: #3b82f6;
|
|
57
|
+
border-right-color: #2563eb;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.app-loading-spinner::after {
|
|
61
|
+
border-bottom-color: #60a5fa;
|
|
62
|
+
border-left-color: #3b82f6;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.app-loading img {
|
|
67
|
+
width: auto;
|
|
68
|
+
position: relative;
|
|
69
|
+
z-index: 1;
|
|
70
|
+
margin-bottom: 32px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.app-loading-spinner {
|
|
74
|
+
width: 48px;
|
|
75
|
+
height: 48px;
|
|
76
|
+
position: relative;
|
|
77
|
+
z-index: 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.app-loading-spinner::before {
|
|
81
|
+
content: "";
|
|
82
|
+
position: absolute;
|
|
83
|
+
inset: 0;
|
|
84
|
+
border: 3px solid transparent;
|
|
85
|
+
border-radius: 50%;
|
|
86
|
+
border-top-color: #60a5fa;
|
|
87
|
+
border-right-color: #3b82f6;
|
|
88
|
+
animation: spinClockwise 1.2s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.dark .app-loading-spinner::before {
|
|
92
|
+
border-top-color: #3b82f6;
|
|
93
|
+
border-right-color: #2563eb;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.app-loading-spinner::after {
|
|
97
|
+
content: "";
|
|
98
|
+
position: absolute;
|
|
99
|
+
inset: 8px;
|
|
100
|
+
border: 3px solid transparent;
|
|
101
|
+
border-radius: 50%;
|
|
102
|
+
border-bottom-color: #93c5fd;
|
|
103
|
+
border-left-color: #60a5fa;
|
|
104
|
+
animation: spinCounterClockwise 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.dark .app-loading-spinner::after {
|
|
108
|
+
border-bottom-color: #60a5fa;
|
|
109
|
+
border-left-color: #3b82f6;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@keyframes spinClockwise {
|
|
113
|
+
0% {
|
|
114
|
+
transform: rotate(0deg);
|
|
115
|
+
opacity: 1;
|
|
116
|
+
}
|
|
117
|
+
100% {
|
|
118
|
+
transform: rotate(360deg);
|
|
119
|
+
opacity: 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes spinCounterClockwise {
|
|
124
|
+
0% {
|
|
125
|
+
transform: rotate(0deg);
|
|
126
|
+
opacity: 0.8;
|
|
127
|
+
}
|
|
128
|
+
100% {
|
|
129
|
+
transform: rotate(-360deg);
|
|
130
|
+
opacity: 0.8;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
134
|
+
</head>
|
|
135
|
+
<body>
|
|
136
|
+
<div id="root">
|
|
137
|
+
<div class="app-loading">
|
|
138
|
+
<img src="assets/logo-landscape.png" alt="logo" />
|
|
139
|
+
<div class="app-loading-spinner"></div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
<script type="module" src="src/main.tsx"></script>
|
|
143
|
+
</body>
|
|
144
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/client-admin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{description}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@{{projectName}}/db-main": "workspace:*",
|
|
9
|
+
"@simplysm/core-browser": "~13.0.74",
|
|
10
|
+
"@simplysm/core-common": "~13.0.74",
|
|
11
|
+
"@simplysm/excel": "^13.0.71",
|
|
12
|
+
"@simplysm/orm-common": "~13.0.74",
|
|
13
|
+
"@simplysm/service-client": "~13.0.74",
|
|
14
|
+
"@simplysm/solid": "~13.0.74",
|
|
15
|
+
"@solid-primitives/event-listener": "^2.4.5",
|
|
16
|
+
"@solidjs/router": "^0.15.4",
|
|
17
|
+
"@tabler/icons-solidjs": "^3.37.1",
|
|
18
|
+
"clsx": "^2.1.1",
|
|
19
|
+
"consola": "^3.4.2",
|
|
20
|
+
"solid-js": "^1.9.11",
|
|
21
|
+
"zod": "^4.3.6"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"tailwindcss": "^3.4.19"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { RouteSectionProps } from "@solidjs/router";
|
|
2
|
+
import { createEventListener } from "@solid-primitives/event-listener";
|
|
3
|
+
import { useDialog } from "@simplysm/solid";
|
|
4
|
+
import { env } from "@simplysm/core-common";
|
|
5
|
+
import { DevDialog } from "./dev/DevDialog";
|
|
6
|
+
|
|
7
|
+
export function App(props: RouteSectionProps) {
|
|
8
|
+
const dialog = useDialog();
|
|
9
|
+
|
|
10
|
+
if (env.DEV) {
|
|
11
|
+
let preservedKeys: string[] = [];
|
|
12
|
+
|
|
13
|
+
createEventListener(document, "keydown", async (event) => {
|
|
14
|
+
if (
|
|
15
|
+
event.ctrlKey &&
|
|
16
|
+
event.altKey &&
|
|
17
|
+
event.shiftKey &&
|
|
18
|
+
(event.key === "F11" || event.key === "F12")
|
|
19
|
+
) {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
event.stopPropagation();
|
|
22
|
+
|
|
23
|
+
preservedKeys.push(event.key);
|
|
24
|
+
preservedKeys = preservedKeys.slice(-4);
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
preservedKeys[0] === "F12" &&
|
|
28
|
+
preservedKeys[1] === "F11" &&
|
|
29
|
+
preservedKeys[2] === "F12" &&
|
|
30
|
+
preservedKeys[3] === "F12"
|
|
31
|
+
) {
|
|
32
|
+
preservedKeys = [];
|
|
33
|
+
await dialog.show(() => <DevDialog />, {
|
|
34
|
+
header: "DEV",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return <>{props.children}</>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createSignal } from "solid-js";
|
|
2
|
+
import { BusyContainer, Button, useNotification } from "@simplysm/solid";
|
|
3
|
+
import { useAppService } from "../providers/AppServiceProvider";
|
|
4
|
+
import { useAppStructure } from "../providers/AppStructureProvider";
|
|
5
|
+
|
|
6
|
+
export function DevDialog() {
|
|
7
|
+
const serv = useAppService();
|
|
8
|
+
const noti = useNotification();
|
|
9
|
+
const appStructure = useAppStructure();
|
|
10
|
+
const [busyCount, setBusyCount] = createSignal(0);
|
|
11
|
+
|
|
12
|
+
const handleInitDb = async () => {
|
|
13
|
+
setBusyCount((c) => c + 1);
|
|
14
|
+
try {
|
|
15
|
+
const permCodes = appStructure.allFlatPerms.map((p) => p.code);
|
|
16
|
+
await serv.dev.initDb(permCodes);
|
|
17
|
+
noti.success("DB 초기화 완료");
|
|
18
|
+
} catch (err) {
|
|
19
|
+
noti.error(err, "오류");
|
|
20
|
+
}
|
|
21
|
+
setBusyCount((c) => c - 1);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<BusyContainer busy={busyCount() > 0}>
|
|
26
|
+
<div class="inline-flex flex-row gap-2 p-2">
|
|
27
|
+
<Button onClick={handleInitDb} theme="primary" variant="solid">
|
|
28
|
+
DB 초기화
|
|
29
|
+
</Button>
|
|
30
|
+
<Button onClick={() => location.reload()}>새로고침</Button>
|
|
31
|
+
</div>
|
|
32
|
+
</BusyContainer>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { render } from "solid-js/web";
|
|
2
|
+
import { HashRouter, Navigate, Route } from "@solidjs/router";
|
|
3
|
+
import { For } from "solid-js";
|
|
4
|
+
import {
|
|
5
|
+
DialogProvider,
|
|
6
|
+
PrintProvider,
|
|
7
|
+
SystemProvider,
|
|
8
|
+
useLogger,
|
|
9
|
+
useSyncStorage,
|
|
10
|
+
} from "@simplysm/solid";
|
|
11
|
+
import { DateTime, env, jsonStringify } from "@simplysm/core-common";
|
|
12
|
+
import { expr } from "@{{projectName}}/db-main";
|
|
13
|
+
import { App } from "./App";
|
|
14
|
+
|
|
15
|
+
import { NotFoundView } from "./views/not-found/NotFoundView";
|
|
16
|
+
import { AppStructureProvider, useAppStructure } from "./providers/AppStructureProvider";
|
|
17
|
+
import { AppServiceProvider, useAppService } from "./providers/AppServiceProvider";
|
|
18
|
+
import { AuthProvider, useAuth } from "./providers/AuthProvider";
|
|
19
|
+
import { configureSharedData } from "./providers/configureSharedData";
|
|
20
|
+
import "./main.css";
|
|
21
|
+
import { LoginView } from "./views/auth/LoginView";
|
|
22
|
+
import { HomeView } from "./views/home/HomeView";
|
|
23
|
+
|
|
24
|
+
function AppRoot() {
|
|
25
|
+
const appService = useAppService();
|
|
26
|
+
const auth = useAuth();
|
|
27
|
+
const appStructure = useAppStructure();
|
|
28
|
+
|
|
29
|
+
// SyncStorage
|
|
30
|
+
useSyncStorage()!.configure((origin) => ({
|
|
31
|
+
async getItem(key) {
|
|
32
|
+
const info = auth.authInfo();
|
|
33
|
+
if (!info) return origin.getItem(key);
|
|
34
|
+
|
|
35
|
+
return appService.orm.connectWithoutTransaction(async (db) => {
|
|
36
|
+
const row = await db
|
|
37
|
+
.employeeConfig()
|
|
38
|
+
.where((c) => [expr.eq(c.employeeId, info.employeeId), expr.eq(c.code, key)])
|
|
39
|
+
.first();
|
|
40
|
+
return row?.valueJson ?? null;
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async setItem(key, value) {
|
|
45
|
+
const info = auth.authInfo();
|
|
46
|
+
if (!info) {
|
|
47
|
+
await origin.setItem(key, value);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await appService.orm.connect(async (db) => {
|
|
52
|
+
await db
|
|
53
|
+
.employeeConfig()
|
|
54
|
+
.where((c) => [expr.eq(c.employeeId, info.employeeId), expr.eq(c.code, key)])
|
|
55
|
+
.upsert(() => ({
|
|
56
|
+
employeeId: expr.val("number", info.employeeId),
|
|
57
|
+
code: expr.val("string", key),
|
|
58
|
+
valueJson: expr.val("string", value),
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async removeItem(key) {
|
|
64
|
+
const info = auth.authInfo();
|
|
65
|
+
if (!info) {
|
|
66
|
+
await origin.removeItem(key);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await appService.orm.connect(async (db) => {
|
|
71
|
+
await db
|
|
72
|
+
.employeeConfig()
|
|
73
|
+
.where((c) => [expr.eq(c.employeeId, info.employeeId), expr.eq(c.code, key)])
|
|
74
|
+
.delete();
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
// Logger
|
|
80
|
+
useLogger().configure((origin) => ({
|
|
81
|
+
async write(severity, ...data) {
|
|
82
|
+
if (env.DEV) {
|
|
83
|
+
await origin.write(severity, ...data);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const message = data.map((d) => (typeof d === "string" ? d : jsonStringify(d))).join(" ");
|
|
89
|
+
|
|
90
|
+
await appService.orm.connect(async (db) => {
|
|
91
|
+
await db._log().insert([
|
|
92
|
+
{
|
|
93
|
+
clientName: "client-admin",
|
|
94
|
+
dateTime: new DateTime(),
|
|
95
|
+
severity,
|
|
96
|
+
message,
|
|
97
|
+
employeeId: auth.authInfo()?.employeeId,
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
100
|
+
});
|
|
101
|
+
} catch (err) {
|
|
102
|
+
await origin.write(severity, ...data);
|
|
103
|
+
await origin.write("error", "로그 저장 실패:", err);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
// SharedData
|
|
109
|
+
configureSharedData();
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<HashRouter>
|
|
113
|
+
<Route path="/" component={App}>
|
|
114
|
+
<Route path="/" component={() => <Navigate href="/login" />} />
|
|
115
|
+
<Route path="/login" component={LoginView} />
|
|
116
|
+
<Route path="/home" component={HomeView}>
|
|
117
|
+
<Route path="/" component={() => <Navigate href="/home/main" />} />
|
|
118
|
+
<For each={appStructure.usableRoutes()}>
|
|
119
|
+
{(r) => <Route path={r.path} component={r.component} />}
|
|
120
|
+
</For>
|
|
121
|
+
<Route path="*" component={NotFoundView} />
|
|
122
|
+
</Route>
|
|
123
|
+
<Route path="*" component={NotFoundView} />
|
|
124
|
+
</Route>
|
|
125
|
+
</HashRouter>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
render(
|
|
130
|
+
() => (
|
|
131
|
+
<SystemProvider clientName="client-admin">
|
|
132
|
+
<AppServiceProvider>
|
|
133
|
+
<AuthProvider>
|
|
134
|
+
<AppStructureProvider>
|
|
135
|
+
<PrintProvider>
|
|
136
|
+
<DialogProvider>
|
|
137
|
+
<AppRoot />
|
|
138
|
+
</DialogProvider>
|
|
139
|
+
</PrintProvider>
|
|
140
|
+
</AppStructureProvider>
|
|
141
|
+
</AuthProvider>
|
|
142
|
+
</AppServiceProvider>
|
|
143
|
+
</SystemProvider>
|
|
144
|
+
),
|
|
145
|
+
document.getElementById("root")!,
|
|
146
|
+
);
|