@gadmin2n/schematics 0.0.110 → 0.0.112

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.
Files changed (19) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +0 -12
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/ITActivityDay.ts +1 -1
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/taihu.ts +8 -2
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/authProvider.ts +26 -27
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DevShell.tsx +19 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/OnboardingModal.tsx +101 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/style.css +129 -0
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/login.ts +0 -24
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useUserPageAccess.ts +17 -28
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/index.tsx +15 -2
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +10 -9
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +12 -11
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasCell.tsx +103 -0
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasComponentLibrary.tsx +449 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +794 -713
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +3 -239
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasContextMenuRegistry.tsx +3 -2
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/hooks/useCanvasContextMenu.tsx +79 -8
  19. package/package.json +1 -1
@@ -30,18 +30,11 @@ export const Event: ModelConfig = {
30
30
  fields: [
31
31
  "*",
32
32
  "!created_at",
33
- "!businessRequester",
34
- "!number",
35
- "!additionalInfo",
36
- "!snSysId",
37
- "!oitOwner",
38
33
  "!createdAt",
39
34
  "!endDate",
40
35
  "!creator",
41
36
  "!updatedAt",
42
37
  "!startDate",
43
- "!eventType",
44
- "!country",
45
38
  ],
46
39
  rowSelection: {
47
40
  actions: [
@@ -86,11 +79,6 @@ export const Event: ModelConfig = {
86
79
  fields: [
87
80
  "*",
88
81
  "!created_at",
89
- "!businessRequester",
90
- "!number",
91
- "!additionalInfo",
92
- "!snSysId",
93
- "!oitOwner",
94
82
  "!createdAt",
95
83
  "!endDate",
96
84
  "!creator",
@@ -27,7 +27,7 @@ export const ITActivityDay: ModelConfig = {
27
27
  filter: [],
28
28
  sorter: ["id"],
29
29
  },
30
- fields: ["*", "!createdAt", "!updatedAt", "!dateKey"],
30
+ fields: ["*", "!createdAt", "!updatedAt"],
31
31
  rowSelection: {
32
32
  actions: [
33
33
  { action: "DELETE", desc: "Delete" },
@@ -31,6 +31,12 @@ export default class TaiHuId {
31
31
  static config = {
32
32
  clientId: process.env.TAIHU_ODC_CLIENT_ID,
33
33
  clientSecret: process.env.TAIHU_ODC_APP_TOKEN || 'gadmin-default-secret',
34
+ // JWT secret 与 portal 共享,独立于太湖 client_secret;
35
+ // 分支只用它 verify portal 签发的 token,不 sign。
36
+ jwtSecret:
37
+ process.env.GADMIN_JWT_SECRET ||
38
+ process.env.TAIHU_ODC_APP_TOKEN ||
39
+ 'gadmin-default-secret',
34
40
  apiBase: process.env.TAIHU_BASE,
35
41
  callback: process.env.TAIHU_CALLBACK,
36
42
  logout: process.env.TAIHU_LOGOUT,
@@ -138,7 +144,7 @@ export default class TaiHuId {
138
144
  return new Promise<string>((resolve, reject) => {
139
145
  jwt.sign(
140
146
  payload,
141
- TaiHuId.config.clientSecret,
147
+ TaiHuId.config.jwtSecret,
142
148
  { expiresIn },
143
149
  (err, token) => {
144
150
  if (err) {
@@ -163,7 +169,7 @@ export default class TaiHuId {
163
169
  return;
164
170
  }
165
171
 
166
- jwt.verify(token, TaiHuId.config.clientSecret, {}, (err, decoded) => {
172
+ jwt.verify(token, TaiHuId.config.jwtSecret, {}, (err, decoded) => {
167
173
  if (err) {
168
174
  reject(err);
169
175
  return;
@@ -1,29 +1,19 @@
1
1
  import { AuthBindings } from '@refinedev/core';
2
2
  import { getApiUrl } from 'config/http';
3
- import { requestHeaders, GAdminTokenName, checkLogin } from 'helpers';
3
+ import { requestHeaders, GAdminTokenName } from 'helpers';
4
+ import { login as taihuLogin, logout as taihuLogout } from './helpers/login';
4
5
  import Cookies from 'js-cookie';
5
6
  import memoize from 'lodash.memoize';
6
7
 
7
- const getUserInfoOnce = memoize(async () => {
8
+ export const getUserInfoOnce = memoize(async () => {
8
9
  const headers = requestHeaders();
9
-
10
- if (import.meta.env.DEV) {
11
- console.log('[getUserInfoOnce] Fetching /userinfo');
12
- console.log('[getUserInfoOnce] Request headers:', headers);
13
- console.log('[getUserInfoOnce] Hostname:', window.location.hostname);
14
- }
15
-
16
10
  const res = await fetch(`${getApiUrl()}/userinfo`, { headers });
17
11
 
18
12
  if (!res.ok) {
19
- console.error(`[getUserInfoOnce] Got ${res.status} response`);
20
- if (res.status === 401) {
21
- console.error('[getUserInfoOnce] 401 - Missing or invalid credentials');
22
- }
13
+ throw new Error(`userinfo ${res.status}`);
23
14
  }
24
15
 
25
- const user = await res.json();
26
- return user;
16
+ return res.json();
27
17
  });
28
18
 
29
19
  export const authProvider: AuthBindings = {
@@ -107,9 +97,12 @@ export const authProvider: AuthBindings = {
107
97
  },
108
98
  logout: async () => {
109
99
  localStorage.removeItem('email');
100
+ // taihuLogout() 通过 window.location.href 跳转后端 /logout,
101
+ // 后端清 cookie 后再 302 到太湖 IDP 退出 SSO,最后回到首页重新触发 OAuth。
102
+ // 因此这里不需要再返回 redirectTo——页面已经在跳走了。
103
+ taihuLogout();
110
104
  return {
111
105
  success: true,
112
- redirectTo: '/',
113
106
  };
114
107
  },
115
108
  onError: async (error) => {
@@ -117,19 +110,25 @@ export const authProvider: AuthBindings = {
117
110
  return { error };
118
111
  },
119
112
  check: async () => {
120
- // checkLogin() 会处理:URL 参数中的 token 写入 cookie、cookie 检查
121
- if (checkLogin()) {
113
+ try {
114
+ await getUserInfoOnce();
115
+ sessionStorage.removeItem('gadmin-relogin-inflight');
122
116
  return { authenticated: true };
117
+ } catch {
118
+ // 清掉本地缓存,让 helpers/login.ts 的 login() 重新走一遍 OAuth。
119
+ // 不要 redirectTo: '/login' —— 前端没有这个路由,会进死路。
120
+ // 防重入:避免 401 拦截器和 check() 同时跳登录。
121
+ if (!sessionStorage.getItem('gadmin-relogin-inflight')) {
122
+ sessionStorage.setItem('gadmin-relogin-inflight', '1');
123
+ getUserInfoOnce.cache.clear?.();
124
+ taihuLogin();
125
+ }
126
+ return {
127
+ authenticated: false,
128
+ logout: true,
129
+ error: { message: 'Check failed', name: 'Not authenticated' },
130
+ };
123
131
  }
124
-
125
- return {
126
- authenticated: false,
127
- redirectTo: '/login',
128
- error: {
129
- message: 'Check failed',
130
- name: 'Not authenticated',
131
- },
132
- };
133
132
  },
134
133
  getPermissions: async () => ['admin'],
135
134
  getIdentity: async () => {
@@ -10,12 +10,15 @@ import AddDataModal from './AddDataModal';
10
10
  import AddPageModal from './AddPageModal';
11
11
  import DeleteDataConfirm from './DeleteDataConfirm';
12
12
  import EditDataModal from './EditDataModal';
13
+ import OnboardingModal from './OnboardingModal';
13
14
  import { resolvePagePaths } from '../components/agentPanel/pagePathUtils';
14
15
  import SkillMenu from './SkillMenu';
15
16
  import { agentAllowedPromise } from '../config/agentAllowed';
16
17
  import './style.css';
17
18
  import UndoConfirm from './UndoConfirm';
18
19
 
20
+ const ONBOARDING_KEY = 'gadmin-onboarded';
21
+
19
22
  declare global {
20
23
  interface Window {
21
24
  ChatSDK: any;
@@ -82,6 +85,9 @@ function DevShellInner() {
82
85
  const [modal, setModal] = useState<ModalType>(null);
83
86
  const [isDataMngtPage, setIsDataMngtPage] = useState(false);
84
87
  const [pageType, setPageType] = useState<string>('');
88
+ const [showOnboarding, setShowOnboarding] = useState(
89
+ () => !localStorage.getItem(ONBOARDING_KEY),
90
+ );
85
91
  const pageContextRef = useRef<PageContext | null>(null);
86
92
  const inspectorSendDepthRef = useRef(0);
87
93
 
@@ -475,6 +481,19 @@ function DevShellInner() {
475
481
  />
476
482
 
477
483
  {/* Modals */}
484
+ {showOnboarding && (
485
+ <OnboardingModal
486
+ onAddData={() => {
487
+ localStorage.setItem(ONBOARDING_KEY, '1');
488
+ setShowOnboarding(false);
489
+ setModal('addData');
490
+ }}
491
+ onDismiss={() => {
492
+ localStorage.setItem(ONBOARDING_KEY, '1');
493
+ setShowOnboarding(false);
494
+ }}
495
+ />
496
+ )}
478
497
  {modal === 'undo' && (
479
498
  <UndoConfirm
480
499
  onConfirm={(prompt) => {
@@ -0,0 +1,101 @@
1
+ import React, { useState } from 'react';
2
+
3
+ interface Props {
4
+ onAddData: () => void;
5
+ onDismiss: () => void;
6
+ }
7
+
8
+ const CAPABILITIES = [
9
+ {
10
+ icon: '🗄️',
11
+ title: '管理数据表',
12
+ desc: '用自然语言描述业务数据,AI 自动生成数据库和完整的数据管理页面',
13
+ },
14
+ {
15
+ icon: '✏️',
16
+ title: '所见即所得定制',
17
+ desc: 'Inspector模式下,点击数据管理页面任意元素,告诉 AI 要改什么,立即生效',
18
+ },
19
+ {
20
+ icon: '📊',
21
+ title: '新建 Dashboard',
22
+ desc: '在 Canvas 画布上拖拽搭建数据可视化看板,完成后一键发布到导航菜单',
23
+ },
24
+ ];
25
+
26
+ export default function OnboardingModal({ onAddData, onDismiss }: Props) {
27
+ const [step, setStep] = useState<1 | 2>(1);
28
+
29
+ return (
30
+ <div id="onboarding-overlay">
31
+ <div id="onboarding-card">
32
+ {step === 1 ? (
33
+ <>
34
+ <div className="onboarding-header">
35
+ <div className="onboarding-welcome-icon">👋</div>
36
+ <h2 className="onboarding-title">欢迎使用 AI+ Ops Admin</h2>
37
+ <p className="onboarding-subtitle">
38
+ 这里是你的 AI 驱动开发环境,你可以做这些事:
39
+ </p>
40
+ </div>
41
+
42
+ <div className="onboarding-caps">
43
+ {CAPABILITIES.map((cap) => (
44
+ <div key={cap.title} className="onboarding-cap-card">
45
+ <div className="onboarding-cap-icon">{cap.icon}</div>
46
+ <div className="onboarding-cap-title">{cap.title}</div>
47
+ <div className="onboarding-cap-desc">{cap.desc}</div>
48
+ </div>
49
+ ))}
50
+ </div>
51
+
52
+ <div className="onboarding-footer">
53
+ <button className="add-data-btn-cancel" onClick={onDismiss}>
54
+ 我已了解,跳过
55
+ </button>
56
+ <button
57
+ className="add-data-btn-confirm"
58
+ onClick={() => setStep(2)}
59
+ >
60
+ 下一步:如何开始 →
61
+ </button>
62
+ </div>
63
+ </>
64
+ ) : (
65
+ <>
66
+ <div className="onboarding-step2-body">
67
+ <div className="onboarding-step2-icon">🚀</div>
68
+ <h3 className="onboarding-step2-title">开始旅程:添加数据表</h3>
69
+ <p className="onboarding-step2-desc">
70
+ 数据表是一切的基础。描述你要管理什么业务数据,AI
71
+ 会自动设计数据库结构,并同步生成完整的管理页面。
72
+ <br />
73
+ <br />
74
+ 有了数据表之后,你就可以用 Inspector 点击页面元素进行定制, 或在
75
+ Canvas 上搭建可视化看板。
76
+ </p>
77
+ <div className="onboarding-step2-hint">
78
+ 💡 之后随时可以通过右下角菜单「添加数据表」重新触发
79
+ </div>
80
+ </div>
81
+
82
+ <div className="onboarding-footer">
83
+ <button
84
+ className="add-data-btn-cancel"
85
+ onClick={() => setStep(1)}
86
+ >
87
+ ← 返回
88
+ </button>
89
+ <button className="add-data-btn-cancel" onClick={onDismiss}>
90
+ 先跳过
91
+ </button>
92
+ <button className="add-data-btn-confirm" onClick={onAddData}>
93
+ 立即添加数据表
94
+ </button>
95
+ </div>
96
+ </>
97
+ )}
98
+ </div>
99
+ </div>
100
+ );
101
+ }
@@ -833,3 +833,132 @@ div.chat-header {
833
833
  opacity: 1;
834
834
  transform: translateY(-50%) scale(1);
835
835
  }
836
+
837
+ /* ── Onboarding Modal ────────────────────────────────────────────────────── */
838
+ #onboarding-overlay {
839
+ position: fixed;
840
+ inset: 0;
841
+ z-index: 999999;
842
+ background: rgba(0, 0, 0, 0.5);
843
+ display: flex;
844
+ align-items: center;
845
+ justify-content: center;
846
+ }
847
+
848
+ #onboarding-card {
849
+ background: #fff;
850
+ border-radius: 16px;
851
+ width: 600px;
852
+ max-width: calc(100vw - 32px);
853
+ box-shadow: 0 12px 48px rgba(0, 0, 0, 0.22);
854
+ display: flex;
855
+ flex-direction: column;
856
+ overflow: hidden;
857
+ }
858
+
859
+ .onboarding-header {
860
+ padding: 32px 32px 20px;
861
+ text-align: center;
862
+ }
863
+
864
+ .onboarding-welcome-icon {
865
+ font-size: 36px;
866
+ line-height: 1;
867
+ margin-bottom: 12px;
868
+ }
869
+
870
+ .onboarding-title {
871
+ font-size: 18px;
872
+ font-weight: 700;
873
+ color: #1a1a1a;
874
+ margin: 0 0 8px;
875
+ }
876
+
877
+ .onboarding-subtitle {
878
+ font-size: 13px;
879
+ color: #8c8c8c;
880
+ margin: 0;
881
+ }
882
+
883
+ .onboarding-caps {
884
+ display: flex;
885
+ gap: 12px;
886
+ padding: 0 32px 24px;
887
+ }
888
+
889
+ .onboarding-cap-card {
890
+ flex: 1;
891
+ display: flex;
892
+ flex-direction: column;
893
+ align-items: center;
894
+ gap: 6px;
895
+ padding: 20px 12px;
896
+ border: 1.5px solid #f0f0f0;
897
+ border-radius: 12px;
898
+ background: #fafafa;
899
+ text-align: center;
900
+ }
901
+
902
+ .onboarding-cap-icon {
903
+ font-size: 28px;
904
+ line-height: 1;
905
+ }
906
+
907
+ .onboarding-cap-title {
908
+ font-size: 13px;
909
+ font-weight: 600;
910
+ color: #1a1a1a;
911
+ }
912
+
913
+ .onboarding-cap-desc {
914
+ font-size: 12px;
915
+ color: #8c8c8c;
916
+ line-height: 1.5;
917
+ }
918
+
919
+ .onboarding-footer {
920
+ display: flex;
921
+ gap: 8px;
922
+ justify-content: flex-end;
923
+ padding: 16px 32px 24px;
924
+ border-top: 1px solid #f0f0f0;
925
+ }
926
+
927
+ /* Step 2 */
928
+ .onboarding-step2-body {
929
+ padding: 32px 40px 24px;
930
+ text-align: center;
931
+ display: flex;
932
+ flex-direction: column;
933
+ align-items: center;
934
+ gap: 12px;
935
+ }
936
+
937
+ .onboarding-step2-icon {
938
+ font-size: 44px;
939
+ line-height: 1;
940
+ }
941
+
942
+ .onboarding-step2-title {
943
+ font-size: 17px;
944
+ font-weight: 700;
945
+ color: #1a1a1a;
946
+ margin: 0;
947
+ }
948
+
949
+ .onboarding-step2-desc {
950
+ font-size: 13px;
951
+ color: #595959;
952
+ line-height: 1.7;
953
+ margin: 0;
954
+ max-width: 440px;
955
+ }
956
+
957
+ .onboarding-step2-hint {
958
+ font-size: 12px;
959
+ color: #8c8c8c;
960
+ background: #f5f5f5;
961
+ border-radius: 8px;
962
+ padding: 10px 16px;
963
+ margin-top: 4px;
964
+ }
@@ -42,30 +42,6 @@ export function isWoaDomain(): boolean {
42
42
  return hostname === 'woa.com' || hostname.endsWith('.woa.com');
43
43
  }
44
44
 
45
- /**
46
- * 通过cookie检测是否登录及参数转cookie(for dev)
47
- */
48
- export function checkLogin(): boolean {
49
- // 优先处理 URL 参数中的 token,确保 token 被写入 cookie(任何域名下都需要)
50
- const params = getUrlParams();
51
- const gadminToken = params[GAdminTokenName];
52
- if (gadminToken) {
53
- setLoginCookie({ gadminToken });
54
- return true;
55
- }
56
-
57
- if (Cookies.get(GAdminTokenName)) {
58
- return true;
59
- }
60
-
61
- // woa.com 域名下 SmartGate 保证进入页面时已有登录态,无需跳转 Taihu
62
- if (isWoaDomain()) {
63
- return true;
64
- }
65
-
66
- return false;
67
- }
68
-
69
45
  export function setLoginCookie({ gadminToken }: Required<LoginCookie>) {
70
46
  const expires = 1; // 本地登录态缓存1天
71
47
  Cookies.set(GAdminTokenName, gadminToken, { expires });
@@ -1,5 +1,6 @@
1
1
  import { useMemo, useState, useEffect, useRef, useCallback } from 'react';
2
2
  import { customRequest } from 'helpers/http';
3
+ import { getUserInfoOnce } from 'authProvider';
3
4
  import type { Role } from 'types/role';
4
5
 
5
6
  const RoleCacheKey = 'oiterproles';
@@ -82,12 +83,12 @@ function writeCache(key: string, value: unknown): void {
82
83
  }
83
84
 
84
85
  export const useUserPageAccess = (): UseUserPageAccessResult => {
86
+ const [isIdentityLoading, setIsIdentityLoading] = useState(true);
87
+
85
88
  // Role names — initialise from cache so first render is non-empty
86
89
  const [roleNames, setRoleNames] = useState<string[]>(
87
90
  () => readCache<string[]>(RoleCacheKey) ?? [],
88
91
  );
89
- // isRoleLoading only stays true while we wait for the live /userinfo response
90
- const [isRoleLoading, setIsRoleLoading] = useState(true);
91
92
 
92
93
  // Data states — initialise from cache for instant first render
93
94
  const [rolesData, setRolesData] = useState<Role[] | null>(() =>
@@ -114,35 +115,23 @@ export const useUserPageAccess = (): UseUserPageAccessResult => {
114
115
  }
115
116
  }, []);
116
117
 
117
- // Step 1: Fetch roles directly from backend to ensure fresh data
118
+ // Step 1: getUserInfoOnce 提取角色(复用缓存,不额外发请求)
118
119
  useEffect(() => {
119
- const fetchRolesFromBackend = async () => {
120
- try {
121
- const res = await customRequest<any>('userinfo', 'GET', {});
122
- const roles = res?.configPerms?.roles;
120
+ getUserInfoOnce()
121
+ .then((identity) => {
122
+ const roles = identity?.configPerms?.roles;
123
123
  if (roles && Array.isArray(roles)) {
124
124
  updateRoles(roles as string[]);
125
- }
126
- } catch (error) {
127
- console.error(
128
- '[useUserPageAccess] Failed to fetch roles from backend:',
129
- error,
130
- );
131
- // Fallback to sessionStorage if backend request fails
132
- try {
125
+ } else {
133
126
  const cached = readCache<string[]>(RoleCacheKey);
134
- if (cached) {
135
- updateRoles(cached);
136
- }
137
- } catch (e) {
138
- console.error('[useUserPageAccess] Failed to parse cached roles:', e);
127
+ if (cached) updateRoles(cached);
139
128
  }
140
- } finally {
141
- setIsRoleLoading(false);
142
- }
143
- };
144
-
145
- fetchRolesFromBackend();
129
+ })
130
+ .catch(() => {
131
+ const cached = readCache<string[]>(RoleCacheKey);
132
+ if (cached) updateRoles(cached);
133
+ })
134
+ .finally(() => setIsIdentityLoading(false));
146
135
  }, [updateRoles]);
147
136
 
148
137
  // Step 2: Fetch all page definitions
@@ -331,7 +320,7 @@ export const useUserPageAccess = (): UseUserPageAccessResult => {
331
320
  const isLoading = useMemo(() => {
332
321
  // If cache was available on mount, never block rendering
333
322
  if (hasCache && pagesData && rolePagesData) return false;
334
- if (isRoleLoading) return true;
323
+ if (isIdentityLoading) return true;
335
324
  if (!pagesData) return true;
336
325
  // 等待 /role/findMany 返回(roleNames 已拿到但 rolesData 还在路上)
337
326
  if (roleNames.length > 0 && !rolesData) return true;
@@ -339,7 +328,7 @@ export const useUserPageAccess = (): UseUserPageAccessResult => {
339
328
  if (roleIds.length > 0 && !rolePagesData) return true;
340
329
  return false;
341
330
  }, [
342
- isRoleLoading,
331
+ isIdentityLoading,
343
332
  pagesData,
344
333
  roleNames,
345
334
  rolesData,
@@ -4,7 +4,13 @@ import { createRoot } from 'react-dom/client';
4
4
  import App from './App';
5
5
  import './i18n';
6
6
 
7
- import { checkLogin, login } from './helpers/login';
7
+ import {
8
+ login,
9
+ GAdminTokenName,
10
+ setLoginCookie,
11
+ getUrlParams,
12
+ } from './helpers/login';
13
+ import Cookies from 'js-cookie';
8
14
 
9
15
  // 屏蔽 ECharts 在 RadarChart 多 Y 轴场景下的 alignTicks 噪音警告
10
16
  const _warn = console.warn.bind(console);
@@ -13,7 +19,14 @@ console.warn = (...args: any[]) => {
13
19
  _warn(...args);
14
20
  };
15
21
 
16
- if (!checkLogin()) {
22
+ // DEV 模式下支持 URL 参数传 token(跨域开发用)
23
+ if (import.meta.env.DEV) {
24
+ const params = getUrlParams();
25
+ const gadminToken = params[GAdminTokenName];
26
+ if (gadminToken) setLoginCookie({ gadminToken });
27
+ }
28
+
29
+ if (!Cookies.get(GAdminTokenName)) {
17
30
  login();
18
31
  } else {
19
32
  const container = document.getElementById('root') as HTMLElement;
@@ -159,7 +159,7 @@
159
159
  "emptyHint": "Drag components from the library",
160
160
  "exitPreview": "Exit Preview",
161
161
  "config": "Configure",
162
- "configCharts": "Configure Charts & Order",
162
+ "configCharts": "Configure switchable chart types",
163
163
  "editCode": "Edit Code",
164
164
  "duplicate": "Duplicate",
165
165
  "delete": "Delete",
@@ -245,14 +245,14 @@
245
245
  "changePageSize": "Change Page Size",
246
246
  "removePagination": "Remove Pagination",
247
247
  "addPagination": "Add Pagination",
248
- "direction": "Direction",
248
+ "direction": "Chart Direction",
249
249
  "vertical": "Vertical",
250
250
  "horizontal": "Horizontal",
251
251
  "hideGrid": "Hide Grid",
252
252
  "showGrid": "Show Grid",
253
253
  "hideLabel": "Hide Labels",
254
254
  "showLabel": "Show Labels",
255
- "switchVariant": "Switch Style",
255
+ "switchVariant": "Switch Display Style",
256
256
  "pie": "Pie",
257
257
  "ring": "Ring",
258
258
  "topN": "Top N Items",
@@ -262,22 +262,23 @@
262
262
  "showDots": "Show Dots",
263
263
  "hideDotsLabel": "Hide Dot Values",
264
264
  "showDotsLabel": "Show Dot Values",
265
- "disableStacked": "Disable Stacking",
266
- "enableStacked": "Enable Stacking",
267
- "disablePercent": "Disable Percent",
268
- "enablePercent": "Enable Percent",
265
+ "disableStacked": "Disable Data Stacking",
266
+ "enableStacked": "Enable Data Stacking",
267
+ "disablePercent": "Disable Percent Stacking",
268
+ "enablePercent": "Enable Percent Stacking",
269
269
  "disableArea": "Disable Area Fill",
270
270
  "enableArea": "Enable Area Fill",
271
271
  "disableSmooth": "Disable Smooth",
272
272
  "enableSmooth": "Enable Smooth",
273
- "switchTo": "Switch to",
273
+ "switchTo": "Switch Chart Type",
274
274
  "showDownload": "Show Download",
275
275
  "hideDownload": "Hide Download",
276
276
  "enableSorting": "Enable Sorting",
277
277
  "disableSorting": "Disable Sorting",
278
278
  "enableFiltering": "Enable Filtering",
279
279
  "disableFiltering": "Disable Filtering",
280
- "configColumns": "Configure Columns"
280
+ "configColumns": "Configure Columns",
281
+ "visibilityGroup": "Display Elements"
281
282
  },
282
283
  "modal": {
283
284
  "columnConfigTitle": "Column Settings",