Skip to content

Commit eb85e4c

Browse files
authored
Merge pull request #933 from FaheemOnHub/fix/faheemonhub/update-meshery-cloud-design
feat: add similar design by type
2 parents 2e91d50 + aa248b4 commit eb85e4c

File tree

10 files changed

+192
-63
lines changed

10 files changed

+192
-63
lines changed

src/custom/CatalogDetail/Carousel.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { ChevronLeft, ChevronRight } from '@mui/icons-material';
2+
import React, { ReactNode, useRef } from 'react';
3+
4+
interface CarouselProps {
5+
items: ReactNode[];
6+
title?: string;
7+
scrollAmount?: number;
8+
showNavButtons?: boolean;
9+
itemClassName?: string;
10+
}
11+
12+
import { CarouselButton, CarouselContainer, CarouselWrapper } from './style';
13+
14+
const Carousel: React.FC<CarouselProps> = ({
15+
items,
16+
scrollAmount = 300,
17+
showNavButtons = true,
18+
itemClassName = 'carousel-item'
19+
}) => {
20+
const carouselRef = useRef<HTMLDivElement>(null);
21+
22+
// Skip rendering if no items
23+
if (!items.length) return null;
24+
25+
const scroll = (direction: 'left' | 'right') => {
26+
if (carouselRef.current) {
27+
carouselRef.current.scrollBy({
28+
left: direction === 'left' ? -scrollAmount : scrollAmount,
29+
behavior: 'smooth'
30+
});
31+
}
32+
};
33+
34+
return (
35+
<CarouselWrapper>
36+
{showNavButtons && (
37+
<CarouselButton onClick={() => scroll('left')}>
38+
<ChevronLeft />
39+
</CarouselButton>
40+
)}
41+
<CarouselContainer ref={carouselRef}>
42+
{items.map((item, index) => (
43+
<div key={`carousel-item-${index}`} className={itemClassName}>
44+
{item}
45+
</div>
46+
))}
47+
</CarouselContainer>
48+
{showNavButtons && (
49+
<CarouselButton onClick={() => scroll('right')}>
50+
<ChevronRight />
51+
</CarouselButton>
52+
)}
53+
</CarouselWrapper>
54+
);
55+
};
56+
57+
export default Carousel;

src/custom/CatalogDetail/RelatedDesigns.tsx

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,83 @@
11
import { CatalogCardDesignLogo } from '../CustomCatalog';
22
import CustomCatalogCard, { Pattern } from '../CustomCatalog/CustomCard';
3+
import Carousel from './Carousel';
34
import { getHeadingText } from './helper';
4-
import { AdditionalContainer, ContentHeading, DesignCardContainer } from './style';
5+
import { AdditionalContainer, ContentHeading } from './style';
56
import { UserProfile } from './types';
6-
77
export interface PatternsPerUser {
88
patterns: Pattern[];
99
}
10-
10+
export interface DetailsByType {
11+
patterns: Pattern[];
12+
}
1113
interface RelatedDesignsProps {
1214
details: Pattern;
1315
type: string;
1416
patternsPerUser: PatternsPerUser;
17+
detailsByType?: DetailsByType;
1518
onSuggestedPatternClick: (pattern: Pattern) => void;
1619
userProfile?: UserProfile;
1720
technologySVGPath: string;
1821
technologySVGSubpath: string;
1922
orgName: string;
2023
fetchingOrgError: boolean;
24+
filterByType?: boolean;
2125
}
2226

