Skip to content

Commit 7553286

Browse files
authored
Merge pull request #1425 from lowcoder-org/public_app_editor
Editor for website
2 parents dcd76c3 + 96cbee6 commit 7553286

25 files changed

+576
-60
lines changed

client/packages/lowcoder/src/api/commonSettingApi.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface CommonSettingResponseData {
2424

2525
export type SetCommonSettingPayload<T = any> = {
2626
orgId: string;
27+
isPublicApp?: boolean;
2728
data: {
2829
key: string;
2930
value: T;

client/packages/lowcoder/src/app.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
ORG_AUTH_FORGOT_PASSWORD_URL,
3030
ORG_AUTH_RESET_PASSWORD_URL,
3131
ADMIN_AUTH_URL,
32+
PUBLIC_APP_EDITOR_URL,
3233
} from "constants/routesURL";
3334
import React from "react";
3435
import { createRoot } from "react-dom/client";
@@ -65,6 +66,7 @@ const LazyInviteLanding = React.lazy(() => import("pages/common/inviteLanding"))
6566
const LazyComponentDoc = React.lazy(() => import("pages/ComponentDoc"));
6667
const LazyComponentPlayground = React.lazy(() => import("pages/ComponentPlayground"));
6768
const LazyAppEditor = React.lazy(() => import("pages/editor/AppEditor"));
69+
const LazyPublicAppEditor = React.lazy(() => import("pages/editor/AppEditorPublic"));
6870
const LazyAppFromTemplate = React.lazy(() => import("pages/ApplicationV2/AppFromTemplate"));
6971
const LazyApplicationHome = React.lazy(() => import("pages/ApplicationV2"));
7072
const LazyDebugComp = React.lazy(() => import("./debug"));
@@ -301,6 +303,14 @@ class AppIndex extends React.Component<AppIndexProps, any> {
301303
path={IMPORT_APP_FROM_TEMPLATE_URL}
302304
component={LazyAppFromTemplate}
303305
/>
306+
307+
<LazyRoute
308+
exact
309+
fallback="layout"
310+
path={PUBLIC_APP_EDITOR_URL}
311+
component={LazyPublicAppEditor}
312+
/>
313+
304314
<LazyRoute
305315
fallback="layout"
306316
path={APP_EDITOR_URL}

client/packages/lowcoder/src/components/JSLibraryModal.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { RecommendedJSLibraryMeta } from "api/jsLibraryApi";
2323
import log from "loglevel";
2424
import { TacoMarkDown } from "components/markdown";
2525
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
26+
import { isPublicApplication } from "@lowcoder-ee/redux/selectors/applicationSelector";
2627

2728
const ModalLabel = styled.div`
2829
display: flex;
@@ -232,12 +233,15 @@ export function JSLibraryModal(props: JSLibraryModalProps) {
232233
const [url, setURL] = useState("");
233234
const [urlError, setURLError] = useState<string | undefined>(undefined);
234235
const [installError, setInstallError] = useState<URLErrorType>(undefined);
236+
const isPublicApp = useSelector(isPublicApplication);
235237

236238
const dispatch = useDispatch();
237239

238240
const recommends = useSelector(recommendJSLibrarySelector);
239241

240242
useEffect(() => {
243+
if (isPublicApp) return;
244+
241245
dispatch(fetchJSLibraryRecommendsAction());
242246
}, [dispatch]);
243247

client/packages/lowcoder/src/components/ResCreatePanel.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { useSelector } from "react-redux";
2626
import { getUser } from "../redux/selectors/usersSelectors";
2727
import DataSourceIcon from "./DataSourceIcon";
2828
import { genRandomKey } from "comps/utils/idGenerator";
29+
import { isPublicApplication } from "@lowcoder-ee/redux/selectors/applicationSelector";
2930

3031
const Wrapper = styled.div<{ $placement: PageType }>`
3132
width: 100%;
@@ -232,6 +233,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
232233
const [isScrolling, setScrolling] = useState(false);
233234
const [visible, setVisible] = useState(false);
234235

236+
const isPublicApp = useSelector(isPublicApplication);
235237
const user = useSelector(getUser);
236238

237239
const { width, ref } = useResizeDetector({ handleHeight: false });
@@ -289,7 +291,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
289291
onSelect={onSelect}
290292
/>
291293
<ResButton size={buttonSize} identifier={"js"} onSelect={onSelect} />
292-
<ResButton size={buttonSize} identifier={"libraryQuery"} onSelect={onSelect} />
294+
{!isPublicApp && <ResButton size={buttonSize} identifier={"libraryQuery"} onSelect={onSelect} /> }
293295
<ResButton
294296
size={buttonSize}
295297
identifier={BottomResTypeEnum.Folder}
@@ -337,7 +339,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
337339
<ResButton size={buttonSize} key={i.id} identifier={i} onSelect={onSelect} />
338340
))}
339341

