mirror of https://github.com/buster-so/buster.git
sharing updates part 1
This commit is contained in:
parent
e4a8957d72
commit
4608e8573e
|
@ -16,9 +16,6 @@ export enum ShareAssetType {
|
||||||
export interface BusterShare {
|
export interface BusterShare {
|
||||||
sharingKey: string;
|
sharingKey: string;
|
||||||
individual_permissions: null | BusterShareIndividual[];
|
individual_permissions: null | BusterShareIndividual[];
|
||||||
team_permissions: null | { name: string; id: string; role: ShareRole }[];
|
|
||||||
organization_permissions: null | [];
|
|
||||||
password_secret_id: string | null;
|
|
||||||
public_expiry_date: string | null;
|
public_expiry_date: string | null;
|
||||||
public_enabled_by: string | null;
|
public_enabled_by: string | null;
|
||||||
publicly_accessible: boolean;
|
publicly_accessible: boolean;
|
||||||
|
@ -29,6 +26,5 @@ export interface BusterShare {
|
||||||
export interface BusterShareIndividual {
|
export interface BusterShareIndividual {
|
||||||
email: string;
|
email: string;
|
||||||
role: ShareRole;
|
role: ShareRole;
|
||||||
id: string;
|
name?: string;
|
||||||
name: string;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,31 +5,19 @@ import { ShareRole } from '../share/shareInterfaces';
|
||||||
*
|
*
|
||||||
* @interface ShareRequest
|
* @interface ShareRequest
|
||||||
*/
|
*/
|
||||||
export type ShareRequest = {
|
export type SharePostRequest = {
|
||||||
/** The unique identifier of the dashboard */
|
email: string;
|
||||||
id: string;
|
role: ShareRole;
|
||||||
/** User-specific permissions array */
|
}[];
|
||||||
user_permissions?: {
|
|
||||||
/** Email of the user to grant permissions to */
|
export type ShareDeleteRequest = string[];
|
||||||
user_email: string;
|
|
||||||
/** Role to assign to the user */
|
export type ShareUpdateRequest = {
|
||||||
|
users?: {
|
||||||
|
email: string;
|
||||||
role: ShareRole;
|
role: ShareRole;
|
||||||
}[];
|
}[];
|
||||||
/** Array of user IDs to remove access from */
|
|
||||||
remove_users?: string[];
|
|
||||||
/** Team-specific permissions array */
|
|
||||||
team_permissions?: {
|
|
||||||
/** ID of the team to grant permissions to */
|
|
||||||
team_id: string;
|
|
||||||
/** Role to assign to the team */
|
|
||||||
role: ShareRole;
|
|
||||||
}[];
|
|
||||||
/** Array of team IDs to remove access from */
|
|
||||||
remove_teams?: string[];
|
|
||||||
/** Whether the dashboard is publicly accessible */
|
|
||||||
publicly_accessible?: boolean;
|
publicly_accessible?: boolean;
|
||||||
/** Optional password for public access */
|
|
||||||
public_password?: string | null;
|
public_password?: string | null;
|
||||||
/** Optional expiration date for public access (timestamptz) */
|
|
||||||
public_expiry_date?: string | null;
|
public_expiry_date?: string | null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
|
import { ShareRole } from '@/api/asset_interfaces';
|
||||||
import type { BusterCollection, BusterCollectionListItem } from '@/api/asset_interfaces/collection';
|
import type { BusterCollection, BusterCollectionListItem } from '@/api/asset_interfaces/collection';
|
||||||
|
import {
|
||||||
|
ShareDeleteRequest,
|
||||||
|
SharePostRequest,
|
||||||
|
ShareUpdateRequest
|
||||||
|
} from '@/api/asset_interfaces/shared_interfaces';
|
||||||
import mainApi from '@/api/buster_rest/instances';
|
import mainApi from '@/api/buster_rest/instances';
|
||||||
import type {
|
import type {
|
||||||
CreateCollectionParams,
|
CreateCollectionParams,
|
||||||
|
@ -33,3 +39,29 @@ export const collectionsUpdateCollection = async (params: UpdateCollectionParams
|
||||||
export const collectionsDeleteCollection = async (params: DeleteCollectionParams) => {
|
export const collectionsDeleteCollection = async (params: DeleteCollectionParams) => {
|
||||||
return await mainApi.delete<BusterCollection>('/collections', { params }).then((res) => res.data);
|
return await mainApi.delete<BusterCollection>('/collections', { params }).then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// share collections
|
||||||
|
|
||||||
|
export const shareCollection = async ({ id, params }: { id: string; params: SharePostRequest }) => {
|
||||||
|
return mainApi
|
||||||
|
.post<BusterCollection>(`/collections/${id}/sharing`, params)
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const unshareCollection = async ({ id, data }: { id: string; data: ShareDeleteRequest }) => {
|
||||||
|
return mainApi
|
||||||
|
.delete<BusterCollection>(`/collections/${id}/sharing`, { data })
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateCollectionShare = async ({
|
||||||
|
data,
|
||||||
|
id
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
data: ShareUpdateRequest;
|
||||||
|
}) => {
|
||||||
|
return mainApi
|
||||||
|
.put<BusterCollection>(`/collections/${id}/sharing`, { data })
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
|
@ -4,7 +4,10 @@ import {
|
||||||
dashboardsGetDashboard,
|
dashboardsGetDashboard,
|
||||||
dashboardsCreateDashboard,
|
dashboardsCreateDashboard,
|
||||||
dashboardsUpdateDashboard,
|
dashboardsUpdateDashboard,
|
||||||
dashboardsDeleteDashboard
|
dashboardsDeleteDashboard,
|
||||||
|
shareDashboard,
|
||||||
|
updateDashboardShare,
|
||||||
|
unshareDashboard
|
||||||
} from './requests';
|
} from './requests';
|
||||||
import type { DashboardsListRequest } from '@/api/request_interfaces/dashboards/interfaces';
|
import type { DashboardsListRequest } from '@/api/request_interfaces/dashboards/interfaces';
|
||||||
import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
|
import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
|
||||||
|
@ -228,3 +231,76 @@ export const useRemoveItemFromDashboard = () => {
|
||||||
mutationFn
|
mutationFn
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useShareDashboard = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: shareDashboard,
|
||||||
|
onMutate: (variables) => {
|
||||||
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(variables.id).queryKey;
|
||||||
|
queryClient.setQueryData(queryKey, (previousData) => {
|
||||||
|
return create(previousData!, (draft) => {
|
||||||
|
draft.individual_permissions?.push(...variables.params);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (data) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id).queryKey,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUnshareDashboard = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: unshareDashboard,
|
||||||
|
onMutate: (variables) => {
|
||||||
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(variables.id).queryKey;
|
||||||
|
queryClient.setQueryData(queryKey, (previousData) => {
|
||||||
|
return create(previousData!, (draft) => {
|
||||||
|
draft.individual_permissions =
|
||||||
|
draft.individual_permissions?.filter((t) => !variables.data.includes(t.email)) || [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (data) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id).queryKey,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateDashboardShare = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: updateDashboardShare,
|
||||||
|
onMutate: (variables) => {
|
||||||
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(variables.id).queryKey;
|
||||||
|
queryClient.setQueryData(queryKey, (previousData) => {
|
||||||
|
return create(previousData!, (draft) => {
|
||||||
|
draft.individual_permissions =
|
||||||
|
draft.individual_permissions!.map((t) => {
|
||||||
|
const found = variables.data.users?.find((v) => v.email === t.email);
|
||||||
|
if (found) return found;
|
||||||
|
return t;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
if (variables.data.publicly_accessible !== undefined) {
|
||||||
|
draft.publicly_accessible = variables.data.publicly_accessible;
|
||||||
|
}
|
||||||
|
if (variables.data.public_password !== undefined) {
|
||||||
|
draft.public_password = variables.data.public_password;
|
||||||
|
}
|
||||||
|
if (variables.data.public_expiry_date !== undefined) {
|
||||||
|
draft.public_expiry_date = variables.data.public_expiry_date;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -9,6 +9,12 @@ import type {
|
||||||
BusterDashboardListItem,
|
BusterDashboardListItem,
|
||||||
BusterDashboardResponse
|
BusterDashboardResponse
|
||||||
} from '@/api/asset_interfaces/dashboard';
|
} from '@/api/asset_interfaces/dashboard';
|
||||||
|
import { ShareRole } from '@/api/asset_interfaces';
|
||||||
|
import {
|
||||||
|
ShareDeleteRequest,
|
||||||
|
SharePostRequest,
|
||||||
|
ShareUpdateRequest
|
||||||
|
} from '@/api/asset_interfaces/shared_interfaces';
|
||||||
|
|
||||||
export const dashboardsGetList = async (params: DashboardsListRequest) => {
|
export const dashboardsGetList = async (params: DashboardsListRequest) => {
|
||||||
return await mainApi
|
return await mainApi
|
||||||
|
@ -35,3 +41,29 @@ export const dashboardsUpdateDashboard = async (params: DashboardUpdateRequest)
|
||||||
export const dashboardsDeleteDashboard = async ({ ids }: { ids: string[] }) => {
|
export const dashboardsDeleteDashboard = async ({ ids }: { ids: string[] }) => {
|
||||||
return await mainApi.delete<null>(`/dashboards`, { data: { ids } }).then((res) => res.data);
|
return await mainApi.delete<null>(`/dashboards`, { data: { ids } }).then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// share dashboards
|
||||||
|
|
||||||
|
export const shareDashboard = async ({ id, params }: { id: string; params: SharePostRequest }) => {
|
||||||
|
return mainApi
|
||||||
|
.post<BusterDashboardResponse>(`/dashboards/${id}/sharing`, params)
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const unshareDashboard = async ({ id, data }: { id: string; data: ShareDeleteRequest }) => {
|
||||||
|
return mainApi
|
||||||
|
.delete<BusterDashboardResponse>(`/dashboards/${id}/sharing`, { data })
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateDashboardShare = async ({
|
||||||
|
data,
|
||||||
|
id
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
data: ShareUpdateRequest;
|
||||||
|
}) => {
|
||||||
|
return mainApi
|
||||||
|
.put<BusterDashboardResponse>(`/dashboards/${id}/sharing`, { data })
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type { BusterChartConfigProps } from '@/api/asset_interfaces/metric';
|
import type { BusterChartConfigProps } from '@/api/asset_interfaces/metric';
|
||||||
import type { VerificationStatus } from '@/api/asset_interfaces/share';
|
import type { VerificationStatus } from '@/api/asset_interfaces/share';
|
||||||
import type { ShareRequest } from '@/api/asset_interfaces/shared_interfaces';
|
|
||||||
|
|
||||||
export interface GetMetricParams {
|
export interface GetMetricParams {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -42,4 +41,4 @@ export type UpdateMetricParams = {
|
||||||
status?: VerificationStatus;
|
status?: VerificationStatus;
|
||||||
/** file in yaml format to update */
|
/** file in yaml format to update */
|
||||||
file?: string;
|
file?: string;
|
||||||
} & ShareRequest;
|
};
|
||||||
|
|
|
@ -6,6 +6,12 @@ import type {
|
||||||
BusterMetricData,
|
BusterMetricData,
|
||||||
BusterMetricListItem
|
BusterMetricListItem
|
||||||
} from '@/api/asset_interfaces/metric';
|
} from '@/api/asset_interfaces/metric';
|
||||||
|
import { ShareRole } from '@/api/asset_interfaces/share';
|
||||||
|
import {
|
||||||
|
ShareDeleteRequest,
|
||||||
|
SharePostRequest,
|
||||||
|
ShareUpdateRequest
|
||||||
|
} from '@/api/asset_interfaces/shared_interfaces';
|
||||||
|
|
||||||
export const getMetric = async ({ id, password, version_number }: GetMetricParams) => {
|
export const getMetric = async ({ id, password, version_number }: GetMetricParams) => {
|
||||||
return mainApi
|
return mainApi
|
||||||
|
@ -56,3 +62,23 @@ export const duplicateMetric = async (params: {
|
||||||
}) => {
|
}) => {
|
||||||
return mainApi.post<BusterMetric>(`/metrics/duplicate`, params).then((res) => res.data);
|
return mainApi.post<BusterMetric>(`/metrics/duplicate`, params).then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// share metrics
|
||||||
|
|
||||||
|
export const shareMetric = async ({ id, params }: { id: string; params: SharePostRequest }) => {
|
||||||
|
return mainApi.post<BusterMetric>(`/metrics/${id}/sharing`, params).then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const unshareMetric = async ({ id, data }: { id: string; data: ShareDeleteRequest }) => {
|
||||||
|
return mainApi.delete<BusterMetric>(`/metrics/${id}/sharing`, { data }).then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateMetricShare = async ({
|
||||||
|
params,
|
||||||
|
id
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
params: ShareUpdateRequest;
|
||||||
|
}) => {
|
||||||
|
return mainApi.put<BusterMetric>(`/metrics/${id}/sharing`, { params }).then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import type { ShareRequest } from '@/api/asset_interfaces/shared_interfaces';
|
|
||||||
import type { ShareAssetType } from '../../asset_interfaces';
|
import type { ShareAssetType } from '../../asset_interfaces';
|
||||||
|
|
||||||
export interface GetCollectionListParams {
|
export interface GetCollectionListParams {
|
||||||
|
@ -41,7 +40,7 @@ export type UpdateCollectionParams = {
|
||||||
/** Share request parameters */
|
/** Share request parameters */
|
||||||
share_with?: string[];
|
share_with?: string[];
|
||||||
share_type?: string;
|
share_type?: string;
|
||||||
} & ShareRequest;
|
};
|
||||||
|
|
||||||
export interface DeleteCollectionParams {
|
export interface DeleteCollectionParams {
|
||||||
/** Array of collection IDs to be deleted */
|
/** Array of collection IDs to be deleted */
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type { DashboardConfig } from '@/api/asset_interfaces/dashboard';
|
import type { DashboardConfig } from '@/api/asset_interfaces/dashboard';
|
||||||
import { VerificationStatus } from '@/api/asset_interfaces/share';
|
import { VerificationStatus } from '@/api/asset_interfaces/share';
|
||||||
import type { ShareRequest } from '@/api/asset_interfaces/shared_interfaces';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for dashboard list request parameters
|
* Interface for dashboard list request parameters
|
||||||
|
@ -68,7 +67,7 @@ export type DashboardUpdateRequest = {
|
||||||
metrics?: string[];
|
metrics?: string[];
|
||||||
/** The file content of the dashboard */
|
/** The file content of the dashboard */
|
||||||
file?: string;
|
file?: string;
|
||||||
} & ShareRequest;
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for deleting dashboards
|
* Interface for deleting dashboards
|
||||||
|
|
|
@ -3,16 +3,11 @@ import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
||||||
|
|
||||||
export default async function MetricPage(props: {
|
export default async function MetricPage(props: {
|
||||||
params: Promise<{ metricId: string }>;
|
params: Promise<{ metricId: string }>;
|
||||||
|
|
||||||
searchParams: Promise<{ embed?: string }>;
|
searchParams: Promise<{ embed?: string }>;
|
||||||
}) {
|
}) {
|
||||||
const searchParams = await props.searchParams;
|
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const { embed } = searchParams;
|
|
||||||
const { metricId } = params;
|
const { metricId } = params;
|
||||||
|
|
||||||
const embedView = embed === 'true';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppAssetCheckLayout assetId={metricId} type="metric">
|
<AppAssetCheckLayout assetId={metricId} type="metric">
|
||||||
<MetricController metricId={metricId} />
|
<MetricController metricId={metricId} />
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
import { Avatar } from '@/components/ui/avatar';
|
import { Avatar } from '@/components/ui/avatar';
|
||||||
import { AccessDropdown } from './AccessDropdown';
|
import { AccessDropdown } from './AccessDropdown';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ShareRole } from '@/api/asset_interfaces';
|
import { ShareRole } from '@/api/asset_interfaces';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { Text } from '@/components/ui/typography';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
|
|
||||||
export const IndividualSharePerson: React.FC<{
|
export const IndividualSharePerson: React.FC<{
|
||||||
name: string;
|
name?: string;
|
||||||
email: string;
|
email: string;
|
||||||
role: ShareRole;
|
role: ShareRole;
|
||||||
id: string;
|
onUpdateShareRole: (email: string, role: ShareRole | null) => void;
|
||||||
onUpdateShareRole: (id: string, email: string, role: ShareRole | null) => void;
|
}> = React.memo(({ name, onUpdateShareRole, email, role }) => {
|
||||||
}> = React.memo(({ name, onUpdateShareRole, email, id, role }) => {
|
|
||||||
const isSameEmailName = name === email;
|
const isSameEmailName = name === email;
|
||||||
|
|
||||||
const onChangeShareLevel = useMemoizedFn((v: ShareRole | null) => {
|
const onChangeShareLevel = useMemoizedFn((v: ShareRole | null) => {
|
||||||
onUpdateShareRole(id, email, v);
|
onUpdateShareRole(email, v);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -9,16 +9,15 @@ import { AccessDropdown } from './AccessDropdown';
|
||||||
import { IndividualSharePerson } from './IndividualSharePerson';
|
import { IndividualSharePerson } from './IndividualSharePerson';
|
||||||
import { ShareMenuContentEmbed } from './ShareMenuContentEmbed';
|
import { ShareMenuContentEmbed } from './ShareMenuContentEmbed';
|
||||||
import { ShareMenuContentPublish } from './ShareMenuContentPublish';
|
import { ShareMenuContentPublish } from './ShareMenuContentPublish';
|
||||||
import { ShareWithGroupAndTeam } from './ShareWithTeamAndGroup';
|
|
||||||
import { ShareMenuTopBarOptions } from './ShareMenuTopBar';
|
import { ShareMenuTopBarOptions } from './ShareMenuTopBar';
|
||||||
import { useUserConfigContextSelector } from '@/context/Users';
|
import { useUserConfigContextSelector } from '@/context/Users';
|
||||||
import { inputHasText } from '@/lib/text';
|
import { inputHasText } from '@/lib/text';
|
||||||
import { UserGroup, ChevronRight } from '@/components/ui/icons';
|
import { UserGroup, ChevronRight } from '@/components/ui/icons';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import type { ShareRequest } from '@/api/asset_interfaces/shared_interfaces';
|
|
||||||
import { useUpdateCollection } from '@/api/buster_rest/collections';
|
import { useUpdateCollection } from '@/api/buster_rest/collections';
|
||||||
import { useSaveMetric } from '@/api/buster_rest/metrics';
|
import { useSaveMetric } from '@/api/buster_rest/metrics';
|
||||||
import { useUpdateDashboard } from '@/api/buster_rest/dashboards';
|
import { useUpdateDashboard } from '@/api/buster_rest/dashboards';
|
||||||
|
import { ShareUpdateRequest } from '@/api/asset_interfaces/shared_interfaces';
|
||||||
|
|
||||||
export const ShareMenuContentBody: React.FC<{
|
export const ShareMenuContentBody: React.FC<{
|
||||||
selectedOptions: ShareMenuTopBarOptions;
|
selectedOptions: ShareMenuTopBarOptions;
|
||||||
|
@ -44,8 +43,6 @@ export const ShareMenuContentBody: React.FC<{
|
||||||
|
|
||||||
const selectedClass = selectedOptions === ShareMenuTopBarOptions.Share ? '' : '';
|
const selectedClass = selectedOptions === ShareMenuTopBarOptions.Share ? '' : '';
|
||||||
const individual_permissions = shareAssetConfig.individual_permissions;
|
const individual_permissions = shareAssetConfig.individual_permissions;
|
||||||
const team_permissions = shareAssetConfig.team_permissions;
|
|
||||||
const organization_permissions = shareAssetConfig.organization_permissions;
|
|
||||||
const publicly_accessible = shareAssetConfig.publicly_accessible;
|
const publicly_accessible = shareAssetConfig.publicly_accessible;
|
||||||
const publicExpirationDate = shareAssetConfig.public_expiry_date;
|
const publicExpirationDate = shareAssetConfig.public_expiry_date;
|
||||||
const password = shareAssetConfig.public_password;
|
const password = shareAssetConfig.public_password;
|
||||||
|
@ -57,8 +54,6 @@ export const ShareMenuContentBody: React.FC<{
|
||||||
goBack={goBack}
|
goBack={goBack}
|
||||||
onCopyLink={onCopyLink}
|
onCopyLink={onCopyLink}
|
||||||
individual_permissions={individual_permissions}
|
individual_permissions={individual_permissions}
|
||||||
team_permissions={team_permissions}
|
|
||||||
organization_permissions={organization_permissions}
|
|
||||||
publicly_accessible={publicly_accessible}
|
publicly_accessible={publicly_accessible}
|
||||||
publicExpirationDate={publicExpirationDate}
|
publicExpirationDate={publicExpirationDate}
|
||||||
password={password}
|
password={password}
|
||||||
|
@ -90,7 +85,6 @@ const ShareMenuContentShare: React.FC<{
|
||||||
ShareRole.CAN_VIEW
|
ShareRole.CAN_VIEW
|
||||||
);
|
);
|
||||||
const disableSubmit = !inputHasText(inputValue) || !validate(inputValue);
|
const disableSubmit = !inputHasText(inputValue) || !validate(inputValue);
|
||||||
const hasUserTeams = userTeams?.length > 0;
|
|
||||||
const hasIndividualPermissions = !!individual_permissions?.length;
|
const hasIndividualPermissions = !!individual_permissions?.length;
|
||||||
|
|
||||||
const onSubmitNewEmail = useMemoizedFn(async () => {
|
const onSubmitNewEmail = useMemoizedFn(async () => {
|
||||||
|
@ -122,20 +116,17 @@ const ShareMenuContentShare: React.FC<{
|
||||||
setInputValue('');
|
setInputValue('');
|
||||||
});
|
});
|
||||||
|
|
||||||
const onUpdateShareRole = useMemoizedFn(
|
const onUpdateShareRole = useMemoizedFn(async (email: string, role: ShareRole | null) => {
|
||||||
async (userId: string, email: string, role: ShareRole | null) => {
|
if (role) {
|
||||||
const payload: ShareRequest = { id: assetId };
|
const payload: ShareUpdateRequest & { id: string } = {
|
||||||
|
id: assetId,
|
||||||
if (!role) {
|
users: [
|
||||||
payload.remove_users = [userId];
|
|
||||||
} else {
|
|
||||||
payload.user_permissions = [
|
|
||||||
{
|
{
|
||||||
user_email: email,
|
email,
|
||||||
role
|
role
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
}
|
};
|
||||||
if (assetType === ShareAssetType.METRIC) {
|
if (assetType === ShareAssetType.METRIC) {
|
||||||
await onShareMetric(payload);
|
await onShareMetric(payload);
|
||||||
} else if (assetType === ShareAssetType.DASHBOARD) {
|
} else if (assetType === ShareAssetType.DASHBOARD) {
|
||||||
|
@ -143,8 +134,9 @@ const ShareMenuContentShare: React.FC<{
|
||||||
} else if (assetType === ShareAssetType.COLLECTION) {
|
} else if (assetType === ShareAssetType.COLLECTION) {
|
||||||
await onShareCollection(payload);
|
await onShareCollection(payload);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
const onChangeInputValue = useMemoizedFn((e: React.ChangeEvent<HTMLInputElement>) => {
|
const onChangeInputValue = useMemoizedFn((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setInputValue(e.target.value);
|
setInputValue(e.target.value);
|
||||||
|
@ -195,7 +187,7 @@ const ShareMenuContentShare: React.FC<{
|
||||||
<div className="flex flex-col space-y-2 overflow-hidden px-3">
|
<div className="flex flex-col space-y-2 overflow-hidden px-3">
|
||||||
{individual_permissions?.map((permission) => (
|
{individual_permissions?.map((permission) => (
|
||||||
<IndividualSharePerson
|
<IndividualSharePerson
|
||||||
key={permission.id}
|
key={permission.email}
|
||||||
{...permission}
|
{...permission}
|
||||||
onUpdateShareRole={onUpdateShareRole}
|
onUpdateShareRole={onUpdateShareRole}
|
||||||
/>
|
/>
|
||||||
|
@ -241,8 +233,6 @@ const ContentRecord: Record<
|
||||||
goBack: () => void;
|
goBack: () => void;
|
||||||
onCopyLink: () => void;
|
onCopyLink: () => void;
|
||||||
individual_permissions: BusterShare['individual_permissions'];
|
individual_permissions: BusterShare['individual_permissions'];
|
||||||
team_permissions: BusterShare['team_permissions'];
|
|
||||||
organization_permissions: BusterShare['organization_permissions'];
|
|
||||||
publicly_accessible: boolean;
|
publicly_accessible: boolean;
|
||||||
publicExpirationDate: string | null | undefined;
|
publicExpirationDate: string | null | undefined;
|
||||||
password: string | null | undefined;
|
password: string | null | undefined;
|
||||||
|
@ -253,6 +243,5 @@ const ContentRecord: Record<
|
||||||
> = {
|
> = {
|
||||||
Share: ShareMenuContentShare,
|
Share: ShareMenuContentShare,
|
||||||
Embed: ShareMenuContentEmbed,
|
Embed: ShareMenuContentEmbed,
|
||||||
ShareWithGroupAndTeam: ShareWithGroupAndTeam,
|
|
||||||
Publish: ShareMenuContentPublish
|
Publish: ShareMenuContentPublish
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,8 +8,7 @@ import { type SegmentedItem } from '@/components/ui/segmented';
|
||||||
export enum ShareMenuTopBarOptions {
|
export enum ShareMenuTopBarOptions {
|
||||||
Share = 'Share',
|
Share = 'Share',
|
||||||
Publish = 'Publish',
|
Publish = 'Publish',
|
||||||
Embed = 'Embed',
|
Embed = 'Embed'
|
||||||
ShareWithGroupAndTeam = 'ShareWithGroupAndTeam'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShareMenuTopBar: React.FC<{
|
export const ShareMenuTopBar: React.FC<{
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { CopyLinkButton } from './CopyLinkButton';
|
|
||||||
import { BackButton, Button } from '@/components/ui/buttons';
|
|
||||||
import { AccessDropdown } from './AccessDropdown';
|
|
||||||
import { useUserConfigContextSelector } from '@/context/Users';
|
|
||||||
import { ShareRole } from '@/api/asset_interfaces';
|
|
||||||
import { useMemoizedFn } from '@/hooks';
|
|
||||||
import { Text } from '@/components/ui/typography';
|
|
||||||
import { UserGroup } from '@/components/ui/icons';
|
|
||||||
import { ShareAssetType } from '@/api/asset_interfaces';
|
|
||||||
import type { ShareRequest } from '@/api/asset_interfaces/shared_interfaces';
|
|
||||||
import { useGetCollection, useUpdateCollection } from '@/api/buster_rest/collections';
|
|
||||||
import { useGetMetric, useSaveMetric } from '@/api/buster_rest/metrics';
|
|
||||||
import { useGetDashboard, useUpdateDashboard } from '@/api/buster_rest/dashboards';
|
|
||||||
|
|
||||||
export const ShareWithGroupAndTeam: React.FC<{
|
|
||||||
goBack: () => void;
|
|
||||||
onCopyLink: () => void;
|
|
||||||
assetType: ShareAssetType;
|
|
||||||
assetId: string;
|
|
||||||
}> = ({ assetType, assetId, goBack, onCopyLink }) => {
|
|
||||||
const userTeams = useUserConfigContextSelector((state) => state.userTeams);
|
|
||||||
const { mutateAsync: onShareDashboard } = useUpdateDashboard();
|
|
||||||
const { mutateAsync: onShareMetric } = useSaveMetric();
|
|
||||||
const { mutateAsync: onShareCollection } = useUpdateCollection();
|
|
||||||
const { data: dashboardResponse } = useGetDashboard(
|
|
||||||
assetType === ShareAssetType.DASHBOARD ? assetId : undefined
|
|
||||||
);
|
|
||||||
const { data: collection } = useGetCollection(
|
|
||||||
assetType === ShareAssetType.COLLECTION ? assetId : undefined
|
|
||||||
);
|
|
||||||
const { data: metric } = useGetMetric(
|
|
||||||
assetType === ShareAssetType.METRIC ? { id: assetId } : { id: undefined }
|
|
||||||
);
|
|
||||||
|
|
||||||
const onUpdateShareRole = useMemoizedFn(
|
|
||||||
async ({ teamId, role }: { teamId: string; role: ShareRole | null }) => {
|
|
||||||
const payload: ShareRequest = { id: assetId };
|
|
||||||
if (!role) {
|
|
||||||
payload.remove_teams = [teamId];
|
|
||||||
} else {
|
|
||||||
payload.team_permissions = [{ team_id: teamId, role }];
|
|
||||||
}
|
|
||||||
if (assetType === ShareAssetType.METRIC) {
|
|
||||||
await onShareMetric(payload);
|
|
||||||
} else if (assetType === ShareAssetType.DASHBOARD) {
|
|
||||||
await onShareDashboard(payload);
|
|
||||||
} else if (assetType === ShareAssetType.COLLECTION) {
|
|
||||||
await onShareCollection(payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const listedTeam: { id: string; name: string; role: ShareRole | null }[] = useMemo(() => {
|
|
||||||
const assosciatedPermissiongSearch = (teamId: string) => {
|
|
||||||
if (assetType === ShareAssetType.METRIC && metric) {
|
|
||||||
return metric.team_permissions?.find((t) => t.id === teamId);
|
|
||||||
} else if (assetType === ShareAssetType.DASHBOARD && dashboardResponse) {
|
|
||||||
return dashboardResponse.team_permissions?.find((t) => t.id === teamId);
|
|
||||||
} else if (assetType === ShareAssetType.COLLECTION && collection) {
|
|
||||||
return collection.team_permissions?.find((t) => t.id === teamId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return userTeams.reduce<{ id: string; name: string; role: ShareRole | null }[]>((acc, team) => {
|
|
||||||
const assosciatedPermission = assosciatedPermissiongSearch(team.id);
|
|
||||||
acc.push({
|
|
||||||
id: team.id,
|
|
||||||
name: team.name,
|
|
||||||
role: assosciatedPermission?.role || null
|
|
||||||
});
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}, [userTeams, dashboardResponse, metric, assetId, collection, assetType]);
|
|
||||||
|
|
||||||
const stuffToShow = listedTeam.length > 0 || userTeams.length === 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<div className="flex h-[40px] items-center justify-between space-x-1 px-3">
|
|
||||||
<BackButton onClick={goBack} />
|
|
||||||
<div>
|
|
||||||
<CopyLinkButton onCopyLink={onCopyLink} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<div className="">
|
|
||||||
{listedTeam.map((team) => (
|
|
||||||
<ShareOption
|
|
||||||
key={team.id}
|
|
||||||
title={userTeams.length > 1 ? team.name : 'Your team'}
|
|
||||||
role={team.role}
|
|
||||||
onUpdateShareRole={(role) => {
|
|
||||||
onUpdateShareRole({
|
|
||||||
teamId: team.id,
|
|
||||||
role
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{userTeams.length === 0 && (
|
|
||||||
<div className="flex w-full items-center justify-center p-3">
|
|
||||||
<Text variant="secondary">Not currently a member of any teams</Text>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!stuffToShow && (
|
|
||||||
<div className="flex w-full items-center justify-center p-3">
|
|
||||||
<Text variant="secondary">No teams to share with</Text>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ShareOption: React.FC<{
|
|
||||||
title: string;
|
|
||||||
onUpdateShareRole: (role: ShareRole | null) => void;
|
|
||||||
role: ShareRole | null;
|
|
||||||
}> = ({ onUpdateShareRole, title, role }) => {
|
|
||||||
return (
|
|
||||||
<div className={'flex h-[40px] cursor-pointer items-center justify-between space-x-2 px-3'}>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Button prefix={<UserGroup />} />
|
|
||||||
|
|
||||||
<Text>{title}</Text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<AccessDropdown
|
|
||||||
groupShare
|
|
||||||
shareLevel={role}
|
|
||||||
onChangeShareLevel={(v) => {
|
|
||||||
onUpdateShareRole(v);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -56,7 +56,6 @@ export const SelectChartType: React.FC<SelectChartTypeProps> = ({
|
||||||
|
|
||||||
const onSelectChartType = useMemoizedFn((chartIconType: ChartIconType) => {
|
const onSelectChartType = useMemoizedFn((chartIconType: ChartIconType) => {
|
||||||
const chartConfig = selectedChartTypeMethod(chartIconType, columnSettings);
|
const chartConfig = selectedChartTypeMethod(chartIconType, columnSettings);
|
||||||
console.log('chartConfig', chartConfig);
|
|
||||||
onUpdateMetricChartConfig({ chartConfig });
|
onUpdateMetricChartConfig({ chartConfig });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue