diff --git a/README.md b/README.md index 6ba2fff86..ede4bcc29 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,22 @@

Create software applications (internal and customer-facing!) and Meeting/Collaboration tools for your Company and your Customers with minimal coding experience.

-

Lowcoder is the best Retool, Appsmith or Tooljet Alternative.

+

We think, Lowcoder is simply better than Retool, Appsmith Tooljet, Outsystems or Mendix.

+--- - - +## 🎥 Lowcoder Intro Video +
+ + Lowcoder Intro Video + +

Click the image above to watch the video on YouTube 📺

+
+--- ## 📢 Use Lowcoder in 3 steps 1. Connect to any data sources or APIs. -2. Build flexible and responsive UI with 100+ components and free layout / design possibilities. +2. Build flexible and responsive UI with 120+ components and free layout / design possibilities. 3. Share with colleagues and customers. ## 💡 Why Lowcoder @@ -23,9 +30,9 @@ One platform for everything instead so many different softwares. (like Website B It's cumbersome to create a single app. You had to design user interfaces, write code in multiple languages and frameworks, and understand how all of that code works together. -NewGen Lowcode Platforms like Retool and others are great for their simplicity and flexibility - like Lowcoder too, but they can also be limited in different ways, especially when it comes to "external" applications for everyone. +NewGen Lowcode Platforms like Retool and others are great for their simplicity and flexibility - like Lowcoder too, but they can also be limited in different ways, especially when it comes to "external" applications for everyone - because their pricing focusses to internal apps and "pay per User". -Lowcoder wants to take a step forward. More specifically, Lowcoder is: +With Lowcoder we did a step forward. More specifically, Lowcoder is: - An all-in-one IDE to create internal or customer-facing (external) apps. - A place to create, build and share building blocks of web applications and whole websites. - The tool and community to support your business, and lower the cost and time to develop interactive applications. @@ -34,9 +41,9 @@ Lowcoder wants to take a step forward. More specifically, Lowcoder is: - The only platform which has extensibility plugin architecture [Check Community Contributions](https://www.npmjs.com/search?q=lowcoder-comp) ## 🪄 Features -- **Visual UI builder** with 100+ built-in components. Save 90% of time to build apps. +- **Visual UI builder** with 120+ built-in components. Save 90% of time to build apps. - **Modules** for reusable (!) embedable component sets in the UI builder. -- **Embed Lowcoder Apps as native parts of any Website** instead of iFrame (!). [Demo](https://lowcoder.cloud/about), [Docu](https://docs.lowcoder.cloud/lowcoder-documentation/lowcoder-extension/native-embed-sdk) +- **Embed Lowcoder Apps as native parts of any Website** instead of iFrame (!). [Demo](http://demo-lowcoder.42web.io/ecommerce/), [Docu](https://docs.lowcoder.cloud/lowcoder-documentation/lowcoder-extension/native-embed-sdk) - **Video Meeting Components** to create your own individual Web-Meeting tool. - **Query Library** for reusable data queries of your data sources. - **Custom components** to develop own components and use them in the UI builder. @@ -107,7 +114,3 @@ Accelerate the growth of Lowcoder and unleash its potential with your Sponsorshi [Be a Sponsor](https://github.com/sponsors/lowcoder-org) Like ... [@Darkjamin](https://github.com/Darkjamin), [@spacegoats-io](https://github.com/spacegoats-io), [@Jomedya](https://github.com/Jomedya), [@CHSchuepfer](https://github.com/CHSchuepfer), Thank you very much!! - -## Intro Video - -[![Watch the video](https://i.ytimg.com/vi/s4ltAqS0hzM/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGD0gSShyMA8=&rs=AOn4CLAlPOIFdtauythoBKNPXhi6XGwlDQ)](https://youtu.be/s4ltAqS0hzM?feature=shared) diff --git a/client/packages/lowcoder/src/api/subscriptionApi.ts b/client/packages/lowcoder/src/api/subscriptionApi.ts index 02a3ff5f6..6bfcdb259 100644 --- a/client/packages/lowcoder/src/api/subscriptionApi.ts +++ b/client/packages/lowcoder/src/api/subscriptionApi.ts @@ -182,6 +182,22 @@ export const createCustomer = async (subscriptionCustomer: LowcoderNewCustomer) } }; +export const cleanupCustomer = async (subscriptionCustomer: LowcoderSearchCustomer) => { + const apiBody = { + path: "webhook/secure/cleanup-customer", + data: subscriptionCustomer, + method: "post", + headers: lcHeaders + }; + try { + const result = await SubscriptionApi.secureRequest(apiBody, 15000); + return result?.data as any; + } catch (error) { + console.error("Error creating customer:", error); + throw error; + } +}; + export const getProduct = async (productId : string) => { const apiBody = { path: "webhook/secure/get-product", diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx index 066b351fe..7e2cfaecd 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx @@ -363,7 +363,7 @@ QueryCompTmp = class extends QueryCompTmp { } if (action.type === CompActionTypes.EXECUTE_QUERY) { if (getReduceContext().disableUpdateState) return this; - if(!action.args) action.args = this.children.variables.children.variables.toJsonValue().reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {}); + if(!action.args) action.args = this.children.variables.children.variables.toJsonValue().filter(kv => kv.key).reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {}); action.args.$queryName = this.children.name.getView(); return this.executeQuery(action); @@ -664,23 +664,6 @@ export const QueryComp = withExposingConfigs(QueryCompTmp, [ new NameConfig("isFetching", trans("query.isFetchingExportDesc")), new NameConfig("runTime", trans("query.runTimeExportDesc")), new NameConfig("latestEndTime", trans("query.latestEndTimeExportDesc")), - new DepsConfig( - "variables", - (children: any) => { - return {data: children.variables.children.variables.node()}; - }, - (input) => { - if (!input.data) { - return undefined; - } - const newNode = Object.values(input.data) - .filter((kvNode: any) => kvNode.key) - .map((kvNode: any) => ({[kvNode.key]: kvNode.value})) - .reduce((prev, obj) => ({...prev, ...obj}), {}); - return newNode; - }, - trans("query.variables") - ), new NameConfig("triggerType", trans("query.triggerTypeExportDesc")), ]); @@ -777,13 +760,15 @@ class QueryListComp extends QueryListTmpComp implements BottomResListComp { const jsonData = originQuery.toJsonValue(); //Regenerate variable header + const newKeys:string[] = []; jsonData.variables?.variables?.forEach(kv => { const [prefix, _] = (kv.key as string).split(/(?=\d+$)/); let i=1, newName = ""; do { newName = prefix + (i++); - } while(editorState.checkRename("", newName)); + } while(editorState.checkRename("", newName) || newKeys.includes(newName)); kv.key = newName; + newKeys.push(newName); }) const newQueryName = this.genNewName(editorState); diff --git a/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx b/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx index d319689e7..dcb913a3d 100644 --- a/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx @@ -28,10 +28,9 @@ export function toQueryView(params: FunctionProperty[]) { variables?: any; timeout: InstanceType; }): Promise => { - console.log("toQueryView props", props, params); const { applicationId, isViewMode } = getGlobalSettings(); - const mappedVariables = Object.keys(props.variables).map(key => ({key: `${props.args?.$queryName}.variables.${key}`, value: props.variables[key]})); + const mappedVariables = Object.keys(props.variables).filter(k => k !== "$queryName").map(key => ({key: `${props.args?.$queryName}.variables.${key}`, value: props.variables[key] || ""})); let request: QueryExecuteRequest = { path: props.applicationPath, params: [ diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx index ccbdf1a78..71c13d039 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx @@ -10,6 +10,7 @@ import { TRASH_URL, NEWS_URL, ORG_HOME_URL, + SUBSCRIPTION_SETTING, } from "constants/routesURL"; import { getUser, isFetchingUser } from "redux/selectors/usersSelectors"; import { useDispatch, useSelector } from "react-redux"; @@ -57,6 +58,7 @@ import { trans } from "../../i18n"; import { foldersSelector } from "../../redux/selectors/folderSelector"; import Setting from "pages/setting"; import { Support } from "pages/support"; +import { Subscription } from "pages/setting/subscriptions" // import { TypographyText } from "../../components/TypographyText"; // import { messageInstance } from "lowcoder-design/src/components/GlobalInstances"; import { isEE } from "util/envUtils"; @@ -248,6 +250,19 @@ export default function ApplicationHome() { ], } : { items: [] }, + !supportSubscription && user.orgDev ? { + items: [ + { + text: {trans("home.support")}, + routePath: SUBSCRIPTION_SETTING, + routeComp: Subscription, + routePathExact: false, + icon: ({ selected, ...otherProps }) => selected ? : , + mobileVisible: true, + }, + ], + } : { items: [] }, + supportSubscription && user.orgDev ? { items: [ { diff --git a/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts b/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts index 8f43b9fc1..1e94116d7 100644 --- a/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts @@ -11,9 +11,7 @@ import { Subscription, LowcoderSearchCustomer } from '@lowcoder-ee/constants/sub function* fetchSubscriptionsSaga(action: ReturnType) { try { const user: User = yield select(getUser); - const currentUser: CurrentUser = yield select(getCurrentUser); const orgID = user.currentOrgId; - const domain = `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}`; const deploymentId: string = yield select(getDeploymentId); const subscriptionSearchCustomer: LowcoderSearchCustomer = { diff --git a/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx b/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx index f89e5ff77..1d1b16d93 100644 --- a/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx +++ b/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx @@ -1,4 +1,4 @@ -import { createCheckoutLink, createCustomer, getProducts, searchCustomer } from "@lowcoder-ee/api/subscriptionApi"; +import { createCheckoutLink, cleanupCustomer } from "@lowcoder-ee/api/subscriptionApi"; import { StripeCustomer, SubscriptionProduct, InitSubscriptionProducts, LowcoderSearchCustomer, LowcoderNewCustomer, Subscription } from "@lowcoder-ee/constants/subscriptionConstants"; import { getDeploymentId } from "@lowcoder-ee/redux/selectors/configSelectors"; import { getFetchSubscriptionsFinished, getSubscriptions, getSubscriptionsError } from "@lowcoder-ee/redux/selectors/subscriptionSelectors"; @@ -78,17 +78,6 @@ export const SubscriptionContextProvider = (props: { userId: user.id, }; - const subscriptionNewCustomer: LowcoderNewCustomer = { - hostname: domain, - hostId: deploymentId, - email: currentUser.email, - orgId: orgID, - userId: user.id, - userName: user.username, - type: admin, - companyName: currentOrg?.name || "Unknown", - }; - useEffect(() => { // If products are already loaded in the outer context, reuse them if (productsLoaded) { @@ -104,28 +93,11 @@ export const SubscriptionContextProvider = (props: { const initializeCustomer = async () => { if (existingCustomer) { setCustomer(existingCustomer); + + cleanupCustomer(subscriptionSearchCustomer); + return; } - - /* try { - setIsCreatingCustomer(true); - const subscriptionSearchCustomer: LowcoderSearchCustomer = { - hostId: deploymentId, - orgId: orgID, - userId: user.id, - }; - const existingCustomer = await searchCustomer(subscriptionSearchCustomer); - if (existingCustomer) { - setCustomer(existingCustomer); - } else { - const newCustomer = await createCustomer(subscriptionNewCustomer); - setCustomer(newCustomer); - } - } catch (error) { - setCustomerDataError(true); - } finally { - setIsCreatingCustomer(false); - } */ }; if (!customer && isCustomerInitializationComplete) { diff --git a/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md b/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md index edd912076..7ff5814e8 100644 --- a/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md +++ b/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md @@ -1,6 +1,6 @@ # Develop Data Source Plugins -This document provides basic information and guides for developing data source plugins. Developers are highly welcomed to make contributions to [Lowcoder](https://github.com/Lowcoder-dev/Lowcoder)--the open source project. +This document provides basic information and guides for developing data source plugins. Developers are highly welcomed to make contributions to [Lowcoder](https://github.com/lowcoder-org/lowcoder)--the open source project. ## Basics @@ -12,7 +12,7 @@ A data source plugin is described by a **JavaScript Object** which mainly consis * Definition of the **Action list** for data source queries and the configuration form for each Action. * Definition of the **execution logic** for Actions. -Currently, all data source plugins are maintained in the `src/plugins` directory of the `node-service` project. Click to view [the project](https://github.com/Lowcoder-dev/Lowcoder/tree/develop/server/node-service), and you might take a quick look at the [S3 plugin](https://github.com/Lowcoder-dev/Lowcoder/tree/develop/server/node-service/src/plugins/s3). +Currently, all data source plugins are maintained in the `src/plugins` directory of the `node-service` project. Click to view [the project](https://github.com/lowcoder-org/lowcoder/tree/main/server/node-service), and you might take a quick look at the [S3 plugin](https://github.com/lowcoder-org/lowcoder/tree/main/server/node-service/src/plugins/s3). ## Overall definition of a plugin @@ -268,7 +268,7 @@ Due to various reasons, the generated plugin code needs to be correctly validate ## Testing -Necessary testing should be done before publishing the plugin. Testing a data source plugin requires a backend environment. You can start a local environment by following the documentation [Start a local backend server](https://github.com/Lowcoder-dev/Lowcoder/tree/develop/client#readme) and test the data source plugin in following aspects: +Necessary testing should be done before publishing the plugin. Testing a data source plugin requires a backend environment. You can start a local environment by following the documentation [Start a local backend server](https://github.com/lowcoder-org/lowcoder/tree/main/server/api-service#readme) and test the data source plugin in following aspects: 1. Make sure the data source plugin has been added to the plugin list in the file `src/plugins/index.ts`. 2. Start the node-service server in the `node-service` directory by executing `yarn dev`. @@ -292,4 +292,4 @@ You can then use the data source plugin just developed in this environment. ## What's next -Congrats! After testing the data source plugin, you can submit a [Pull Request](https://github.com/Lowcoder-dev/Lowcoder/pulls) now. +Congrats! After testing the data source plugin, you can submit a [Pull Request](https://github.com/lowcoder-org/lowcoder/pulls) now. diff --git a/docs/setup-and-run/self-hosting/heroku.md b/docs/setup-and-run/self-hosting/heroku.md index 460850231..133252d19 100644 --- a/docs/setup-and-run/self-hosting/heroku.md +++ b/docs/setup-and-run/self-hosting/heroku.md @@ -3,7 +3,7 @@ ## Deploy 1. [Sign up](https://signup.heroku.com/) for a new Heroku account, or [log in](https://id.heroku.com/login) to get started. -2. Click to start Heroku [one-click deployment](https://heroku.com/deploy?template=https://github.com/Lowcoder-dev/Lowcoder). +2. Click to start Heroku [one-click deployment](https://heroku.com/deploy?template=https://github.com/lowcoder-org/lowcoder). 3. Set the **App name** which will be part of the app URL later, and choose a region. 4. (Not required) Fill in the **Config Vars** according to the descriptions. These are all optional variables used for environment-specific configuration. You can skip this step and manage environment variables later. 5. Click the **Deploy app** button. diff --git a/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml b/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml index 03ae32762..a109e8a7a 100644 --- a/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml +++ b/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml @@ -31,8 +31,8 @@ com.oracle.database.jdbc - ojdbc6 - 11.2.0.4 + ojdbc11 + 23.7.0.25.01 org.testcontainers