mirror of https://github.com/buster-so/buster.git
revise front end to use shared components
This commit is contained in:
parent
cd8a50403f
commit
0a34db8989
|
@ -2,6 +2,10 @@
|
||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"extends": ["//"],
|
"extends": ["//"],
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"dev": {}
|
"dev": {
|
||||||
|
"dependsOn": ["@buster/database#start"],
|
||||||
|
"with": [],
|
||||||
|
"outputs": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import type { ShareAssetType, ShareRole } from '@buster/server-shared/share';
|
import type { ShareAssetType, ShareRole, WorkspaceShareRole } from '@buster/server-shared/share';
|
||||||
import type { DropdownItem } from '@/components/ui/dropdown';
|
import type { DropdownItem } from '@/components/ui/dropdown';
|
||||||
import { Dropdown } from '@/components/ui/dropdown';
|
import { Dropdown } from '@/components/ui/dropdown';
|
||||||
import { ChevronDown } from '@/components/ui/icons/NucleoIconFilled';
|
import { ChevronDown } from '@/components/ui/icons/NucleoIconFilled';
|
||||||
|
@ -7,24 +7,45 @@ import { Paragraph, Text } from '@/components/ui/typography';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
|
|
||||||
type DropdownValue = ShareRole | 'remove' | 'notShared';
|
type DropdownValue = ShareRole | WorkspaceShareRole | 'remove';
|
||||||
|
|
||||||
export const AccessDropdown: React.FC<{
|
type AccessDropdownBaseProps = {
|
||||||
className?: string;
|
|
||||||
showRemove?: boolean;
|
|
||||||
shareLevel?: ShareRole | null;
|
|
||||||
onChangeShareLevel?: (level: ShareRole | null) => void;
|
|
||||||
assetType: ShareAssetType;
|
assetType: ShareAssetType;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}> = ({
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccessDropdownUserProps = {
|
||||||
|
showRemove?: boolean;
|
||||||
|
shareLevel?: ShareRole | null;
|
||||||
|
type: 'user';
|
||||||
|
onChangeShareLevel?: (level: ShareRole | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccessDropdownWorkspaceProps = {
|
||||||
|
type: 'workspace';
|
||||||
|
shareLevel?: WorkspaceShareRole | null;
|
||||||
|
onChangeShareLevel?: (level: WorkspaceShareRole | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccessDropdownProps = AccessDropdownBaseProps &
|
||||||
|
(AccessDropdownUserProps | AccessDropdownWorkspaceProps);
|
||||||
|
|
||||||
|
export const AccessDropdown: React.FC<AccessDropdownProps> = ({
|
||||||
shareLevel,
|
shareLevel,
|
||||||
showRemove = true,
|
|
||||||
disabled,
|
disabled,
|
||||||
className = '',
|
className = '',
|
||||||
onChangeShareLevel,
|
onChangeShareLevel,
|
||||||
assetType
|
assetType,
|
||||||
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
const showRemove = props.type === 'user' && props.showRemove !== false;
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
|
if (props.type === 'workspace') {
|
||||||
|
return workspaceItems;
|
||||||
|
}
|
||||||
|
|
||||||
const baseItems: DropdownItem<DropdownValue>[] = [...(itemsRecord[assetType] || [])];
|
const baseItems: DropdownItem<DropdownValue>[] = [...(itemsRecord[assetType] || [])];
|
||||||
|
|
||||||
if (showRemove) {
|
if (showRemove) {
|
||||||
|
@ -38,7 +59,7 @@ export const AccessDropdown: React.FC<{
|
||||||
...item,
|
...item,
|
||||||
selected: item.value === shareLevel
|
selected: item.value === shareLevel
|
||||||
}));
|
}));
|
||||||
}, [showRemove, shareLevel, assetType]);
|
}, [showRemove, shareLevel, assetType, props.type]);
|
||||||
|
|
||||||
const selectedLabel = useMemo(() => {
|
const selectedLabel = useMemo(() => {
|
||||||
const selectedItem = items.find((item) => item.selected) || OWNER_ITEM;
|
const selectedItem = items.find((item) => item.selected) || OWNER_ITEM;
|
||||||
|
@ -51,16 +72,17 @@ export const AccessDropdown: React.FC<{
|
||||||
return 'Full access';
|
return 'Full access';
|
||||||
case 'canEdit':
|
case 'canEdit':
|
||||||
return 'Can edit';
|
return 'Can edit';
|
||||||
case 'canFilter':
|
|
||||||
return 'Can filter';
|
|
||||||
case 'canView':
|
case 'canView':
|
||||||
return 'Can view';
|
return 'Can view';
|
||||||
case 'owner':
|
case 'owner':
|
||||||
return 'Owner';
|
return 'Owner';
|
||||||
case 'remove':
|
case 'remove':
|
||||||
return 'Remove';
|
return 'Remove';
|
||||||
case 'notShared':
|
case 'none':
|
||||||
return 'Not shared';
|
return 'Not shared';
|
||||||
|
default:
|
||||||
|
const _exhaustiveCheck: never = value;
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}, [items]);
|
}, [items]);
|
||||||
|
|
||||||
|
@ -128,11 +150,6 @@ const dashboardItems: DropdownItem<ShareRole>[] = [
|
||||||
label: 'Can edit',
|
label: 'Can edit',
|
||||||
secondaryLabel: 'Can edit but not share with others.'
|
secondaryLabel: 'Can edit but not share with others.'
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// value: 'canFilter',
|
|
||||||
// label: 'Can filter',
|
|
||||||
// secondaryLabel: 'Can filter dashboards but not edit.'
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
value: 'canView',
|
value: 'canView',
|
||||||
label: 'Can view',
|
label: 'Can view',
|
||||||
|
@ -158,6 +175,29 @@ const collectionItems: DropdownItem<ShareRole>[] = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const workspaceItems: DropdownItem<WorkspaceShareRole>[] = [
|
||||||
|
{
|
||||||
|
value: 'fullAccess',
|
||||||
|
label: 'Full access',
|
||||||
|
secondaryLabel: 'Can edit and share with others.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'canEdit',
|
||||||
|
label: 'Can edit',
|
||||||
|
secondaryLabel: 'Can edit, but not share with others.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'canView',
|
||||||
|
label: 'Can view',
|
||||||
|
secondaryLabel: 'Cannot edit or share with others.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'none',
|
||||||
|
label: 'Not shared',
|
||||||
|
secondaryLabel: 'Does not have access.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const itemsRecord: Record<ShareAssetType, DropdownItem<ShareRole>[]> = {
|
const itemsRecord: Record<ShareAssetType, DropdownItem<ShareRole>[]> = {
|
||||||
['dashboard']: dashboardItems,
|
['dashboard']: dashboardItems,
|
||||||
['metric']: metricItems,
|
['metric']: metricItems,
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { useMemoizedFn } from '@/hooks';
|
|
||||||
import { AccessDropdown } from './AccessDropdown';
|
|
||||||
import type { ShareAssetType, ShareRole } from '@buster/server-shared/share';
|
|
||||||
import { AvatarUserButton } from '../../ui/avatar/AvatarUserButton';
|
|
||||||
|
|
||||||
export const IndividualSharePerson: React.FC<{
|
|
||||||
name?: string;
|
|
||||||
email: string;
|
|
||||||
role: ShareRole;
|
|
||||||
avatar_url?: string | null;
|
|
||||||
onUpdateShareRole: (email: string, role: ShareRole | null) => void;
|
|
||||||
assetType: ShareAssetType;
|
|
||||||
disabled: boolean;
|
|
||||||
}> = React.memo(({ name, onUpdateShareRole, email, avatar_url, role, assetType, disabled }) => {
|
|
||||||
const onChangeShareLevel = useMemoizedFn((v: ShareRole | null) => {
|
|
||||||
onUpdateShareRole(email, v);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="flex h-8 items-center justify-between space-x-2 overflow-hidden"
|
|
||||||
data-testid={`share-person-${email}`}>
|
|
||||||
<AvatarUserButton username={name} email={email} avatarUrl={avatar_url} avatarSize={24} />
|
|
||||||
|
|
||||||
<AccessDropdown
|
|
||||||
shareLevel={role}
|
|
||||||
showRemove={true}
|
|
||||||
disabled={disabled}
|
|
||||||
onChangeShareLevel={onChangeShareLevel}
|
|
||||||
assetType={assetType}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
IndividualSharePerson.displayName = 'IndividualSharePerson';
|
|
|
@ -1,16 +1,23 @@
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import type { ShareAssetType, ShareConfig, ShareRole, WorkspaceShareRole } from '@buster/server-shared/share';
|
import type {
|
||||||
|
ShareAssetType,
|
||||||
|
ShareConfig,
|
||||||
|
ShareRole,
|
||||||
|
WorkspaceShareRole
|
||||||
|
} from '@buster/server-shared/share';
|
||||||
import { useUnshareCollection, useUpdateCollectionShare } from '@/api/buster_rest/collections';
|
import { useUnshareCollection, useUpdateCollectionShare } from '@/api/buster_rest/collections';
|
||||||
import { useUnshareDashboard, useUpdateDashboardShare } from '@/api/buster_rest/dashboards';
|
import { useUnshareDashboard, useUpdateDashboardShare } from '@/api/buster_rest/dashboards';
|
||||||
import { useUnshareMetric, useUpdateMetricShare } from '@/api/buster_rest/metrics';
|
import { useUnshareMetric, useUpdateMetricShare } from '@/api/buster_rest/metrics';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { IndividualSharePerson } from './IndividualSharePerson';
|
|
||||||
import { ShareMenuContentEmbed } from './ShareMenuContentEmbed';
|
import { ShareMenuContentEmbed } from './ShareMenuContentEmbed';
|
||||||
import { ShareMenuContentPublish } from './ShareMenuContentPublish';
|
import { ShareMenuContentPublish } from './ShareMenuContentPublish';
|
||||||
import type { ShareMenuTopBarOptions } from './ShareMenuTopBar';
|
import type { ShareMenuTopBarOptions } from './ShareMenuTopBar';
|
||||||
import { ShareMenuInvite } from './ShareMenuInvite';
|
import { ShareMenuInvite } from './ShareMenuInvite';
|
||||||
import { WorkspaceShareSection } from './WorkspaceShareSection';
|
import { WorkspaceShareSection } from './WorkspaceShareSection';
|
||||||
|
import { ShareRowItem } from './ShareRowItem';
|
||||||
|
import { WorkspaceAvatar } from './WorkspaceAvatar';
|
||||||
|
import pluralize from 'pluralize';
|
||||||
|
|
||||||
export const ShareMenuContentBody: React.FC<{
|
export const ShareMenuContentBody: React.FC<{
|
||||||
selectedOptions: ShareMenuTopBarOptions;
|
selectedOptions: ShareMenuTopBarOptions;
|
||||||
|
@ -55,7 +62,14 @@ export const ShareMenuContentBody: React.FC<{
|
||||||
ShareMenuContentBody.displayName = 'ShareMenuContentBody';
|
ShareMenuContentBody.displayName = 'ShareMenuContentBody';
|
||||||
|
|
||||||
const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
||||||
({ canEditPermissions, assetType, individual_permissions, assetId, className, shareAssetConfig }) => {
|
({
|
||||||
|
canEditPermissions,
|
||||||
|
assetType,
|
||||||
|
individual_permissions,
|
||||||
|
assetId,
|
||||||
|
className,
|
||||||
|
shareAssetConfig
|
||||||
|
}) => {
|
||||||
const { mutateAsync: onUpdateMetricShare } = useUpdateMetricShare();
|
const { mutateAsync: onUpdateMetricShare } = useUpdateMetricShare();
|
||||||
const { mutateAsync: onUpdateDashboardShare } = useUpdateDashboardShare();
|
const { mutateAsync: onUpdateDashboardShare } = useUpdateDashboardShare();
|
||||||
const { mutateAsync: onUpdateCollectionShare } = useUpdateCollectionShare();
|
const { mutateAsync: onUpdateCollectionShare } = useUpdateCollectionShare();
|
||||||
|
@ -64,6 +78,7 @@ const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
||||||
const { mutateAsync: onUnshareCollection } = useUnshareCollection();
|
const { mutateAsync: onUnshareCollection } = useUnshareCollection();
|
||||||
|
|
||||||
const hasIndividualPermissions = !!individual_permissions?.length;
|
const hasIndividualPermissions = !!individual_permissions?.length;
|
||||||
|
const workspaceMemberCount = shareAssetConfig.workspace_member_count || 0;
|
||||||
|
|
||||||
const onUpdateShareRole = useMemoizedFn(async (email: string, role: ShareRole | null) => {
|
const onUpdateShareRole = useMemoizedFn(async (email: string, role: ShareRole | null) => {
|
||||||
if (role) {
|
if (role) {
|
||||||
|
@ -130,24 +145,36 @@ const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
||||||
{hasIndividualPermissions && (
|
{hasIndividualPermissions && (
|
||||||
<div className="flex flex-col space-y-2.5 overflow-hidden">
|
<div className="flex flex-col space-y-2.5 overflow-hidden">
|
||||||
{individual_permissions?.map((permission) => (
|
{individual_permissions?.map((permission) => (
|
||||||
<IndividualSharePerson
|
<ShareRowItem
|
||||||
key={permission.email}
|
primary={permission.name}
|
||||||
{...permission}
|
secondary={permission.email}
|
||||||
onUpdateShareRole={onUpdateShareRole}
|
role={permission.role}
|
||||||
|
avatar={permission.avatar_url}
|
||||||
|
onChangeShareLevel={useMemoizedFn((role) =>
|
||||||
|
onUpdateShareRole(permission.email, role)
|
||||||
|
)}
|
||||||
assetType={assetType}
|
assetType={assetType}
|
||||||
disabled={!canEditPermissions || permission.role === 'owner'}
|
disabled={!canEditPermissions || permission.role === 'owner'}
|
||||||
|
type="user"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{canEditPermissions && (
|
{canEditPermissions && (
|
||||||
<WorkspaceShareSection
|
<ShareRowItem
|
||||||
shareAssetConfig={shareAssetConfig}
|
primary={'Workspace'}
|
||||||
|
secondary={`Share with ${workspaceMemberCount} ${pluralize('member', workspaceMemberCount)}`}
|
||||||
|
role={shareAssetConfig.workspace_sharing || 'none'}
|
||||||
|
type="workspace"
|
||||||
|
avatar={useMemo(
|
||||||
|
() => (
|
||||||
|
<WorkspaceAvatar />
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
)}
|
||||||
|
onChangeShareLevel={onUpdateWorkspacePermissions}
|
||||||
assetType={assetType}
|
assetType={assetType}
|
||||||
assetId={assetId}
|
|
||||||
canEditPermissions={canEditPermissions}
|
|
||||||
onUpdateWorkspacePermissions={onUpdateWorkspacePermissions}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -143,6 +143,7 @@ export const ShareMenuInvite: React.FC<ShareMenuInviteProps> = React.memo(
|
||||||
onChangeShareLevel={onChangeAccessDropdown}
|
onChangeShareLevel={onChangeAccessDropdown}
|
||||||
assetType={assetType}
|
assetType={assetType}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
|
type="user"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import type { ShareAssetType, ShareRole, WorkspaceShareRole } from '@buster/server-shared/share';
|
||||||
|
import React from 'react';
|
||||||
|
import { AvatarUserButton } from '../../ui/avatar/AvatarUserButton';
|
||||||
|
import { AccessDropdown } from './AccessDropdown';
|
||||||
|
|
||||||
|
type ShareRowItemBaseProps = {
|
||||||
|
primary: string | undefined;
|
||||||
|
secondary: string | undefined;
|
||||||
|
avatar?: string | null | React.ReactNode;
|
||||||
|
disabled?: boolean;
|
||||||
|
assetType: ShareAssetType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ShareRowItemUserProps = {
|
||||||
|
type: 'user';
|
||||||
|
role: ShareRole;
|
||||||
|
showRemove?: boolean;
|
||||||
|
onChangeShareLevel?: (role: ShareRole | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ShareRowItemWorkspaceProps = {
|
||||||
|
type: 'workspace';
|
||||||
|
role: WorkspaceShareRole;
|
||||||
|
onChangeShareLevel?: (role: WorkspaceShareRole | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ShareRowItemProps = ShareRowItemBaseProps &
|
||||||
|
(ShareRowItemUserProps | ShareRowItemWorkspaceProps);
|
||||||
|
|
||||||
|
export const ShareRowItem: React.FC<ShareRowItemProps> = React.memo(
|
||||||
|
({ primary, secondary, avatar, disabled = false, assetType, ...props }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex h-8 items-center justify-between space-x-2 overflow-hidden"
|
||||||
|
data-testid={`share-row-${primary || secondary}`}>
|
||||||
|
<AvatarUserButton username={primary} email={secondary} avatarUrl={avatar} avatarSize={24} />
|
||||||
|
|
||||||
|
<AccessDropdown disabled={disabled} assetType={assetType} {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ApartmentBuilding } from '@/components/ui/icons';
|
||||||
|
import { cn } from '@/lib/classMerge';
|
||||||
|
|
||||||
|
export const WorkspaceAvatar: React.FC<{
|
||||||
|
className?: string;
|
||||||
|
}> = React.memo(({ className }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'text-md flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gray-100 text-gray-600',
|
||||||
|
className
|
||||||
|
)}>
|
||||||
|
<ApartmentBuilding />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,114 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import type { ShareAssetType, ShareConfig, WorkspaceShareRole } from '@buster/server-shared/share';
|
|
||||||
import { Dropdown } from '@/components/ui/dropdown';
|
|
||||||
import type { DropdownItem } from '@/components/ui/dropdown';
|
|
||||||
import { ChevronDown } from '@/components/ui/icons/NucleoIconFilled';
|
|
||||||
import { ApartmentBuilding } from '@/components/ui/icons/NucleoIconFilled';
|
|
||||||
import { Text } from '@/components/ui/typography';
|
|
||||||
import { useMemoizedFn } from '@/hooks';
|
|
||||||
import { cn } from '@/lib/classMerge';
|
|
||||||
|
|
||||||
const workspaceShareRoleItems: DropdownItem<WorkspaceShareRole>[] = [
|
|
||||||
{
|
|
||||||
value: 'fullAccess',
|
|
||||||
label: 'Full access',
|
|
||||||
secondaryLabel: 'Can edit and share with others.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'canEdit',
|
|
||||||
label: 'Can edit',
|
|
||||||
secondaryLabel: 'Can edit, but not share with others.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'canView',
|
|
||||||
label: 'Can view',
|
|
||||||
secondaryLabel: 'Cannot edit or share with others.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'none',
|
|
||||||
label: 'Not shared',
|
|
||||||
secondaryLabel: 'Does not have access.'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
interface WorkspaceShareSectionProps {
|
|
||||||
shareAssetConfig: ShareConfig;
|
|
||||||
assetType: ShareAssetType;
|
|
||||||
assetId: string;
|
|
||||||
canEditPermissions: boolean;
|
|
||||||
onUpdateWorkspacePermissions: (role: WorkspaceShareRole | null) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WorkspaceShareSection: React.FC<WorkspaceShareSectionProps> = React.memo(({
|
|
||||||
shareAssetConfig,
|
|
||||||
canEditPermissions,
|
|
||||||
onUpdateWorkspacePermissions
|
|
||||||
}) => {
|
|
||||||
const currentRole = shareAssetConfig.workspace_sharing || 'none';
|
|
||||||
|
|
||||||
const selectedLabel = React.useMemo(() => {
|
|
||||||
const selectedItem = workspaceShareRoleItems.find(item => item.value === currentRole);
|
|
||||||
return selectedItem?.label || 'Not shared';
|
|
||||||
}, [currentRole]);
|
|
||||||
|
|
||||||
const onSelectMenuItem = useMemoizedFn((value: string) => {
|
|
||||||
onUpdateWorkspacePermissions(value as WorkspaceShareRole);
|
|
||||||
});
|
|
||||||
|
|
||||||
const items = React.useMemo(() => {
|
|
||||||
return workspaceShareRoleItems.map(item => ({
|
|
||||||
...item,
|
|
||||||
selected: item.value === currentRole
|
|
||||||
}));
|
|
||||||
}, [currentRole]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-8 items-center justify-between space-x-2 overflow-hidden">
|
|
||||||
<div className="flex w-full items-center gap-x-2 rounded-md p-1">
|
|
||||||
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gray-100 text-gray-600 text-md">
|
|
||||||
<ApartmentBuilding />
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full flex-col gap-y-0 overflow-hidden">
|
|
||||||
<Text className="truncate">Workspace</Text>
|
|
||||||
<Text variant="secondary" size="sm" className="truncate">
|
|
||||||
Share with {shareAssetConfig.workspace_member_count?.toLocaleString() || 0} members
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Dropdown
|
|
||||||
items={items}
|
|
||||||
disabled={!canEditPermissions}
|
|
||||||
footerContent={
|
|
||||||
<div className="bg-item-hover flex justify-center overflow-hidden rounded-b p-2 px-2.5">
|
|
||||||
<Text variant="secondary" size="xs">
|
|
||||||
Sharing cannot override permissions set by your account admins.
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
footerClassName="p-0!"
|
|
||||||
onSelect={onSelectMenuItem}
|
|
||||||
sideOffset={16}
|
|
||||||
selectType="single"
|
|
||||||
align="end"
|
|
||||||
side="bottom">
|
|
||||||
<Text
|
|
||||||
variant="secondary"
|
|
||||||
size="xs"
|
|
||||||
className={cn(
|
|
||||||
'flex! items-center! space-x-1',
|
|
||||||
canEditPermissions && 'cursor-pointer'
|
|
||||||
)}>
|
|
||||||
<span className="truncate">{selectedLabel}</span>
|
|
||||||
{canEditPermissions && (
|
|
||||||
<span className="text-2xs text-icon-color">
|
|
||||||
<ChevronDown />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
WorkspaceShareSection.displayName = 'WorkspaceShareSection';
|
|
|
@ -7,7 +7,7 @@ export const AvatarUserButton = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
{
|
{
|
||||||
username?: string | null;
|
username?: string | null;
|
||||||
avatarUrl?: string | null;
|
avatarUrl?: string | null | React.ReactNode;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
className?: string;
|
className?: string;
|
||||||
avatarSize?: number;
|
avatarSize?: number;
|
||||||
|
@ -17,7 +17,11 @@ export const AvatarUserButton = React.forwardRef<
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className={cn('flex w-full items-center gap-x-2 rounded-md p-1', className)}>
|
<div ref={ref} className={cn('flex w-full items-center gap-x-2 rounded-md p-1', className)}>
|
||||||
|
{typeof avatarUrl === 'string' || avatarUrl === null ? (
|
||||||
<Avatar size={avatarSize} fallbackClassName="text-base" image={avatarUrl} name={username} />
|
<Avatar size={avatarSize} fallbackClassName="text-base" image={avatarUrl} name={username} />
|
||||||
|
) : (
|
||||||
|
avatarUrl
|
||||||
|
)}
|
||||||
<div className="flex w-full flex-col gap-y-0 overflow-hidden">
|
<div className="flex w-full flex-col gap-y-0 overflow-hidden">
|
||||||
<Text truncate className="flex-grow">
|
<Text truncate className="flex-grow">
|
||||||
{username}
|
{username}
|
||||||
|
|
|
@ -88,7 +88,7 @@ const useSupabaseContextInternal = ({
|
||||||
async ({ accessToken, expiresAt: _expiresAt }: { accessToken: string; expiresAt: number }) => {
|
async ({ accessToken, expiresAt: _expiresAt }: { accessToken: string; expiresAt: number }) => {
|
||||||
setAccessToken(accessToken);
|
setAccessToken(accessToken);
|
||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
openInfoMessage('Token refreshed');
|
//noop
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,17 +4,10 @@ export const ShareRoleSchema = z.enum([
|
||||||
'owner', //owner of the asset
|
'owner', //owner of the asset
|
||||||
'fullAccess', //same as owner, can share with others
|
'fullAccess', //same as owner, can share with others
|
||||||
'canEdit', //can edit, cannot share
|
'canEdit', //can edit, cannot share
|
||||||
'canFilter', //can filter dashboard
|
|
||||||
'canView', //can view asset
|
'canView', //can view asset
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const WorkspaceShareRoleSchema = z.enum([
|
export const WorkspaceShareRoleSchema = z.enum([...ShareRoleSchema.options, 'none']);
|
||||||
'owner',
|
|
||||||
'fullAccess',
|
|
||||||
'canEdit',
|
|
||||||
'canView',
|
|
||||||
'none',
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const ShareAssetTypeSchema = z.enum(['metric', 'dashboard', 'collection', 'chat']);
|
export const ShareAssetTypeSchema = z.enum(['metric', 'dashboard', 'collection', 'chat']);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue