create-cundi-app 1.0.0

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 ADDED
@@ -0,0 +1,70 @@
1
+ # create-cundi-app
2
+
3
+ Create a new Cundi app with React + Refine + Ant Design.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ # Using npm
9
+ npm create cundi-app@latest my-app
10
+
11
+ # Using npx
12
+ npx create-cundi-app my-app
13
+
14
+ # Using yarn
15
+ yarn create cundi-app my-app
16
+
17
+ # Using pnpm
18
+ pnpm create cundi-app my-app
19
+ ```
20
+
21
+ ## What's Included
22
+
23
+ The generated project includes:
24
+
25
+ - **Framework**: React 19 + TypeScript
26
+ - **Build Tool**: Vite
27
+ - **UI Library**: Ant Design
28
+ - **Metasystem**: [Refine](https://refine.dev/)
29
+ - **Core SDK**: `@cundi/refine-xaf`
30
+
31
+ ### Features
32
+
33
+ - 🔐 Authentication (Local + Keycloak SSO)
34
+ - 👤 User Management
35
+ - 🛡️ Role-based Access Control
36
+ - 🌍 Internationalization (i18n)
37
+ - 🌙 Dark Mode Support
38
+ - 📊 Dashboard Template
39
+
40
+ ## After Creating
41
+
42
+ 1. Navigate to your project:
43
+ ```bash
44
+ cd my-app
45
+ ```
46
+
47
+ 2. Install dependencies:
48
+ ```bash
49
+ npm install
50
+ ```
51
+
52
+ 3. Configure environment:
53
+ ```bash
54
+ cp .env.example .env
55
+ # Edit .env with your API URL and Keycloak settings
56
+ ```
57
+
58
+ 4. Start development server:
59
+ ```bash
60
+ npm run dev
61
+ ```
62
+
63
+ ## Documentation
64
+
65
+ - [Refine Documentation](https://refine.dev/docs/)
66
+ - [@cundi/refine-xaf SDK](https://www.npmjs.com/package/@cundi/refine-xaf)
67
+
68
+ ## License
69
+
70
+ MIT
package/index.js ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import prompts from "prompts";
6
+ import kleur from "kleur";
7
+ import { fileURLToPath } from "url";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ /**
13
+ * Main CLI entry point
14
+ */
15
+ async function main() {
16
+ console.log();
17
+ console.log(kleur.cyan().bold("🚀 Create Cundi App"));
18
+ console.log(kleur.gray("────────────────────────────────────"));
19
+ console.log();
20
+
21
+ // Get project name from command line or prompt
22
+ let projectName = process.argv[2];
23
+
24
+ if (!projectName) {
25
+ const response = await prompts({
26
+ type: "text",
27
+ name: "projectName",
28
+ message: "Project name:",
29
+ initial: "my-cundi-app",
30
+ validate: (value) => {
31
+ if (!value) return "Project name is required";
32
+ if (!/^[a-z0-9-_]+$/i.test(value)) {
33
+ return "Project name can only contain letters, numbers, hyphens and underscores";
34
+ }
35
+ return true;
36
+ },
37
+ });
38
+
39
+ if (!response.projectName) {
40
+ console.log(kleur.red("✖ Operation cancelled"));
41
+ process.exit(1);
42
+ }
43
+
44
+ projectName = response.projectName;
45
+ }
46
+
47
+ const targetDir = path.resolve(process.cwd(), projectName);
48
+
49
+ // Check if directory already exists
50
+ if (fs.existsSync(targetDir)) {
51
+ const { overwrite } = await prompts({
52
+ type: "confirm",
53
+ name: "overwrite",
54
+ message: `Directory "${projectName}" already exists. Overwrite?`,
55
+ initial: false,
56
+ });
57
+
58
+ if (!overwrite) {
59
+ console.log(kleur.red("✖ Operation cancelled"));
60
+ process.exit(1);
61
+ }
62
+
63
+ await fs.emptyDir(targetDir);
64
+ }
65
+
66
+ console.log();
67
+ console.log(kleur.blue(`Creating project in ${kleur.bold(targetDir)}...`));
68
+ console.log();
69
+
70
+ // Copy template files
71
+ const templateDir = path.join(__dirname, "template");
72
+
73
+ try {
74
+ await fs.copy(templateDir, targetDir, {
75
+ filter: (src) => {
76
+ // Skip node_modules and dist if they exist
77
+ const relativePath = path.relative(templateDir, src);
78
+ return !relativePath.includes("node_modules") && !relativePath.includes("dist");
79
+ },
80
+ });
81
+
82
+ // Generate package.json with project name
83
+ const pkgTemplatePath = path.join(targetDir, "package.json.template");
84
+ const pkgTargetPath = path.join(targetDir, "package.json");
85
+
86
+ if (fs.existsSync(pkgTemplatePath)) {
87
+ let pkgContent = await fs.readFile(pkgTemplatePath, "utf-8");
88
+ pkgContent = pkgContent.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
89
+ await fs.writeFile(pkgTargetPath, pkgContent);
90
+ await fs.remove(pkgTemplatePath);
91
+ }
92
+
93
+ // Rename .env.example to .env.example (ensure it exists)
94
+ const envExamplePath = path.join(targetDir, ".env.example");
95
+ if (!fs.existsSync(envExamplePath)) {
96
+ await fs.writeFile(
97
+ envExamplePath,
98
+ `# API Configuration
99
+ VITE_API_URL=http://localhost:5000/api
100
+
101
+ # Keycloak Configuration (Optional)
102
+ VITE_KEYCLOAK_URL=http://localhost:8080
103
+ VITE_KEYCLOAK_REALM=your-realm
104
+ VITE_KEYCLOAK_CLIENT_ID=your-client-id
105
+ VITE_REDIRECT_URI=http://localhost:5173/auth/callback
106
+ `
107
+ );
108
+ }
109
+
110
+ // Rename _gitignore to .gitignore (npm ignores .gitignore in packages)
111
+ const gitignoreSrc = path.join(targetDir, "_gitignore");
112
+ const gitignoreDest = path.join(targetDir, ".gitignore");
113
+ if (fs.existsSync(gitignoreSrc)) {
114
+ await fs.rename(gitignoreSrc, gitignoreDest);
115
+ }
116
+
117
+ console.log(kleur.green("✔ Project created successfully!"));
118
+ console.log();
119
+ console.log(kleur.cyan("Next steps:"));
120
+ console.log();
121
+ console.log(` ${kleur.gray("$")} cd ${projectName}`);
122
+ console.log(` ${kleur.gray("$")} npm install`);
123
+ console.log(` ${kleur.gray("$")} cp .env.example .env ${kleur.gray("# Configure your environment")}`);
124
+ console.log(` ${kleur.gray("$")} npm run dev`);
125
+ console.log();
126
+ console.log(kleur.gray("────────────────────────────────────"));
127
+ console.log(kleur.blue("Happy coding! 🎉"));
128
+ console.log();
129
+ } catch (error) {
130
+ console.error(kleur.red("✖ Error creating project:"), error);
131
+ process.exit(1);
132
+ }
133
+ }
134
+
135
+ main().catch((error) => {
136
+ console.error(error);
137
+ process.exit(1);
138
+ });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "create-cundi-app",
3
+ "version": "1.0.0",
4
+ "description": "Create a new Cundi app with React + Refine + Ant Design",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-cundi-app": "./index.js"
8
+ },
9
+ "files": [
10
+ "index.js",
11
+ "template"
12
+ ],
13
+ "keywords": [
14
+ "create",
15
+ "cundi",
16
+ "refine",
17
+ "react",
18
+ "antd",
19
+ "xaf",
20
+ "scaffold",
21
+ "template"
22
+ ],
23
+ "author": "",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/antonylu0826/Cundi"
28
+ },
29
+ "dependencies": {
30
+ "prompts": "^2.4.2",
31
+ "kleur": "^4.1.5",
32
+ "fs-extra": "^11.2.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ }
37
+ }
@@ -0,0 +1,8 @@
1
+ # API Configuration
2
+ VITE_API_URL=http://localhost:5000/api
3
+
4
+ # Keycloak Configuration (Optional)
5
+ VITE_KEYCLOAK_URL=http://localhost:8080
6
+ VITE_KEYCLOAK_REALM=your-realm
7
+ VITE_KEYCLOAK_CLIENT_ID=your-client-id
8
+ VITE_REDIRECT_URI=http://localhost:5173/auth/callback
@@ -0,0 +1,54 @@
1
+ # Cundi App
2
+
3
+ Built with React + Refine + Ant Design.
4
+
5
+ ## Getting Started
6
+
7
+ ### 1. Install Dependencies
8
+
9
+ ```bash
10
+ npm install
11
+ ```
12
+
13
+ ### 2. Configure Environment
14
+
15
+ ```bash
16
+ cp .env.example .env
17
+ ```
18
+
19
+ Edit `.env` with your settings:
20
+
21
+ ```env
22
+ VITE_API_URL=http://localhost:5000/api
23
+
24
+ # Keycloak Configuration (Optional)
25
+ VITE_KEYCLOAK_URL=http://localhost:8080
26
+ VITE_KEYCLOAK_REALM=your-realm
27
+ VITE_KEYCLOAK_CLIENT_ID=your-client-id
28
+ VITE_REDIRECT_URI=http://localhost:5173/auth/callback
29
+ ```
30
+
31
+ ### 3. Start Development Server
32
+
33
+ ```bash
34
+ npm run dev
35
+ ```
36
+
37
+ The application will launch at `http://localhost:5173`.
38
+
39
+ ## Login
40
+
41
+ | Route | Description |
42
+ |-------|-------------|
43
+ | `/login` | Keycloak SSO login (default) |
44
+ | `/login/api` | Local account login |
45
+
46
+ ### Default Local Account
47
+
48
+ - **Username**: Admin
49
+ - **Password**: Admin123
50
+
51
+ ## Documentation
52
+
53
+ - [Refine Documentation](https://refine.dev/docs/)
54
+ - [@cundi/refine-xaf SDK](https://www.npmjs.com/package/@cundi/refine-xaf)
@@ -0,0 +1,27 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Build output
5
+ dist/
6
+
7
+ # Environment files
8
+ .env
9
+ .env.local
10
+ .env.*.local
11
+
12
+ # IDE
13
+ .idea/
14
+ .vscode/
15
+ *.swp
16
+ *.swo
17
+
18
+ # OS
19
+ .DS_Store
20
+ Thumbs.db
21
+
22
+ # Logs
23
+ *.log
24
+ npm-debug.log*
25
+
26
+ # TypeScript
27
+ *.tsbuildinfo
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <link rel="icon" href="/favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="theme-color" content="#000000" />
8
+ <meta name="description" content="Cundi App - Built with React + Refine + Ant Design" />
9
+ <link rel="manifest" href="/manifest.json" />
10
+ <title>Cundi App</title>
11
+ </head>
12
+ <body>
13
+ <noscript>You need to enable JavaScript to run this app.</noscript>
14
+ <div id="root"></div>
15
+ <script type="module" src="/src/index.tsx"></script>
16
+ </body>
17
+ </html>
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc && refine build",
8
+ "dev": "refine dev",
9
+ "refine": "refine",
10
+ "start": "refine start"
11
+ },
12
+ "browserslist": {
13
+ "production": [
14
+ ">0.2%",
15
+ "not dead",
16
+ "not op_mini all"
17
+ ],
18
+ "development": [
19
+ "last 1 chrome version",
20
+ "last 1 firefox version",
21
+ "last 1 safari version"
22
+ ]
23
+ },
24
+ "dependencies": {
25
+ "@ant-design/icons": "^5.5.1",
26
+ "@ant-design/v5-patch-for-react-19": "^1.0.3",
27
+ "@cundi/refine-xaf": "^1.0.3",
28
+ "@refinedev/antd": "^6.0.3",
29
+ "@refinedev/cli": "^2.16.50",
30
+ "@refinedev/core": "^5.0.6",
31
+ "@refinedev/react-router": "^2.0.3",
32
+ "@refinedev/simple-rest": "^6.0.1",
33
+ "antd": "^5.23.0",
34
+ "i18next": "^25.7.3",
35
+ "i18next-browser-languagedetector": "^8.2.0",
36
+ "react": "^19.1.0",
37
+ "react-dom": "^19.1.0",
38
+ "react-i18next": "^16.5.0",
39
+ "react-router": "^7.0.2"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^20",
43
+ "@types/react": "^19.1.0",
44
+ "@types/react-dom": "^19.1.0",
45
+ "@vitejs/plugin-react": "^4.2.1",
46
+ "typescript": "^5.8.3",
47
+ "vite": "^5.4.15"
48
+ },
49
+ "engines": {
50
+ "node": ">=20"
51
+ }
52
+ }
Binary file
@@ -0,0 +1,15 @@
1
+ {
2
+ "short_name": "Cundi App",
3
+ "name": "Cundi App",
4
+ "icons": [
5
+ {
6
+ "src": "favicon.ico",
7
+ "sizes": "64x64 32x32 24x24 16x16",
8
+ "type": "image/x-icon"
9
+ }
10
+ ],
11
+ "start_url": ".",
12
+ "display": "standalone",
13
+ "theme_color": "#1677ff",
14
+ "background_color": "#ffffff"
15
+ }
@@ -0,0 +1,192 @@
1
+ import {
2
+ Refine,
3
+ Authenticated,
4
+ } from "@refinedev/core";
5
+ import {
6
+ useNotificationProvider,
7
+ ThemedLayout,
8
+ ErrorComponent,
9
+ RefineThemes,
10
+ } from "@refinedev/antd";
11
+ import {
12
+ DashboardOutlined,
13
+ UserOutlined,
14
+ SettingOutlined,
15
+ TeamOutlined,
16
+ } from "@ant-design/icons";
17
+
18
+ import routerProvider, {
19
+ NavigateToResource,
20
+ CatchAllNavigate,
21
+ UnsavedChangesNotifier,
22
+ DocumentTitleHandler,
23
+ } from "@refinedev/react-router";
24
+ import { BrowserRouter, Routes, Route, Outlet } from "react-router";
25
+ import { App as AntdApp, ConfigProvider, theme } from "antd";
26
+ import { useTranslation } from "react-i18next";
27
+ import "./i18n";
28
+
29
+ import "@ant-design/v5-patch-for-react-19";
30
+ import "@refinedev/antd/dist/reset.css";
31
+
32
+ import { DashboardPage } from "./pages/dashboard";
33
+ import {
34
+ authProvider,
35
+ dataProvider,
36
+ Header,
37
+ LoginPage,
38
+ KeycloakLoginPage,
39
+ AuthCallback,
40
+ ApplicationUserList,
41
+ ApplicationUserCreate,
42
+ ApplicationUserEdit,
43
+ RoleList,
44
+ RoleCreate,
45
+ RoleEdit,
46
+ } from "@cundi/refine-xaf";
47
+
48
+ import { ColorModeContextProvider, useColorMode } from "./contexts/color-mode";
49
+ import { accessControlProvider } from "./accessControlProvider";
50
+
51
+ const API_URL = import.meta.env.VITE_API_URL + "/odata";
52
+
53
+ const InnerApp: React.FC = () => {
54
+ const { mode } = useColorMode();
55
+ const { t, i18n } = useTranslation();
56
+
57
+ const i18nProvider = {
58
+ translate: (key: string, params: any) => t(key, params) as string,
59
+ changeLocale: (lang: string) => i18n.changeLanguage(lang),
60
+ getLocale: () => i18n.language,
61
+ };
62
+
63
+ return (
64
+ <BrowserRouter>
65
+ <ConfigProvider
66
+ theme={{
67
+ ...RefineThemes.Blue,
68
+ algorithm: mode === "dark" ? theme.darkAlgorithm : theme.defaultAlgorithm,
69
+ }}
70
+ >
71
+ <AntdApp>
72
+ <Refine
73
+ authProvider={authProvider}
74
+ accessControlProvider={accessControlProvider}
75
+ dataProvider={dataProvider(API_URL)}
76
+ i18nProvider={i18nProvider}
77
+ routerProvider={routerProvider}
78
+ resources={[
79
+ {
80
+ name: "dashboard",
81
+ list: "/",
82
+ meta: {
83
+ label: t("sider.dashboard"),
84
+ icon: <DashboardOutlined />,
85
+ },
86
+ },
87
+ {
88
+ name: "ApplicationUser",
89
+ list: "/ApplicationUsers",
90
+ create: "/ApplicationUsers/create",
91
+ edit: "/ApplicationUsers/edit/:id",
92
+ meta: {
93
+ label: t("sider.users"),
94
+ icon: <UserOutlined />,
95
+ parent: t("sider.settings"),
96
+ },
97
+ },
98
+ {
99
+ name: "PermissionPolicyRole",
100
+ list: "/PermissionPolicyRoles",
101
+ create: "/PermissionPolicyRoles/create",
102
+ edit: "/PermissionPolicyRoles/edit/:id",
103
+ meta: {
104
+ label: t("sider.roles"),
105
+ parent: t("sider.settings"),
106
+ icon: <TeamOutlined />,
107
+ },
108
+ },
109
+ {
110
+ name: "Settings",
111
+ meta: {
112
+ label: t("sider.settings"),
113
+ icon: <SettingOutlined />,
114
+ },
115
+ },
116
+ ]}
117
+ notificationProvider={useNotificationProvider}
118
+ options={{
119
+ syncWithLocation: true,
120
+ warnWhenUnsavedChanges: true,
121
+ }}
122
+ >
123
+ <Routes>
124
+ <Route
125
+ element={
126
+ <Authenticated
127
+ key="authenticated-routes"
128
+ fallback={<CatchAllNavigate to="/login" />}
129
+ >
130
+ <ThemedLayout Header={Header}>
131
+ <Outlet />
132
+ </ThemedLayout>
133
+ </Authenticated>
134
+ }
135
+ >
136
+ <Route index element={<DashboardPage />} />
137
+
138
+ <Route path="/ApplicationUsers">
139
+ <Route index element={<ApplicationUserList />} />
140
+ <Route path="create" element={<ApplicationUserCreate />} />
141
+ <Route path="edit/:id" element={<ApplicationUserEdit />} />
142
+ </Route>
143
+
144
+ <Route path="/PermissionPolicyRoles">
145
+ <Route index element={<RoleList />} />
146
+ <Route path="create" element={<RoleCreate />} />
147
+ <Route path="edit/:id" element={<RoleEdit />} />
148
+ </Route>
149
+ </Route>
150
+
151
+ <Route
152
+ element={
153
+ <Authenticated key="auth-pages" fallback={<Outlet />}>
154
+ <NavigateToResource resource="dashboard" />
155
+ </Authenticated>
156
+ }
157
+ >
158
+ <Route path="/login" element={<KeycloakLoginPage />} />
159
+ <Route path="/login/api" element={<LoginPage />} />
160
+ <Route path="/auth/callback" element={<AuthCallback />} />
161
+ </Route>
162
+
163
+ <Route
164
+ element={
165
+ <Authenticated key="catch-all">
166
+ <ThemedLayout Header={Header}>
167
+ <Outlet />
168
+ </ThemedLayout>
169
+ </Authenticated>
170
+ }
171
+ >
172
+ <Route path="*" element={<ErrorComponent />} />
173
+ </Route>
174
+ </Routes>
175
+ <UnsavedChangesNotifier />
176
+ <DocumentTitleHandler />
177
+ </Refine>
178
+ </AntdApp>
179
+ </ConfigProvider>
180
+ </BrowserRouter>
181
+ );
182
+ };
183
+
184
+ const App: React.FC = () => {
185
+ return (
186
+ <ColorModeContextProvider>
187
+ <InnerApp />
188
+ </ColorModeContextProvider>
189
+ );
190
+ };
191
+
192
+ export default App;
@@ -0,0 +1,41 @@
1
+ import { AccessControlProvider } from "@refinedev/core";
2
+
3
+ /**
4
+ * Protected resources that require admin access
5
+ */
6
+ const ADMIN_ONLY_RESOURCES = ["ApplicationUser", "PermissionPolicyRole", "Settings"];
7
+
8
+ /**
9
+ * Access control provider with role-based permissions
10
+ * Currently supports Admin/non-Admin model with action-level control
11
+ */
12
+ export const accessControlProvider: AccessControlProvider = {
13
+ can: async ({ resource, action, params }) => {
14
+ const isAdmin = localStorage.getItem("user_is_admin") === "true";
15
+ const roles = JSON.parse(localStorage.getItem("user_roles") || "[]") as string[];
16
+
17
+ // Admin-only resources check
18
+ if (resource && ADMIN_ONLY_RESOURCES.includes(resource) && !isAdmin) {
19
+ return {
20
+ can: false,
21
+ reason: "Only Administrators can access this resource",
22
+ };
23
+ }
24
+
25
+ // Delete action requires admin privileges for all resources
26
+ if (action === "delete" && !isAdmin) {
27
+ return {
28
+ can: false,
29
+ reason: "Only Administrators can delete records",
30
+ };
31
+ }
32
+
33
+ return { can: true };
34
+ },
35
+ options: {
36
+ buttons: {
37
+ enableAccessControl: true,
38
+ hideIfUnauthorized: true,
39
+ },
40
+ },
41
+ };
@@ -0,0 +1,2 @@
1
+ import { ColorModeContextProvider, useColorMode } from "@cundi/refine-xaf";
2
+ export { ColorModeContextProvider, useColorMode };
@@ -0,0 +1,29 @@
1
+ import i18n from "i18next";
2
+ import { initReactI18next } from "react-i18next";
3
+ import LanguageDetector from "i18next-browser-languagedetector";
4
+ import { refineXafTranslations } from "@cundi/refine-xaf";
5
+
6
+ // Use translations from the SDK
7
+ const { en, "zh-TW": zhTW } = refineXafTranslations;
8
+
9
+ i18n
10
+ .use(LanguageDetector)
11
+ .use(initReactI18next)
12
+ .init({
13
+ resources: {
14
+ en: {
15
+ translation: en,
16
+ },
17
+ "zh-TW": {
18
+ translation: zhTW,
19
+ },
20
+ },
21
+ lng: "zh-TW", // Default language
22
+ fallbackLng: "en",
23
+ debug: false,
24
+ interpolation: {
25
+ escapeValue: false,
26
+ },
27
+ });
28
+
29
+ export default i18n;
@@ -0,0 +1,5 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
5
+ }
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { createRoot } from "react-dom/client";
3
+
4
+ import App from "./App";
5
+
6
+ import "./index.css";
7
+
8
+ const container = document.getElementById("root");
9
+ const root = createRoot(container!);
10
+ root.render(
11
+ <React.StrictMode>
12
+ <App />
13
+ </React.StrictMode>
14
+ );
@@ -0,0 +1,73 @@
1
+ import { useGetIdentity } from "@refinedev/core";
2
+ import { Row, Col, Card, Avatar, Typography, Space } from "antd";
3
+ import { useTranslation } from "react-i18next";
4
+
5
+ const { Text, Title } = Typography;
6
+
7
+ export const DashboardPage: React.FC = () => {
8
+ const { t } = useTranslation();
9
+ const { data: identity } = useGetIdentity<{
10
+ id: string;
11
+ name: string;
12
+ avatar: string;
13
+ }>();
14
+
15
+ return (
16
+ <div style={{ padding: "24px" }}>
17
+ <Title level={2}>{t("sider.dashboard")}</Title>
18
+ <Row gutter={[24, 24]}>
19
+ <Col xs={24} sm={12} lg={8}>
20
+ <Card
21
+ title={t("dashboard.welcome")}
22
+ style={{ borderRadius: "12px" }}
23
+ >
24
+ <Space align="center">
25
+ <Avatar size={64} src={identity?.avatar}>
26
+ {identity?.name?.charAt(0)}
27
+ </Avatar>
28
+ <div>
29
+ <Text strong style={{ fontSize: "18px" }}>
30
+ {identity?.name}
31
+ </Text>
32
+ <br />
33
+ <Text type="secondary">{t("dashboard.welcomeMessage")}</Text>
34
+ </div>
35
+ </Space>
36
+ </Card>
37
+ </Col>
38
+
39
+ <Col xs={24} sm={12} lg={8}>
40
+ <Card
41
+ title={t("dashboard.quickStart")}
42
+ style={{ borderRadius: "12px" }}
43
+ >
44
+ <ul style={{ paddingLeft: "20px", margin: 0 }}>
45
+ <li>{t("dashboard.tip1")}</li>
46
+ <li>{t("dashboard.tip2")}</li>
47
+ <li>{t("dashboard.tip3")}</li>
48
+ </ul>
49
+ </Card>
50
+ </Col>
51
+
52
+ <Col xs={24} sm={12} lg={8}>
53
+ <Card
54
+ title={t("dashboard.resources")}
55
+ style={{ borderRadius: "12px" }}
56
+ >
57
+ <Space direction="vertical">
58
+ <a href="https://refine.dev/docs/" target="_blank" rel="noopener noreferrer">
59
+ Refine Documentation
60
+ </a>
61
+ <a href="https://www.npmjs.com/package/@cundi/refine-xaf" target="_blank" rel="noopener noreferrer">
62
+ @cundi/refine-xaf SDK
63
+ </a>
64
+ <a href="https://ant.design/components/overview" target="_blank" rel="noopener noreferrer">
65
+ Ant Design Components
66
+ </a>
67
+ </Space>
68
+ </Card>
69
+ </Col>
70
+ </Row>
71
+ </div>
72
+ );
73
+ };
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "lib": [
6
+ "DOM",
7
+ "DOM.Iterable",
8
+ "ESNext"
9
+ ],
10
+ "allowJs": false,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": false,
13
+ "allowSyntheticDefaultImports": true,
14
+ "strict": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "module": "ESNext",
17
+ "moduleResolution": "Node",
18
+ "resolveJsonModule": true,
19
+ "isolatedModules": true,
20
+ "noEmit": true,
21
+ "jsx": "react-jsx"
22
+ },
23
+ "include": [
24
+ "src"
25
+ ],
26
+ "references": [
27
+ {
28
+ "path": "./tsconfig.node.json"
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node"
6
+ },
7
+ "include": [
8
+ "vite.config.ts"
9
+ ]
10
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ resolve: {
7
+ dedupe: ["react", "react-dom", "antd", "@refinedev/core", "@refinedev/antd", "@tanstack/react-query"],
8
+ },
9
+ });