Skip to content

Commit 3d063e2

Browse files
Merge pull request #1779 from kamalqureshi/update_app_card_ui
Update Card UI on Homepage
2 parents 077842b + 84ee3e5 commit 3d063e2

File tree

6 files changed

+267
-95
lines changed

6 files changed

+267
-95
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export interface ApplicationMeta {
8181
title?: string;
8282
description?: string;
8383
image?: string;
84+
icon?: string;
8485
category?: ApplicationCategoriesEnum;
8586
showheader?: boolean;
8687
orgId: string;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3931,6 +3931,10 @@ export const en = {
39313931
"datasource": "Data Sources",
39323932
"selectDatasourceType": "Select Data Source Type",
39333933
"home": "Home",
3934+
"desc": "Description",
3935+
"renameApp": "Rename app",
3936+
"updateAppName": "Update Application Name",
3937+
"titleUpdateWarning": "The card displays the app title. Changing the app name will not update the card view.",
39343938
"all": "All",
39353939
"app": "App",
39363940
"navigation": "Navigation",

client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ export function HomeLayout(props: HomeLayoutProps) {
469469
title: e.title,
470470
description: e.description,
471471
category: e.category,
472-
icon: e.image,
472+
icon: e.icon,
473473
type: HomeResTypeEnum[HomeResTypeEnum[e.applicationType] as HomeResKey],
474474
creator: e?.creatorEmail ?? e.createBy,
475475
lastModifyTime: e.lastModifyTime,
@@ -630,7 +630,7 @@ export function HomeLayout(props: HomeLayoutProps) {
630630

631631
<Divider />
632632

633-
<ContentWrapper>
633+
<ContentWrapper>
634634

635635
{isFetching && resList.length === 0 ? (
636636
<SkeletonStyle active paragraph={{ rows: 8, width: 648 }} title={false} />

client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx

Lines changed: 202 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { TacoButton } from "lowcoder-design/src/components/button"
2-
import { ReactNode, useState } from "react";
1+
import { TacoButton, CustomModal, Alert } from "lowcoder-design"
2+
import { useState, useEffect } from "react";
33
import { useDispatch } from "react-redux";
44
import { updateAppMetaAction } from "redux/reduxActions/applicationActions";
55
import styled from "styled-components";
@@ -25,6 +25,11 @@ import { useParams } from "react-router-dom";
2525
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
2626
import {FolderIcon} from "icons";
2727
import { BrandedIcon } from "@lowcoder-ee/components/BrandedIcon";
28+
import { Typography } from "antd";
29+
import { default as Form } from "antd/es/form";
30+
import { default as Input } from "antd/es/input";
31+
import { MultiIconDisplay } from "@lowcoder-ee/comps/comps/multiIconDisplay";
32+
import { FormStyled } from "../setting/idSource/styledComponents";
2833

2934
const ExecButton = styled(TacoButton)`
3035
width: 52px;
@@ -50,14 +55,16 @@ const ExecButton = styled(TacoButton)`
5055
`;
5156

5257
const Wrapper = styled.div`
53-
height: 67px;
5458
padding: 0 6px;
5559
border-radius: 8px;
56-
margin-bottom: -1px;
57-
margin-top: 1px;
58-
60+
margin-bottom: 2px;
61+
margin-top: 2px;
62+
padding-top: 10px;
63+
padding-bottom: 10px;
64+
background-color: #fcfcfc;
65+
min-height: 100px;
5966
&:hover {
60-
background-color: #f5f7fa;
67+
background-color: #f5f5f6
6168
}
6269
`;
6370

@@ -98,7 +105,6 @@ const CardInfo = styled.div`
98105
height: 100%;
99106
flex-grow: 1;
100107
cursor: pointer;
101-
overflow: hidden;
102108
padding-right: 12px;
103109
104110
&:hover {
@@ -124,6 +130,7 @@ const AppTimeOwnerInfoLabel = styled.div`
124130
const OperationWrapper = styled.div`
125131
display: flex;
126132
align-items: center;
133+
padding-right: 10px;
127134
@media screen and (max-width: 500px) {
128135
> svg {
129136
display: none;
@@ -133,9 +140,75 @@ const OperationWrapper = styled.div`
133140

134141
const MONTH_MILLIS = 30 * 24 * 60 * 60 * 1000;
135142

143+
interface UpdateAppModalProps {
144+
visible: boolean;
145+
onCancel: () => void;
146+
onOk: (values: any) => void;
147+
res: HomeRes;
148+
folderId?: string;
149+
}
150+
151+
export function UpdateAppModal({ visible, onCancel, onOk, res, folderId }: UpdateAppModalProps) {
152+
const [detailsForm] = Form.useForm();
153+
154+
// Reset form values when res changes
155+
useEffect(() => {
156+
if (res && visible) {
157+
detailsForm.setFieldsValue({
158+
appName: res.name,
159+
title: res.title
160+
});
161+
}
162+
}, [res, visible, detailsForm]);
163+
164+
return (
165+
<CustomModal
166+
title={trans("home.updateAppName")}
167+
open={visible}
168+
destroyOnHidden
169+
onCancel={onCancel}
170+
showCancelButton={false}
171+
showOkButton
172+
width="440px"
173+
okText={trans("finish")}
174+
onOk={() => {
175+
detailsForm.validateFields().then((values) => {
176+
onOk(values);
177+
}).catch((errorInfo) => {
178+
console.error('Validation failed:', errorInfo);
179+
});
180+
}}
181+
>
182+
<FormStyled
183+
form={detailsForm}
184+
name="general"
185+
layout="vertical"
186+
style={{ maxWidth: '100%' }}
187+
autoComplete="off"
188+
>
189+
{res.title &&
190+
<Alert label={trans("home.titleUpdateWarning")} type="warning" />}
191+
<br/>
192+
193+
<Form.Item label={trans("home.name")} name="appName">
194+
<Input/>
195+
</Form.Item>
196+
197+
{res.title && (
198+
<Form.Item label={trans("title")} name="title">
199+
<Input disabled />
200+
</Form.Item>
201+
)}
202+
203+
</FormStyled>
204+
</CustomModal>
205+
);
206+
}
207+
136208
export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => void; setModify:any; modify: boolean }) {
137209
const { res, onMove, setModify, modify } = props;
138210
const [appNameEditing, setAppNameEditing] = useState(false);
211+
const [dialogVisible, setDialogVisible] = useState(false)
139212
const dispatch = useDispatch();
140213

141214
const { folderId } = useParams<{ folderId: string }>();
@@ -161,96 +234,137 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi
161234
else if (res.type === HomeResTypeEnum.NavLayout || res.type === HomeResTypeEnum.MobileTabLayout) {
162235
iconColor = "#af41ff";
163236
}
164-
165237
const Icon = resInfo.icon;
166238

239+
const handleModalOk = (values: any) => {
240+
dispatch(
241+
updateAppMetaAction({ applicationId: res.id, name: values.appName || res.name, folderId: folderId })
242+
);
243+
244+
setDialogVisible(false);
245+
setTimeout(() => {
246+
setModify(!modify);
247+
}, 200);
248+
};
249+
167250
return (
168-
<Wrapper>
169-
<Card>
170-
{Icon && (
171-
<BrandedIcon>
172-
<Icon width={"42px"} height={"42px"} style={
173-
{
174-
color: iconColor,
175-
marginRight: "10px",
176-
flexShrink: 0
177-
}
178-
} />
179-
</BrandedIcon>
180-
)}
181-
<CardInfo
182-
onClick={(e) => {
183-
if (appNameEditing) {
184-
return;
185-
}
186-
if (res.type === HomeResTypeEnum.Folder) {
187-
handleFolderViewClick(res.id);
188-
} else {
189-
if (checkIsMobile(window.innerWidth)) {
190-
history.push(APPLICATION_VIEW_URL(res.id, "view"));
191-
return;
192-
}
193-
if(res.isMarketplace) {
194-
handleMarketplaceAppViewClick(res.id);
195-
return;
196-
}
197-
res.isEditable ? handleAppEditClick(e, res.id) : handleAppViewClick(res.id);
198-
}
199-
}}
200-
>
201-
<TypographyText
202-
value={res.name}
203-
editing={appNameEditing}
204-
onChange={(value) => {
205-
if (!value.trim()) {
206-
messageInstance.warning(trans("home.nameCheckMessage"));
251+
<>
252+
<UpdateAppModal
253+
visible={dialogVisible}
254+
onCancel={() => setDialogVisible(false)}
255+
onOk={handleModalOk}
256+
res={res}
257+
folderId={folderId}
258+
/>
259+
260+
<Wrapper>
261+
<Card>
262+
{res.icon ?
263+
<MultiIconDisplay
264+
identifier={res.icon && typeof res.icon === 'string' ? res.icon : '/icon:antd/appstoreoutlined'}
265+
width="30px"
266+
height="30px"
267+
style={{
268+
marginRight: "6px",
269+
flexShrink: 0,
270+
color: "#b766db"
271+
}}
272+
/> :
273+
Icon && (
274+
<BrandedIcon>
275+
<Icon width={"42px"} height={"42px"} style={
276+
{
277+
color: iconColor,
278+
marginRight: "10px",
279+
flexShrink: 0
280+
}
281+
} />
282+
</BrandedIcon>
283+
)
284+
}
285+
<CardInfo
286+
onClick={(e) => {
287+
if (appNameEditing) {
207288
return;
208289
}
209290
if (res.type === HomeResTypeEnum.Folder) {
210-
dispatch(updateFolder({ id: res.id, name: value }));
211-
setTimeout(() => {
212-
setModify(!modify);
213-
}, 200);
291+
handleFolderViewClick(res.id);
214292
} else {
215-
dispatch(
216-
updateAppMetaAction({ applicationId: res.id, name: value, folderId: folderId })
217-
);
218-
setTimeout(() => {
219-
setModify(!modify);
220-
}, 200);
293+
if (checkIsMobile(window.innerWidth)) {
294+
history.push(APPLICATION_VIEW_URL(res.id, "view"));
295+
return;
296+
}
297+
if(res.isMarketplace) {
298+
handleMarketplaceAppViewClick(res.id);
299+
return;
300+
}
301+
res.isEditable ? handleAppEditClick(e, res.id) : handleAppViewClick(res.id);
221302
}
222-
setAppNameEditing(false);
223303
}}
224-
/>
225-
<AppTimeOwnerInfoLabel title={subTitle}>{subTitle}</AppTimeOwnerInfoLabel>
226-
</CardInfo>
227-
<OperationWrapper>
228-
{/* {res.isEditable && (
229-
<EditButton onClick={(e) => handleAppEditClick(e, res.id)} buttonType="primary">
230-
{trans("edit")}
231-
</EditButton>
232-
)} */}
233-
<ExecButton
234-
onClick={() =>
235-
res.type === HomeResTypeEnum.Folder
236-
? handleFolderViewClick(res.id)
237-
: res.isMarketplace
238-
? handleMarketplaceAppViewClick(res.id)
239-
: handleAppViewClick(res.id)
240-
}
241304
>
242-
{trans("view")}
243-
</ExecButton>
244-
<HomeResOptions
245-
res={res}
246-
onRename={() => setAppNameEditing(true)}
247-
onMove={(res) => onMove(res)}
248-
setModify={setModify}
249-
modify={modify}
250-
/>
251-
</OperationWrapper>
252-
</Card>
253-
</Wrapper>
305+
<TypographyText
306+
value={res.title || res.name}
307+
editing={false}
308+
onChange={(value) => {
309+
if (!value.trim()) {
310+
messageInstance.warning(trans("home.nameCheckMessage"));
311+
return;
312+
}
313+
if (res.type === HomeResTypeEnum.Folder) {
314+
dispatch(updateFolder({ id: res.id, name: value }));
315+
setTimeout(() => {
316+
setModify(!modify);
317+
}, 200);
318+
} else {
319+
dispatch(
320+
updateAppMetaAction({ applicationId: res.id, name: value, folderId: folderId })
321+
);
322+
setTimeout(() => {
323+
setModify(!modify);
324+
}, 200);
325+
}
326+
setAppNameEditing(false);
327+
}}
328+
/>
329+
330+
{res?.description
331+
&& <Typography.Text
332+
type="secondary"
333+
style={{ fontSize: 12, textWrap: "wrap"}}
334+
>
335+
{res.description.length > 150 ? res.description.substring(0, 150) + '...' : res.description}
336+
</Typography.Text>}
337+
338+
<AppTimeOwnerInfoLabel title={subTitle}>{subTitle}</AppTimeOwnerInfoLabel>
339+
</CardInfo>
340+
<OperationWrapper>
341+
{/* {res.isEditable && (
342+
<EditButton onClick={(e) => handleAppEditClick(e, res.id)} buttonType="primary">
343+
{trans("edit")}
344+
</EditButton>
345+
)} */}
346+
<ExecButton
347+
onClick={() =>
348+
res.type === HomeResTypeEnum.Folder
349+
? handleFolderViewClick(res.id)
350+
: res.isMarketplace
351+
? handleMarketplaceAppViewClick(res.id)
352+
: handleAppViewClick(res.id)
353+
}
354+
>
355+
{trans("view")}
356+
</ExecButton>
357+
<HomeResOptions
358+
res={res}
359+
onRename={() => setDialogVisible(true)}
360+
onMove={(res) => onMove(res)}
361+
setModify={setModify}
362+
modify={modify}
363+
/>
364+
</OperationWrapper>
365+
</Card>
366+
</Wrapper>
367+
</>
254368
);
255369
}
256370

client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const HomeResOptions = (props: {
5353
if (res.isEditable) {
5454
options = [
5555
...options,
56-
{ text: trans("rename"), onClick: () => onRename(res) },
56+
{ text: trans("home.renameApp"), onClick: () => onRename(res) },
5757
{
5858
text: trans("header.duplicate", { type: HomeResInfo[res.type].name.toLowerCase() }),
5959
onClick: () => {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy