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
+
+
+
+
+
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
-
-[](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