Skip to content

Commit e288bf2

Browse files
authored
Merge branch 'master' into patch-1
2 parents b418429 + 1e26ba3 commit e288bf2

File tree

8 files changed

+160
-48
lines changed

8 files changed

+160
-48
lines changed

src/custom/Modal/index.tsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React, { useRef, useState } from 'react';
33
import { Box, Dialog, IconButton, Paper, Typography } from '../../base';
44
import { ContainedButton, OutlinedButton, TextButton } from '../../base/Button/Button';
55
import { iconLarge, iconMedium } from '../../constants/iconsSizes';
6-
import { CloseIcon, InfoCircleIcon } from '../../icons';
6+
import { CloseIcon, FullScreenIcon, InfoCircleIcon } from '../../icons';
7+
import FullScreenExitIcon from '../../icons/Fullscreen/FullScreenExitIcon';
78
import { darkModalGradient, lightModalGradient } from '../../theme/colors/colors';
89
import { CustomTooltip } from '../CustomTooltip';
910

@@ -12,6 +13,7 @@ interface ModalProps extends DialogProps {
1213
title: string;
1314
headerIcon?: React.ReactNode;
1415
reactNode?: React.ReactNode;
16+
isFullScreenModeAllowed?: boolean;
1517
}
1618

1719
interface ModalFooterProps {
@@ -56,6 +58,20 @@ const StyledDialog = styled(Dialog)`
5658
}
5759
`;
5860

61+
const FullscreenButton = styled(FullScreenIcon)(({ theme }) => ({
62+
height: '2.25rem',
63+
width: '2.25rem',
64+
fill: theme.palette.common.white,
65+
cursor: 'pointer'
66+
}));
67+
68+
const FullscreenExitButton = styled(FullScreenExitIcon)(({ theme }) => ({
69+
height: '2.25rem',
70+
width: '2.25rem',
71+
fill: theme.palette.common.white,
72+
cursor: 'pointer'
73+
}));
74+
5975
export const ModalStyledHeader = styled('div')(({ theme }) => ({
6076
background: theme.palette.mode === 'light' ? lightModalGradient.header : darkModalGradient.header,
6177
color: '#eee',
@@ -103,7 +119,8 @@ export const useModal = ({ headerIcon }: { headerIcon: React.ReactNode }): UseMo
103119
export const ModalBody = styled(Paper)(({ theme }) => ({
104120
padding: '1rem',
105121
backgroundColor: theme.palette.background.surfaces,
106-
overflowY: 'auto'
122+
overflowY: 'auto',
123+
height: '100%'
107124
}));
108125

109126
const StyledFooter = styled('div', {
@@ -135,16 +152,22 @@ export const Modal: React.FC<ModalProps> = ({
135152
reactNode,
136153
children,
137154
maxWidth = 'xs',
155+
isFullScreenModeAllowed,
138156
...props
139157
}) => {
158+
const [fullScreen, setFullScreen] = useState(false);
159+
const toggleFullScreen = () => {
160+
setFullScreen((prev) => !prev);
161+
};
140162
return (
141163
<StyledDialog
142-
fullWidth={true}
143164
maxWidth={maxWidth}
144165
open={open}
145166
onClose={closeModal}
146167
aria-labelledby="alert-dialog-slide-title"
147168
aria-describedby="alert-dialog-slide-description"
169+
fullScreen={fullScreen}
170+
fullWidth={!fullScreen}
148171
{...props}
149172
>
150173
{title && (
@@ -153,9 +176,20 @@ export const Modal: React.FC<ModalProps> = ({
153176
<Typography component={'div'} variant="h6">
154177
{title}
155178
</Typography>
156-
<CloseBtn onClick={closeModal}>
157-
<CloseIcon {...iconLarge} fill="#fff"></CloseIcon>
158-
</CloseBtn>
179+
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
180+
{isFullScreenModeAllowed && (
181+
<CustomTooltip title={fullScreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}>
182+
{fullScreen ? (
183+
<FullscreenExitButton onClick={toggleFullScreen} />
184+
) : (
185+
<FullscreenButton onClick={toggleFullScreen} />
186+
)}
187+
</CustomTooltip>
188+
)}
189+
<CloseBtn onClick={closeModal}>
190+
<CloseIcon {...iconLarge} fill="#fff"></CloseIcon>
191+
</CloseBtn>
192+
</div>
159193
</ModalStyledHeader>
160194
)}
161195

src/custom/ShareModal/ShareModal.tsx

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,11 @@ interface SelectedResource {
151151
[key: string]: unknown;
152152
}
153153

154+
type SelectedResources = SelectedResource | SelectedResource[];
155+
154156
export type ResourceAccessArg = {
155157
resourceType: string;
156-
resourceId: string;
158+
resourceId: string | string[];
157159
resourceAccessMappingPayload: {
158160
grant_access: string[];
159161
revoke_access: string[];
@@ -167,8 +169,8 @@ import { VISIBILITY } from '../../constants/constants';
167169
interface ShareModalProps {
168170
/** Function to close the share modal */
169171
handleShareModalClose: () => void;
170-
/** The resource that is selected for sharing.*/
171-
selectedResource: SelectedResource;
172+
/** The resource(s) that is selected for sharing.*/
173+
selectedResource: SelectedResources;
172174
/** The name of the data being shared, like design or filter */
173175
dataName: string;
174176
/** Data of the user who owns the resource */
@@ -212,26 +214,29 @@ const ShareModal: React.FC<ShareModalProps> = ({
212214
useGetAllUsersQuery,
213215
shareableLink
214216
}: ShareModalProps): JSX.Element => {
217+
console.log('amit selectdResource', selectedResource);
215218
const theme = useTheme();
216219
const [openMenu, setMenu] = useState<boolean>(false);
217220
const [shareUserData, setShareUserData] = useState<User[]>([]);
218-
const [resourceVisibility, setVisibility] = useState(selectedResource.visibility);
221+
const [resourceVisibility, setVisibility] = useState(
222+
Array.isArray(selectedResource) ? selectedResource[0].visibility : selectedResource.visibility
223+
);
224+
console.log('amit resourceVisibility', resourceVisibility);
219225
const [isUpdatingVisibility, setUpdatingVisibility] = useState(false);
220226

221-
const userCanUpdateVisibility = canUpdateResourceVisibility(
222-
selectedResource,
223-
currentUser,
224-
ownerData
225-
);
226-
const userCanShareWithNewUsers = canShareResourceWithNewUsers(
227-
selectedResource,
228-
currentUser,
229-
ownerData
230-
);
227+
const userCanUpdateVisibility = Array.isArray(selectedResource)
228+
? selectedResource.every((resource) =>
229+
canUpdateResourceVisibility(resource, currentUser, ownerData)
230+
)
231+
: canUpdateResourceVisibility(selectedResource, currentUser, ownerData);
232+
233+
const userCanShareWithNewUsers = Array.isArray(selectedResource)
234+
? selectedResource.every((resource) =>
235+
canShareResourceWithNewUsers(resource, currentUser, ownerData)
236+
)
237+
: canShareResourceWithNewUsers(selectedResource, currentUser, ownerData);
231238

232239
const handleCopy = () => {
233-
// const shareableLink = getShareableResourceRoute(dataName,selectedResource.id,selectedResource.name)
234-
console.log('shareableLink', shareableLink);
235240
navigator.clipboard.writeText(shareableLink);
236241
notify({
237242
message: 'Link copied to clipboard',
@@ -242,16 +247,49 @@ const ShareModal: React.FC<ShareModalProps> = ({
242247
const resourceType = dataName === 'design' ? 'pattern' : dataName;
243248

244249
const handleShareWithNewUsers = async (newUsers: User[]) => {
245-
console.log('new users', newUsers);
246250
const grantAccessList = newUsers.map((user) => ({
247251
actor_id: user.user_id,
248252
actor_type: 'user'
249253
}));
250254
const emails = newUsers.map((u) => u.email);
251255

256+
if (Array.isArray(selectedResource)) {
257+
const responses = await Promise.all(
258+
selectedResource.map((resource) =>
259+
resourceAccessMutator({
260+
resourceType,
261+
resourceId: resource.id,
262+
resourceAccessMappingPayload: {
263+
grant_access: [...grantAccessList],
264+
revoke_access: [],
265+
notify_users: true
266+
}
267+
})
268+
)
269+
);
270+
271+
const hasError = responses.some((response) => response?.error);
272+
273+
if (!hasError) {
274+
notify({
275+
message: `${dataName}s shared with ${emails.join(', ')}`,
276+
event_type: 'success'
277+
});
278+
} else {
279+
notify({
280+
message: `An error occurred. Some ${dataName}s may not have been shared`,
281+
event_type: 'error'
282+
});
283+
}
284+
285+
return {
286+
error: hasError ? 'Some resources failed to share' : ''
287+
};
288+
}
289+
252290
const response = await resourceAccessMutator({
253291
resourceType,
254-
resourceId: selectedResource?.id,
292+
resourceId: !Array.isArray(selectedResource) ? selectedResource.id : selectedResource[0].id,
255293
resourceAccessMappingPayload: {
256294
grant_access: [...grantAccessList],
257295
revoke_access: [],
@@ -260,9 +298,8 @@ const ShareModal: React.FC<ShareModalProps> = ({
260298
});
261299

262300
if (!response?.error) {
263-
const msg = `${dataName} shared with ${emails.join(', ')} `;
264301
notify({
265-
message: msg,
302+
message: `${dataName} shared with ${emails.join(', ')}`,
266303
event_type: 'success'
267304
});
268305
}
@@ -288,7 +325,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
288325

289326
const response = await resourceAccessMutator({
290327
resourceType,
291-
resourceId: selectedResource?.id,
328+
resourceId: !Array.isArray(selectedResource) ? selectedResource.id : selectedResource[0].id,
292329
resourceAccessMappingPayload: {
293330
grant_access: [],
294331
revoke_access: [...revokeAccessList],
@@ -327,7 +364,9 @@ const ShareModal: React.FC<ShareModalProps> = ({
327364

328365
/* eslint-disable @typescript-eslint/no-explicit-any */
329366
const notifyVisibilityChange = (res: any, value: any) => {
330-
const UPDATE_VISIBILITY_MSG = `${startCase(dataName)} '${selectedResource.name}' is now ${value}`;
367+
const UPDATE_VISIBILITY_MSG = Array.isArray(selectedResource)
368+
? `${startCase(dataName)}s (${selectedResource.length}) are now ${value}`
369+
: `${startCase(dataName)} '${selectedResource.name}' is now ${value}`;
331370
const FAILED_TO_UPDATE_VISIBILITY_MSG = `Failed to update visibility. ${res?.error?.error || ''}`;
332371

333372
if (!res.error) {
@@ -378,7 +417,11 @@ const ShareModal: React.FC<ShareModalProps> = ({
378417
<Modal
379418
open={true}
380419
closeModal={handleShareModalClose}
381-
title={`Share ${dataName} "${selectedResource?.name}"`}
420+
title={
421+
Array.isArray(selectedResource)
422+
? `Share ${selectedResource.length} ${dataName}s`
423+
: `Share ${dataName} "${selectedResource?.name}"`
424+
}
382425
>
383426
<ModalBody>
384427
<UserShareSearch
@@ -395,7 +438,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
395438
hostURL={hostURL}
396439
/>
397440

398-
{resourceVisibility !== 'published' && (
441+
{!Array.isArray(selectedResource) && resourceVisibility !== 'published' && (
399442
<>
400443
<CustomListItemText>
401444
<Typography variant="h6">General Access</Typography>

src/custom/UniversalFilter.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface UniversalFilterProps {
2929
}
3030

3131
export const FilterHeader = styled('div')(({ theme }) => ({
32-
background: theme.palette.mode === 'light' ? lightModalGradient.fotter : darkModalGradient.fotter,
32+
background: theme.palette.mode === 'light' ? lightModalGradient.header : darkModalGradient.header,
3333
padding: '0.75rem 1rem',
3434
margin: '-1rem -1rem 1rem -1rem',
3535
display: 'flex',
@@ -136,7 +136,11 @@ function UniversalFilter({
136136
anchorEl={anchorEl}
137137
placement="bottom-end"
138138
>
139-
<ClickAwayListener onClickAway={handleClose}>
139+
<ClickAwayListener
140+
onClickAway={handleClose}
141+
mouseEvent="onMouseDown"
142+
touchEvent="onTouchStart"
143+
>
140144
<Paper
141145
sx={{
142146
padding: '1rem',

src/icons/Export/ExportIcon.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { FC } from 'react';
2+
import { DEFAULT_HEIGHT, DEFAULT_WIDTH } from '../../constants/constants';
3+
import { IconProps } from '../types';
4+
5+
export const DownloadIcon: FC<IconProps> = ({
6+
width = DEFAULT_WIDTH,
7+
height = DEFAULT_HEIGHT,
8+
fill = '#455a64',
9+
style = {}
10+
}) => (
11+
<svg
12+
xmlns="http://www.w3.org/2000/svg"
13+
height={height}
14+
viewBox="0 0 24 24"
15+
width={width}
16+
fill={fill}
17+
style={style}
18+
>
19+
<path d="M19 9h-4V3H9v6H5l7 7zM5 18v2h14v-2z"></path>
20+
</svg>
21+
);
22+
23+
export default DownloadIcon;

src/icons/Export/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as ExportIcon } from './ExportIcon';

src/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export * from './Drag';
5858
export * from './Edit';
5959
export * from './EmptyStyle';
6060
export * from './Environment';
61+
export * from './Export';
6162
export * from './ExternalLink';
6263
export * from './Feedback';
6364
export * from './File';

src/theme/palette.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,8 @@ export const darkModePalette: PaletteOptions = {
422422
},
423423

424424
icon: {
425-
default: Colors.accentGrey[90],
426-
secondary: Colors.charcoal[80],
425+
default: Colors.charcoal[80],
426+
secondary: Colors.charcoal[70],
427427
brand: Colors.keppel[40],
428428
inverse: Colors.charcoal[10],
429429
weather: Colors.saffron[40],

src/utils/permissions.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { VISIBILITY } from "../constants/constants";
1+
import { VISIBILITY } from '../constants/constants';
22

33
export interface User {
44
id: string;
@@ -8,22 +8,28 @@ export interface User {
88
email: string;
99
avatar_url?: string;
1010
deleted_at?: { Valid: boolean };
11-
role_names?:string[]
11+
role_names?: string[];
1212
}
1313

14-
15-
export const canUpdateResource= (selectedResource:{visibility:string}, currentUser:User,resourceOwner:User) => {
16-
const isOwner = resourceOwner.user_id == currentUser.user_id
17-
const isAdmin = currentUser.role_names?.includes("admin")
18-
return isOwner || isAdmin
14+
export const canUpdateResource = (
15+
selectedResource: { visibility: string },
16+
currentUser: User,
17+
resourceOwner: User
18+
) => {
19+
const isOwner = resourceOwner.user_id == currentUser.user_id;
20+
const isAdmin = currentUser.role_names?.includes('admin');
21+
return isOwner || isAdmin;
1922
};
2023

21-
export const canUpdateResourceVisibility= canUpdateResource
22-
24+
export const canUpdateResourceVisibility = canUpdateResource;
2325

24-
export const canShareResourceWithNewUsers= (selectedResource:{visibility:string}, currentUser:User,resourceOwner:User) => {
25-
if (selectedResource.visibility == VISIBILITY.PUBLIC){
26-
return true
27-
}
28-
return canUpdateResource(selectedResource,currentUser,resourceOwner)
26+
export const canShareResourceWithNewUsers = (
27+
selectedResource: { visibility: string },
28+
currentUser: User,
29+
resourceOwner: User
30+
) => {
31+
if (selectedResource.visibility == VISIBILITY.PUBLIC) {
32+
return true;
33+
}
34+
return canUpdateResource(selectedResource, currentUser, resourceOwner);
2935
};

0 commit comments

Comments
 (0)