diff --git a/client/packages/lowcoder-design/src/icons/index.tsx b/client/packages/lowcoder-design/src/icons/index.tsx index b033d52e92..75f0d8a687 100644 --- a/client/packages/lowcoder-design/src/icons/index.tsx +++ b/client/packages/lowcoder-design/src/icons/index.tsx @@ -1,5 +1,8 @@ import React, { lazy, Suspense } from "react"; +export { ReactComponent as AIGenerate } from "./remix/ai-generate.svg"; +export { ReactComponent as AIGenerate2 } from "./remix/ai-generate-2.svg"; +export { ReactComponent as AIGenerateText } from "./remix/ai-generate-text.svg"; export { ReactComponent as AppSnapshotIcon } from "./v1/app-snapshot.svg"; export { ReactComponent as ArchiveIcon } from "./remix/archive-fill.svg"; export { ReactComponent as HookCompDropIcon } from "./v1/hook-comp-drop.svg"; diff --git a/client/packages/lowcoder/package.json b/client/packages/lowcoder/package.json index e838c870d8..04a4c30535 100644 --- a/client/packages/lowcoder/package.json +++ b/client/packages/lowcoder/package.json @@ -6,7 +6,13 @@ "main": "src/index.sdk.ts", "types": "src/index.sdk.ts", "dependencies": { + "@ai-sdk/openai": "^1.3.22", "@ant-design/icons": "^5.3.0", + "@assistant-ui/react": "^0.10.24", + "@assistant-ui/react-ai-sdk": "^0.10.14", + "@assistant-ui/react-markdown": "^0.10.5", + "@assistant-ui/styles": "^0.1.13", + "@bany/curl-to-json": "^1.2.8", "@codemirror/autocomplete": "^6.11.1", "@codemirror/commands": "^6.3.2", "@codemirror/lang-css": "^6.2.1", @@ -27,6 +33,8 @@ "@jsonforms/core": "^3.5.1", "@lottiefiles/dotlottie-react": "^0.13.0", "@manaflair/redux-batch": "^1.0.0", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tooltip": "^1.2.7", "@rjsf/antd": "^5.24.9", "@rjsf/core": "^5.24.9", "@rjsf/utils": "^5.24.9", @@ -36,11 +44,13 @@ "@types/react-signature-canvas": "^1.0.2", "@types/react-test-renderer": "^18.0.0", "@types/react-virtualized": "^9.21.21", + "ai": "^4.3.16", "alasql": "^4.6.6", "animate.css": "^4.1.1", "antd": "^5.25.2", "axios": "^1.7.7", "buffer": "^6.0.3", + "class-variance-authority": "^0.7.1", "clsx": "^2.0.0", "cnchar": "^3.2.4", "coolshapes-react": "lowcoder-org/coolshapes-react", @@ -60,6 +70,7 @@ "loglevel": "^1.8.0", "lowcoder-core": "workspace:^", "lowcoder-design": "workspace:^", + "lucide-react": "^0.525.0", "mime": "^3.0.0", "moment": "^2.29.4", "numbro": "^2.3.6", @@ -97,7 +108,7 @@ "regenerator-runtime": "^0.13.9", "rehype-raw": "^6.1.1", "rehype-sanitize": "^5.0.1", - "remark-gfm": "^4.0.0", + "remark-gfm": "^4.0.1", "resize-observer-polyfill": "^1.5.1", "simplebar-react": "^3.2.4", "sql-formatter": "^8.2.0", diff --git a/client/packages/lowcoder/src/api/applicationApi.ts b/client/packages/lowcoder/src/api/applicationApi.ts index 2411b50d80..8ed818b371 100644 --- a/client/packages/lowcoder/src/api/applicationApi.ts +++ b/client/packages/lowcoder/src/api/applicationApi.ts @@ -99,6 +99,7 @@ class ApplicationApi extends Api { static publicToMarketplaceURL = (applicationId: string) => `/applications/${applicationId}/public-to-marketplace`; static getMarketplaceAppURL = (applicationId: string) => `/applications/${applicationId}/view_marketplace`; static setAppEditingStateURL = (applicationId: string) => `/applications/editState/${applicationId}`; + static getAvailableGroupsMembersURL = (applicationId: string) => `/applications/${applicationId}/groups-members/available`; static serverSettingsURL = () => `/serverSettings`; static fetchHomeData(request: HomeDataPayload): AxiosPromise { @@ -217,6 +218,10 @@ class ApplicationApi extends Api { }); } + static getAvailableGroupsMembers(applicationId: string, search: string): AxiosPromise { + return Api.get(ApplicationApi.getAvailableGroupsMembersURL(applicationId), {search}) + } + /** * set app as public */ diff --git a/client/packages/lowcoder/src/api/orgApi.ts b/client/packages/lowcoder/src/api/orgApi.ts index 588a20df51..379234b32e 100644 --- a/client/packages/lowcoder/src/api/orgApi.ts +++ b/client/packages/lowcoder/src/api/orgApi.ts @@ -62,6 +62,7 @@ export class OrgApi extends Api { static updateOrgURL = (orgId: string) => `/organizations/${orgId}/update`; static fetchUsage = (orgId: string) => `/organizations/${orgId}/api-usage`; static fetchOrgsByEmailURL = (email: string) => `organizations/byuser/${email}`; + static fetchGroupPotentialMembersURL = (groupId: string) => `/groups/${groupId}/potential-members`; static createGroup(request: { name: string }): AxiosPromise> { return Api.post(OrgApi.createGroupURL, request); @@ -110,6 +111,10 @@ export class OrgApi extends Api { return Api.get(OrgApi.fetchGroupUsersURL(groupId)); } + static fetchGroupPotentialMembers(searchName: string, groupId: string): AxiosPromise { + return Api.get(OrgApi.fetchGroupPotentialMembersURL(groupId), {searchName}) + } + static fetchGroupUsersPagination(request: fetchGroupUserRequestType): AxiosPromise { const {groupId, ...res} = request; return Api.get(OrgApi.fetchGroupUsersURL(groupId), {...res}); diff --git a/client/packages/lowcoder/src/api/userApi.ts b/client/packages/lowcoder/src/api/userApi.ts index c80d4b19dd..5955071a84 100644 --- a/client/packages/lowcoder/src/api/userApi.ts +++ b/client/packages/lowcoder/src/api/userApi.ts @@ -1,6 +1,6 @@ import Api from "api/api"; import { AxiosPromise } from "axios"; -import { OrgAndRole } from "constants/orgConstants"; +import { Org, OrgAndRole } from "constants/orgConstants"; import { BaseUserInfo, CurrentUser } from "constants/userConstants"; import { MarkUserStatusPayload, UpdateUserPayload } from "redux/reduxActions/userActions"; import { ApiResponse, GenericApiResponse } from "./apiResponses"; @@ -60,10 +60,25 @@ export interface FetchApiKeysResponse extends ApiResponse { export type GetCurrentUserResponse = GenericApiResponse; +export interface GetMyOrgsResponse extends ApiResponse { + data: { + data: Array<{ + orgId: string; + orgName: string; + createdAt?: number; + updatedAt?: number; + }>; + pageNum: number; + pageSize: number; + total: number; + }; +} + class UserApi extends Api { static thirdPartyLoginURL = "/auth/tp/login"; static thirdPartyBindURL = "/auth/tp/bind"; static usersURL = "/users"; + static myOrgsURL = "/users/myorg"; static sendVerifyCodeURL = "/auth/otp/send"; static logoutURL = "/auth/logout"; static userURL = "/users/me"; @@ -127,6 +142,19 @@ class UserApi extends Api { static getCurrentUser(): AxiosPromise { return Api.get(UserApi.currentUserURL); } + static getMyOrgs( + pageNum: number = 1, + pageSize: number = 20, + orgName?: string + ): AxiosPromise { + const params = new URLSearchParams({ + pageNum: pageNum.toString(), + pageSize: pageSize.toString(), + ...(orgName && { orgName }) + }); + + return Api.get(`${UserApi.myOrgsURL}?${params}`); + } static getRawCurrentUser(): AxiosPromise { return Api.get(UserApi.rawCurrentUserURL); diff --git a/client/packages/lowcoder/src/app.tsx b/client/packages/lowcoder/src/app.tsx index 1fb49720d4..a4857882ee 100644 --- a/client/packages/lowcoder/src/app.tsx +++ b/client/packages/lowcoder/src/app.tsx @@ -60,7 +60,6 @@ import GlobalInstances from 'components/GlobalInstances'; import { fetchHomeData, fetchServerSettingsAction } from "./redux/reduxActions/applicationActions"; import { getNpmPackageMeta } from "./comps/utils/remote"; import { packageMetaReadyAction, setLowcoderCompsLoading } from "./redux/reduxActions/npmPluginActions"; -import { fetchBrandingSetting } from "./redux/reduxActions/enterpriseActions"; import { EnterpriseProvider } from "./util/context/EnterpriseContext"; import { SimpleSubscriptionContextProvider } from "./util/context/SimpleSubscriptionContext"; import { getBrandingSetting } from "./redux/selectors/enterpriseSelectors"; @@ -137,7 +136,6 @@ type AppIndexProps = { defaultHomePage: string | null | undefined; fetchHomeDataFinished: boolean; fetchConfig: (orgId?: string) => void; - fetchBrandingSetting: (orgId?: string) => void; fetchHomeData: (currentUserAnonymous?: boolean | undefined) => void; fetchLowcoderCompVersions: () => void; getCurrentUser: () => void; @@ -167,7 +165,6 @@ class AppIndex extends React.Component { if (!this.props.currentUserAnonymous) { this.props.fetchHomeData(this.props.currentUserAnonymous); this.props.fetchLowcoderCompVersions(); - this.props.fetchBrandingSetting(this.props.currentOrgId); } } } @@ -521,7 +518,6 @@ const mapDispatchToProps = (dispatch: any) => ({ fetchHomeData: (currentUserAnonymous: boolean | undefined) => { dispatch(fetchHomeData({})); }, - fetchBrandingSetting: (orgId?: string) => dispatch(fetchBrandingSetting({ orgId, fallbackToGlobal: true })), fetchLowcoderCompVersions: async () => { try { dispatch(setLowcoderCompsLoading(true)); diff --git a/client/packages/lowcoder/src/components/CurlImport.tsx b/client/packages/lowcoder/src/components/CurlImport.tsx new file mode 100644 index 0000000000..a15d54bbf3 --- /dev/null +++ b/client/packages/lowcoder/src/components/CurlImport.tsx @@ -0,0 +1,97 @@ +import React, { useState } from "react"; +import { Modal, Input, Button, message } from "antd"; +import { trans } from "i18n"; +import parseCurl from "@bany/curl-to-json"; +const { TextArea } = Input; +interface CurlImportModalProps { + open: boolean; + onCancel: () => void; + onSuccess: (parsedData: any) => void; +} + +export function CurlImportModal(props: CurlImportModalProps) { + const { open, onCancel, onSuccess } = props; + const [curlCommand, setCurlCommand] = useState(""); + const [loading, setLoading] = useState(false); + + const handleImport = async () => { + if (!curlCommand.trim()) { + message.error("Please enter a cURL command"); + return; + } + + setLoading(true); + try { + // Parse the cURL command using the correct import + const parsedData = parseCurl(curlCommand); + + + + // Log the result for now as requested + // console.log("Parsed cURL data:", parsedData); + + // Call success callback with parsed data + onSuccess(parsedData); + + // Reset form and close modal + setCurlCommand(""); + onCancel(); + + message.success("cURL command imported successfully!"); + } catch (error: any) { + console.error("Error parsing cURL command:", error); + message.error(`Failed to parse cURL command: ${error.message}`); + } finally { + setLoading(false); + } + }; + + const handleCancel = () => { + setCurlCommand(""); + onCancel(); + }; + + return ( + + Cancel + , + , + ]} + width={600} + > +
+
+ Paste cURL Command Here +
+
+
+ Examples: +
+
+ GET: curl -X GET https://jsonplaceholder.typicode.com/posts/1 +
+
+ POST: curl -X POST https://jsonplaceholder.typicode.com/posts -H "Content-Type: application/json" -d '{"title":"foo","body":"bar","userId":1}' +
+
+ Users: curl -X GET https://jsonplaceholder.typicode.com/users +
+
+