2327
const RelatedDesigns: React.FC<RelatedDesignsProps> = ({
2428
details,
2529
type,
2630
patternsPerUser,
31+
detailsByType,
2732
onSuggestedPatternClick,
2833
userProfile,
2934
technologySVGPath,
3035
technologySVGSubpath,
3136
orgName,
32-
fetchingOrgError
37+
fetchingOrgError,
38+
filterByType
3339
}) => {
34-
const filteredPatternsPerUser = patternsPerUser?.patterns?.filter(
35-
(pattern) => pattern.id !== details.id
36-
);
40+
const filteredPatterns = filterByType
41+
? detailsByType?.patterns?.filter((pattern) => pattern.id !== details.id) || []
42+
: patternsPerUser?.patterns?.filter((pattern) => pattern.id !== details.id) || [];
43+
44+
if (!filteredPatterns.length) return null;
45+
const organizationName = filterByType
46+
? 'Similar Designs by Type'
47+
: fetchingOrgError || !orgName
48+
? 'Unknown Organization'
49+
: orgName;
3750

38-
if (!filteredPatternsPerUser?.length) return null;
39-
const organizationName = fetchingOrgError || !orgName ? 'Unknown Organization' : orgName;
51+
const carouselItems = filteredPatterns.map((pattern) => (
52+
<CustomCatalogCard
53+
pattern={pattern}
54+
patternType={type}
55+
onCardClick={() => onSuggestedPatternClick(pattern)}
56+
UserName={`${userProfile?.first_name ?? ''} ${userProfile?.last_name ?? ''}`}
57+
avatarUrl={userProfile?.avatar_url}
58+
basePath={technologySVGPath}
59+
subBasePath={technologySVGSubpath}
60+
cardTechnologies={true}
61+
>
62+
<CatalogCardDesignLogo
63+
imgURL={pattern?.catalog_data?.imageURL}
64+
height={'5.5rem'}
65+
width={'100%'}
66+
zoomEffect={false}
67+
type={{ type: type }}
68+
/>
69+
</CustomCatalogCard>
70+
));
4071
return (
4172
<AdditionalContainer>
4273
<ContentHeading>
4374
<h2 style={{ margin: '0', textTransform: 'uppercase' }}>
44-
{getHeadingText({ type, userProfile, organizationName, fetchingOrgError })}
75+
{filterByType
76+
? organizationName
77+
: getHeadingText({ type, userProfile, organizationName, fetchingOrgError })}
4578
</h2>
4679
</ContentHeading>
47-
<DesignCardContainer>
48-
{filteredPatternsPerUser.map((pattern, index) => (
49-
<CustomCatalogCard
50-
key={`design-${index}`}
51-
pattern={pattern}
52-
patternType={type}
53-
onCardClick={() => onSuggestedPatternClick(pattern)}
54-
UserName={`${userProfile?.first_name ?? ''} ${userProfile?.last_name ?? ''}`}
55-
avatarUrl={userProfile?.avatar_url}
56-
basePath={technologySVGPath}
57-
subBasePath={technologySVGSubpath}
58-
cardTechnologies={true}
59-
>
60-
<CatalogCardDesignLogo
61-
imgURL={pattern?.catalog_data?.imageURL}
62-
height={'7.5rem'}
63-
width={'100%'}
64-
zoomEffect={false}
65-
type={{ type: type }}
66-
/>
67-
</CustomCatalogCard>
68-
))}
69-
</DesignCardContainer>
80+
<Carousel items={carouselItems} scrollAmount={300} />
7081
</AdditionalContainer>
7182
);
7283
};

src/custom/CatalogDetail/RightPanel.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Pattern } from '../CustomCatalog/CustomCard';
33
import { VIEW_VISIBILITY } from '../VisibilityChipMenu/VisibilityChipMenu';
44
import CaveatsSection from './CaveatsSection';
55
import OverviewSection from './OverviewSection';
6-
import RelatedDesigns, { PatternsPerUser } from './RelatedDesigns';
6+
import RelatedDesigns, { DetailsByType, PatternsPerUser } from './RelatedDesigns';
77
import { Class } from './types';
88

