Skip to content

Commit 07136fc

Browse files
author
FalkWolsky
committed
Support Tickets Table
1 parent 41affd4 commit 07136fc

File tree

8 files changed

+118
-59
lines changed

8 files changed

+118
-59
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,32 +87,32 @@ export const searchCustomerTickets = async (orgID : string, currentUserId : stri
8787

8888
const apiBody = {
8989
path: "webhook/support/get-issues",
90-
data: {host : domain, orgId : orgID, userId : currentUserId, supportsubscriptionId : "1PostVDDlQgecLSfhG52o5rB"},
90+
data: {"host" : domain, "orgId" : orgID, "userId" : currentUserId, "supportsubscriptionId" : "1PostVDDlQgecLSfhG52o5rB"},
9191
method: "post",
9292
headers: lcHeaders
9393
};
9494
try {
9595
const result = await SupportApi.secureRequest(apiBody);
9696
return result.data as TicketList;
9797
} catch (error) {
98-
console.error("Error searching customer:", error);
98+
console.error("Error searching Support Tickets: ", error);
9999
throw error;
100100
}
101101
};
102102

103-
export const getTicket = async (orgID : string, currentUserId : string, domain : string) => {
103+
export const getTicket = async (ticketKey : string) => {
104104

105105
const apiBody = {
106-
path: "webhook/support/get-issues",
107-
data: {host : domain, orgId : orgID, userId : currentUserId, supportsubscriptionId : "1PostVDDlQgecLSfhG52o5rB"},
106+
path: "webhook/support/get-issue",
107+
data: {"ticketKey" : ticketKey},
108108
method: "post",
109109
headers: lcHeaders
110110
};
111111
try {
112112
const result = await SupportApi.secureRequest(apiBody);
113-
return result?.data?.data?.length === 1 ? result.data.data as TicketList : null;
113+
return result?.data?.data?.length === 1 ? result.data.data as any : null;
114114
} catch (error) {
115-
console.error("Error searching customer:", error);
115+
console.error("Error getting individual Support Ticket: ", error);
116116
throw error;
117117
}
118118
};

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const NEWS_URL = "/news";
1010
export const ORG_HOME_URL = "/org/home";
1111
export const COMPONENT_DOC_URL = "/components";
1212
export const SETTING_URL = "/setting";
13-
export const SUPPORT_URL = `/support`;
13+
export const SUPPORT_URL = "/support";
1414
export const PERMISSION_SETTING = "/setting/permission";
1515
export const ORGANIZATION_SETTING = "/setting/organization";
1616
export const SUBSCRIPTION_SETTING = "/setting/subscription";
@@ -28,6 +28,7 @@ export const SUBSCRIPTION_SUCCESS = `${SUBSCRIPTION_SETTING}/success`;
2828
export const SUBSCRIPTION_ERROR = `${SUBSCRIPTION_SETTING}/error`;
2929
export const SUBSCRIPTION_DETAIL = `${SUBSCRIPTION_SETTING}/details/:subscriptionId/:productId`;
3030
export const SUBSCRIPTION_INFO = `${SUBSCRIPTION_SETTING}/info/:productId`;
31+
3132
export const SUPPORT_DETAIL = `${SUPPORT_URL}/details/:ticketId`;
3233

3334
export const ALL_APPLICATIONS_URL = "/apps";
@@ -109,3 +110,5 @@ export const buildOrgId = (orgId: string) => `${ORGANIZATION_SETTING}/${orgId}`;
109110

110111
export const buildSubscriptionSettingsLink = (subscriptionId: string, productId : string) => `${SUBSCRIPTION_SETTING}/details/${subscriptionId}/${productId}`;
111112
export const buildSubscriptionInfoLink = (productId: string) => `${SUBSCRIPTION_SETTING}/info/${productId}`;
113+
114+
export const buildSupportTicketLink = (ticketId: string) => `${SUPPORT_URL}/details/${ticketId}`;

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3135,10 +3135,23 @@ export const en = {
31353135
"createNavigation": "Create Navigation",
31363136
"howToUseAPI": "How to use the Open Rest API",
31373137
"support": "Support",
3138+
},
3139+
"support" : {
31383140
"supportTitle": "Lowcoder Support",
31393141
"supportContent": "If you have any questions or need help, please use the ticket system get your issue solved fast.",
31403142
"newSupportTicket": "New Support Ticket",
3143+
"ticketTitle": "Title",
3144+
"priority": "Priority",
3145+
"assignee": "Assignee",
3146+
"status": "Ticket Status",
3147+
"updatedTime": "Updated Time",
3148+
"active": "Active",
3149+
"inactive": "Inactive",
3150+
"noEmail": "No Email",
3151+
"details": "Details",
3152+
"reloadTickets" : "Reload Tickets",
31413153
},
3154+
31423155
"carousel": {
31433156
"dotPosition": "Navigation Dots position",
31443157
"autoPlay": "AutoPlay",

client/packages/lowcoder/src/pages/datasource/datasourceList.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export const DatasourceList = () => {
145145
</HeaderWrapper>
146146
<BodyWrapper>
147147
<StyledTable
148+
loading={!datasource.length}
148149
rowClassName={(record: any) => (!record.edit ? "datasource-can-not-edit" : "")}
149150
tableLayout={"auto"}
150151
scroll={{ x: "100%" }}

client/packages/lowcoder/src/pages/setting/subscriptions/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// index.tsx for routes
2-
import { Route, Switch, useLocation } from 'react-router-dom';
2+
import { Route, Switch } from 'react-router-dom';
33
import { SUBSCRIPTION_SETTING, SUBSCRIPTION_DETAIL, SUBSCRIPTION_INFO, SUBSCRIPTION_SUCCESS, SUBSCRIPTION_ERROR } from 'constants/routesURL';
44
import { SubscriptionSetting } from './subscriptionSetting';
55
import SubscriptionSuccess from './subscriptionSuccess';

client/packages/lowcoder/src/pages/support/supportDetail.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { trans } from "i18n";
44
import { useParams } from "react-router-dom";
55
import history from "util/history";
66
import { getTicket } from '@lowcoder-ee/api/supportApi';
7-
import { useUserDetails } from "./useUserDetails";
87

98
const FieldWrapper = styled.div`
109
margin-bottom: 32px;
@@ -17,13 +16,11 @@ const Wrapper = styled.div`
1716
`;
1817

1918
export function SupportDetail() {
20-
const { ticketId } = useParams<{ ticketId: string }>();
21-
22-
const { orgID, currentUser, domain } = useUserDetails();
2319

24-
const ticket = getTicket(orgID, currentUser.id, domain);
20+
const { ticketId } = useParams<{ ticketId: string }>();
21+
const ticket = getTicket(ticketId);
2522

26-
console.log("product", ticket);
23+
console.log("ticket", ticket);
2724

2825
return (
2926
<Wrapper>
@@ -35,4 +32,4 @@ export function SupportDetail() {
3532
);
3633
}
3734

38-
export default SupportDetail;
35+
export default SupportDetail;

client/packages/lowcoder/src/pages/support/supportOverview.tsx

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import StepModal from "components/StepModal";
88
import { Search, TacoButton } from "lowcoder-design";
99
import { Table } from "../../components/Table";
1010
import { timestampToHumanReadable } from "../../util/dateTimeUtils";
11+
import { Avatar, Flex, Tooltip } from "antd";
12+
import { buildSupportTicketLink } from "constants/routesURL";
13+
import history from "util/history";
1114

1215
const SupportWrapper = styled.div`
1316
display: flex;
@@ -45,6 +48,16 @@ const AddBtn = styled(TacoButton)`
4548
height: 32px;
4649
`;
4750

51+
const ReloadBtn = styled(TacoButton)`
52+
min-width: 96px;
53+
width: fit-content;
54+
height: 32px;
55+
`;
56+
57+
const EditBtn = styled(TacoButton)`
58+
59+
`;
60+
4861
const BodyWrapper = styled.div`
4962
width: 100%;
5063
flex-grow: 1;
@@ -61,6 +74,15 @@ const SubColumnCell = styled.div`
6174
color: #8b8fa3;
6275
`;
6376

77+
const StatusDot = styled.span<{ active: boolean }>`
78+
display: inline-block;
79+
margin-left: 14px;
80+
width: 8px;
81+
height: 8px;
82+
border-radius: 50%;
83+
background-color: ${(props) => (props.active ? "green" : "gray")};
84+
`;
85+
6486
function formatDateToMinute(dateString: string): string {
6587
// Create a Date object from the string
6688
const date = new Date(dateString);
@@ -76,6 +98,11 @@ function formatDateToMinute(dateString: string): string {
7698
return `${year}-${month}-${day} ${hour}:${minute}`;
7799
}
78100

101+
// Function to handle edit button click
102+
const handleEditClick = (ticketId: string) => {
103+
history.push(buildSupportTicketLink(ticketId));
104+
};
105+
79106
export function SupportOverview() {
80107
const { orgID, currentUser, domain } = useUserDetails();
81108
const [supportTickets, setSupportTickets] = useState<any>([]);
@@ -84,19 +111,21 @@ export function SupportOverview() {
84111
const [searchValue, setSearchValue] = useState("");
85112
const [isCreateFormShow, showCreateForm] = useState(false);
86113

87-
useEffect(() => {
88-
const fetchSupportTickets = async () => {
89-
try {
90-
const ticketData = await searchCustomerTickets(orgID, currentUser.id, domain);
91-
setSupportTickets(ticketData);
92-
} catch (err) {
93-
setError("Failed to fetch support tickets.");
94-
console.error(err);
95-
} finally {
96-
setLoading(false);
97-
}
98-
};
114+
// Function to fetch support tickets
115+
const fetchSupportTickets = async () => {
116+
setLoading(true); // Set loading to true while fetching data
117+
try {
118+
const ticketData = await searchCustomerTickets(orgID, currentUser.id, domain);
119+
setSupportTickets(ticketData);
120+
} catch (err) {
121+
setError("Failed to fetch support tickets.");
122+
console.error(err);
123+
} finally {
124+
setLoading(false); // Set loading to false after fetching data
125+
}
126+
};
99127

128+
useEffect(() => {
100129
fetchSupportTickets();
101130
}, [orgID, currentUser.id, domain]);
102131

@@ -112,7 +141,7 @@ export function SupportOverview() {
112141

113142
return (
114143
<>
115-
<Helmet><title>{trans("home.supportTitle")}</title></Helmet>
144+
<Helmet><title>{trans("support.supportTitle")}</title></Helmet>
116145
<SupportWrapper>
117146

118147
<StepModal
@@ -135,56 +164,70 @@ export function SupportOverview() {
135164
]} />
136165

137166
<HeaderWrapper>
138-
<Title>{trans("home.supportTickets")}</Title>
139-
<Search
140-
placeholder={trans("search")}
141-
value={searchValue}
142-
onChange={(e) => setSearchValue(e.target.value)}
143-
style={{ width: "192px", height: "32px", margin: "0 12px 0 0" }} />
144-
<AddBtn buttonType={"primary"} onClick={() => showCreateForm(true)}>
145-
{trans("home.newSupportTicket")}
146-
</AddBtn>
167+
<Title>{trans("support.supportTitle")}</Title>
168+
<Flex gap="12px">
169+
<Search
170+
placeholder={trans("search")}
171+
value={searchValue}
172+
onChange={(e) => setSearchValue(e.target.value)}
173+
style={{ width: "192px", height: "32px", margin: "0 12px 0 0" }} />
174+
<AddBtn buttonType={"primary"} onClick={() => showCreateForm(true)}>
175+
{trans("support.newSupportTicket")}
176+
</AddBtn>
177+
<ReloadBtn buttonType={"normal"} onClick={() => fetchSupportTickets()}>
178+
{trans("support.reloadTickets")}
179+
</ReloadBtn>
180+
</Flex>
147181
</HeaderWrapper>
148182
<BodyWrapper>
149-
{!loading ? (
150183
<StyledTable
184+
loading={loading}
151185
rowClassName="datasource-can-not-edit"
152186
tableLayout={"auto"}
153187
scroll={{ x: "100%" }}
154188
pagination={false}
155189
columns={[
156190
{
157-
title: trans("home.ticketTitle"),
191+
title: trans("support.ticketTitle"),
158192
dataIndex: "title",
159193
ellipsis: true,
160194
sorter: (a: any, b: any) => a.title.localeCompare(b.title),
161195
},
162196
{
163-
title: trans("home.priority"),
197+
title: trans("support.priority"),
164198
dataIndex: "priority",
165199
ellipsis: true,
166200
width: "192px",
167201
sorter: (a: any, b: any) => a.priority.name.localeCompare(b.priority.name),
168202
render: (priority: any) => <SubColumnCell>{priority.name}</SubColumnCell>,
169203
},
170204
{
171-
title: trans("home.assignee"),
205+
title: trans("support.assignee"),
172206
dataIndex: "assignee",
173207
ellipsis: true,
174208
width: "192px",
175-
sorter: (a: any, b: any) => a.assignee.email.localeCompare(b.assignee.email),
176-
render: (assignee: any) => <SubColumnCell>{assignee.email}</SubColumnCell>,
209+
render: (assignee: any) => (
210+
<SubColumnCell>
211+
<Tooltip title={"Support Member is active in: " +
212+
assignee.timeZone + ", " +
213+
(assignee.email || trans("support.noEmail"))
214+
}>
215+
<Avatar src={assignee.avatar} alt={assignee.email} />
216+
</Tooltip>
217+
<StatusDot active={assignee.active} />
218+
</SubColumnCell>
219+
),
177220
},
178221
{
179-
title: trans("home.status"),
222+
title: trans("support.status"),
180223
dataIndex: "status",
181224
ellipsis: true,
182-
width: "192px",
225+
width: "220px",
183226
sorter: (a: any, b: any) => a.status.name.localeCompare(b.status.name),
184227
render: (status: any) => <SubColumnCell>{status.name}</SubColumnCell>,
185228
},
186229
{
187-
title: trans("home.updatedTime"),
230+
title: trans("support.updatedTime"),
188231
dataIndex: "updated",
189232
ellipsis: true,
190233
width: "192px",
@@ -195,6 +238,19 @@ export function SupportOverview() {
195238
</SubColumnCell>
196239
),
197240
},
241+
{
242+
title: trans("support.details"),
243+
dataIndex: "actions",
244+
width: "120px",
245+
render: (key: string) => (
246+
<EditBtn
247+
buttonType={"normal"}
248+
onClick={() => handleEditClick(key)}
249+
>
250+
{trans("support.details")}
251+
</EditBtn>
252+
),
253+
},
198254
]}
199255
dataSource={filteredTickets.map((ticket: any, index: number) => ({
200256
key: index,
@@ -203,11 +259,9 @@ export function SupportOverview() {
203259
assignee: ticket.assignee,
204260
status: ticket.status,
205261
updated: ticket.updated,
262+
actions: ticket.key,
206263
}))}
207264
/>
208-
) : (
209-
<div>Loading...</div>
210-
)}
211265
{error && <div>Error: {error}</div>}
212266
</BodyWrapper>
213267
</SupportWrapper>

client/packages/lowcoder/src/util/envUtils.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { SystemConfig } from "constants/configConstants";
22
import { useSelector } from "react-redux";
33
import { selectSystemConfig } from "redux/selectors/configSelectors";
4-
// import { useCheckSubscriptions, SubscriptionProducts, SubscriptionsData } from "@lowcoder-ee/api/subscriptionApi";
54

65
export function localEnv(): boolean {
76
return REACT_APP_ENV === "local";
@@ -39,11 +38,3 @@ export function useCloudHosting() {
3938
const systemConfig = useSelector(selectSystemConfig);
4039
return systemConfig?.cloudHosting ?? true;
4140
}
42-
43-
/* export function useSubscription(type: SubscriptionProducts): boolean {
44-
const { subscriptions, subscriptionDataLoaded, subscriptionDataError } = useCheckSubscriptions();
45-
if (!subscriptionDataLoaded || subscriptionDataError) {
46-
return false;
47-
}
48-
return subscriptions.some(subscription => subscription.product === type);
49-
} */

0 commit comments

Comments
 (0)