🕹 Manage root users
🕹 Manage root users
This commit is contained in:
@@ -175,4 +175,11 @@ export class DriveApiClient {
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static async updateLevel(companyId: string, id: string, update: any) {
|
||||
return await Api.post<any, any>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}${appendTdriveToken()}/level`,
|
||||
update,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { DriveApiClient } from '../api-client/api-client';
|
||||
import { DriveItemAtom, DriveItemChildrenAtom } from '../state/store';
|
||||
import { BrowseFilter, DriveItem, DriveItemVersion } from '../types';
|
||||
import { SharedWithMeFilterState } from '../state/shared-with-me-filter';
|
||||
import Languages from "features/global/services/languages-service";
|
||||
import Languages from 'features/global/services/languages-service';
|
||||
|
||||
/**
|
||||
* Returns the children of a drive item
|
||||
@@ -21,7 +21,7 @@ export const useDriveActions = () => {
|
||||
({ set, snapshot }) =>
|
||||
async (parentId: string) => {
|
||||
if (parentId) {
|
||||
const filter:BrowseFilter = {
|
||||
const filter: BrowseFilter = {
|
||||
company_id: companyId,
|
||||
mime_type: sharedFilter.mimeType.value,
|
||||
};
|
||||
@@ -110,5 +110,22 @@ export const useDriveActions = () => {
|
||||
[refresh],
|
||||
);
|
||||
|
||||
return { create, refresh, download, downloadZip, remove, update };
|
||||
const updateLevel = useCallback(
|
||||
async (id: string, userId: string, level: string) => {
|
||||
try {
|
||||
const updateBody = {
|
||||
company_id: companyId,
|
||||
user_id: userId,
|
||||
level: level
|
||||
}
|
||||
await DriveApiClient.updateLevel(companyId, id, updateBody);
|
||||
await refresh(id || '');
|
||||
} catch (e) {
|
||||
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_update_file'));
|
||||
}
|
||||
},
|
||||
[refresh],
|
||||
);
|
||||
|
||||
return { create, refresh, download, downloadZip, remove, update, updateLevel };
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ export const useDriveItem = (id: string) => {
|
||||
const item = useRecoilValue(DriveItemAtom(id));
|
||||
const children = useRecoilValue(DriveItemChildrenAtom(id));
|
||||
const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
|
||||
const { refresh: refreshItem, create, update: _update, remove: _remove } = useDriveActions();
|
||||
const { refresh: refreshItem, create, update: _update, updateLevel: _updateLevel, remove: _remove } = useDriveActions();
|
||||
const { uploadVersion: _uploadVersion } = useDriveUpload();
|
||||
|
||||
const refresh = useCallback(
|
||||
@@ -57,6 +57,19 @@ export const useDriveItem = (id: string) => {
|
||||
[id, setLoading, refresh, item?.item?.parent_id],
|
||||
);
|
||||
|
||||
const updateLevel = useCallback(
|
||||
async (userId: string, level: string) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await _updateLevel(id, userId, level);
|
||||
} catch (e) {
|
||||
ToasterService.error('Unable to update user access.');
|
||||
}
|
||||
setLoading(false);
|
||||
},
|
||||
[id, setLoading, refresh, item?.item?.parent_id],
|
||||
);
|
||||
|
||||
const uploadVersion = useCallback(
|
||||
async (file: File) => {
|
||||
setLoading(true);
|
||||
@@ -87,6 +100,7 @@ export const useDriveItem = (id: string) => {
|
||||
uploadVersion,
|
||||
create,
|
||||
update,
|
||||
updateLevel,
|
||||
remove,
|
||||
refresh,
|
||||
};
|
||||
|
||||
@@ -97,6 +97,28 @@ class UserAPIClientService {
|
||||
return res;
|
||||
}
|
||||
|
||||
async all(
|
||||
companyId: string,
|
||||
options?: { bufferize?: boolean; callback?: (res: UserType[]) => void },
|
||||
): Promise<UserType[]> {
|
||||
|
||||
const res = await new Promise<UserType[]>(resolve =>
|
||||
Api.post(
|
||||
`/internal/services/users/v1/users/${companyId}/all`,
|
||||
{
|
||||
include_companies: true,
|
||||
},
|
||||
(res: { resources: UserType[] }): void => {
|
||||
resolve(res.resources && res.resources.length ? res.resources : []);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (options?.callback) options.callback(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async getCurrentUserCompanies(): Promise<CompanyType[]> {
|
||||
return this.listCompanies(CurrentUser.get()?.id || '');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
import { UserType } from '@features/users/types/user';
|
||||
import UserAPIClient from '@features/users/api/user-api-client';
|
||||
|
||||
export const useUserCompanyList = (): UserType[] => {
|
||||
const companyId = useRouterCompany();
|
||||
const [users, setUsers] = useState<UserType[]>([]);
|
||||
const refresh = async () => {
|
||||
const updatedData = await UserAPIClient.all(companyId);
|
||||
setUsers(updatedData);
|
||||
};
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, []);
|
||||
return users;
|
||||
};
|
||||
@@ -30,6 +30,7 @@ import { CreateModalAtom } from './modals/create';
|
||||
import { PropertiesModal } from './modals/properties';
|
||||
import { AccessModal } from './modals/update-access';
|
||||
import { VersionsModal } from './modals/versions';
|
||||
import { UsersModal } from './modals/manage-users';
|
||||
import { SharedFilesTable } from './shared-files-table';
|
||||
import useRouteState from 'app/features/router/hooks/use-route-state';
|
||||
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
|
||||
@@ -59,6 +60,7 @@ export default memo(
|
||||
}) => {
|
||||
const { user } = useCurrentUser();
|
||||
const companyId = useRouterCompany();
|
||||
const role = user ? (user?.companies || []).find(company => company?.company.id === companyId)?.role : "member";
|
||||
setTdriveTabToken(tdriveTabContextToken || null);
|
||||
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
|
||||
const { viewId } = useRouteState();
|
||||
@@ -189,6 +191,7 @@ export default memo(
|
||||
|
||||
>
|
||||
<DriveRealtimeObject id={parentId} key={parentId} />
|
||||
{role == "admin" && <UsersModal />}
|
||||
<VersionsModal />
|
||||
<AccessModal />
|
||||
<PropertiesModal />
|
||||
|
||||
@@ -8,6 +8,7 @@ import { PropertiesModalAtom } from './modals/properties';
|
||||
import { SelectorModalAtom } from './modals/selector';
|
||||
import { AccessModalAtom } from './modals/update-access';
|
||||
import { VersionsModalAtom } from './modals/versions';
|
||||
import { UsersModalAtom } from './modals/manage-users';
|
||||
import { DriveApiClient, getPublicLinkToken } from '@features/drive/api-client/api-client';
|
||||
import { useDriveActions } from '@features/drive/hooks/use-drive-actions';
|
||||
import { getPublicLink } from '@features/drive/hooks/use-drive-item';
|
||||
@@ -40,6 +41,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
const setVersionModal = useSetRecoilState(VersionsModalAtom);
|
||||
const setAccessModalState = useSetRecoilState(AccessModalAtom);
|
||||
const setPropertiesModalState = useSetRecoilState(PropertiesModalAtom);
|
||||
const setUsersModalState = useSetRecoilState(UsersModalAtom);
|
||||
const { open: preview } = useDrivePreview();
|
||||
|
||||
return useCallback(
|
||||
@@ -266,6 +268,13 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
);
|
||||
},
|
||||
},
|
||||
{ type: 'separator', hide: parent.item!.id != 'root', },
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.manage_users'),
|
||||
hide: parent.item!.id != 'root',
|
||||
onClick: () => setUsersModalState({ open: true }),
|
||||
},
|
||||
{ type: 'separator', hide: inTrash || parent.access === 'read' },
|
||||
{
|
||||
type: 'menu',
|
||||
@@ -301,6 +310,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
setCreationModalState,
|
||||
setVersionModal,
|
||||
setAccessModalState,
|
||||
setUsersModalState,
|
||||
setPropertiesModalState,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import Select from '@atoms/input/input-select';
|
||||
import { DriveFileAccessLevel } from '@features/drive/types';
|
||||
import Languages from 'features/global/services/languages-service';
|
||||
|
||||
export const AccessLevel = ({
|
||||
level,
|
||||
onChange,
|
||||
canRemove,
|
||||
hiddenLevels,
|
||||
className,
|
||||
}: {
|
||||
disabled?: boolean;
|
||||
level: DriveFileAccessLevel | null;
|
||||
onChange: (level: DriveFileAccessLevel & 'remove') => void;
|
||||
canRemove?: boolean;
|
||||
className?: string;
|
||||
hiddenLevels?: string[];
|
||||
}) => {
|
||||
return (
|
||||
<Select
|
||||
className={
|
||||
className +
|
||||
' w-auto ' +
|
||||
(level === 'none' ? '!text-rose-500 !bg-rose-100 dark-bg-rose-800' : '')
|
||||
}
|
||||
value={level || 'read'}
|
||||
onChange={e => onChange(e.target.value as DriveFileAccessLevel & 'remove')}
|
||||
>
|
||||
{!hiddenLevels?.includes('manage') && <option value={'manage'}>{Languages.t('common.access-level_full_acess')}</option>}
|
||||
{!hiddenLevels?.includes('read') && <option value={'read'}>{Languages.t('common.access-level_read')}</option>}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Modal } from '@atoms/modal';
|
||||
import Avatar from '@atoms/avatar';
|
||||
import { Base, Info } from '@atoms/text';
|
||||
import { atom, useRecoilState } from 'recoil';
|
||||
import { useDriveItem } from '@features/drive/hooks/use-drive-item';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
import { useCurrentUser } from '@features/users/hooks/use-current-user';
|
||||
import { useUser } from '@features/users/hooks/use-user';
|
||||
import currentUserService from '@features/users/services/current-user-service';
|
||||
import { useUserCompanyList } from '@features/users/hooks/use-user-company-list';
|
||||
import { AccessLevel } from './common';
|
||||
import Languages from 'features/global/services/languages-service';
|
||||
|
||||
export type UsersModalType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
export const UsersModalAtom = atom<UsersModalType>({
|
||||
key: 'UsersModalAtom',
|
||||
default: {
|
||||
open: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const UsersModal = () => {
|
||||
const [state, setState] = useRecoilState(UsersModalAtom);
|
||||
const userList = useUserCompanyList();
|
||||
return (
|
||||
<Modal open={state.open} onClose={() => setState({ open: false })}>
|
||||
<Base className="block mt-4 mb-1">
|
||||
{Languages.t('components.internal-manage_root_users')}
|
||||
</Base>
|
||||
<div className="rounded-md border mt-2">
|
||||
{userList?.map(user => (
|
||||
<UserAccessLevel key={user.id} id="root" userId={user?.id || ""} username={user.username} role={user?.companies?.[0]?.role || ""} />
|
||||
))}
|
||||
<div className="-mb-px" />
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const UserAccessLevel = ({
|
||||
id,
|
||||
userId,
|
||||
username,
|
||||
role
|
||||
}: {
|
||||
id: string;
|
||||
userId: string;
|
||||
username: string;
|
||||
role: string;
|
||||
}) => {
|
||||
const user = useUser(userId);
|
||||
const { user: currentUser } = useCurrentUser();
|
||||
const { item, loading, updateLevel } = useDriveItem(id);
|
||||
const level = role == "admin" ? "manage" : "read";
|
||||
|
||||
return (
|
||||
<div className="p-4 border-t flex flex-row items-center justify-center">
|
||||
<div className="shrink-0">
|
||||
<Avatar
|
||||
avatar={user?.thumbnail || ''}
|
||||
title={!user ? '-' : currentUserService.getFullName(user)}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="grow ml-2">
|
||||
<Base>{username} </Base>{' '}
|
||||
{user?.id === currentUser?.id && (
|
||||
<Info>{Languages.t('components.internal-access_specific_rules_you')}</Info>
|
||||
)}
|
||||
</div>
|
||||
<div className="shrink-0 ml-2">
|
||||
<AccessLevel
|
||||
disabled={user?.id === currentUser?.id}
|
||||
level={level}
|
||||
canRemove
|
||||
onChange={level => {
|
||||
updateLevel(user?.id || '', level);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -22,4 +22,4 @@ export default () => {
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -147,4 +147,4 @@ export default () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user