99
interface RightPanelProps {
1010
details: Pattern;
11+
detailsByType: DetailsByType;
1112
type: string;
1213
cardId?: string;
1314
title: string;
@@ -35,6 +36,7 @@ interface RightPanelProps {
3536

3637
const RightPanel: React.FC<RightPanelProps> = ({
3738
details,
39+
detailsByType,
3840
type,
3941
cardId = details.id,
4042
title,
@@ -64,7 +66,7 @@ const RightPanel: React.FC<RightPanelProps> = ({
6466
});
6567

6668
return (
67-
<div style={{ fontFamily }}>
69+
<div>
6870
<OverviewSection
6971
details={details}
7072
type={cleanedType}
@@ -86,6 +88,7 @@ const RightPanel: React.FC<RightPanelProps> = ({
8688
{showCaveats && <CaveatsSection details={details} />}
8789
<RelatedDesigns
8890
details={details}
91+
detailsByType={detailsByType}
8992
orgName={orgName}
9093
fetchingOrgError={fetchingOrgError}
9194
type={type}
@@ -94,6 +97,20 @@ const RightPanel: React.FC<RightPanelProps> = ({
9497
userProfile={userProfile}
9598
technologySVGPath={technologySVGPath}
9699
technologySVGSubpath={technologySVGSubpath}
100+
filterByType={false}
101+
/>
102+
<RelatedDesigns
103+
details={details}
104+
detailsByType={detailsByType}
105+
orgName={orgName}
106+
fetchingOrgError={fetchingOrgError}
107+
type={type}
108+
patternsPerUser={patternsPerUser}
109+
onSuggestedPatternClick={onSuggestedPatternClick}
110+
userProfile={userProfile}
111+
technologySVGPath={technologySVGPath}
112+
technologySVGSubpath={technologySVGSubpath}
113+
filterByType={true}
97114
/>
98115
</div>
99116
);

src/custom/CatalogDetail/style.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,51 @@ export const ContentHeading = styled('div')(() => ({
6868
width: '100%',
6969
marginBottom: '1rem'
7070
}));
71+
export const CarouselContainer = styled('div')({
72+
display: 'flex',
73+
overflowX: 'auto',
74+
scrollBehavior: 'smooth',
75+
scrollSnapType: 'x mandatory',
76+
gap: '1rem',
77+
padding: '1rem 0',
78+
width: '100%',
79+
'&::-webkit-scrollbar': {
80+
display: 'none' // Hide scrollbar for a cleaner look
81+
},
82+
'.carousel-item': {
83+
flex: '0 0 auto',
84+
scrollSnapAlign: 'center',
85+
width: 'auto' // Adjust based on your card size
86+
}
87+
});
88+
export const CarouselButton = styled('button')(({ theme }) => ({
89+
position: 'absolute',
90+
top: '50%',
91+
transform: 'translateY(-50%)',
92+
zIndex: '1',
93+
background: theme.palette.background.paper,
94+
border: 'none',
95+
cursor: 'pointer',
96+
padding: '0.5rem',
97+
borderRadius: '50%',
98+
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
99+
'&:hover': {
100+
background: theme.palette.primary.main,
101+
color: '#fff'
102+
},
103+
'&:first-of-type': {
104+
left: '-1.2rem'
105+
},
106+
'&:last-of-type': {
107+
right: '-1.2rem'
108+
}
109+
}));
110+
export const CarouselWrapper = styled('div')({
111+
display: 'flex',
112+
alignItems: 'center',
113+
width: '100%',
114+
position: 'relative'
115+
});
71116

72117
export const CaveatsContainer = styled('div')(({ theme }) => ({
73118
width: '100%',

src/custom/CustomCatalog/CustomCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const ClassWrap = ({ catalogClassName }: { catalogClassName: string }) => {
114114
const CustomCatalogCard: React.FC<CatalogCardProps> = ({
115115
pattern,
116116
patternType,
117-
cardHeight = '18rem',
117+
cardHeight = '16.5rem',
118118
cardWidth = '15rem',
119119
cardStyles,
120120
shouldFlip = true,
@@ -280,7 +280,7 @@ const CustomCatalogCard: React.FC<CatalogCardProps> = ({
280280
</DesignDetailsDiv>
281281

282282
{isDetailed && (
283-
<DesignDetailsDiv style={{ marginTop: '40px' }}>
283+
<DesignDetailsDiv style={{ marginTop: '20px' }}>
284284
<Grid container style={{ justifyContent: 'space-between', alignItems: 'center' }}>
285285
<Grid
286286
item

src/custom/CustomCatalog/Helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const handleImage = async ({
6161
export const DEFAULT_DESIGN_VERSION = '0.0.0';
6262

6363
export const getVersion = (design: Pattern) => {
64-
if (design.visibility === 'published') {
64+
if (design.visibility === 'public') {
6565
return design?.catalog_data?.published_version || DEFAULT_DESIGN_VERSION;
6666
}
6767
try {

src/custom/CustomCatalog/style.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const DesignCard = styled('div')<DesignCardProps>(
9393
}),
9494
...(isDetailed && {
9595
[theme.breakpoints.down('lg')]: {
96-
height: '18.75rem'
96+
height: '16.75rem'
9797
}
9898
}),
9999
...outerStyles
@@ -150,7 +150,7 @@ export const DesignName = styled(Typography)(({ theme }) => ({
150150
textOverflow: 'ellipsis',
151151
textAlign: 'center',
152152
width: '100%',
153-
margin: '3rem 0 1.59rem 0',
153+
margin: '2rem 0 1.59rem 0',
154154
fontFamily: 'inherit'
155155
}));
156156

src/custom/PerformersSection/PerformersToogleButton.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ const PerformersSectionButton: React.FC<PerformersSectionButtonProps> = ({ open,
2121
onClick={handleClick}
2222
sx={{
2323
height: '3.7rem',
24-
padding: '0rem'
24+
padding: '0.3rem'
2525
}}
2626
style={{
2727
backgroundColor: open ? undefined : theme.palette.background.constant?.disabled
2828
}}
2929
>
30-
<TropyIcon style={{ height: '2rem', width: '2rem' }} />
30+
<TropyIcon style={{ height: '2rem', width: '2rem', marginRight: '10px' }} />
31+
{open ? 'Hide Performers' : 'Show Performers'}
3132
</Button>
3233
</span>
3334
</CustomTooltip>

0 commit comments

Comments
 (0)