Skip to content

Commit 6d1a877

Browse files
committed
add permissions
Signed-off-by: aabidsofi19 <[email protected]>
1 parent 6e4370c commit 6d1a877

File tree

5 files changed

+157
-113
lines changed

5 files changed

+157
-113
lines changed

src/constants/constants.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,27 @@ export const CARIBBEAN_GREEN_FILL = '#00D3A9';
77
export const DEFAULT_STROKE = '#000';
88
export const DEFAULT_STROKE_WIDTH = '2';
99
export const CLOUD_URL = 'https://cloud.layer5.io';
10-
export const PLAYGROUND_MODES = {
11-
DESIGNER: 'design',
12-
OPERATOR: 'operator'
10+
11+
export const KANVAS_MODE = {
12+
DESIGN: "design",
13+
OPERATOR: "operator"
1314
} as const;
15+
16+
17+
export const PLAYGROUND_MODES = KANVAS_MODE
18+
19+
export const VISIBILITY = {
20+
PUBLIC : "public",
21+
PRIVATE :"private"
22+
}
23+
24+
export const RESOURCE_TYPE = {
25+
FILTER: "filter",
26+
DESIGN: "design",
27+
CATALOG: "catalog",
28+
VIEW: "view"
29+
} as const;
30+
31+
32+
export type ResourceType = (typeof RESOURCE_TYPE)[keyof typeof RESOURCE_TYPE];
33+

src/custom/ShareModal/ShareModal.tsx

Lines changed: 25 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { ChainIcon, DeleteIcon, LockIcon, PublicIcon } from '../../icons';
1515
import { useTheme } from '../../theme';
1616
import { BLACK, WHITE } from '../../theme/colors';
1717
import { CustomTooltip } from '../CustomTooltip';
18-
import { Modal, ModalBody, ModalButtonPrimary, ModalButtonSecondary, ModalFooter } from '../Modal';
18+
import { Modal, ModalBody, ModalButtonSecondary, ModalFooter } from '../Modal';
1919
import UserShareSearch from '../UserSearchField/UserSearchField';
2020
import {
2121
CustomDialogContentText,
@@ -26,27 +26,14 @@ import {
2626
ListWrapper,
2727
VisibilityIconWrapper
2828
} from './style';
29-
import { TypedMutationTrigger, TypedUseQuery } from '@reduxjs/toolkit/dist/query/react';
3029

3130
const options = {
3231
PUBLIC: 'Anyone with the link can edit',
3332
PRIVATE: 'Only people with access can open with the link'
3433
};
3534

36-
const SHARE_MODE = {
37-
PRIVATE: 'private',
38-
PUBLIC: 'public'
39-
};
40-
41-
interface User {
42-
id: string;
43-
user_id: string;
44-
first_name: string;
45-
last_name: string;
46-
email: string;
47-
avatar_url?: string;
48-
deleted_at?: { Valid: boolean };
49-
}
35+
const SHARE_MODE = VISIBILITY
36+
import { canShareResourceWithNewUsers, canUpdateResourceVisibility, User } from '../../utils/permissions';
5037

5138
interface AccessListProps {
5239
accessList: User[];
@@ -170,8 +157,8 @@ type ResourceAccessArg = {
170157
};
171158

172159

173-
import type { MutationTrigger } from '@reduxjs/toolkit/query/react';
174160
import { startCase } from 'lodash';
161+
import { VISIBILITY } from '../../constants/constants';
175162

176163
interface ShareModalProps {
177164
/** Function to close the share modal */
@@ -186,28 +173,17 @@ interface ShareModalProps {
186173
fetchAccessActors: () => Promise<User[]>;
187174
/** Optional URL of the host application. Defaults to `null` if not provided */
188175
hostURL?: string | null;
189-
/**
190-
* Optional URL of the resource. Defaults to empty string if not provided
191-
* Resource URL will be the URL which user will copy with Copy Link Button
192-
*/
193-
resourceURL?: string;
194-
/** Optional flag to disable the visibility selector. Defaults to `false` if not provided */
195-
isVisibilitySelectorDisabled?: boolean;
196-
/**
197-
* Function to fetch user suggestions based on the input value.
198-
* @param {string} value - The input value for which suggestions are to be fetched.
199-
* @returns {Promise<User[]>} A promise that resolves to an array of user suggestions.
200-
*/
201-
fetchSuggestions: (value: string) => Promise<User[]>;
202-
handleCopy: () => void;
203176
handleUpdateVisibility: (value: string) => Promise<{ error: string }>,
204177
handleShareWithNewUsers: (newUsers: User[]) => Promise<{ error: string }>,
205178
canShareWithNewUsers: boolean,
206179
handleRevokeAccess: (revokedUsser: User[]) => Promise<{ error: string }>
207180
canRevokeAccess: boolean,
208-
resourceAccessMutator: MutationTrigger<ResourceAccessArg>,
181+
resourceAccessMutator: any,
209182
notify: ({ message, event_type }: { message: string, event_type: "success" | "error" }) => void,
210183
useGetAllUsersQuery: any,
184+
shareableLink:string,
185+
mesheryURL : string, // url to hosted meshery
186+
currentUser: User,
211187
}
212188

213189
/**
@@ -221,14 +197,12 @@ const ShareModal: React.FC<ShareModalProps> = ({
221197
ownerData,
222198
fetchAccessActors,
223199
hostURL = null,
224-
handleCopy,
200+
currentUser,
225201
handleUpdateVisibility,
226-
canShareWithNewUsers,
227-
isVisibilitySelectorDisabled = false,
228-
fetchSuggestions,
229202
resourceAccessMutator,
230203
notify,
231204
useGetAllUsersQuery,
205+
shareableLink
232206

233207
}: ShareModalProps): JSX.Element => {
234208
const theme = useTheme();
@@ -237,9 +211,21 @@ const ShareModal: React.FC<ShareModalProps> = ({
237211
const [resourceVisibility, setVisibility] = useState(selectedResource.visibility)
238212
const [isUpdatingVisibility, setUpdatingVisibility] = useState(false)
239213

214+
const userCanUpdateVisibility = canUpdateResourceVisibility(selectedResource,currentUser,ownerData)
215+
const userCanShareWithNewUsers = canShareResourceWithNewUsers(selectedResource,currentUser,ownerData)
240216

241217

242218

219+
const handleCopy = () => {
220+
// const shareableLink = getShareableResourceRoute(dataName,selectedResource.id,selectedResource.name)
221+
console.log("shareableLink",shareableLink)
222+
navigator.clipboard.writeText(shareableLink);
223+
notify({
224+
message: "Link copied to clipboard",
225+
event_type: "success"
226+
});
227+
};
228+
243229
const resourceType = dataName === "design" ? "pattern" : dataName;
244230

245231

@@ -397,22 +383,10 @@ const ShareModal: React.FC<ShareModalProps> = ({
397383
>
398384
<ModalBody>
399385
<UserShareSearch
400-
setUsersData={setShareUserData}
401386
usersData={shareUserData}
402-
label="Search Users"
403387
shareWithNewUsers={handleShareWithNewUsers}
404-
// isSharing={isSharing}
405-
disabled={canShareWithNewUsers}
406-
customUsersList={
407-
<AccessList
408-
accessList={shareUserData}
409-
ownerData={ownerData}
410-
handleDelete={handleDelete}
411-
hostURL={hostURL}
412-
/>
413-
}
388+
disabled={!userCanShareWithNewUsers}
414389
useGetAllUsersQuery={useGetAllUsersQuery}
415-
fetchSuggestions={fetchSuggestions}
416390
/>
417391

418392
<AccessList
@@ -463,7 +437,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
463437
onClose={handleMenuClose}
464438
onOpen={() => setMenu(true)}
465439
onChange={updateVisisbility}
466-
disabled={isVisibilitySelectorDisabled || isUpdatingVisibility}
440+
disabled={!userCanUpdateVisibility || isUpdatingVisibility}
467441
>
468442
{Object.values(SHARE_MODE).map((option) => (
469443
<MenuItem
@@ -513,15 +487,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
513487
</IconButtonWrapper>
514488
<Typography>Copy Link</Typography>
515489
</ModalButtonSecondary>
516-
{/* <ModalButtonPrimary
517-
disabled={isShareDisabled()}
518-
variant="contained"
519-
color="primary"
520-
onClick={() => handleShare(shareUserData, selectedOption)}
521-
>
522-
Share
523-
</ModalButtonPrimary> */}
524-
</div>
490+
</div>
525491
</ModalFooter>
526492
</Modal>
527493
</div>

src/custom/UserSearchField/UserSearchField.tsx

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Button } from '@mui/material';
22
import Autocomplete from '@mui/material/Autocomplete';
33
import CircularProgress from '@mui/material/CircularProgress';
4-
import { debounce } from 'lodash';
5-
import React, { useEffect, useMemo, useState } from 'react';
4+
import React, { useState } from 'react';
65
import { Avatar, Box, Chip, Grid, TextField, Typography } from '../../base';
76
import { PersonIcon } from '../../icons/Person';
87
import { useTheme } from '../../theme';
@@ -21,28 +20,9 @@ interface User {
2120
interface UserSearchFieldProps {
2221
// Array of user objects currently selected.
2322
usersData: User[];
24-
// Function to update the selected users data.
25-
setUsersData: React.Dispatch<React.SetStateAction<User[]>>;
26-
// Label for the text field.
27-
label?: string;
28-
// Function to enable or disable the save button.
29-
setDisableSave?: (disabled: boolean) => void;
30-
// Type of search being performed, e.g., 'user', 'admin'.
31-
searchType?: string;
32-
// Boolean indicating whether the search field is disabled.
3323
disabled?: boolean;
34-
// Custom component to change rendering style of users list, if not given
35-
// by default it will show list with avatar and email of selected users
36-
customUsersList?: JSX.Element;
37-
/**
38-
* Function to fetch user suggestions based on the input value.
39-
* @param {string} value - The input value for which suggestions are to be fetched.
40-
* @returns {Promise<User[]>} A promise that resolves to an array of user suggestions.
41-
*/
42-
fetchSuggestions: (value: string) => Promise<User[]>;
4324
shareWithNewUsers: (newUsers: User[]) => Promise<{ error: string }>;
4425
useGetAllUsersQuery: any
45-
// isSharing : boolean
4626
}
4727

4828
const UserShareSearch: React.FC<UserSearchFieldProps> = ({
@@ -92,43 +72,13 @@ const UserShareSearch: React.FC<UserSearchFieldProps> = ({
9272
}
9373
};
9474

95-
// // Memoize the debounced function to prevent recreation on each render
96-
// const debouncedFetchSuggestions = useMemo(
97-
// () =>
98-
// debounce(async (value: string) => {
99-
// console.log('debounced fetch running for:', value);
100-
// if (value === '') {
101-
// setOptions([]);
102-
// setOpen(false);
103-
// } else {
104-
// setSearchUserLoading(true);
105-
// const suggestions = await fetchSuggestions(value);
106-
// console.log("suggestions",suggestions)
107-
// setOptions(suggestions);
108-
// setSearchUserLoading(false);
109-
// setError(false);
110-
// setOpen(true);
111-
// }
112-
// }, 300),
113-
// [fetchSuggestions]
114-
// );
115-
116-
// // Clean up debounce on unmount
117-
// useEffect(() => {
118-
// return () => {
119-
// debouncedFetchSuggestions.cancel();
120-
// };
121-
// }, [debouncedFetchSuggestions]);
122-
12375
// Handler for input changes
12476
const handleInputChange = (event: React.SyntheticEvent, value: string, reason: string) => {
12577
// Only process actual typing events, not clearing or blurring
12678
if (reason === 'input') {
12779
setInputValue(value);
128-
// debouncedFetchSuggestions(value);
12980
} else if (reason === 'clear') {
13081
setInputValue('');
131-
// setOptions([]);
13282
}
13383
};
13484

src/utils/permissions.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { VISIBILITY } from "../constants/constants";
2+
3+
export interface User {
4+
id: string;
5+
user_id: string;
6+
first_name: string;
7+
last_name: string;
8+
email: string;
9+
avatar_url?: string;
10+
deleted_at?: { Valid: boolean };
11+
role_names?:string[]
12+
}
13+
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
19+
};
20+
21+
export const canUpdateResourceVisibility= canUpdateResource
22+
23+
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)
29+
};

src/utils/routing.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { KANVAS_MODE,ResourceType,RESOURCE_TYPE } from "../constants/constants";
2+
3+
4+
type PathParams = {
5+
id:string
6+
name: string
7+
}
8+
9+
export const viewPath = ({ id, name }:PathParams) => {
10+
const currentRoute = new URL(window.location.href);
11+
const currentURI = currentRoute.origin + currentRoute.pathname;
12+
const newParams = new URLSearchParams({
13+
mode: KANVAS_MODE.OPERATOR,
14+
type: RESOURCE_TYPE.VIEW,
15+
...(id ? { id } : {}),
16+
...(name ? { name } : {})
17+
});
18+
const newURI = currentURI + "?" + newParams.toString();
19+
return newURI;
20+
};
21+
22+
export const catalogPath = ({ id, name }:PathParams) => {
23+
const currentRoute = new URL(window.location.href);
24+
const currentURI = currentRoute.origin + currentRoute.pathname;
25+
const newParams = new URLSearchParams({
26+
mode: KANVAS_MODE.DESIGN,
27+
type: RESOURCE_TYPE.CATALOG,
28+
...(id ? { id } : {}),
29+
...(name ? { name } : {})
30+
});
31+
const newURI = currentURI + "?" + newParams.toString();
32+
return newURI;
33+
};
34+
35+
export const getRouteParams = () => {
36+
const currentRoute = new URL(window.location.href);
37+
const params = currentRoute.searchParams;
38+
return params;
39+
};
40+
41+
export const getDesignPath = (id?: string) => {
42+
const currentRoute = new URL(window.location.href);
43+
const currentURI = currentRoute.origin + currentRoute.pathname;
44+
45+
const newParams = new URLSearchParams({
46+
mode: KANVAS_MODE.DESIGN,
47+
...(id ? { design: id } : {})
48+
});
49+
const newURI = currentURI + "?" + newParams.toString();
50+
return newURI;
51+
};
52+
53+
export const getShareableResourceRoute = (
54+
type: ResourceType,
55+
id: string,
56+
name: string
57+
) => {
58+
if (type === RESOURCE_TYPE.DESIGN) {
59+
return getDesignPath(id);
60+
}
61+
62+
if (type === RESOURCE_TYPE.VIEW) {
63+
return viewPath({ id, name });
64+
}
65+
66+
if (type === RESOURCE_TYPE.CATALOG) {
67+
return catalogPath({ id, name });
68+
}
69+
70+
throw new Error(`Unknown resource type ${type}`);
71+
};
72+
73+
export const emptyViewPath = () => {
74+
return viewPath({id:"",name:""});
75+
};
76+
77+
export const getEmptyDesignPath = () => {
78+
return getDesignPath();
79+
};

0 commit comments

Comments
 (0)