Skip to content
4 changes: 2 additions & 2 deletions packages/app/src/app/hooks/useWorkspaceAuthorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ export const useWorkspaceAuthorization = (): WorkspaceAuthorizationReturn => {

const isTeamAdmin = isAdmin;

const isTeamEditor = authorization === TeamMemberAuthorization.Write;
const isTeamEditor = authorization === TeamMemberAuthorization.Write || isTeamAdmin;

const isTeamViewer = authorization === TeamMemberAuthorization.Read;
const isTeamViewer = authorization === TeamMemberAuthorization.Read || isTeamEditor || isTeamAdmin;

return {
isBillingManager: Boolean(teamManager) || isAdmin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IconButton,
InteractiveOverlay,
} from '@codesandbox/components';
import { useWorkspaceAuthorization } from 'app/hooks/useWorkspaceAuthorization';
import { FolderItemComponentProps } from './types';
import { StyledCard } from '../shared/StyledCard';

Expand Down Expand Up @@ -35,7 +36,10 @@ export const FolderCard: React.FC<FolderItemComponentProps> = ({

'data-selection-id': dataSelectionId,
...props
}) => (
}) => {
const { isTeamEditor } = useWorkspaceAuthorization();

return (
<InteractiveOverlay>
<StyledCard
data-selection-id={dataSelectionId}
Expand All @@ -44,7 +48,7 @@ export const FolderCard: React.FC<FolderItemComponentProps> = ({
>
<Stack justify="space-between">
<Icon size={20} name="folder" color="#E3FF73" />
{!isNewFolder ? (
{!isNewFolder && isTeamEditor ? (
<IconButton
css={{
marginRight: '-4px',
Expand Down Expand Up @@ -99,4 +103,4 @@ export const FolderCard: React.FC<FolderItemComponentProps> = ({
</Stack>
</StyledCard>
</InteractiveOverlay>
);
)};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import track from '@codesandbox/common/lib/utils/analytics';
import { ESC } from '@codesandbox/common/lib/utils/keycodes';
import { dashboard as dashboardUrls } from '@codesandbox/common/lib/utils/url-generator';
import { useAppState, useActions } from 'app/overmind';
import { useWorkspaceAuthorization } from 'app/hooks/useWorkspaceAuthorization';
import { FolderCard } from './FolderCard';
import { FolderListItem } from './FolderListItem';
import { useSelection } from '../Selection';
Expand All @@ -18,6 +19,7 @@ export const Folder = (folderItem: DashboardFolder) => {
const {
dashboard: { renameFolder },
} = useActions();
const { isTeamEditor } = useWorkspaceAuthorization();

const {
name = '',
Expand Down Expand Up @@ -93,7 +95,7 @@ export const Folder = (folderItem: DashboardFolder) => {

/* Drop target logic */

const accepts = ['sandbox'];
const accepts = isTeamEditor ? ['sandbox'] : [];

const [{ isOver, canDrop }, dropRef] = useDrop({
accept: accepts,
Expand All @@ -112,6 +114,7 @@ export const Folder = (folderItem: DashboardFolder) => {

const [, dragRef, preview] = useDrag({
item: folderItem,
canDrag: isTeamEditor,
end: (item, monitor) => {
const dropResult = monitor.getDropResult();

Expand All @@ -124,7 +127,7 @@ export const Folder = (folderItem: DashboardFolder) => {

const dragProps = {
ref: dragRef,
onDragStart: event => onDragStart(event, path, 'folder'),
onDragStart: isTeamEditor ? (event => onDragStart(event, path, 'folder')) : undefined,
};

React.useEffect(() => {
Expand Down Expand Up @@ -185,7 +188,7 @@ export const Folder = (folderItem: DashboardFolder) => {
onClick,
onDoubleClick,
// edit mode
editing: isRenaming && selected,
editing: isRenaming && selected && isTeamEditor,
isNewFolder: false,
isDragging,
newName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { useLocation } from 'react-router-dom';
import { useAppState, useActions } from 'app/overmind';
import { Stack, Text, Button, Icon } from '@codesandbox/components';
import { useWorkspaceAuthorization } from 'app/hooks/useWorkspaceAuthorization';
import { Breadcrumbs, BreadcrumbProps } from '../Breadcrumbs';
import { ViewOptions } from '../Filters/ViewOptions';
import { SortOptions } from '../Filters/SortOptions';
Expand Down Expand Up @@ -35,6 +36,7 @@ export const Header = ({
const location = useLocation();
const { modalOpened, dashboard: dashboardActions } = useActions();
const { dashboard } = useAppState();
const { isTeamEditor } = useWorkspaceAuthorization();

const repositoriesListPage =
location.pathname.includes('/repositories') &&
Expand Down Expand Up @@ -68,7 +70,7 @@ export const Header = ({
)}
</Stack>
<Stack gap={1} align="center">
{location.pathname.includes('/sandboxes') && (
{location.pathname.includes('/sandboxes') && isTeamEditor && (
<Button onClick={createNewFolder} variant="ghost" autoWidth>
<Icon
name="folder"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,18 @@ const GenericSandbox = ({ isScrolling, item, page }: GenericSandboxProps) => {
screenshotUrl = '/static/img/default-sandbox-thumbnail.png';
}

/** Access restrictions */
let { noDrag } = item;

if (activeWorkspaceAuthorization === 'READ') {
noDrag = true;
}

/* Drag logic */

const [, dragRef, preview] = useDrag({
item,
canDrag: !noDrag,
end: (_item, monitor) => {
const dropResult = monitor.getDropResult();

Expand All @@ -120,13 +128,6 @@ const GenericSandbox = ({ isScrolling, item, page }: GenericSandboxProps) => {
const Component: React.FC<SandboxItemComponentProps> =
viewMode === 'list' ? SandboxListItem : SandboxCard;

/** Access restrictions */
let { noDrag } = item;

if (activeWorkspaceAuthorization === 'READ') {
noDrag = true;
}

// interactions
const {
selectedIds,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { useActions } from 'app/overmind';
import { Menu } from '@codesandbox/components';
import track from '@codesandbox/common/lib/utils/analytics';
import { useWorkspaceAuthorization } from 'app/hooks/useWorkspaceAuthorization';
import { Context, MenuItem } from '../ContextMenu';
import { DashboardBaseFolder } from '../../../types';

Expand All @@ -14,8 +15,13 @@ export const FolderMenu = ({ folder, setRenaming }: FolderMenuProps) => {
const {
dashboard: { deleteFolder },
} = useActions();
const { isTeamEditor } = useWorkspaceAuthorization();
const { visible, setVisibility, position } = React.useContext(Context);

if (!isTeamEditor) {
return null;
}

return (
<Menu.ContextMenu
visible={visible}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SelectionProvider } from 'app/pages/Dashboard/Components/Selection';
import { VariableGrid } from 'app/pages/Dashboard/Components/VariableGrid';
import { DashboardGridItem, PageTypes } from 'app/pages/Dashboard/types';
import { useWorkspaceLimits } from 'app/hooks/useWorkspaceLimits';
import { useWorkspaceAuthorization } from 'app/hooks/useWorkspaceAuthorization';
import { ActionCard } from 'app/pages/Dashboard/Components/shared/ActionCard';
import { useFilteredItems } from './useFilteredItems';

Expand All @@ -21,6 +22,7 @@ export const SandboxesPage = () => {
const items = useFilteredItems(currentPath, cleanParam, level);
const actions = useActions();
const { isFrozen } = useWorkspaceLimits();
const { isTeamEditor } = useWorkspaceAuthorization();
const {
dashboard: { allCollections },
activeTeam,
Expand Down Expand Up @@ -87,23 +89,25 @@ export const SandboxesPage = () => {
{isEmpty ? (
<EmptyPage.StyledWrapper>
<EmptyPage.StyledGrid>
<ActionCard
icon="plus"
disabled={isFrozen}
onClick={() => {
track('Empty Folder - Create devbox', {
codesandbox: 'V1',
event_source: 'UI',
});
{(isTeamEditor) && (
<ActionCard
icon="plus"
disabled={isFrozen}
onClick={() => {
track('Empty Folder - Create devbox', {
codesandbox: 'V1',
event_source: 'UI',
});

actions.modalOpened({
modal: 'create',
itemId: currentCollection.id,
});
}}
>
Create
</ActionCard>
actions.modalOpened({
modal: 'create',
itemId: currentCollection.id,
});
}}
>
Create
</ActionCard>
)}
</EmptyPage.StyledGrid>
</EmptyPage.StyledWrapper>
) : (
Expand Down
10 changes: 6 additions & 4 deletions packages/app/src/app/pages/Dashboard/Sidebar/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useHistory, useLocation } from 'react-router-dom';
import { Stack, Menu, Icon, Text } from '@codesandbox/components';
import track from '@codesandbox/common/lib/utils/analytics';
import { dashboard as dashboardUrls } from '@codesandbox/common/lib/utils/url-generator';
import { useWorkspaceAuthorization } from 'app/hooks/useWorkspaceAuthorization';
import { Position } from '../Components/Selection';
import { DashboardBaseFolder } from '../types';
import { NEW_FOLDER_ID } from './constants';
Expand All @@ -14,7 +15,6 @@ const Context = React.createContext({

interface ContextMenuProps {
activeTeam: string | null;
authorization: string | null;
visible: boolean;
setVisibility: (val: boolean) => void;
position: Position;
Expand All @@ -25,7 +25,6 @@ interface ContextMenuProps {

export const ContextMenu: React.FC<ContextMenuProps> = ({
activeTeam,
authorization,
visible,
position,
setVisibility,
Expand All @@ -34,15 +33,18 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
setNewFolderPath,
}) => {
const { deleteFolder } = useActions().dashboard;
const { isTeamEditor } = useWorkspaceAuthorization();

const history = useHistory();
const location = useLocation();

if (!visible || !folder) return null;
if (!visible || !folder || !isTeamEditor) {
return null;
};

let menuOptions;

if (folder.name === 'Drafts' || authorization === 'READ') {
if (folder.name === 'Drafts') {
menuOptions = (
<MenuItem onSelect={() => {}}>
<Stack gap={1}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ interface NestableRowItemProps {
path: string;
page: PageTypes;
folders: DashboardBaseFolder[];
canEdit?: boolean;
}

export const NestableRowItem: React.FC<NestableRowItemProps> = ({
Expand All @@ -43,6 +44,7 @@ export const NestableRowItem: React.FC<NestableRowItemProps> = ({
page,
folderPath,
folders,
canEdit = true,
}) => {
const actions = useActions();
const state = useAppState();
Expand Down Expand Up @@ -96,6 +98,11 @@ export const NestableRowItem: React.FC<NestableRowItemProps> = ({

const onContextMenu = event => {
event.preventDefault();

if (!canEdit) {
return;
};

setMenuVisibility(true);
setMenuFolder({ name, path: folderPath });
setMenuPosition({ x: event.clientX, y: event.clientY });
Expand Down Expand Up @@ -206,6 +213,7 @@ export const NestableRowItem: React.FC<NestableRowItemProps> = ({
icon="folder"
nestingLevel={nestingLevel}
setFoldersVisibility={setFoldersVisibility}
canEdit={canEdit}
>
<Link
to={folderUrl}
Expand Down Expand Up @@ -315,6 +323,7 @@ export const NestableRowItem: React.FC<NestableRowItemProps> = ({
path={dashboardUrls.sandboxes(folder.path, state.activeTeam)}
folderPath={folder.path}
folders={folders}
canEdit={canEdit}
/>
))}
</motion.ul>
Expand Down
13 changes: 9 additions & 4 deletions packages/app/src/app/pages/Dashboard/Sidebar/RowItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ interface RowItemProps {
folderPath?: string;
nestingLevel?: number;
style?: CSSProperties;
canEdit?: boolean;
}

export const RowItem: React.FC<RowItemProps> = ({
Expand All @@ -71,14 +72,18 @@ export const RowItem: React.FC<RowItemProps> = ({
icon,
setFoldersVisibility = null,
style = {},
canEdit = true,
...props
}) => {
const accepts: Array<'sandbox' | 'folder' | 'template'> = [];
if (!canNotAcceptSandboxes.includes(page)) {
accepts.push('template');
accepts.push('sandbox');

if (canEdit && !canNotAcceptSandboxes.includes(page)) {
accepts.push('template', 'sandbox');
}

if (canEdit && !canNotAcceptFolders.includes(page)) {
accepts.push('folder');
}
if (!canNotAcceptFolders.includes(page)) accepts.push('folder');

const usedPath = folderPath || path;
const [{ canDrop, isOver, isDragging }, dropRef] = useDrop({
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/app/pages/Dashboard/Sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
React.useEffect(() => {
// Used to fetch collections
actions.dashboard.getAllFolders();
}, [state.activeTeam]);
}, [state.activeTeam, actions.dashboard]);

React.useEffect(() => {
if (state.hasLoadedApp && state.activeTeam) {
Expand Down Expand Up @@ -79,7 +79,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
const showRespositories = !state.environment.isOnPrem;

const { ubbBeta } = useWorkspaceFeatureFlags();
const { isPrimarySpace, isTeamAdmin } = useWorkspaceAuthorization();
const { isPrimarySpace, isTeamAdmin, isTeamEditor } = useWorkspaceAuthorization();
const { isPro } = useWorkspaceSubscription();

const showTemplates = state.activeTeam
Expand Down Expand Up @@ -225,6 +225,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
? [{ path: newFolderPath, name: '', parent: null }]
: []),
]}
canEdit={isTeamEditor}
/>

{showTemplates ? (
Expand Down Expand Up @@ -288,7 +289,6 @@ export const Sidebar: React.FC<SidebarProps> = ({
</AnimatePresence>
<ContextMenu
activeTeam={activeTeam}
authorization={state.activeWorkspaceAuthorization}
visible={menuVisible}
setVisibility={setMenuVisibility}
position={menuPosition}
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
]);
const usedAs = pAs || (props.to ? Link : 'button');
// default type is button unless props.as was changed
const type = usedAs === 'button' && 'button';
const type = usedAs === 'button' ? 'button' : undefined;

return (
<Element
Expand Down
Loading