Skip to content

Commit cc45a08

Browse files
committed
Publish App, Test App, Apply Global JS and CSS
1 parent 0cc5892 commit cc45a08

File tree

3 files changed

+273
-4
lines changed

3 files changed

+273
-4
lines changed

client/packages/lowcoder/src/comps/comps/preLoadComp/actionConfigs.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import {
99
addEventHandlerAction,
1010
applyStyleAction,
1111
nestComponentAction,
12-
updateDynamicLayoutAction
12+
updateDynamicLayoutAction,
13+
publishAppAction,
14+
shareAppAction,
15+
testAllDatasourcesAction,
16+
applyGlobalJSAction,
17+
applyGlobalCSSAction
1318
} from "./actions";
1419

1520
export const actionCategories: ActionCategory[] = [
@@ -28,7 +33,14 @@ export const actionCategories: ActionCategory[] = [
2833
{
2934
key: 'app-configuration',
3035
label: 'App Configuration',
31-
actions: [configureAppMetaAction]
36+
actions: [
37+
configureAppMetaAction,
38+
publishAppAction,
39+
shareAppAction,
40+
testAllDatasourcesAction,
41+
applyGlobalJSAction,
42+
applyGlobalCSSAction
43+
]
3244
},
3345
{
3446
key: 'layout',

client/packages/lowcoder/src/comps/comps/preLoadComp/actions/appConfiguration.ts

Lines changed: 253 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { message } from "antd";
22
import { ActionConfig, ActionExecuteParams } from "../types";
3+
import ApplicationApi from "api/applicationApi";
4+
import { getApplicationIdInReducer } from "comps/utils/reduceContext";
5+
import { executeQueryAction } from "lowcoder-core";
6+
import { getPromiseAfterDispatch } from "util/promiseUtils";
7+
import { runScript } from "../utils";
8+
39

410
export const configureAppMetaAction: ActionConfig = {
511
key: 'configure-app-meta',
@@ -43,4 +49,250 @@ export const configureAppMetaAction: ActionConfig = {
4349
message.error('Failed to update app configuration');
4450
}
4551
}
46-
};
52+
};
53+
54+
export const publishAppAction: ActionConfig = {
55+
key: 'publish-app',
56+
label: 'Publish app',
57+
category: 'app-configuration',
58+
requiresInput: false,
59+
execute: async () => {
60+
try {
61+
const applicationId = getApplicationIdInReducer();
62+
63+
if (!applicationId) {
64+
message.error('Application ID not found');
65+
return;
66+
}
67+
68+
const response = await ApplicationApi.publishApplication({ applicationId });
69+
70+
if (response.data.success) {
71+
message.success('Application published successfully');
72+
window.open(`/applications/${applicationId}/view`, '_blank');
73+
} else {
74+
message.error('Failed to publish application');
75+
}
76+
77+
} catch (error) {
78+
console.error('Error publishing application:', error);
79+
message.error('Failed to publish application');
80+
}
81+
}
82+
};
83+
84+
export const shareAppAction: ActionConfig = {
85+
key: 'share-app',
86+
label: 'Share app',
87+
category: 'app-configuration',
88+
requiresInput: false,
89+
execute: async () => {
90+
// TODO: Implement share app
91+
console.log('Share app');
92+
}
93+
};
94+
95+
export const testAllDatasourcesAction: ActionConfig = {
96+
key: 'test-all-datasources',
97+
label: 'Test all datasources',
98+
category: 'app-configuration',
99+
requiresInput: false,
100+
execute: async (params: ActionExecuteParams) => {
101+
const { editorState } = params;
102+
103+
try {
104+
const allQueries = editorState.getQueriesComp().getView();
105+
106+
if (!allQueries || !allQueries.length) {
107+
message.info('No queries found in the application');
108+
return;
109+
}
110+
111+
console.log(`Found ${allQueries.length} queries to test`);
112+
113+
const results = {
114+
total: allQueries.length,
115+
successful: 0,
116+
failed: 0,
117+
errors: [] as Array<{ queryName: string; error: string }>
118+
};
119+
120+
message.loading(`Testing ${allQueries.length} queries...`, 0);
121+
122+
for (let i = 0; i < allQueries.length; i++) {
123+
const query = allQueries[i];
124+
const queryName = query.children.name.getView();
125+
126+
try {
127+
await getPromiseAfterDispatch(
128+
query.dispatch,
129+
executeQueryAction({
130+
// In some data queries, we need to pass args to the query
131+
// Currently, we don't have a way to pass args to the query
132+
// So we are passing an empty object
133+
args: {},
134+
afterExecFunc: () => {
135+
console.log(`Query ${queryName} executed successfully`);
136+
}
137+
}),
138+
{
139+
notHandledError: `Failed to execute query: ${queryName}`
140+
}
141+
);
142+
143+
results.successful++;
144+
145+
} catch (error) {
146+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
147+
console.error(`Query ${queryName} failed:`, error);
148+
149+
results.failed++;
150+
results.errors.push({
151+
queryName,
152+
error: errorMessage
153+
});
154+
}
155+
156+
if (i < allQueries.length - 1) {
157+
await new Promise(resolve => setTimeout(resolve, 500));
158+
}
159+
}
160+
161+
message.destroy();
162+
163+
if (results.failed === 0) {
164+
message.success(`All ${results.total} queries executed successfully!`);
165+
} else if (results.successful === 0) {
166+
message.error(`All ${results.total} queries failed. Check console for details.`);
167+
} else {
168+
message.warning(
169+
`Query test completed: ${results.successful} successful, ${results.failed} failed`
170+
);
171+
}
172+
173+
console.group('Query Test Results');
174+
console.log(`Total queries: ${results.total}`);
175+
console.log(`Successful: ${results.successful}`);
176+
console.log(`Failed: ${results.failed}`);
177+
178+
if (results.errors.length > 0) {
179+
console.group('Failed Queries:');
180+
results.errors.forEach(({ queryName, error }) => {
181+
console.error(`${queryName}: ${error}`);
182+
});
183+
console.groupEnd();
184+
}
185+
console.groupEnd();
186+
187+
} catch (error) {
188+
message.destroy();
189+
console.error('Error during application testing:', error);
190+
message.error('Failed to test application. Check console for details.');
191+
}
192+
}
193+
};
194+
195+
export const applyGlobalJSAction: ActionConfig = {
196+
key: 'apply-global-js',
197+
label: 'Apply global JS',
198+
category: 'app-configuration',
199+
requiresInput: true,
200+
inputPlaceholder: 'Enter JavaScript code to apply globally...',
201+
inputType: 'textarea',
202+
validation: (value: string) => {
203+
if (!value.trim()) {
204+
return 'JavaScript code is required';
205+
}
206+
try {
207+
new Function(value);
208+
return null;
209+
} catch (error) {
210+
return 'Invalid JavaScript syntax';
211+
}
212+
},
213+
execute: async (params: ActionExecuteParams) => {
214+
const { editorState, actionValue } = params;
215+
216+
try {
217+
const defaultJS = `console.log('Please provide a valid JavaScript code');`.trim();
218+
219+
const jsCode = actionValue.trim() || defaultJS;
220+
221+
const preloadComp = editorState.rootComp.children.preload;
222+
if (!preloadComp) {
223+
message.error('Preload component not found');
224+
return;
225+
}
226+
227+
const scriptComp = preloadComp.children.script;
228+
if (!scriptComp) {
229+
message.error('Script component not found');
230+
return;
231+
}
232+
233+
scriptComp.dispatchChangeValueAction(jsCode);
234+
runScript(jsCode, false);
235+
236+
message.success('Global JavaScript applied successfully!');
237+
238+
} catch (error) {
239+
console.error('Error applying global JavaScript:', error);
240+
message.error('Failed to apply global JavaScript. Check console for details.');
241+
}
242+
}
243+
};
244+
245+
export const applyGlobalCSSAction: ActionConfig = {
246+
key: 'apply-global-css',
247+
label: 'Apply global CSS',
248+
category: 'app-configuration',
249+
requiresInput: true,
250+
requiresStyle: true,
251+
inputPlaceholder: 'Enter CSS code to apply globally...',
252+
inputType: 'textarea',
253+
validation: (value: string) => {
254+
if (!value.trim()) {
255+
return 'CSS code is required';
256+
}
257+
const css = value.trim();
258+
if (!css.includes('{') || !css.includes('}')) {
259+
return 'Invalid CSS syntax - missing braces';
260+
}
261+
return null;
262+
},
263+
execute: async (params: ActionExecuteParams) => {
264+
const { editorState, actionValue } = params;
265+
266+
try {
267+
const defaultCSS = `
268+
body {
269+
font-family: Arial, sans-serif;
270+
}
271+
`.trim();
272+
273+
const cssCode = actionValue.trim() || defaultCSS;
274+
275+
const preloadComp = editorState.rootComp.children.preload;
276+
if (!preloadComp) {
277+
message.error('Preload component not found');
278+
return;
279+
}
280+
281+
const globalCSSComp = preloadComp.children.globalCSS;
282+
if (!globalCSSComp) {
283+
message.error('Global CSS component not found');
284+
return;
285+
}
286+
287+
globalCSSComp.dispatchChangeValueAction(cssCode);
288+
289+
await globalCSSComp.run('global-css', cssCode);
290+
291+
message.success('Global CSS applied successfully!');
292+
293+
} catch (error) {
294+
console.error('Error applying global CSS:', error);
295+
message.error('Failed to apply global CSS. Check console for details.');
296+
}
297+
}
298+
};

client/packages/lowcoder/src/comps/comps/preLoadComp/tabPanes.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import React, { useEffect } from "react";
33
import { trans } from "i18n";
44
import { ConstructorToComp } from "lowcoder-core";
55
import { ScriptComp, CSSComp } from "./components";
6+
import { runScript } from "./utils";
67

78
export function JavaScriptTabPane(props: { comp: ConstructorToComp<typeof ScriptComp> }) {
89
useEffect(() => {
9-
props.comp.runPreloadScript();
10+
// Use the imported runScript function instead of the component's method to avoid require() issues
11+
const code = props.comp.getView();
12+
if (code) {
13+
runScript(code, false);
14+
}
1015
}, [props.comp]);
1116

1217
const codePlaceholder = `window.name = 'Tom';\nwindow.greet = () => "hello world";`;

0 commit comments

Comments
 (0)