Skip to content

Subscription handling #2 #1137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d4a2a2c
Search & Create Customer
Aug 10, 2024
b6e19ee
Update for Subscription Handling
Aug 12, 2024
9759a6f
Merge branch 'dev' into subscription-handling
FalkWolsky Aug 17, 2024
45b37cd
Reorganizing and cleaning up
Aug 17, 2024
19ebdc8
Reorganizing and cleaning up
Aug 21, 2024
8faa3bb
Merge branch 'dev' into subscription-handling
FalkWolsky Aug 24, 2024
79fcd60
Updating package version
Aug 24, 2024
d5e1c64
Update Home Page
Aug 24, 2024
3e0450c
Showing Support Product Page
Aug 24, 2024
b415d1e
Subscription-Check Saga
Aug 24, 2024
33c7134
Cleanup dispatchers
Aug 25, 2024
9b1e389
Merge branch 'dev' into subscription-handling
FalkWolsky Aug 31, 2024
eb7c77f
Updating Vite Build Goals
Aug 31, 2024
41affd4
Introducing Subscription Overview and Support Handling
Aug 31, 2024
07136fc
Support Tickets Table
Aug 31, 2024
25890f3
fix: enhance docker image build github action
ludomikula Sep 3, 2024
4c46225
fixed support detail route
raheeliftikhar5 Sep 6, 2024
3dbdcd6
Merge branch 'main' into subscription-handling
FalkWolsky Sep 7, 2024
7dc68d3
Merge branch 'dev' into subscription-handling
FalkWolsky Sep 7, 2024
0d68d67
Support Detail Page
Sep 7, 2024
fc55724
Merge branch 'dev' into subscription-handling
FalkWolsky Sep 14, 2024
88e61b9
Merge branch 'dev' into subscription-handling
FalkWolsky Sep 14, 2024
9f7fc35
Merge branch 'dev' into subscription-handling
FalkWolsky Sep 14, 2024
b20ed56
Update React Markdown
Sep 14, 2024
fbdf8d5
Full Support and introducing a translation management script.
Sep 14, 2024
831bcbd
Adding Language Files
Sep 14, 2024
916d55e
Cleaning up Translation Folder
Sep 14, 2024
909a030
Updated Language Files including Variables
Sep 14, 2024
86757ee
Fixed Language Files and Merge preparation
Sep 15, 2024
fe89b29
Merge branch 'dev' into subscription-handling
FalkWolsky Sep 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update Home Page
  • Loading branch information
FalkWolsky committed Aug 24, 2024
commit d5e1c645c5b09ea2a95ca40a8e6953863a515a17
1 change: 1 addition & 0 deletions client/packages/lowcoder-design/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export { ReactComponent as HomeListIcon } from "./v1/icon-application-list.svg";
export { ReactComponent as HomeCardIcon } from "./v1/icon-application-card.svg";
export { ReactComponent as APIDocsIcon } from "./remix/instance-line.svg";
export { ReactComponent as SubscriptionIcon } from "./remix/award-fill.svg";
export { ReactComponent as SupportIcon } from "./remix/user-heart-line.svg";
// export { ReactComponent as AllAppIcon } from "./v1/icon-all-app.svg";


Expand Down
51 changes: 45 additions & 6 deletions client/packages/lowcoder/src/api/subscriptionApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,29 @@
quantity: number;
}

export interface Subscription {
id: string;
collection_method: string;
current_period_end: number;
current_period_start: number;
product: string;
currency: string;
interval: string;
tiers_mode: string;
status: string;
start_date: number;
quantity: number;
billing_scheme: string;
price: string;
}

export interface SubscriptionsData {
subscriptions: Subscription[];
subscriptionDataLoaded: boolean;
subscriptionDataError: boolean;
loading: boolean;
}

export type ResponseType = {
response: any;
};
Expand Down Expand Up @@ -398,18 +421,34 @@
};
};