340-
{user.orgDev && (
342+
{(user.orgDev || isPublicApp) && (
341343
<DataSourceButton size={buttonSize} onClick={() => setVisible(true)}>
342344
<LargeBottomResIconWrapper>
343345
<AddIcon />
@@ -351,7 +353,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
351353
</ScrollBar>
352354
</Content>
353355
<CreateDataSourceModal
354-
open={visible}
356+
open={visible}
355357
onCancel={() => setVisible(false)}
356358
onCreated={() => setVisible(false)}
357359
/>

client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx

+11-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type { AppState } from "@lowcoder-ee/redux/reducers";
2525
import { ColorControl } from "../controls/colorControl";
2626
import { DEFAULT_ROW_COUNT } from "@lowcoder-ee/layout/calculateUtils";
2727
import { AppSettingContext } from "../utils/appSettingContext";
28+
import { isPublicApplication } from "@lowcoder-ee/redux/selectors/applicationSelector";
2829

2930
const TITLE = trans("appSetting.title");
3031
const USER_DEFINE = "__USER_DEFINE";
@@ -337,6 +338,7 @@ function AppGeneralSettingsModal(props: ChildrenInstance) {
337338
}
338339

339340
function AppCanvasSettingsModal(props: ChildrenInstance) {
341+
const isPublicApp = useSelector(isPublicApplication);
340342
const {
341343
themeList,
342344
defaultTheme,
@@ -415,13 +417,15 @@ function AppCanvasSettingsModal(props: ChildrenInstance) {
415417
placement="bottom"
416418
itemNode={(value) => <DropdownItem value={value} />}
417419
preNode={() => (
418-
<>
419-
<CreateDiv onClick={() => window.open(THEME_SETTING)}>
420-
<StyledAddIcon />
421-
{trans("appSetting.themeCreate")}
422-
</CreateDiv>
423-
<DividerStyled />
424-
</>
420+
isPublicApp ? <></> : (
421+
<>
422+
<CreateDiv onClick={() => window.open(THEME_SETTING)}>
423+
<StyledAddIcon />
424+
{trans("appSetting.themeCreate")}
425+
</CreateDiv>
426+
<DividerStyled />
427+
</>
428+
)
425429
)}
426430
allowClear
427431
onChange={(value) => {

client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { PUBLIC_APP_ID } from "@lowcoder-ee/constants/publicApp";
12
import { sdkConfig } from "@lowcoder-ee/constants/sdkConfig";
23
import { ASSETS_BASE_URL, NPM_PLUGIN_ASSETS_BASE_URL } from "constants/npmPlugins";
34
import { trans } from "i18n";
@@ -23,7 +24,9 @@ async function npmLoader(
2324
? `${sdkConfig.baseURL}/${ASSETS_BASE_URL}`
2425
: NPM_PLUGIN_ASSETS_BASE_URL;
2526

26-
const entry = `${pluginBaseUrl}/${appId || 'none'}/${packageName}@${localPackageVersion}/index.js`;
27+
const applicationId = (!appId || appId && appId === PUBLIC_APP_ID) ? 'none' : appId;
28+
29+
const entry = `${pluginBaseUrl}/${applicationId}/${packageName}@${localPackageVersion}/index.js`;
2730

2831
try {
2932
const module = await import(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
export const PUBLIC_APP_ID = "public_app";
2+
export const PUBLIC_APP_ORG_ID = "645b53eb86b4c862d8ae0fb9";
3+
4+
export const publicAppResponse = {
5+
status: 200,
6+
statusText: 'OK',
7+
headers: {},
8+
config: {
9+
headers: {} as any,
10+
},
11+
data: {
12+
code: 1,
13+
success: true,
14+
message: "",
15+
data: {
16+
orgCommonSettings: undefined,
17+
applicationDSL: {
18+
"ui": {
19+
"compType": "normal",
20+
"comp": {}
21+
},
22+
"refTree": {
23+
"value": ""
24+
},
25+
"hooks": [
26+
{
27+
"compType": "urlParams",
28+
"comp": {},
29+
"name": "url"
30+
},
31+
{
32+
"compType": "dayJsLib",
33+
"comp": {},
34+
"name": "dayjs"
35+
},
36+
{
37+
"compType": "lodashJsLib",
38+
"comp": {},
39+
"name": "_"
40+
},
41+
{
42+
"compType": "utils",
43+
"comp": {},
44+
"name": "utils"
45+
},
46+
{
47+
"compType": "message",
48+
"comp": {},
49+
"name": "message"
50+
},
51+
{
52+
"compType": "toast",
53+
"comp": {},
54+
"name": "toast"
55+
},
56+
{
57+
"compType": "localStorage",
58+
"comp": {},
59+
"name": "localStorage"
60+
},
61+
{
62+
"compType": "currentUser",
63+
"comp": {},
64+
"name": "currentUser"
65+
},
66+
{
67+
"compType": "screenInfo",
68+
"comp": {},
69+
"name": "screenInfo"
70+
},
71+
{
72+
"compType": "theme",
73+
"comp": {},
74+
"name": "theme"
75+
}
76+
],
77+
"settings": {
78+
"title": "",
79+
"description": "",
80+
"category": "Business",
81+
"showHeaderInPublic": true,
82+
"themeId": "default",
83+
"preventAppStylesOverwriting": true,
84+
"disableCollision": false,
85+
"lowcoderCompVersion": "latest",
86+
"maxWidth": {
87+
"dropdown": "1920",
88+
"input": 0
89+
},
90+
"gridRowCount": "Infinity",
91+
"gridPaddingX": "20",
92+
"gridPaddingY": "20"
93+
},
94+
"preload": {
95+
"script": "",
96+
"css": "",
97+
"globalCSS": ""
98+
}
99+
},
100+
moduleDSL: {},
101+
applicationInfoView: {
102+
"orgId": "",
103+
"applicationId": PUBLIC_APP_ID,
104+
"name": "Public App",
105+
"createAt": 1735651262539,
106+
"createBy": "",
107+
"role": "owner",
108+
"applicationType": 1,
109+
"applicationStatus": "NORMAL",
110+
"folderId": '',
111+
"lastViewTime": 0,
112+
"lastModifyTime": 1735747724691,
113+
"lastEditedAt": 1735737886323,
114+
"folder": false,
115+
"extra": {},
116+
"editingUserId": "",
117+
},
118+
}
119+
}
120+
};
121+
122+
export const publicAppJSDatasourceResponse = {
123+
status: 200,
124+
statusText: 'OK',
125+
headers: {},
126+
config: {
127+
headers: {} as any,
128+
},
129+
data: {
130+
code: 1,
131+
data: [],
132+
message: "",
133+
success: true,
134+
total: 0,
135+
}
136+
};

client/packages/lowcoder/src/constants/routesURL.ts

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const FOLDERS_URL = `/folders`;
4949
export const TRASH_URL = `/trash`;
5050
export const IMPORT_APP_FROM_TEMPLATE_URL = `${ALL_APPLICATIONS_URL}/template-import/:templateId`;
5151
export const APP_EDITOR_URL = `${ALL_APPLICATIONS_URL}/:applicationId/:viewMode/:appPageId?`;
52+
export const PUBLIC_APP_EDITOR_URL = `/editor/public`;
5253

5354
export const AUTH_BIND_URL = `${USER_AUTH_URL}/bind`;
5455
export const AUTH_LOGIN_URL = `${USER_AUTH_URL}/login`;

client/packages/lowcoder/src/i18n/locales/en.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export const en = {
139139
"pluginListTitle": "Plugins",
140140
"emptyModules": "Modules are reusable Mikro-Apps. You can embed them in your App.",
141141
"searchNotFound": "Can't find the right component?",
142-
"emptyPlugins": "No Plugins Added",
142+
"emptyPlugins": "No plugins added yet. Add npm plugins to enhance your project.",
143143
"contactUs": "Contact Us",
144144
"issueHere": "here.",
145145
"folderListTitle": "Folders"

client/packages/lowcoder/src/pages/common/header.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import {
4040
recoverSnapshotAction,
4141
setShowAppSnapshot,
4242
} from "redux/reduxActions/appSnapshotActions";
43-
import { currentApplication } from "redux/selectors/applicationSelector";
43+
import { currentApplication, isPublicApplication } from "redux/selectors/applicationSelector";
4444
import {
4545
getSelectedAppSnapshot,
4646
showAppSnapshotSelector,
@@ -369,6 +369,7 @@ export default function Header(props: HeaderProps) {
369369
const { left, bottom, right } = props.panelStatus;
370370
const user = useSelector(getUser);
371371
const application = useSelector(currentApplication);
372+
const isPublicApp = useSelector(isPublicApplication);
372373
const applicationId = useApplicationId();
373374
const dispatch = useDispatch();
374375
const showAppSnapshot = useSelector(showAppSnapshotSelector);
@@ -536,7 +537,7 @@ export default function Header(props: HeaderProps) {
536537
) : (
537538
<>
538539
{/* Display a hint about who is editing the app */}
539-
{blockEditing && (
540+
{blockEditing && Boolean(applicationId) && (
540541
<>
541542
<Popover
542543
style={{ width: 200 }}
@@ -587,7 +588,7 @@ export default function Header(props: HeaderProps) {
587588
</>
588589
)}
589590

590-
{applicationId && (
591+
{Boolean(applicationId) && !isPublicApp && (
591592
<AppPermissionDialog
592593
applicationId={applicationId}
593594
visible={permissionDialogVisible}

client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { trans, transToNode } from "i18n";
1313
import { exportApplicationAsJSONFile } from "pages/ApplicationV2/components/AppImport";
1414
import { useContext, useMemo, useState } from "react";
1515
import { useDispatch, useSelector } from "react-redux";
16-
import { currentApplication } from "redux/selectors/applicationSelector";
16+
import { currentApplication, isPublicApplication } from "redux/selectors/applicationSelector";
1717
import { showAppSnapshotSelector } from "redux/selectors/appSnapshotSelector";
1818
import styled from "styled-components";
1919
import history from "util/history";
@@ -74,9 +74,10 @@ export function HeaderStartDropdown(props: { setEdit: () => void, isViewMarketpl
7474
const showAppSnapshot = useSelector(showAppSnapshotSelector);
7575
const applicationId = useApplicationId();
7676
const application = useSelector(currentApplication);
77+
const isPublicApp = useSelector(isPublicApplication);
7778
const [showCopyModal, setShowCopyModal] = useState(false);
7879
const dispatch = useDispatch();
79-
const { appType } = useContext(ExternalEditorContext);
80+
const { appType, exportPublicAppToJson } = useContext(ExternalEditorContext);
8081
const isModule = appType === AppTypeEnum.Module;
8182

8283
const isEditable = canEditApp(user, application);
@@ -137,6 +138,9 @@ export function HeaderStartDropdown(props: { setEdit: () => void, isViewMarketpl
137138
if (e.key === "edit") {
138139
props.setEdit();
139140
} else if (e.key === "export") {
141+
if (isPublicApp && exportPublicAppToJson) {
142+
return exportPublicAppToJson?.();
143+
}
140144
exportApplicationAsJSONFile(applicationId);
141145
} else if (e.key === "duplicate") {
142146
setShowCopyModal(true);

0 commit comments

Comments
 (0)