mirror of https://github.com/buster-so/buster.git
move types to server
This commit is contained in:
parent
dd01142273
commit
6d02d8900f
|
@ -9,6 +9,7 @@ export interface ListPermissionUsersResponse {
|
|||
name: string;
|
||||
email: string;
|
||||
assigned: boolean;
|
||||
avatar_url: string | null;
|
||||
}
|
||||
|
||||
export interface DatasetPermissionOverviewUser {
|
||||
|
@ -16,6 +17,7 @@ export interface DatasetPermissionOverviewUser {
|
|||
name: string;
|
||||
email: string;
|
||||
can_query: boolean;
|
||||
avatar_url: string | null;
|
||||
lineage: {
|
||||
name: string;
|
||||
id: string;
|
||||
|
|
|
@ -1,25 +1,10 @@
|
|||
export interface BusterOrganization {
|
||||
created_at: string;
|
||||
id: string;
|
||||
deleted_at: string | null;
|
||||
domain: string;
|
||||
name: string;
|
||||
updated_at: string;
|
||||
role: BusterOrganizationRole;
|
||||
}
|
||||
import type { OrganizationRole } from '@buster/server-shared/organization';
|
||||
|
||||
export enum BusterOrganizationRole {
|
||||
WORKSPACE_ADMIN = 'workspaceAdmin',
|
||||
DATA_ADMIN = 'dataAdmin',
|
||||
QUERIER = 'querier',
|
||||
RESTRICTED_QUERIER = 'restrictedQuerier',
|
||||
VIEWER = 'viewer'
|
||||
}
|
||||
|
||||
export const BusterOrganizationRoleLabels = {
|
||||
[BusterOrganizationRole.WORKSPACE_ADMIN]: 'Workspace Admin',
|
||||
[BusterOrganizationRole.DATA_ADMIN]: 'Data Admin',
|
||||
[BusterOrganizationRole.QUERIER]: 'Querier',
|
||||
[BusterOrganizationRole.RESTRICTED_QUERIER]: 'Restricted Querier',
|
||||
[BusterOrganizationRole.VIEWER]: 'Viewer'
|
||||
export const BusterOrganizationRoleLabels: Record<OrganizationRole, string> = {
|
||||
workspace_admin: 'Workspace Admin',
|
||||
data_admin: 'Data Admin',
|
||||
querier: 'Querier',
|
||||
restricted_querier: 'Restricted Querier',
|
||||
viewer: 'Viewer',
|
||||
none: 'None'
|
||||
};
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import type { BusterDatasetListItem } from '../datasets';
|
||||
import type { BusterOrganizationRole } from '../organizations';
|
||||
import type { OrganizationRole } from '@buster/server-shared/organization';
|
||||
|
||||
export interface BusterPermissionListUser {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
belongs_to: boolean;
|
||||
//only shows up with no filters. To lazy to type this out better
|
||||
team_role?: BusterOrganizationRole;
|
||||
team_role?: OrganizationRole;
|
||||
//???
|
||||
team_count: number;
|
||||
permission_group_count: number;
|
||||
|
@ -18,7 +18,7 @@ export interface BusterPermissionUser {
|
|||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
created_at: string;
|
||||
permission_group_count: number;
|
||||
permission_groups: {
|
||||
|
@ -33,7 +33,7 @@ export interface BusterPermissionUser {
|
|||
id: string;
|
||||
member_count: number;
|
||||
name: string;
|
||||
team_role: BusterOrganizationRole;
|
||||
team_role: OrganizationRole;
|
||||
}[];
|
||||
updated_at: string;
|
||||
edit_sql: boolean;
|
||||
|
@ -49,7 +49,7 @@ export interface BusterPermissionListTeam {
|
|||
name: string;
|
||||
member_count: number;
|
||||
permission_group_count: number;
|
||||
team_role: BusterOrganizationRole;
|
||||
team_role: OrganizationRole;
|
||||
belongs_to: boolean;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ export interface BusterPermissionTeam {
|
|||
email: string;
|
||||
id: string;
|
||||
name: string;
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
}[];
|
||||
permission_groups: {
|
||||
dataset_count: number;
|
||||
|
@ -113,7 +113,7 @@ export interface BusterPermissionGroup {
|
|||
id: string;
|
||||
member_count: number;
|
||||
name: string;
|
||||
team_role: BusterOrganizationRole;
|
||||
team_role: OrganizationRole;
|
||||
}[];
|
||||
updated_by: string;
|
||||
user_count: number;
|
||||
|
@ -124,10 +124,10 @@ export interface BusterPermissionListUser {
|
|||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
belongs_to: boolean;
|
||||
//only shows up with no filters. To lazy to type this out better
|
||||
team_role?: BusterOrganizationRole;
|
||||
team_role?: OrganizationRole;
|
||||
//???
|
||||
team_count: number;
|
||||
permission_group_count: number;
|
||||
|
@ -137,7 +137,7 @@ export interface BusterPermissionUser {
|
|||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
created_at: string;
|
||||
permission_group_count: number;
|
||||
permission_groups: {
|
||||
|
@ -152,7 +152,7 @@ export interface BusterPermissionUser {
|
|||
id: string;
|
||||
member_count: number;
|
||||
name: string;
|
||||
team_role: BusterOrganizationRole;
|
||||
team_role: OrganizationRole;
|
||||
}[];
|
||||
updated_at: string;
|
||||
edit_sql: boolean;
|
||||
|
@ -168,7 +168,7 @@ export interface BusterPermissionListTeam {
|
|||
name: string;
|
||||
member_count: number;
|
||||
permission_group_count: number;
|
||||
team_role: BusterOrganizationRole;
|
||||
team_role: OrganizationRole;
|
||||
belongs_to: boolean;
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ export interface BusterPermissionTeam {
|
|||
email: string;
|
||||
id: string;
|
||||
name: string;
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
}[];
|
||||
permission_groups: {
|
||||
dataset_count: number;
|
||||
|
@ -232,7 +232,7 @@ export interface BusterPermissionGroup {
|
|||
id: string;
|
||||
member_count: number;
|
||||
name: string;
|
||||
team_role: BusterOrganizationRole;
|
||||
team_role: OrganizationRole;
|
||||
}[];
|
||||
updated_by: string;
|
||||
user_count: number;
|
||||
|
|
|
@ -16,6 +16,7 @@ export interface GetPermissionGroupUsersResponse {
|
|||
assigned: boolean;
|
||||
email: string;
|
||||
name: string;
|
||||
avatar_url: string | null;
|
||||
}
|
||||
|
||||
export interface GetPermissionGroupDatasetsResponse {
|
||||
|
|
|
@ -1,76 +1,12 @@
|
|||
import type { BusterOrganization, BusterOrganizationRole } from '../organizations';
|
||||
import type { BusterPermissionUser } from '../permission';
|
||||
import type { ShareAssetType } from '@buster/server-shared/share';
|
||||
|
||||
export interface BusterUserPalette {
|
||||
id: string;
|
||||
palette: string[];
|
||||
}
|
||||
|
||||
export enum TeamRole {
|
||||
MANAGER = 'manager',
|
||||
MEMBER = 'member',
|
||||
NONE = 'none'
|
||||
}
|
||||
|
||||
export interface BusterUserTeam {
|
||||
id: string;
|
||||
name: string;
|
||||
edit_sql: boolean;
|
||||
email_slack_enabled: boolean;
|
||||
export_assets: boolean;
|
||||
organization_id: string;
|
||||
sharing_settings: BusterPermissionUser['sharing_setting'];
|
||||
upload_csv: boolean;
|
||||
updated_at: string;
|
||||
created_at: string;
|
||||
deleted_at: string | null;
|
||||
role: TeamRole;
|
||||
}
|
||||
|
||||
export interface BusterUserFavorite {
|
||||
id: string;
|
||||
asset_type: ShareAssetType;
|
||||
index?: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type BusterUserFavoriteAsset = {
|
||||
id: string;
|
||||
ype: ShareAssetType;
|
||||
index?: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export interface BusterUser {
|
||||
config: Record<string, unknown>;
|
||||
created_at: string;
|
||||
email: string;
|
||||
favorites: BusterUserFavorite[];
|
||||
id: string;
|
||||
name: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface BusterUserResponse {
|
||||
user: BusterUser;
|
||||
teams: BusterUserTeam[];
|
||||
organizations: BusterOrganization[] | null;
|
||||
}
|
||||
|
||||
export interface BusterUserListItem {
|
||||
email: string;
|
||||
id: string;
|
||||
name: string;
|
||||
role: null;
|
||||
}
|
||||
import type { OrganizationRole } from '@buster/server-shared/organization';
|
||||
|
||||
export interface OrganizationUser {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
avatar_url: string | null;
|
||||
status: 'active' | 'inactive';
|
||||
role: BusterOrganizationRole;
|
||||
role: OrganizationRole;
|
||||
datasets: OrganizationUserDataset[];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { TeamRole } from './interfaces';
|
||||
import type { TeamRole } from '@buster/server-shared/teams';
|
||||
|
||||
export interface BusterUserDatasetGroup {
|
||||
id: string;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { BusterOrganization } from '@/api/asset_interfaces/organizations';
|
||||
import type { OrganizationUser } from '@/api/asset_interfaces/users';
|
||||
import { serverFetch } from '../../createServerInstance';
|
||||
import { mainApi } from '../instances';
|
||||
import type { Organization } from '@buster/server-shared/organization';
|
||||
|
||||
export const getOrganizationUsers = async ({
|
||||
organizationId
|
||||
|
@ -22,5 +22,5 @@ export const getOrganizationUsers_server = async ({
|
|||
};
|
||||
|
||||
export const createOrganization = async (organization: { name: string }) => {
|
||||
return mainApi.post<BusterOrganization>('/organizations', organization).then((res) => res.data);
|
||||
return mainApi.post<Organization>('/organizations', organization).then((res) => res.data);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { BusterUserTeam } from '@/api/asset_interfaces/users';
|
||||
import type { TeamListResponse, GetTeamListRequest } from '@buster/server-shared/teams';
|
||||
import { mainApi } from '../instances';
|
||||
|
||||
export const createTeam = async (params: {
|
||||
|
@ -10,12 +10,6 @@ export const createTeam = async (params: {
|
|||
return mainApi.post<{ id: string }>('/teams', params).then((res) => res.data);
|
||||
};
|
||||
|
||||
export const getTeamsList = async (params: {
|
||||
page_size?: number;
|
||||
page?: number;
|
||||
permission_group_id?: string | null;
|
||||
user_id?: string | null;
|
||||
belongs_to?: boolean | null;
|
||||
}) => {
|
||||
return mainApi.get<BusterUserTeam[]>('/teams', { params }).then((res) => res.data);
|
||||
export const getTeamsList = async (params: GetTeamListRequest) => {
|
||||
return mainApi.get<TeamListResponse>('/teams', { params }).then((res) => res.data);
|
||||
};
|
||||
|
|
|
@ -3,9 +3,9 @@ import type {
|
|||
BusterUserDataset,
|
||||
BusterUserDatasetGroup,
|
||||
BusterUserPermissionGroup,
|
||||
BusterUserTeamListItem,
|
||||
TeamRole
|
||||
BusterUserTeamListItem
|
||||
} from '@/api/asset_interfaces/users';
|
||||
import type { TeamRole } from '@buster/server-shared/teams';
|
||||
import { serverFetch } from '../../../createServerInstance';
|
||||
import { mainApi } from '../../instances';
|
||||
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import type { ShareAssetType } from '@buster/server-shared/share';
|
||||
import type {
|
||||
BusterUserFavorite,
|
||||
BusterUserListItem,
|
||||
BusterUserResponse,
|
||||
OrganizationUser
|
||||
} from '@/api/asset_interfaces/users';
|
||||
import type { OrganizationUser } from '@/api/asset_interfaces/users';
|
||||
import { BASE_URL } from '../config';
|
||||
import { serverFetch } from '../../createServerInstance';
|
||||
import { mainApi } from '../instances';
|
||||
import type {
|
||||
UserResponse,
|
||||
UserFavoriteResponse,
|
||||
UserListResponse
|
||||
} from '@buster/server-shared/user';
|
||||
|
||||
export const getMyUserInfo = async (): Promise<BusterUserResponse> => {
|
||||
return mainApi.get<BusterUserResponse>('/users').then((response) => response.data);
|
||||
export const getMyUserInfo = async () => {
|
||||
return mainApi.get<UserResponse>('/users').then((response) => response.data);
|
||||
};
|
||||
|
||||
export const getMyUserInfo_server = async ({
|
||||
jwtToken
|
||||
}: {
|
||||
jwtToken: string | undefined;
|
||||
}): Promise<BusterUserResponse | null> => {
|
||||
}): Promise<UserResponse | null> => {
|
||||
if (!jwtToken) {
|
||||
//If Anonymous user, it will fail, so we catch the error and return undefined
|
||||
return await serverFetch<BusterUserResponse>('/users', {
|
||||
return await serverFetch<UserResponse>('/users', {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export const getMyUserInfo_server = async ({
|
|||
...errorData
|
||||
};
|
||||
}
|
||||
return response.json();
|
||||
return (await response.json()) as UserResponse;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -82,11 +82,11 @@ export const inviteUser = async ({
|
|||
//USER FAVORITES
|
||||
|
||||
export const getUserFavorites = async () => {
|
||||
return mainApi.get<BusterUserFavorite[]>('/users/favorites').then((response) => response.data);
|
||||
return mainApi.get<UserFavoriteResponse>('/users/favorites').then((response) => response.data);
|
||||
};
|
||||
|
||||
export const getUserFavorites_server = async () => {
|
||||
return serverFetch<BusterUserFavorite[]>('/users/favorites');
|
||||
return serverFetch<UserFavoriteResponse>('/users/favorites');
|
||||
};
|
||||
|
||||
export const createUserFavorite = async (
|
||||
|
@ -98,19 +98,19 @@ export const createUserFavorite = async (
|
|||
}[]
|
||||
) => {
|
||||
return mainApi
|
||||
.post<BusterUserFavorite[]>('/users/favorites', payload)
|
||||
.post<UserFavoriteResponse>('/users/favorites', payload)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
export const deleteUserFavorite = async (data: string[]) => {
|
||||
return mainApi
|
||||
.delete<BusterUserFavorite[]>('/users/favorites', { data })
|
||||
.delete<UserFavoriteResponse>('/users/favorites', { data })
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
export const updateUserFavorites = async (payload: string[]) => {
|
||||
return mainApi
|
||||
.put<BusterUserFavorite[]>('/users/favorites', payload)
|
||||
.put<UserFavoriteResponse>('/users/favorites', payload)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
|
@ -122,10 +122,10 @@ export const getUserList = async (payload: {
|
|||
page_size?: number;
|
||||
}) => {
|
||||
return mainApi
|
||||
.get<BusterUserListItem[]>('/users', { params: payload })
|
||||
.get<UserListResponse>('/users', { params: payload })
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
export const getUserList_server = async (payload: Parameters<typeof getUserList>[0]) => {
|
||||
return serverFetch<BusterUserListItem[]>('/users', { params: payload });
|
||||
return serverFetch<UserListResponse>('/users', { params: payload });
|
||||
};
|
||||
|
|
|
@ -3,22 +3,24 @@ import type {
|
|||
BusterUserAttribute,
|
||||
BusterUserDataset,
|
||||
BusterUserDatasetGroup,
|
||||
BusterUserFavorite,
|
||||
BusterUserListItem,
|
||||
BusterUserPermissionGroup,
|
||||
BusterUserResponse,
|
||||
BusterUserTeamListItem,
|
||||
OrganizationUser
|
||||
} from '@/api/asset_interfaces/users';
|
||||
import type {
|
||||
UserFavoriteResponse,
|
||||
UserResponse,
|
||||
UserListResponse
|
||||
} from '@buster/server-shared/user';
|
||||
|
||||
const favoritesGetList = queryOptions<BusterUserFavorite[]>({
|
||||
const favoritesGetList = queryOptions<UserFavoriteResponse>({
|
||||
queryKey: ['myself', 'list', 'favorites'] as const,
|
||||
staleTime: 1000 * 60 * 60, // 1 hour,
|
||||
initialData: [],
|
||||
initialDataUpdatedAt: 0
|
||||
});
|
||||
|
||||
const userGetUserMyself = queryOptions<BusterUserResponse | null>({
|
||||
const userGetUserMyself = queryOptions<UserResponse | null>({
|
||||
queryKey: ['myself'] as const,
|
||||
staleTime: 1000 * 60 * 60 // 1 hour
|
||||
});
|
||||
|
@ -54,7 +56,7 @@ const userGetUserDatasetGroups = (userId: string) =>
|
|||
});
|
||||
|
||||
const userGetUserList = (params: { team_id: string; page?: number; page_size?: number }) =>
|
||||
queryOptions<BusterUserListItem[]>({
|
||||
queryOptions<UserListResponse>({
|
||||
queryKey: ['users', 'list', params] as const
|
||||
});
|
||||
|
||||
|
|
|
@ -104,7 +104,12 @@ export const PermissionListUserContainer: React.FC<{
|
|||
rows={rows}
|
||||
showHeader={false}
|
||||
showSelectAll={false}
|
||||
emptyState={useMemo(() => <EmptyStateList text="No users found" />, [])}
|
||||
emptyState={useMemo(
|
||||
() => (
|
||||
<EmptyStateList text="No users found" />
|
||||
),
|
||||
[]
|
||||
)}
|
||||
/>
|
||||
</InfiniteListContainer>
|
||||
</>
|
||||
|
@ -113,7 +118,7 @@ export const PermissionListUserContainer: React.FC<{
|
|||
PermissionListUserContainer.displayName = 'PermissionListUserContainer';
|
||||
|
||||
const UserInfoCell = React.memo(({ user }: { user: DatasetPermissionOverviewUser }) => {
|
||||
return <ListUserItem name={user.name} email={user.email} />;
|
||||
return <ListUserItem name={user.name} email={user.email} avatarURL={user.avatar_url} />;
|
||||
});
|
||||
UserInfoCell.displayName = 'UserInfoCell';
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@ export const DatasetGroupUsersListContainer: React.FC<{
|
|||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
render: (name, user: GetPermissionGroupUsersResponse) => {
|
||||
return <ListUserItem name={name as string} email={user.email} />;
|
||||
return (
|
||||
<ListUserItem name={name as string} email={user.email} avatarURL={user.avatar_url} />
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -34,7 +34,7 @@ export const PermissionGroupUsersListContainer: React.FC<{
|
|||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
render: (name: string, user: GetPermissionGroupUsersResponse) => {
|
||||
return <ListUserItem name={name} email={user.email} />;
|
||||
return <ListUserItem name={name} email={user.email} avatarURL={user.avatar_url} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@ export const ListUsersComponent: React.FC<{
|
|||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
render: (name: string, user: OrganizationUser) => {
|
||||
return <ListUserItem name={name} email={user.email} />;
|
||||
return <ListUserItem name={name} email={user.email} avatarURL={user.avatar_url} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ UserHeader.displayName = 'UserHeader';
|
|||
const UserInfo: React.FC<{ user: OrganizationUser }> = ({ user }) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-3">
|
||||
<Avatar size={32} fallbackClassName="text-base" name={user.name} />
|
||||
<Avatar size={32} fallbackClassName="text-base" name={user.name} image={user.avatar_url} />
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Title as="h4">{user.name}</Title>
|
||||
<Text size="sm" variant="secondary">
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
BusterOrganizationRole,
|
||||
BusterOrganizationRoleLabels,
|
||||
type BusterUser,
|
||||
type OrganizationUser
|
||||
} from '@/api/asset_interfaces';
|
||||
import { BusterOrganizationRoleLabels, type OrganizationUser } from '@/api/asset_interfaces';
|
||||
import { useUpdateUser } from '@/api/buster_rest/users';
|
||||
import {
|
||||
Card,
|
||||
|
@ -17,11 +12,12 @@ import { Select, type SelectItem } from '@/components/ui/select';
|
|||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { User } from '@buster/server-shared/user';
|
||||
|
||||
export const UserDefaultAccess: React.FC<{
|
||||
user: OrganizationUser;
|
||||
isAdmin: boolean;
|
||||
myUser: BusterUser;
|
||||
myUser: User;
|
||||
refetchUser: () => void;
|
||||
}> = ({ user, isAdmin, myUser, refetchUser }) => {
|
||||
const { mutateAsync } = useUpdateUser();
|
||||
|
@ -45,17 +41,17 @@ export const UserDefaultAccess: React.FC<{
|
|||
};
|
||||
|
||||
const accessOptions: SelectItem<OrganizationUser['role']>[] = [
|
||||
{ label: BusterOrganizationRoleLabels.dataAdmin, value: BusterOrganizationRole.DATA_ADMIN },
|
||||
{ label: BusterOrganizationRoleLabels.data_admin, value: 'data_admin' },
|
||||
{
|
||||
label: BusterOrganizationRoleLabels.workspaceAdmin,
|
||||
value: BusterOrganizationRole.WORKSPACE_ADMIN
|
||||
label: BusterOrganizationRoleLabels.workspace_admin,
|
||||
value: 'workspace_admin'
|
||||
},
|
||||
{ label: BusterOrganizationRoleLabels.querier, value: BusterOrganizationRole.QUERIER },
|
||||
{ label: BusterOrganizationRoleLabels.querier, value: 'querier' },
|
||||
{
|
||||
label: BusterOrganizationRoleLabels.restrictedQuerier,
|
||||
value: BusterOrganizationRole.RESTRICTED_QUERIER
|
||||
label: BusterOrganizationRoleLabels.restricted_querier,
|
||||
value: 'restricted_querier'
|
||||
},
|
||||
{ label: BusterOrganizationRoleLabels.viewer, value: BusterOrganizationRole.VIEWER }
|
||||
{ label: BusterOrganizationRoleLabels.viewer, value: 'viewer' }
|
||||
];
|
||||
|
||||
const DefaultAccessCard = React.memo(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import pluralize from 'pluralize';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { BusterUserTeamListItem, TeamRole } from '@/api/asset_interfaces';
|
||||
import type { BusterUserTeamListItem } from '@/api/asset_interfaces';
|
||||
import { useUpdateUserTeams } from '@/api/buster_rest/users/permissions';
|
||||
import { PermissionAssignTeamRole } from '@/components/features/PermissionComponents';
|
||||
import {
|
||||
|
@ -11,6 +11,7 @@ import {
|
|||
EmptyStateList,
|
||||
InfiniteListContainer
|
||||
} from '@/components/ui/list';
|
||||
import type { TeamRole } from '@buster/server-shared/teams';
|
||||
import { BusterInfiniteList } from '@/components/ui/list/BusterInfiniteList';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import type { TeamRole } from '@/api/asset_interfaces';
|
||||
import type { TeamRole } from '@buster/server-shared/teams';
|
||||
import { useUpdateUserTeams } from '@/api/buster_rest/users/permissions';
|
||||
import { PermissionAssignTeamRoleButton } from '@/components/features/PermissionComponents';
|
||||
import { BusterListSelectedOptionPopupContainer } from '@/components/ui/list';
|
||||
|
|
|
@ -6,9 +6,10 @@ export const OrganizationUserStatusText: Record<OrganizationUser['status'], stri
|
|||
};
|
||||
|
||||
export const OrganizationUserRoleText: Record<OrganizationUser['role'], string> = {
|
||||
dataAdmin: 'Data Admin',
|
||||
workspaceAdmin: 'Workspace Admin',
|
||||
data_admin: 'Data Admin',
|
||||
workspace_admin: 'Workspace Admin',
|
||||
querier: 'Querier',
|
||||
restrictedQuerier: 'Restricted Querier',
|
||||
viewer: 'Viewer'
|
||||
restricted_querier: 'Restricted Querier',
|
||||
viewer: 'Viewer',
|
||||
none: 'None'
|
||||
};
|
||||
|
|
|
@ -9,14 +9,19 @@ import { SettingsPageHeader } from '../../_components/SettingsPageHeader';
|
|||
export default function ProfilePage() {
|
||||
const user = useUserConfigContextSelector((state) => state.user);
|
||||
if (!user) return null;
|
||||
const { name, email, created_at } = user;
|
||||
const { name, email, created_at, avatar_url } = user;
|
||||
return (
|
||||
<div>
|
||||
<SettingsPageHeader title="Profile" description="Manage your profile & information" />
|
||||
<div className="bg-background rounded-lg border shadow">
|
||||
{/* Header Section */}
|
||||
<div className="border-border/30 flex flex-col items-center gap-4 border-b p-6 sm:flex-row sm:items-start">
|
||||
<Avatar name={name} className="h-12 w-12" fallbackClassName="text-2xl" />
|
||||
<Avatar
|
||||
name={name}
|
||||
image={user.avatar_url}
|
||||
className="h-12 w-12"
|
||||
fallbackClassName="text-2xl"
|
||||
/>
|
||||
<div className="flex flex-col justify-center gap-1">
|
||||
<Title as="h3" className="text-foreground">
|
||||
{name}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import React from 'react';
|
||||
import { TeamRole } from '@/api/asset_interfaces';
|
||||
import { Select, type SelectItem } from '@/components/ui/select';
|
||||
import type { TeamRole } from '@buster/server-shared/teams';
|
||||
|
||||
export const TEAM_ROLE_OPTIONS: SelectItem<TeamRole>[] = [
|
||||
{
|
||||
label: 'Manager',
|
||||
value: TeamRole.MANAGER
|
||||
value: 'manager'
|
||||
},
|
||||
{
|
||||
label: 'Member',
|
||||
value: TeamRole.MEMBER
|
||||
value: 'member'
|
||||
},
|
||||
{
|
||||
label: 'Not a Member',
|
||||
value: TeamRole.NONE
|
||||
value: 'none'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import type { TeamRole } from '@/api/asset_interfaces';
|
||||
import type { TeamRole } from '@buster/server-shared/teams';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { Dropdown, type DropdownProps } from '@/components/ui/dropdown';
|
||||
import { CheckDouble } from '@/components/ui/icons';
|
||||
|
|
|
@ -9,10 +9,11 @@ export const IndividualSharePerson: React.FC<{
|
|||
name?: string;
|
||||
email: string;
|
||||
role: ShareRole;
|
||||
avatarURL: string | null;
|
||||
onUpdateShareRole: (email: string, role: ShareRole | null) => void;
|
||||
assetType: ShareAssetType;
|
||||
disabled: boolean;
|
||||
}> = React.memo(({ name, onUpdateShareRole, email, role, assetType, disabled }) => {
|
||||
}> = React.memo(({ name, onUpdateShareRole, email, avatarURL, role, assetType, disabled }) => {
|
||||
const isSameEmailName = name === email;
|
||||
|
||||
const onChangeShareLevel = useMemoizedFn((v: ShareRole | null) => {
|
||||
|
@ -25,7 +26,7 @@ export const IndividualSharePerson: React.FC<{
|
|||
data-testid={`share-person-${email}`}>
|
||||
<div className="flex h-full items-center space-x-1.5 overflow-hidden">
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<Avatar className="h-[24px] w-[24px]" name={name || email} />
|
||||
<Avatar className="h-[24px] w-[24px]" name={name || email} image={avatarURL} />
|
||||
</div>
|
||||
<div className="flex flex-col space-y-0 overflow-hidden">
|
||||
<Text truncate className="leading-1.3">
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { User } from '@supabase/supabase-js';
|
|||
import { useRouter } from 'next/navigation';
|
||||
import type React from 'react';
|
||||
import { useState } from 'react';
|
||||
import type { BusterUserResponse } from '@/api/asset_interfaces/users';
|
||||
import type { UserResponse } from '@buster/server-shared/user';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { SuccessCard } from '@/components/ui/card/SuccessCard';
|
||||
import { Input } from '@/components/ui/inputs';
|
||||
|
@ -17,7 +17,7 @@ import { PolicyCheck } from './PolicyCheck';
|
|||
|
||||
export const ResetPasswordForm: React.FC<{
|
||||
supabaseUser: User;
|
||||
busterUser: BusterUserResponse;
|
||||
busterUser: UserResponse;
|
||||
resetPassword: (d: { password: string }) => Promise<{ error: string } | undefined>;
|
||||
}> = ({ supabaseUser, busterUser, resetPassword }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
|
@ -2,22 +2,24 @@ import React from 'react';
|
|||
import { Avatar } from '@/components/ui/avatar';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
|
||||
export const ListUserItem = React.memo(({ name, email }: { name: string; email: string }) => {
|
||||
return (
|
||||
<div className="flex w-full items-center space-x-2">
|
||||
<div className="flex items-center">
|
||||
<Avatar size={24} name={name} />
|
||||
</div>
|
||||
export const ListUserItem = React.memo(
|
||||
({ name, email, avatarURL }: { name: string; email: string; avatarURL: string | null }) => {
|
||||
return (
|
||||
<div className="flex w-full items-center space-x-2">
|
||||
<div className="flex items-center">
|
||||
<Avatar size={24} name={name} image={avatarURL} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-center space-y-0">
|
||||
{name && <Text>{name}</Text>}
|
||||
{email && (
|
||||
<Text variant="secondary" style={{ fontSize: 12 }}>
|
||||
{email}
|
||||
</Text>
|
||||
)}
|
||||
<div className="flex flex-col justify-center space-y-0">
|
||||
{name && <Text>{name}</Text>}
|
||||
{email && (
|
||||
<Text variant="secondary" style={{ fontSize: 12 }}>
|
||||
{email}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
}
|
||||
);
|
||||
ListUserItem.displayName = 'ListUserItem';
|
||||
|
|
|
@ -30,7 +30,8 @@ export const SidebarUserFooter: React.FC = () => {
|
|||
const handleSignOut = useSignOut();
|
||||
if (!user) return null;
|
||||
|
||||
const { name, email } = user;
|
||||
console.log(user);
|
||||
const { name, email, avatar_url } = user;
|
||||
|
||||
if (!name || !email) return null;
|
||||
|
||||
|
@ -41,6 +42,7 @@ export const SidebarUserFooter: React.FC = () => {
|
|||
<AvatarUserButton
|
||||
username={name}
|
||||
email={email}
|
||||
avatarUrl={avatar_url}
|
||||
className={cn(COLLAPSED_HIDDEN, 'w-full')}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useParams } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
import { ShareAssetType } from '@buster/server-shared/share';
|
||||
import type { BusterUserFavorite } from '@/api/asset_interfaces/users';
|
||||
import type { UserFavorite } from '@buster/server-shared/user';
|
||||
import {
|
||||
useDeleteUserFavorite,
|
||||
useGetUserFavorites,
|
||||
|
@ -27,7 +27,7 @@ export const useFavoriteSidebarPanel = () => {
|
|||
updateUserFavorites(itemIds);
|
||||
});
|
||||
|
||||
const isAssetActive = useMemoizedFn((favorite: BusterUserFavorite) => {
|
||||
const isAssetActive = useMemoizedFn((favorite: UserFavorite) => {
|
||||
const assetType = favorite.asset_type;
|
||||
const id = favorite.id;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Tooltip } from '../tooltip/Tooltip';
|
|||
import { Avatar as AvatarBase, AvatarFallback, AvatarImage } from './AvatarBase';
|
||||
|
||||
export interface AvatarProps {
|
||||
image?: string | null;
|
||||
image: string | null | undefined;
|
||||
name?: string | null;
|
||||
className?: string;
|
||||
fallbackClassName?: string;
|
||||
|
|
|
@ -7,7 +7,7 @@ export const AvatarUserButton = React.forwardRef<
|
|||
HTMLDivElement,
|
||||
{
|
||||
username?: string;
|
||||
avatarUrl?: string;
|
||||
avatarUrl?: string | null;
|
||||
email?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import type { PostHogConfig } from 'posthog-js';
|
|||
import posthog from 'posthog-js';
|
||||
import { PostHogProvider } from 'posthog-js/react';
|
||||
import React, { type PropsWithChildren, useEffect } from 'react';
|
||||
import type { BusterUserTeam } from '@/api/asset_interfaces';
|
||||
import { isDev } from '@/config';
|
||||
import { useUserConfigContextSelector } from '../Users';
|
||||
import type { Team } from '@buster/server-shared/teams';
|
||||
|
||||
const POSTHOG_KEY = process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
||||
const DEBUG_POSTHOG = false;
|
||||
|
@ -44,7 +44,7 @@ const PosthogWrapper: React.FC<PropsWithChildren> = ({ children }) => {
|
|||
const user = useUserConfigContextSelector((state) => state.user);
|
||||
const userTeams = useUserConfigContextSelector((state) => state.userTeams);
|
||||
const userOrganizations = useUserConfigContextSelector((state) => state.userOrganizations);
|
||||
const team: BusterUserTeam | undefined = userTeams?.[0];
|
||||
const team: Team | undefined = userTeams?.[0];
|
||||
|
||||
useEffect(() => {
|
||||
if (POSTHOG_KEY && !isServer && user && posthog && team) {
|
||||
|
|
|
@ -38,7 +38,7 @@ export const PermissionListUsersContainer: React.FC<{
|
|||
dataIndex: 'name',
|
||||
width: 270,
|
||||
render: (name: string, user: ListPermissionUsersResponse) => {
|
||||
return <ListUserItem name={name} email={user.email} />;
|
||||
return <ListUserItem name={name} email={user.email} avatarURL={user.avatar_url} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { fn } from '@storybook/test';
|
||||
import { BusterOrganizationRole } from '@/api/asset_interfaces/organizations';
|
||||
import { NewChatWarning } from './NewChatWarning';
|
||||
import { z } from 'zod/v4';
|
||||
import type { OrganizationRole } from '@buster/server-shared/organization';
|
||||
|
||||
const OrganizationRoleSchema: Record<OrganizationRole, string> = {
|
||||
data_admin: 'data_admin',
|
||||
workspace_admin: 'workspace_admin',
|
||||
querier: 'querier',
|
||||
restricted_querier: 'restricted_querier',
|
||||
viewer: 'viewer',
|
||||
none: 'none'
|
||||
};
|
||||
|
||||
const meta: Meta<typeof NewChatWarning> = {
|
||||
title: 'Controllers/HomePage/NewChatWarning',
|
||||
|
@ -30,7 +39,7 @@ const meta: Meta<typeof NewChatWarning> = {
|
|||
},
|
||||
userRole: {
|
||||
control: 'select',
|
||||
options: Object.values(BusterOrganizationRole),
|
||||
options: Object.values(OrganizationRoleSchema),
|
||||
description: "The user's role in the organization"
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +54,7 @@ export const NewUser: Story = {
|
|||
hasDatasets: false,
|
||||
hasDatasources: false,
|
||||
isAdmin: true,
|
||||
userRole: BusterOrganizationRole.DATA_ADMIN
|
||||
userRole: OrganizationRoleSchema.data_admin
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -55,7 +64,7 @@ export const CompleteSetup: Story = {
|
|||
hasDatasets: true,
|
||||
hasDatasources: true,
|
||||
isAdmin: true,
|
||||
userRole: BusterOrganizationRole.DATA_ADMIN
|
||||
userRole: OrganizationRoleSchema.data_admin
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -65,7 +74,7 @@ export const WorkspaceAdminCompleteSetup: Story = {
|
|||
hasDatasets: true,
|
||||
hasDatasources: true,
|
||||
isAdmin: true,
|
||||
userRole: BusterOrganizationRole.WORKSPACE_ADMIN
|
||||
userRole: OrganizationRoleSchema.workspace_admin
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -75,7 +84,7 @@ export const ViewerRole: Story = {
|
|||
hasDatasets: false,
|
||||
hasDatasources: false,
|
||||
isAdmin: false,
|
||||
userRole: BusterOrganizationRole.VIEWER
|
||||
userRole: OrganizationRoleSchema.viewer
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -84,7 +93,7 @@ export const QuerierRole: Story = {
|
|||
hasDatasets: false,
|
||||
hasDatasources: false,
|
||||
isAdmin: false,
|
||||
userRole: BusterOrganizationRole.QUERIER
|
||||
userRole: OrganizationRoleSchema.querier
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,6 +102,6 @@ export const RestrictedQuerierRole: Story = {
|
|||
hasDatasets: false,
|
||||
hasDatasources: false,
|
||||
isAdmin: false,
|
||||
userRole: BusterOrganizationRole.RESTRICTED_QUERIER
|
||||
userRole: OrganizationRoleSchema.restricted_querier
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,12 +5,10 @@ import { ArrowUpRight, CircleCheck, AlertWarning } from '@/components/ui/icons';
|
|||
import { Paragraph, Text } from '@/components/ui/typography';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import type { useNewChatWarning } from './useNewChatWarning';
|
||||
import {
|
||||
BusterOrganizationRoleLabels,
|
||||
type BusterOrganizationRole
|
||||
} from '@/api/asset_interfaces/organizations';
|
||||
import { BusterOrganizationRoleLabels } from '@/api/asset_interfaces/organizations';
|
||||
import type { OrganizationRole } from '@buster/server-shared/organization';
|
||||
|
||||
const translateRole = (role: BusterOrganizationRole) => {
|
||||
const translateRole = (role: OrganizationRole) => {
|
||||
return BusterOrganizationRoleLabels[role];
|
||||
};
|
||||
|
||||
|
@ -157,7 +155,7 @@ const SetupItem = ({ number, status, title, description, link, linkText }: Setup
|
|||
};
|
||||
|
||||
interface ContactAdminCardProps {
|
||||
userRole?: BusterOrganizationRole;
|
||||
userRole?: OrganizationRole;
|
||||
}
|
||||
|
||||
const ContactAdminCard = ({ userRole }: ContactAdminCardProps) => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { BusterOrganizationRole } from '@/api/asset_interfaces/organizations';
|
||||
import type { BusterUserResponse } from '@/api/asset_interfaces/users';
|
||||
import type { UserResponse } from '@buster/server-shared/user';
|
||||
|
||||
export const checkIfUserIsAdmin = (userInfo?: BusterUserResponse | null): boolean => {
|
||||
export const checkIfUserIsAdmin = (userInfo?: UserResponse | null): boolean => {
|
||||
if (!userInfo) return false;
|
||||
|
||||
const userOrganization = userInfo?.organizations?.[0];
|
||||
|
@ -10,8 +9,5 @@ export const checkIfUserIsAdmin = (userInfo?: BusterUserResponse | null): boolea
|
|||
|
||||
const userRole = userOrganization.role;
|
||||
|
||||
return (
|
||||
userRole === BusterOrganizationRole.DATA_ADMIN ||
|
||||
userRole === BusterOrganizationRole.WORKSPACE_ADMIN
|
||||
);
|
||||
return userRole === 'data_admin' || userRole === 'workspace_admin';
|
||||
};
|
||||
|
|
|
@ -40,11 +40,24 @@
|
|||
"./slack": {
|
||||
"types": "./dist/slack/index.d.ts",
|
||||
"default": "./dist/slack/index.js"
|
||||
},
|
||||
"./user": {
|
||||
"types": "./dist/user/index.d.ts",
|
||||
"default": "./dist/user/index.js"
|
||||
},
|
||||
"./organization": {
|
||||
"types": "./dist/organization/index.d.ts",
|
||||
"default": "./dist/organization/index.js"
|
||||
},
|
||||
"./teams": {
|
||||
"types": "./dist/teams/index.d.ts",
|
||||
"default": "./dist/teams/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@buster/vitest-config": "workspace:*",
|
||||
"@buster/typescript-config": "workspace:*",
|
||||
"@buster/database": "workspace:*",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export * from './organization.types';
|
||||
export * from './roles.types';
|
|
@ -0,0 +1,14 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { OrganizationRoleSchema } from './roles.types';
|
||||
|
||||
export const OrganizationSchema = z.object({
|
||||
created_at: z.string(),
|
||||
id: z.string(),
|
||||
deleted_at: z.string().nullable(),
|
||||
domain: z.string(),
|
||||
name: z.string(),
|
||||
updated_at: z.string(),
|
||||
role: OrganizationRoleSchema,
|
||||
});
|
||||
|
||||
export type Organization = z.infer<typeof OrganizationSchema>;
|
|
@ -0,0 +1,6 @@
|
|||
import { userOrganizationRoleEnum } from '@buster/database';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
export const OrganizationRoleSchema = z.enum([...userOrganizationRoleEnum.enumValues, 'none']);
|
||||
|
||||
export type OrganizationRole = z.infer<typeof OrganizationRoleSchema>;
|
|
@ -1,19 +1,25 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ShareRoleSchema = z.enum([
|
||||
'owner', //owner of the asset
|
||||
'fullAccess', //same as owner, can share with others
|
||||
'canEdit', //can edit, cannot share
|
||||
'canFilter', //can filter dashboard
|
||||
'canView', //can view asset
|
||||
"owner", //owner of the asset
|
||||
"fullAccess", //same as owner, can share with others
|
||||
"canEdit", //can edit, cannot share
|
||||
"canFilter", //can filter dashboard
|
||||
"canView", //can view asset
|
||||
]);
|
||||
|
||||
export const ShareAssetTypeSchema = z.enum(['metric', 'dashboard', 'collection', 'chat']);
|
||||
export const ShareAssetTypeSchema = z.enum([
|
||||
"metric",
|
||||
"dashboard",
|
||||
"collection",
|
||||
"chat",
|
||||
]);
|
||||
|
||||
export const ShareIndividualSchema = z.object({
|
||||
email: z.string().email(),
|
||||
email: z.string(),
|
||||
role: ShareRoleSchema,
|
||||
name: z.string().optional(),
|
||||
avatar_url: z.string().nullable(),
|
||||
});
|
||||
|
||||
export const ShareConfigSchema = z.object({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from 'zod';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
// POST /api/v2/slack/auth/init
|
||||
export const InitiateOAuthSchema = z.object({
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export * from './teams.types';
|
||||
export * from './responses';
|
||||
export * from './requests';
|
|
@ -0,0 +1,18 @@
|
|||
import { z } from 'zod/v4';
|
||||
|
||||
export const CreateTeamRequestSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export type CreateTeamRequest = z.infer<typeof CreateTeamRequestSchema>;
|
||||
|
||||
export const GetTeamListRequestSchema = z.object({
|
||||
page_size: z.number().optional(),
|
||||
page: z.number().optional(),
|
||||
permission_group_id: z.string().optional(),
|
||||
user_id: z.string().optional(),
|
||||
belongs_to: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type GetTeamListRequest = z.infer<typeof GetTeamListRequestSchema>;
|
|
@ -0,0 +1,6 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { TeamSchema } from './teams.types';
|
||||
|
||||
export const TeamListResponseSchema = z.array(TeamSchema);
|
||||
|
||||
export type TeamListResponse = z.infer<typeof TeamListResponseSchema>;
|
|
@ -0,0 +1,24 @@
|
|||
import { teamRoleEnum } from '@buster/database';
|
||||
import { z } from 'zod/v4';
|
||||
import { SharingSettingSchema } from '../user/sharing-setting.types';
|
||||
|
||||
export const TeamRoleSchema = z.enum([...teamRoleEnum.enumValues, 'none']);
|
||||
|
||||
export type TeamRole = z.infer<typeof TeamRoleSchema>;
|
||||
|
||||
export const TeamSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
edit_sql: z.boolean(),
|
||||
email_slack_enabled: z.boolean(),
|
||||
export_assets: z.boolean(),
|
||||
organization_id: z.string(),
|
||||
sharing_settings: SharingSettingSchema,
|
||||
upload_csv: z.boolean(),
|
||||
updated_at: z.string(),
|
||||
created_at: z.string(),
|
||||
deleted_at: z.string().nullable(),
|
||||
role: TeamRoleSchema,
|
||||
});
|
||||
|
||||
export type Team = z.infer<typeof TeamSchema>;
|
|
@ -0,0 +1,11 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { ShareAssetTypeSchema } from '../share';
|
||||
|
||||
export const UserFavoriteSchema = z.object({
|
||||
id: z.string(),
|
||||
asset_type: ShareAssetTypeSchema,
|
||||
index: z.number().optional(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export type UserFavorite = z.infer<typeof UserFavoriteSchema>;
|
|
@ -0,0 +1,7 @@
|
|||
export * from './request.types';
|
||||
export * from './responses.types';
|
||||
export * from './users.types';
|
||||
export * from './roles.types';
|
||||
export * from '../teams/teams.types';
|
||||
export * from './sharing-setting.types';
|
||||
export * from './favorites.types';
|
|
@ -0,0 +1,49 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { OrganizationRoleSchema } from '../organization/roles.types';
|
||||
import { ShareAssetTypeSchema } from '../share';
|
||||
|
||||
export const UserRequestSchema = z.object({
|
||||
user_id: z.string(),
|
||||
});
|
||||
|
||||
export type UserRequest = z.infer<typeof UserRequestSchema>;
|
||||
|
||||
export const UserUpdateRequestSchema = z.object({
|
||||
user_id: z.string(),
|
||||
name: z.string().optional(),
|
||||
role: OrganizationRoleSchema.optional(),
|
||||
});
|
||||
|
||||
export type UserUpdateRequest = z.infer<typeof UserUpdateRequestSchema>;
|
||||
|
||||
export const UserInviteRequestSchema = z.object({
|
||||
emails: z.array(z.string().regex(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)),
|
||||
team_ids: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
export type UserInviteRequest = z.infer<typeof UserInviteRequestSchema>;
|
||||
|
||||
export const UserCreateFavoriteRequestSchema = z.object({
|
||||
id: z.string(),
|
||||
asset_type: ShareAssetTypeSchema,
|
||||
index: z.number().optional(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export type UserCreateFavoriteRequest = z.infer<typeof UserCreateFavoriteRequestSchema>;
|
||||
|
||||
export const UserDeleteFavoriteRequestSchema = z.array(z.string());
|
||||
|
||||
export type UserDeleteFavoriteRequest = z.infer<typeof UserDeleteFavoriteRequestSchema>;
|
||||
|
||||
export const UserUpdateFavoriteRequestSchema = z.array(z.string());
|
||||
|
||||
export type UserUpdateFavoriteRequest = z.infer<typeof UserUpdateFavoriteRequestSchema>;
|
||||
|
||||
export const GetUserListRequestSchema = z.object({
|
||||
team_id: z.string(),
|
||||
page: z.number().optional(),
|
||||
page_size: z.number().optional(),
|
||||
});
|
||||
|
||||
export type GetUserListRequest = z.infer<typeof GetUserListRequestSchema>;
|
|
@ -0,0 +1,27 @@
|
|||
import { z } from 'zod/v4';
|
||||
import { OrganizationSchema } from '../organization/organization.types';
|
||||
import { OrganizationRoleSchema } from '../organization/roles.types';
|
||||
import { TeamSchema } from '../teams/teams.types';
|
||||
import { UserFavoriteSchema } from './favorites.types';
|
||||
import { UserSchema } from './users.types';
|
||||
|
||||
export const UserResponseSchema = z.object({
|
||||
user: UserSchema,
|
||||
teams: z.array(TeamSchema),
|
||||
organizations: z.array(OrganizationSchema).nullable(),
|
||||
});
|
||||
|
||||
export const UserListResponseSchema = z.array(
|
||||
z.object({
|
||||
email: z.string(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
role: OrganizationRoleSchema.nullable(),
|
||||
})
|
||||
);
|
||||
|
||||
export const UserFavoriteResponseSchema = z.array(UserFavoriteSchema);
|
||||
|
||||
export type UserResponse = z.infer<typeof UserResponseSchema>;
|
||||
export type UserListResponse = z.infer<typeof UserListResponseSchema>;
|
||||
export type UserFavoriteResponse = z.infer<typeof UserFavoriteResponseSchema>;
|
|
@ -0,0 +1,6 @@
|
|||
import { userOrganizationRoleEnum } from '@buster/database';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
export const UserOrganizationRoleSchema = z.enum([...userOrganizationRoleEnum.enumValues, 'none']);
|
||||
|
||||
export type UserOrganizationRole = z.infer<typeof UserOrganizationRoleSchema>;
|
|
@ -0,0 +1,6 @@
|
|||
import { sharingSettingEnum } from '@buster/database';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
export const SharingSettingSchema = z.enum([...sharingSettingEnum.enumValues, 'none']);
|
||||
|
||||
export type SharingSetting = z.infer<typeof SharingSettingSchema>;
|
|
@ -0,0 +1,21 @@
|
|||
import { z } from "zod/v4";
|
||||
import type { UserFavorite } from "./favorites.types";
|
||||
import type { UserOrganizationRole } from "./roles.types";
|
||||
|
||||
export const UserSchema = z.object({
|
||||
attributes: z.object({
|
||||
organization_id: z.string(),
|
||||
organization_role: z.custom<UserOrganizationRole>(),
|
||||
user_email: z.string().email(),
|
||||
user_id: z.string(),
|
||||
}),
|
||||
created_at: z.string(),
|
||||
email: z.string().email(),
|
||||
favorites: z.array(z.custom<UserFavorite>()),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
avatar_url: z.string().nullable(),
|
||||
updated_at: z.string(),
|
||||
});
|
||||
|
||||
export type User = z.infer<typeof UserSchema>;
|
|
@ -766,6 +766,9 @@ importers:
|
|||
|
||||
packages/server-shared:
|
||||
dependencies:
|
||||
'@buster/database':
|
||||
specifier: workspace:*
|
||||
version: link:../database
|
||||
'@buster/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
|
|
Loading…
Reference in New Issue