export enum SubscriptionProducts {
SUPPORT = "QW8L3WPMiNjQjI",
MEDIAPACKAGE = 'standard',
AZUREAPIS = 'premium',
GOOGLEAPIS = 'enterprise',
AWSAPIS = 'enterprise-global',
PRIVATECLOUD = 'private-cloud',
MATRIXCLOUD = 'matrix-cloud',
AGORATOKENSERVER = 'agora-tokenserver',
SIGNALSERVER = 'signal-server',
DATABASE = 'database',
STORAGE = 'storage',
IOSAPP = 'ios-app',
ANDROIDAPP = 'android-app',
AUDITLOG = 'audit-log',
APPLOG = 'app-log',
ENVIRONMENTS = 'environments',
GITREPOS = 'git-repos',
}


export const CheckSubscriptions = () => {
const [customer, setCustomer] = useState<StripeCustomer | null>(null);
const [customerDataError, setCustomerDataError] = useState<boolean>(false);
const [subscriptions, setSubscriptions] = useState<SubscriptionItem[]>([]);
export const checkSubscriptions = () => {
const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);

Check failure on line 445 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useState" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
const [subscriptionDataLoaded, setSubscriptionDataLoaded] = useState<boolean>(false);

Check failure on line 446 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useState" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
const [subscriptionDataError, setSubscriptionDataError] = useState<boolean>(false);

Check failure on line 447 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useState" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
const [loading, setLoading] = useState<boolean>(true);

Check failure on line 448 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useState" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

const user = useSelector(getUser);

Check failure on line 450 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useSelector" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
const currentUser = useSelector(getCurrentUser);

Check failure on line 451 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useSelector" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
const orgID = user.currentOrgId;
const domain = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');

Expand All @@ -420,7 +459,7 @@
userId: user.id,
};

useEffect(() => {

Check failure on line 462 in client/packages/lowcoder/src/api/subscriptionApi.ts

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useEffect" is called in function "checkSubscriptions" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
const fetchCustomerAndSubscriptions = async () => {
try {
const subs = await searchCustomersSubscriptions(subscriptionSearchCustomer);
Expand All @@ -433,7 +472,7 @@
}
};
fetchCustomerAndSubscriptions();
}, []);
}, [subscriptionSearchCustomer]);

return {
subscriptions,
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/constants/routesURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const NEWS_URL = "/news";
export const ORG_HOME_URL = "/org/home";
export const COMPONENT_DOC_URL = "/components";
export const SETTING = "/setting";
export const SUPPORT = `/support`;
export const PERMISSION_SETTING = "/setting/permission";
export const ORGANIZATION_SETTING = "/setting/organization";
export const SUBSCRIPTION_SETTING = "/setting/subscription";
Expand Down
5 changes: 4 additions & 1 deletion client/packages/lowcoder/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3020,7 +3020,7 @@ export const en = {
"appMarketplace": "Application Marketplace",
"allApplications": "Your Apps",
"allModules": "Your Modules",
"allFolders": "All Folders",
"allFolders": "Your Folders",
"yourFolders" : "Your Folders",
"modules": "Modules",
"module": "Module",
Expand Down Expand Up @@ -3109,6 +3109,9 @@ export const en = {
"chooseNavType": "Please choose navigation type",
"createNavigation": "Create Navigation",
"howToUseAPI": "How to use the Open Rest API",
"support": "Support",
"supportTitle": "Lowcoder Support",
"supportContent": "If you have any questions or need help, please use the ticket system get your issue solved fast.",
},
"carousel": {
"dotPosition": "Navigation Dots position",
Expand Down
167 changes: 49 additions & 118 deletions client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
MODULE_APPLICATIONS_URL,
QUERY_LIBRARY_URL,
SETTING,
SUPPORT,
TRASH_URL,
// ADMIN_APP_URL,
NEWS_URL,
Expand All @@ -26,6 +27,7 @@
HomeModuleIcon,
HomeQueryLibraryIcon,
HomeSettingIcon,
SupportIcon,
PlusIcon,
PointIcon,
RecyclerIcon,
Expand All @@ -34,7 +36,7 @@
EnterpriseIcon,
UserIcon,
} from "lowcoder-design";
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, useMemo } from "react";
import { fetchAllApplications, fetchHomeData } from "redux/reduxActions/applicationActions";
import { getHomeOrg, normalAppListSelector } from "redux/selectors/applicationSelector";
import { DatasourceHome } from "../datasource";
Expand Down Expand Up @@ -64,6 +66,7 @@
import { TypographyText } from "../../components/TypographyText";
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
import { isEE } from "util/envUtils";
import { checkSubscriptions, SubscriptionProducts } from "@lowcoder-ee/api/subscriptionApi";

// adding App Editor, so we can show Apps inside the Admin Area
import AppEditor from "../editor/AppEditor";
Expand Down Expand Up @@ -149,24 +152,20 @@
);
};

