mirror of https://github.com/buster-so/buster.git
create workspace settings
This commit is contained in:
parent
59e6aacd9d
commit
26d77fef53
|
@ -12,7 +12,7 @@ import {
|
|||
import { BusterInfiniteList } from '@/components/ui/list/BusterInfiniteList';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||
import { OrganizationUserRoleText } from './config';
|
||||
import { OrganizationUserRoleText } from '@/lib/organization/translations';
|
||||
|
||||
export const ListUsersComponent: React.FC<{
|
||||
users: OrganizationUser[];
|
||||
|
|
|
@ -14,9 +14,10 @@ import type {
|
|||
UpdateWorkspaceSettingsRequest
|
||||
} from '@buster/server-shared/security';
|
||||
import { Select, type SelectItem } from '@/components/ui/select';
|
||||
import { type OrganizationRole } from '@buster/server-shared/organization';
|
||||
import { OrganizationRoleEnum } from '@buster/server-shared/organization';
|
||||
import { type OrganizationRole, OrganizationRoleEnum } from '@buster/server-shared/organization';
|
||||
import { OrganizationUserRoleText } from '@/lib/organization/translations';
|
||||
import { useGetDatasets } from '@/api/buster_rest/datasets';
|
||||
import { SelectMultiple } from '@/components/ui/select/SelectMultiple';
|
||||
|
||||
export const WorkspaceRestrictions = React.memo(() => {
|
||||
const { data: workspaceSettings } = useGetWorkspaceSettings();
|
||||
|
@ -94,9 +95,9 @@ const DefaultRole = ({
|
|||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex min-w-0 flex-1 flex-col space-y-0.5">
|
||||
<Text>Restrict new user invitations</Text>
|
||||
<Text>Default Role</Text>
|
||||
<Text variant="secondary" size={'sm'}>
|
||||
{`Only allow admins to invite new members to workspace`}
|
||||
{`Select which default role is assigned to new users`}
|
||||
</Text>
|
||||
</div>
|
||||
<Select
|
||||
|
@ -117,15 +118,42 @@ const DefaultDatasets = ({
|
|||
}: Pick<GetWorkspaceSettingsResponse, 'default_datasets'> & {
|
||||
updateWorkspaceSettings: (request: UpdateWorkspaceSettingsRequest) => Promise<unknown>;
|
||||
}) => {
|
||||
const { data: datasets, isFetched: isDatasetsFetched } = useGetDatasets();
|
||||
|
||||
const items: SelectItem<string>[] = useMemo(() => {
|
||||
const baseItems =
|
||||
datasets?.map((dataset) => ({
|
||||
label: dataset.name,
|
||||
value: dataset.id
|
||||
})) || [];
|
||||
|
||||
return [{ label: 'All datasets', value: 'all' }, ...baseItems];
|
||||
}, [datasets]);
|
||||
|
||||
const selectedItems = useMemo(() => {
|
||||
return default_datasets.map((dataset) => dataset.id);
|
||||
}, [default_datasets]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Text>Restrict new user invitations</Text>
|
||||
<Text>Default Datasets</Text>
|
||||
<Text variant="secondary" size={'sm'}>
|
||||
{`Only allow admins to invite new members to workspace`}
|
||||
{`Select which datasets people can access by default`}
|
||||
</Text>
|
||||
</div>
|
||||
<></>
|
||||
<SelectMultiple
|
||||
items={items}
|
||||
value={selectedItems}
|
||||
loading={!isDatasetsFetched}
|
||||
placeholder="Select datasets"
|
||||
className="w-40 max-w-72"
|
||||
align="end"
|
||||
side="left"
|
||||
onChange={(v) => {
|
||||
updateWorkspaceSettings({ default_datasets_ids: v });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -43,6 +43,7 @@ const SelectMultipleWithHooks = () => {
|
|||
onChange={handleSelect}
|
||||
placeholder="Select multiple options..."
|
||||
value={value}
|
||||
loading={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,10 +4,11 @@ import type { VariantProps } from 'class-variance-authority';
|
|||
import React, { useMemo } from 'react';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { Dropdown, type DropdownItem } from '../dropdown/Dropdown';
|
||||
import { Dropdown, type DropdownItem, type DropdownProps } from '../dropdown/Dropdown';
|
||||
import { InputTag } from '../inputs/InputTag';
|
||||
import type { SelectItem } from './Select';
|
||||
import { selectVariants } from './SelectBase';
|
||||
import { CircleSpinnerLoader } from '../loaders';
|
||||
|
||||
interface SelectMultipleProps extends VariantProps<typeof selectVariants> {
|
||||
items: SelectItem[];
|
||||
|
@ -17,6 +18,9 @@ interface SelectMultipleProps extends VariantProps<typeof selectVariants> {
|
|||
value: string[];
|
||||
disabled?: boolean;
|
||||
useSearch?: boolean;
|
||||
loading?: boolean;
|
||||
align?: DropdownProps['align'];
|
||||
side?: DropdownProps['side'];
|
||||
}
|
||||
|
||||
export const SelectMultiple: React.FC<SelectMultipleProps> = React.memo(
|
||||
|
@ -29,6 +33,9 @@ export const SelectMultiple: React.FC<SelectMultipleProps> = React.memo(
|
|||
variant = 'default',
|
||||
value,
|
||||
disabled,
|
||||
align = 'start',
|
||||
side = 'bottom',
|
||||
loading = false,
|
||||
useSearch = true
|
||||
}) => {
|
||||
const selectedRecord = useMemo(() => {
|
||||
|
@ -76,7 +83,8 @@ export const SelectMultiple: React.FC<SelectMultipleProps> = React.memo(
|
|||
onSelect={handleSelect}
|
||||
menuHeader={useSearch ? 'Search...' : undefined}
|
||||
selectType="multiple"
|
||||
align="start"
|
||||
align={align}
|
||||
side={side}
|
||||
modal={false}
|
||||
className="w-[var(--radix-dropdown-menu-trigger-width)] max-w-full!">
|
||||
<div
|
||||
|
@ -104,6 +112,13 @@ export const SelectMultiple: React.FC<SelectMultipleProps> = React.memo(
|
|||
{selectedItems.length > 0 && (
|
||||
<div className="from-background via-background/80 pointer-events-none absolute top-0 right-0 z-10 h-full w-8 bg-gradient-to-l to-transparent" />
|
||||
)}
|
||||
{loading && (
|
||||
<div
|
||||
className="absolute top-0 right-0 flex h-full w-8 items-center justify-center"
|
||||
data-loading="true">
|
||||
<CircleSpinnerLoader size={16} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './translations';
|
|
@ -1,4 +1,3 @@
|
|||
export * from './organization.types';
|
||||
export * from './roles.types';
|
||||
export * from './user.types';
|
||||
export * from './role.enums';
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import type { OrganizationRole } from './roles.types';
|
||||
|
||||
//We need this to avoid postgres dependency in the frontend ☹️
|
||||
export const OrganizationRoleEnum: Record<OrganizationRole, OrganizationRole> = {
|
||||
none: 'none',
|
||||
viewer: 'viewer',
|
||||
workspace_admin: 'workspace_admin',
|
||||
data_admin: 'data_admin',
|
||||
querier: 'querier',
|
||||
restricted_querier: 'restricted_querier',
|
||||
};
|
|
@ -1,6 +1,19 @@
|
|||
import { userOrganizationRoleEnum } from '@buster/database';
|
||||
import type { userOrganizationRoleEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
export const OrganizationRoleSchema = z.enum([...userOrganizationRoleEnum.enumValues, 'none']);
|
||||
type OrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number] | 'none';
|
||||
|
||||
//We need this to avoid postgres dependency in the frontend ☹️
|
||||
export const OrganizationRoleEnum: Record<OrganizationRoleBase, OrganizationRoleBase> =
|
||||
Object.freeze({
|
||||
none: 'none',
|
||||
viewer: 'viewer',
|
||||
workspace_admin: 'workspace_admin',
|
||||
data_admin: 'data_admin',
|
||||
querier: 'querier',
|
||||
restricted_querier: 'restricted_querier',
|
||||
});
|
||||
|
||||
export const OrganizationRoleSchema = z.enum(Object.values(OrganizationRoleEnum));
|
||||
|
||||
export type OrganizationRole = z.infer<typeof OrganizationRoleSchema>;
|
||||
|
|
|
@ -1,30 +1,24 @@
|
|||
import { z } from "zod/v4";
|
||||
import { OrganizationRoleSchema } from "../organization";
|
||||
import { z } from 'zod/v4';
|
||||
import { OrganizationRoleSchema } from '../organization';
|
||||
|
||||
export const UpdateInviteLinkRequestSchema = z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
refresh_link: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type UpdateInviteLinkRequest = z.infer<
|
||||
typeof UpdateInviteLinkRequestSchema
|
||||
>;
|
||||
export type UpdateInviteLinkRequest = z.infer<typeof UpdateInviteLinkRequestSchema>;
|
||||
|
||||
export const AddApprovedDomainRequestSchema = z.object({
|
||||
domains: z.array(z.string()),
|
||||
});
|
||||
|
||||
export type AddApprovedDomainRequest = z.infer<
|
||||
typeof AddApprovedDomainRequestSchema
|
||||
>;
|
||||
export type AddApprovedDomainRequest = z.infer<typeof AddApprovedDomainRequestSchema>;
|
||||
|
||||
export const RemoveApprovedDomainRequestSchema = z.object({
|
||||
domains: z.array(z.string()),
|
||||
});
|
||||
|
||||
export type RemoveApprovedDomainRequest = z.infer<
|
||||
typeof RemoveApprovedDomainRequestSchema
|
||||
>;
|
||||
export type RemoveApprovedDomainRequest = z.infer<typeof RemoveApprovedDomainRequestSchema>;
|
||||
|
||||
export const UpdateWorkspaceSettingsRequestSchema = z.object({
|
||||
restrict_new_user_invitations: z.boolean().optional(),
|
||||
|
@ -38,12 +32,10 @@ export const UpdateWorkspaceSettingsRequestSchema = z.object({
|
|||
.regex(
|
||||
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
|
||||
),
|
||||
z.literal("all"),
|
||||
z.literal('all'),
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type UpdateWorkspaceSettingsRequest = z.infer<
|
||||
typeof UpdateWorkspaceSettingsRequestSchema
|
||||
>;
|
||||
export type UpdateWorkspaceSettingsRequest = z.infer<typeof UpdateWorkspaceSettingsRequestSchema>;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { z } from "zod/v4";
|
||||
import { OrganizationRoleSchema } from "../organization";
|
||||
import { z } from 'zod/v4';
|
||||
import { OrganizationRoleSchema } from '../organization';
|
||||
|
||||
export const GetInviteLinkResponseSchema = z.object({
|
||||
link: z.string(),
|
||||
|
@ -14,12 +14,9 @@ export const GetApprovedDomainsResponseSchema = z.array(
|
|||
created_at: z.string(),
|
||||
})
|
||||
);
|
||||
export const AddApprovedDomainsResponseSchema =
|
||||
GetApprovedDomainsResponseSchema;
|
||||
export const UpdateApprovedDomainsResponseSchema =
|
||||
GetApprovedDomainsResponseSchema;
|
||||
export const RemoveApprovedDomainsResponseSchema =
|
||||
GetApprovedDomainsResponseSchema;
|
||||
export const AddApprovedDomainsResponseSchema = GetApprovedDomainsResponseSchema;
|
||||
export const UpdateApprovedDomainsResponseSchema = GetApprovedDomainsResponseSchema;
|
||||
export const RemoveApprovedDomainsResponseSchema = GetApprovedDomainsResponseSchema;
|
||||
|
||||
export const GetWorkspaceSettingsResponseSchema = z.object({
|
||||
restrict_new_user_invitations: z.boolean(),
|
||||
|
@ -31,31 +28,14 @@ export const GetWorkspaceSettingsResponseSchema = z.object({
|
|||
})
|
||||
),
|
||||
});
|
||||
export const UpdateWorkspaceSettingsResponseSchema =
|
||||
GetWorkspaceSettingsResponseSchema;
|
||||
export const UpdateWorkspaceSettingsResponseSchema = GetWorkspaceSettingsResponseSchema;
|
||||
|
||||
export type RefreshInviteLinkResponse = z.infer<
|
||||
typeof RefreshInviteLinkResponseSchema
|
||||
>;
|
||||
export type UpdateInviteLinkResponse = z.infer<
|
||||
typeof UpdateInviteLinkResponseSchema
|
||||
>;
|
||||
export type RefreshInviteLinkResponse = z.infer<typeof RefreshInviteLinkResponseSchema>;
|
||||
export type UpdateInviteLinkResponse = z.infer<typeof UpdateInviteLinkResponseSchema>;
|
||||
export type GetInviteLinkResponse = z.infer<typeof GetInviteLinkResponseSchema>;
|
||||
export type GetApprovedDomainsResponse = z.infer<
|
||||
typeof GetApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type AddApprovedDomainsResponse = z.infer<
|
||||
typeof AddApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type UpdateApprovedDomainsResponse = z.infer<
|
||||
typeof UpdateApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type RemoveApprovedDomainsResponse = z.infer<
|
||||
typeof RemoveApprovedDomainsResponseSchema
|
||||
>;
|
||||
export type GetWorkspaceSettingsResponse = z.infer<
|
||||
typeof GetWorkspaceSettingsResponseSchema
|
||||
>;
|
||||
export type UpdateWorkspaceSettingsResponse = z.infer<
|
||||
typeof UpdateWorkspaceSettingsResponseSchema
|
||||
>;
|
||||
export type GetApprovedDomainsResponse = z.infer<typeof GetApprovedDomainsResponseSchema>;
|
||||
export type AddApprovedDomainsResponse = z.infer<typeof AddApprovedDomainsResponseSchema>;
|
||||
export type UpdateApprovedDomainsResponse = z.infer<typeof UpdateApprovedDomainsResponseSchema>;
|
||||
export type RemoveApprovedDomainsResponse = z.infer<typeof RemoveApprovedDomainsResponseSchema>;
|
||||
export type GetWorkspaceSettingsResponse = z.infer<typeof GetWorkspaceSettingsResponseSchema>;
|
||||
export type UpdateWorkspaceSettingsResponse = z.infer<typeof UpdateWorkspaceSettingsResponseSchema>;
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
import { teamRoleEnum } from '@buster/database';
|
||||
import type { teamRoleEnum } from '@buster/database';
|
||||
import { z } from 'zod/v4';
|
||||
import { SharingSettingSchema } from '../user/sharing-setting.types';
|
||||
|
||||
export const TeamRoleSchema = z.enum([...teamRoleEnum.enumValues, 'none']);
|
||||
type TeamRoleBase = (typeof teamRoleEnum.enumValues)[number] | 'none';
|
||||
const TeamRoleEnums: Record<TeamRoleBase, TeamRoleBase> = Object.freeze({
|
||||
none: 'none',
|
||||
manager: 'manager',
|
||||
member: 'member',
|
||||
});
|
||||
export const TeamRoleSchema = z.enum(Object.values(TeamRoleEnums));
|
||||
|
||||
export type TeamRole = z.infer<typeof TeamRoleSchema>;
|
||||
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
import { userOrganizationRoleEnum } from '@buster/database';
|
||||
import type { userOrganizationRoleEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
export const UserOrganizationRoleSchema = z.enum([...userOrganizationRoleEnum.enumValues, 'none']);
|
||||
type UserOrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number] | 'none';
|
||||
|
||||
const UserOrganizationRoleEnums: Record<UserOrganizationRoleBase, UserOrganizationRoleBase> =
|
||||
Object.freeze({
|
||||
none: 'none',
|
||||
viewer: 'viewer',
|
||||
workspace_admin: 'workspace_admin',
|
||||
data_admin: 'data_admin',
|
||||
querier: 'querier',
|
||||
restricted_querier: 'restricted_querier',
|
||||
});
|
||||
|
||||
export const UserOrganizationRoleSchema = z.enum(Object.values(UserOrganizationRoleEnums));
|
||||
|
||||
export type UserOrganizationRole = z.infer<typeof UserOrganizationRoleSchema>;
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { sharingSettingEnum } from '@buster/database';
|
||||
import type { sharingSettingEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
export const SharingSettingSchema = z.enum([...sharingSettingEnum.enumValues, 'none']);
|
||||
type SharingSettingBase = (typeof sharingSettingEnum.enumValues)[number] | 'none';
|
||||
|
||||
const SharingSettingEnums: Record<SharingSettingBase, SharingSettingBase> = Object.freeze({
|
||||
none: 'none',
|
||||
public: 'public',
|
||||
team: 'team',
|
||||
organization: 'organization',
|
||||
});
|
||||
export const SharingSettingSchema = z.enum(Object.values(SharingSettingEnums));
|
||||
|
||||
export type SharingSetting = z.infer<typeof SharingSettingSchema>;
|
||||
|
|
Loading…
Reference in New Issue