Skip to content

Commit 5c1b2dd

Browse files
committed
add fav icon and fix formatting
Signed-off-by: Atif Ali <atali@redhat.com> more Details formating Signed-off-by: Atif Ali <atali@redhat.com>
1 parent 3829735 commit 5c1b2dd

File tree

9 files changed

+185
-2
lines changed

9 files changed

+185
-2
lines changed

src/gitops/components/appset/AppsTab.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
flex-direction: column;
55
}
66
}
7+
8+

src/gitops/components/appset/EventsTab.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
gap: 12px;
66
}
77
}
8+
9+
10+
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import * as React from 'react';
2+
import { Button, Tooltip, Form, FormGroup, TextInput, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core';
3+
import { Modal, ModalVariant } from '@patternfly/react-core/deprecated';
4+
import { StarIcon } from '@patternfly/react-icons';
5+
6+
type FavoriteButtonProps = {
7+
defaultName?: string;
8+
};
9+
10+
const FavoriteButton: React.FC<FavoriteButtonProps> = ({ defaultName }) => {
11+
const [isStarred, setIsStarred] = React.useState(false);
12+
const [isModalOpen, setIsModalOpen] = React.useState(false);
13+
const [name, setName] = React.useState<string>('');
14+
const [error, setError] = React.useState<string | null>(null);
15+
16+
// Check if this page is already favorited on mount
17+
React.useEffect(() => {
18+
const currentUrlPath = window.location.pathname;
19+
const favorites = JSON.parse(localStorage.getItem('console-favorites') || '[]');
20+
const isCurrentlyFavorited = favorites.some((favorite: any) => favorite.url === currentUrlPath);
21+
setIsStarred(isCurrentlyFavorited);
22+
}, []);
23+
24+
const handleStarClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
25+
e.preventDefault();
26+
e.stopPropagation();
27+
28+
const currentUrlPath = window.location.pathname;
29+
const favorites = JSON.parse(localStorage.getItem('console-favorites') || '[]');
30+
const isCurrentlyFavorited = favorites.some((favorite: any) => favorite.url === currentUrlPath);
31+
32+
if (isCurrentlyFavorited) {
33+
// Remove from favorites
34+
const updatedFavorites = favorites.filter((favorite: any) => favorite.url !== currentUrlPath);
35+
localStorage.setItem('console-favorites', JSON.stringify(updatedFavorites));
36+
setIsStarred(false);
37+
} else {
38+
// Open modal to add to favorites
39+
const currentUrlSplit = currentUrlPath.includes('~')
40+
? currentUrlPath.split('~')
41+
: currentUrlPath.split('/');
42+
const sanitizedDefaultName = (
43+
defaultName ?? currentUrlSplit.slice(-1)[0].split('?')[0]
44+
).replace(/[^\p{L}\p{N}\s-]/gu, '-');
45+
setName(sanitizedDefaultName);
46+
setIsModalOpen(true);
47+
}
48+
};
49+
50+
const handleModalClose = () => {
51+
setError('');
52+
setName('');
53+
setIsModalOpen(false);
54+
};
55+
56+
const handleNameChange = (value: string) => {
57+
setName(value);
58+
setError('');
59+
};
60+
61+
const handleConfirmStar = () => {
62+
const trimmedName = name.trim();
63+
64+
if (!trimmedName) {
65+
setError('Name is required');
66+
return;
67+
}
68+
69+
if (trimmedName.length > 50) {
70+
setError('Name must be 50 characters or less');
71+
return;
72+
}
73+
74+
// Check for duplicate names
75+
const favorites = JSON.parse(localStorage.getItem('console-favorites') || '[]');
76+
const isDuplicate = favorites.some((favorite: any) => favorite.name === trimmedName);
77+
78+
if (isDuplicate) {
79+
setError('A favorite with this name already exists');
80+
return;
81+
}
82+
83+
// Add to favorites
84+
const currentUrlPath = window.location.pathname;
85+
const newFavorite = {
86+
url: currentUrlPath,
87+
name: trimmedName,
88+
};
89+
const updatedFavorites = [...favorites, newFavorite];
90+
localStorage.setItem('console-favorites', JSON.stringify(updatedFavorites));
91+
setIsStarred(true);
92+
handleModalClose();
93+
};
94+
95+
const tooltipText = isStarred ? 'Remove from favorites' : 'Add to favorites';
96+
97+
return (
98+
<>
99+
<div className="co-fav-actions-icon">
100+
<Tooltip content={tooltipText} position="top">
101+
<Button
102+
icon={<StarIcon color={isStarred ? 'gold' : 'gray'} />}
103+
className="co-xl-icon-button"
104+
data-test="favorite-button"
105+
variant="plain"
106+
aria-label={tooltipText}
107+
aria-pressed={isStarred}
108+
onClick={handleStarClick}
109+
/>
110+
</Tooltip>
111+
</div>
112+
113+
{isModalOpen && (
114+
<Modal
115+
title="Add to favorites"
116+
isOpen={isModalOpen}
117+
onClose={handleModalClose}
118+
variant={ModalVariant.small}
119+
actions={[
120+
<Button
121+
key="confirm"
122+
variant="primary"
123+
onClick={handleConfirmStar}
124+
form="confirm-favorite-form"
125+
>
126+
Save
127+
</Button>,
128+
<Button key="cancel" variant="link" onClick={handleModalClose}>
129+
Cancel
130+
</Button>,
131+
]}
132+
>
133+
<Form id="confirm-favorite-form" onSubmit={handleConfirmStar}>
134+
<FormGroup label="Name" isRequired fieldId="input-name">
135+
<TextInput
136+
id="confirm-favorite-form-name"
137+
data-test="input-name"
138+
name="name"
139+
type="text"
140+
onChange={(e, v) => handleNameChange(v)}
141+
value={name || ''}
142+
autoFocus
143+
required
144+
/>
145+
{error && (
146+
<FormHelperText>
147+
<HelperText>
148+
<HelperTextItem variant="error">
149+
{error}
150+
</HelperTextItem>
151+
</HelperText>
152+
</FormHelperText>
153+
)}
154+
</FormGroup>
155+
</Form>
156+
</Modal>
157+
)}
158+
</>
159+
);
160+
};
161+
162+
export default FavoriteButton;

src/gitops/components/appset/Generators.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ const Generators: React.FC<GeneratorsProps> = ({ generators }) => {
4747
};
4848

4949
export default Generators;
50+
51+

src/gitops/components/appset/GeneratorsTab.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
gap: 16px;
66
}
77
}
8+
9+

src/gitops/components/appset/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,6 @@ Each component has its own SCSS file for styling:
8787
3. **Maintainability**: Easier to maintain and debug individual components
8888
4. **Consistency**: Follows the same pattern as gitops-admin-plugin
8989
5. **Scalability**: Easy to add new generator types or modify existing ones
90+
91+
92+

src/gitops/components/appset/generators/Generators.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,6 @@
5353
word-break: break-all;
5454
}
5555
}
56+
57+
58+

src/gitops/components/shared/ApplicationSetList.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,11 @@ const ApplicationSetList: React.FC<ApplicationSetProps> = ({
189189
return (
190190
<div>
191191
{showTitle == undefined && (
192-
<ListPageHeader title={t('plugin__gitops-plugin~ApplicationSets')} badge={<DevPreviewBadge />}>
192+
<ListPageHeader
193+
title={t('plugin__gitops-plugin~ApplicationSets')}
194+
badge={<DevPreviewBadge />}
195+
hideFavoriteButton={false}
196+
>
193197
<ListPageCreate groupVersionKind={modelToRef(ApplicationSetModel)}>
194198
Create ApplicationSet
195199
</ListPageCreate>

src/gitops/utils/components/DetailsPageTitle/ResourceDetailsTitle.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Action, K8sModel, K8sResourceCommon } from '@openshift-console/dynamic-
88
import { Breadcrumb, BreadcrumbItem, Spinner, Title } from '@patternfly/react-core';
99
import ActionsDropdown from '../../../utils/components/ActionDropDown/ActionDropDown';
1010
import DetailsPageTitle, { PaneHeading } from './DetailsPageTitle';
11+
import FavoriteButton from '../../../components/appset/FavoriteButton';
1112

1213
type ResourceDetailsTitleProps = {
1314
obj: K8sResourceCommon;
@@ -74,7 +75,8 @@ const ResourceDetailsTitle: React.FC<ResourceDetailsTitleProps> = ({
7475
</span>
7576
)}
7677
</Title>
77-
<div className="co-actions">
78+
<div className="co-actions" style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
79+
<FavoriteButton defaultName={name ?? obj?.metadata?.name} />
7880
<ActionsDropdown actions={actions} isKebabToggle={false} />
7981
</div>
8082
</PaneHeading>

0 commit comments

Comments
 (0)