const MoreFoldersWrapper = styled.div<{ $selected: boolean }>`
const MoreFoldersWrapper = styled.div`
${(props) => {
if (props.$selected) {
return css`
font-weight: 500;
`;
}
return css` font-weight: 500;`;
}}
`;

const MoreFoldersIcon = styled(PointIcon)<{ $selected: boolean }>`
/* const MoreFoldersIcon = styled(PointIcon)<{ $selected: boolean }>`
cursor: pointer;
flex-shrink: 0;

g {
fill: ${(props) => (props.$selected ? "#4965f2" : "#8b8fa3")};
}
`;
`; */

const PopoverIcon = styled(PointIcon)`
cursor: pointer;
Expand All @@ -188,26 +187,6 @@
}
`;

const InviteUser = styled.div`
position: absolute;
display: flex;
align-items: center;
left: 10px;
top: calc(100vh - 60px);
padding: 12px 26px;
font-size: 14px;
cursor: pointer;
width: 219px;

&:hover {
color: #315efb;

svg g g {
stroke: #315efb;
}
}
`;

const CreateFolderIcon = styled.div`
margin-left: auto;
cursor: pointer;
Expand Down Expand Up @@ -259,15 +238,34 @@
const orgHomeId = "root";
const isSelfHost = window.location.host !== 'app.lowcoder.cloud';

const handleFolderCreate = useCreateFolder();
const subscriptions = useSelector(state => state.subscriptions.subscriptions);
const subscriptionDataLoaded = useSelector(state => !state.subscriptions.loading);
const subscriptionDataError = useSelector(state => state.subscriptions.error);

// const handleFolderCreate = useCreateFolder();

const isOrgAdmin = org?.createdBy == user.id ? true : false;

// Fetch subscriptions once on component mount
useEffect(() => {
dispatch(checkSubscriptions());
}, [dispatch]);

// Calculate support subscription status
const supportSubscription = useMemo(() => {
return subscriptions.some((sub: { product: SubscriptionProducts; }) => sub.product === SubscriptionProducts.SUPPORT);
}, [subscriptions]);

// Early return if data is still loading or there was an error
if (fetchingUser || !subscriptionDataLoaded || subscriptionDataError) {
return <ProductLoading />;
}

useEffect(() => {

Check failure on line 264 in client/packages/lowcoder/src/pages/ApplicationV2/index.tsx

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?
dispatch(fetchHomeData({}));
}, [user.currentOrgId]);

useEffect(() => {

Check failure on line 268 in client/packages/lowcoder/src/pages/ApplicationV2/index.tsx

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?
if (!org) {
return;
}
Expand All @@ -280,7 +278,7 @@
setIsPreloadCompleted(true);
}, [org, orgHomeId]);

useEffect(() => {

Check failure on line 281 in client/packages/lowcoder/src/pages/ApplicationV2/index.tsx

View workflow job for this annotation

GitHub Actions / Build (20.x)

React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?
if (allAppCount !== 0) {
return;
}
Expand All @@ -298,52 +296,8 @@
return <ProductLoading />;
}

let folderItems: SideBarItemType[] = allFolders
.sort((a, b) => {
if (a.createAt === b.createAt) {
return 0;
}
return a.createAt < b.createAt ? 1 : -1;
})
.slice(0, 5)
.map((folder) => {
const path = FOLDER_URL_PREFIX + `/${folder.folderId}`;
return {
onSelected: (_, currentPath) => currentPath === path,
text: (props: { selected?: boolean }) => (
<FolderNameWrapper $selected={Boolean(props.selected)}>
<FolderName name={folder.name} id={folder.folderId} />
</FolderNameWrapper>
),
routePath: FOLDER_URL,
routePathExact: false,
routeComp: FolderView,
icon: FolderIcon,
size: "small",
onClick: (currentPath) => currentPath !== path && history.push(path),
};
});

if (allFolders.length > 1) {
folderItems = [
...folderItems,
{
text: (props: { selected?: boolean }) => (
<MoreFoldersWrapper $selected={Boolean(props.selected)}>{trans("home.allFolders")}</MoreFoldersWrapper>
),
routePath: FOLDERS_URL,
routeComp: RootFolderListView,
icon: MoreFoldersIcon,
size: "small",
},
];
}

if (folderItems.length > 0) {
folderItems[folderItems.length - 1] = {
...folderItems[folderItems.length - 1],
style: { marginBottom: "4px" },
};
if (fetchingUser || !isPreloadCompleted || !subscriptionDataLoaded || subscriptionDataError) {
return <ProductLoading />;
}

return (
Expand Down Expand Up @@ -386,41 +340,21 @@

{
items: [
{
text: <MoreFoldersWrapper>{trans("home.allFolders")}</MoreFoldersWrapper>,
routePath: FOLDERS_URL,
routeComp: RootFolderListView,
icon: ({ selected, ...otherProps }) => selected ? <FolderIcon {...otherProps} width={"24px"}/> : <FolderIcon {...otherProps} width={"24px"}/>,
},
{
text: <TabLabel>{trans("home.allApplications")}</TabLabel>,
routePath: ALL_APPLICATIONS_URL,
routeComp: HomeView,
icon: ({ selected, ...otherProps }) => selected ? <AppsIcon {...otherProps} width={"24px"}/> : <AppsIcon {...otherProps} width={"24px"}/>,
},
{
text: <TabLabel>{trans("home.allModules")}</TabLabel>,
routePath: MODULE_APPLICATIONS_URL,
routeComp: ModuleView,
icon: ({ selected, ...otherProps }) => selected ? <HomeModuleIcon {...otherProps} width={"24px"}/> : <HomeModuleIcon {...otherProps} width={"24px"}/>,
visible: ({ user }) => isOrgAdmin,
},

],
},

allFolders.length > 0
? {
title: (
<FolderSectionLabel>
{trans("home.yourFolders")}
<FolderCountLabel>{`(${allFolders.length})`}</FolderCountLabel>
{user.orgDev && (
<CreateFolderIcon onClick={handleFolderCreate}>
<PlusIcon />
</CreateFolderIcon>
)}
</FolderSectionLabel>
),
items: folderItems,
style: { marginTop: "8px" },
}
: { items: [] },


{
items: [

Expand Down Expand Up @@ -455,6 +389,17 @@
],
} : { items: [] },

supportSubscription ? {
items: [
{
text: <TabLabel>{trans("home.trash")}</TabLabel>,
routePath: TRASH_URL,
routeComp: TrashView,
icon: ({ selected, ...otherProps }) => selected ? <RecyclerIcon {...otherProps} width={"24px"}/> : <RecyclerIcon {...otherProps} width={"24px"}/>,
},
],
} : { items: [] },

{
items: [
{
Expand All @@ -465,7 +410,7 @@
icon: ({ selected, ...otherProps }) => selected ? <HomeSettingIcon {...otherProps} width={"24px"}/> : <HomeSettingIcon {...otherProps} width={"24px"}/>,
visible: ({ user }) => user.orgDev,
onSelected: (_, currentPath) => currentPath.split("/")[1] === "setting",
},
}
]
},

Expand All @@ -483,20 +428,6 @@

]}
/>

{/*
// we have the invite dialog in the Admin Area, so we don't need it here
{user.orgDev && (
<InviteDialog
trigger={
<InviteUser>
<InviteUserIcon style={{ marginRight: "8px" }} />
{trans("home.inviteUser")}
</InviteUser>
}
style={{ marginLeft: "auto" }}
/>
)} */}
</DivStyled>
);
}
Loading
Loading